summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap3
-rw-r--r--Changelog29
-rw-r--r--README.md33
-rwxr-xr-xbin/efa163
-rw-r--r--lib/Travel/Routing/DE/EFA.pm175
-rw-r--r--lib/Travel/Routing/DE/EFA/Route.pm6
-rw-r--r--lib/Travel/Routing/DE/EFA/Route/Message.pm6
-rw-r--r--lib/Travel/Routing/DE/EFA/Route/Part.pm51
-rw-r--r--lib/Travel/Routing/DE/VRR.pm10
-rwxr-xr-xscripts/check-efa-urls11
-rw-r--r--t/20-vrr.t177
-rw-r--r--t/21-vrr.t2
12 files changed, 332 insertions, 334 deletions
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..3c96bee
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,3 @@
+Birte Kristina Friesel <derf@chaosdorf.de>
+Birte Kristina Friesel <derf@finalrewind.org>
+Birte Kristina Friesel <derf@derf.homelinux.org>
diff --git a/Changelog b/Changelog
index 4e4ce89..19ac46a 100644
--- a/Changelog
+++ b/Changelog
@@ -1,3 +1,32 @@
+Travel::Routing::DE::VRR 2.24 - Thu Nov 23 2023
+
+ * efa: Make output less horizontally dense; show arrival and departure
+ delays in-line
+ * EFA::Route::Part: Add arrival_delay and departure_delay accessors
+ * EFA::Route::Part: via: Add delay field to returned list refs
+
+Travel::Routing::DE::VRR 2.23 - Mon Oct 02 2023
+
+ * Update default API endpoint as the previously used efa.vrr.de/vrr seems
+ to be gone for good
+ * Rename VRR2 API to VRR and VRR3 to VRR2
+
+Travel::Routing::DE::VRR 2.22 - Fri Sep 01 2023
+
+ * Rewrite code not to use Perl's formerly experimental and now deprecated
+ smartmatch features
+
+Travel::Routing::DE::VRR 2.21 - Sat Jul 22 2023
+
+ * Add service VRR3
+ * Update service URLs for VVO, VRN
+ * Remove discontinued services SVV, TLEM, VBL, Verbundlinie, VOR
+
+Travel::Routing::DE::VRR 2.20 - Fri Mar 26 2021
+
+ * EFA/Route/Part: Add occupancy accessor
+ * efa: Show expected occupancy, if available
+
Travel::Routing::DE::VRR 2.19 - Sat May 02 2020
* Switch from WTFPL to Perl license (GPL 1+ or Artistic)
diff --git a/README.md b/README.md
index bc01936..bc3ba08 100644
--- a/README.md
+++ b/README.md
@@ -10,15 +10,13 @@ for details.
efa has been packaged as
[libtravel-routing-de-vrr-perl](https://packages.debian.org/search?keywords=libtravel-routing-de-vrr-perl)
for Debian, so you can install it using your package manager of choice on
-Debian-based Linux distributions. It is also available as
-[perl-travel-routing-de-vrr-git](https://aur.archlinux.org/packages/perl-travel-routing-de-vrr-git/)
-in the archlinux User Repository (AUR). Both provide the commandline client and
-the Perl module.
+Debian-based Linux distributions. The package provides the commandline client
+as well as the Perl module.
If you are using another distribution or would prefer a more recent version,
you have four installation options:
-* Nightly `.deb` builds for Debian-based distributions
+* `.deb` releases for Debian-based distributions
* Installing the latest release from CPAN
* Installation from source
* Using a Docker image
@@ -27,37 +25,20 @@ Except for Docker, **efa** is available in your PATH after installation. You
can run `efa --version` to verify this. Documentation is available via
`man efa`.
-### Nightly Builds for Debian
+### Release Builds for Debian
[lib.finalrewind.org/deb](https://lib.finalrewind.org/deb) provides Debian
-packages of both development and release versions. Note that these are not part
-of the official Debian repository and are not covered by its quality assurance
-process.
+packages of all release versions. Note that these are not part of the official
+Debian repository and are not covered by its quality assurance process.
To install the latest release, run:
```
wget https://lib.finalrewind.org/deb/libtravel-routing-de-vrr-perl_latest_all.deb
-sudo dpkg -i libtravel-routing-de-vrr-perl_latest_all.deb
-sudo apt --fix-broken install
+sudo apt install ./libtravel-routing-de-vrr-perl_latest_all.deb
rm libtravel-routing-de-vrr-perl_latest_all.deb
```
-For a (possibly broken) development snapshot of the Git master branch, run:
-
-```
-wget https://lib.finalrewind.org/deb/libtravel-routing-de-vrr-perl_dev_all.deb
-sudo dpkg -i libtravel-routing-de-vrr-perl_dev_all.deb
-sudo apt --fix-broken install
-rm libtravel-routing-de-vrr-perl_dev_all.deb
-```
-
-Note that dpkg, unlike apt, does not automatically install missing
-dependencies. If a dependency is not satisfied yet, `dpkg -i` will complain
-about unmet dependencies and bail out. `apt --fix-broken install` installs
-these dependencies and also silently finishes the Travel::Routing::DE::VRR
-installation.
-
Uninstallation works as usual:
```
diff --git a/bin/efa b/bin/efa
index 5840068..6768902 100755
--- a/bin/efa
+++ b/bin/efa
@@ -4,20 +4,18 @@ use warnings;
use 5.010;
use utf8;
-no if $] >= 5.018, warnings => 'experimental::smartmatch';
-
use utf8;
use Encode qw(decode);
use Travel::Routing::DE::EFA;
use Exception::Class;
use Getopt::Long qw/:config no_ignore_case/;
-use List::Util qw(first);
+use List::Util qw(first max);
-our $VERSION = '2.19';
+our $VERSION = '2.24';
my $ignore_info;
my $efa;
-my $efa_url = 'http://efa.vrr.de/vrr/XSLT_TRIP_REQUEST2';
+my $efa_url = 'https://app.vrr.de/vrrstd/XML_TRIP_REQUEST2';
my ( @from, @to, @via, $from_type, $to_type, $via_type );
my $opt = {
'efa-url' => \$efa_url,
@@ -32,6 +30,9 @@ my $opt = {
binmode( STDOUT, ':encoding(utf-8)' );
binmode( STDERR, ':encoding(utf-8)' );
+my $output_bold = -t STDOUT ? "\033[1m" : q{};
+my $output_reset = -t STDOUT ? "\033[0m" : q{};
+
sub show_help {
my ($exit_status) = @_;
@@ -49,7 +50,7 @@ sub new_efa_by_url {
origin => [ @from, $from_type ],
destination => [ @to, $to_type ],
- via => ( @via ? [ @via, $via_type ] : undef ),
+ via => ( @via ? [ @via, $via_type ] : undef ),
arrival_time => $opt->{arrive},
departure_time => $opt->{depart},
@@ -172,6 +173,22 @@ sub format_footpath {
return $str;
}
+sub format_delay_incl {
+ my ( $delay, $len ) = @_;
+ if ( $delay and $len ) {
+ return sprintf( "(%+${len}d)", $delay );
+ }
+ return q{};
+}
+
+sub format_delay_excl {
+ my ( $delay, $len ) = @_;
+ if ( $delay and $len ) {
+ return sprintf( " %+${len}d ", $delay );
+ }
+ return q{};
+}
+
sub display_routes {
my (@routes) = @_;
@@ -191,8 +208,19 @@ sub display_routes {
}
}
+ my $delay_len = 0;
for my $c ( $route->parts ) {
- display_connection($c);
+ if ( $c->departure_delay ) {
+ $delay_len
+ = max( $delay_len, length( $c->departure_delay ) + 1 );
+ }
+ if ( $c->arrival_delay ) {
+ $delay_len = max( $delay_len, length( $c->arrival_delay ) + 1 );
+ }
+ }
+
+ for my $c ( $route->parts ) {
+ display_connection( $c, $delay_len );
}
# last one needs to be shown separately
@@ -211,29 +239,25 @@ sub display_routes {
}
sub display_connection {
- my ($c) = @_;
+ my ( $c, $delay_len ) = @_;
+
+ my $delay_fmt = $delay_len ? $delay_len + 2 : 0;
if ( $c->is_cancelled ) {
say '# FAHRT FÄLLT AUS';
}
- elsif ( $c->delay ) {
- printf( "# +%d, Plan: %s -> %s\n",
- $c->delay, $c->departure_stime, $c->arrival_stime );
- }
- for my $note ( $c->regular_notes ) {
- my $text = $note->summary;
- if ( not( length $ignore_info and $text =~ /$ignore_info/i ) ) {
- say "# $text";
- }
- }
+ my $occupancy = q{};
- for my $notice ( $c->current_notes ) {
- if ( $notice->subtitle ne $notice->subject ) {
- printf( "# %s - %s\n", $notice->subtitle, $notice->subject );
+ if ( $c->occupancy ) {
+ if ( $c->occupancy == 1 ) {
+ $occupancy = '[ ]';
}
- else {
- printf( "# %s\n", $notice->subtitle );
+ elsif ( $c->occupancy == 2 ) {
+ $occupancy = '[* ]';
+ }
+ elsif ( $c->occupancy == 3 ) {
+ $occupancy = '[!!]';
}
}
@@ -244,21 +268,52 @@ sub display_connection {
}
printf(
- "%-5s ab %-30s %-20s %s\n",
+ "${output_bold}%s${output_reset} %s %s\n",
+ $c->train_line || $c->train_product,
+ $c->train_destination ? q{→} : q{ },
+ $c->train_destination
+ );
+
+ printf(
+ "%-5s %-${delay_fmt}s ab %-30s\n",
$c->departure_time,
+ format_delay_incl( $c->departure_delay, $delay_len ),
$c->departure_stop_and_platform,
- $c->train_line || $c->train_product,
- $c->train_destination,
);
if ( $opt->{'full-route'} ) {
for my $via_stop ( $c->via ) {
- printf( "%-5s %-30s %s\n",
- $via_stop->[1], $via_stop->[2], $via_stop->[3] );
+ printf(
+ "%-5s %-${delay_fmt}s %-30s %s\n",
+ $via_stop->[1], format_delay_excl( $via_stop->[4], $delay_len ),
+ $via_stop->[2], $via_stop->[3]
+ );
+ }
+ }
+
+ printf(
+ "%-5s %-${delay_fmt}s an %-30s %s\n",
+ $c->arrival_time,
+ format_delay_incl( $c->arrival_delay, $delay_len ),
+ $c->arrival_stop_and_platform, $occupancy
+ );
+
+ for my $notice ( $c->current_notes ) {
+ if ( $notice->subtitle ne $notice->subject ) {
+ printf( "# %s - %s\n", $notice->subtitle, $notice->subject );
+ }
+ else {
+ printf( "# %s\n", $notice->subtitle );
+ }
+ }
+
+ for my $note ( $c->regular_notes ) {
+ my $text = $note->summary;
+ if ( not( length $ignore_info and $text =~ /$ignore_info/i ) ) {
+ say "# $text";
}
}
- printf( "%-5s an %s\n", $c->arrival_time, $c->arrival_stop_and_platform, );
print "\n";
if ( $opt->{'extended-info'}
@@ -325,14 +380,23 @@ if ( $opt->{exclude} ) {
@{ $opt->{exclude} } = split( qr{,}, join( q{,}, @{ $opt->{exclude} } ) );
}
+my %accessibility_map = (
+ s => 'without_solid_stairs',
+ 'no-stairs' => 'without_solid_stairs',
+ e => 'without_escalators',
+ 'no-escalators' => 'without_escalators',
+ E => 'without_elevators',
+ 'no-elevators' => 'without_elevators',
+ l => 'with_low_platform',
+ nf => 'with_low_platform',
+ 'low-platform' => 'with_low_platform',
+ w => 'with_wheelchair',
+ wheelchair => 'with_wheelchair',
+);
+
for my $field ( @{ $opt->{accessibility} } ) {
- given ($field) {
- when ( [qw[s no-stairs]] ) { $opt->{without_solid_stairs} = 1 }
- when ( [qw[e no-escalators]] ) { $opt->{without_escalators} = 1 }
- when ( [qw[E no-elevators]] ) { $opt->{without_elevators} = 1 }
- when ( [qw[l nf low-platform]] ) { $opt->{with_low_platform} = 1 }
- when ( [qw[w wheelchair]] ) { $opt->{with_wheelchair} = 1 }
- when ( [qw[i info]] ) { } # used for ignore_info default
+ if ( $accessibility_map{$field} ) {
+ $opt->{ $accessibility_map{$field} } = 1;
}
}
@@ -390,16 +454,18 @@ for my $pair ( [ \@from, \$from_type ], [ \@via, \$via_type ],
{$+{target}}x
)
{
- given ( $+{type} ) {
- when ('addr') { ${ $pair->[1] } = 'address' }
- default { ${ $pair->[1] } = $+{type} }
+ if ( $+{type} eq 'addr' ) {
+ ${ $pair->[1] } = 'address';
+ }
+ else {
+ ${ $pair->[1] } = $+{type};
}
}
}
if ( $opt->{service} ) {
my $service = first { lc( $_->{shortname} ) eq lc( $opt->{service} ) }
- Travel::Routing::DE::EFA::get_efa_urls();
+ Travel::Routing::DE::EFA::get_efa_urls();
if ( not $service ) {
printf STDERR (
"Error: Unknown service '%s'. See 'efa -l' for a "
@@ -457,7 +523,7 @@ efa - Command line client for EFA-based public transit routing services
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -472,6 +538,11 @@ instance can be selected using B<-s> I<service> or B<-u> I<url>. It is also
possible to probe all known EFA services for a specific connection using the
B<-A> and B<-D> options.
+If available, B<efa> shows the expected occupancy of each route part.
+It ranges from C<< [ ] >> (low occupancy) to C<< [!!] >> (very high
+occupancy). Occupation data appears to be limited to certain VRR trains
+at the moment.
+
=head1 OPTIONS
=over
@@ -531,7 +602,7 @@ entry points which did not return an error.
=item B<-u>|B<--efa-url> I<url>
URL to the EFA entry point, defaults to
-L<http://efa.vrr.de/vrr/XSLT_TRIP_REQUEST2>. Depending on your location, some
+L<https://app.vrr.de/vrrstd/XML_TRIP_REQUEST2>. Depending on your location, some
I<url>s may contain more specific data than others. See
Travel::Routing::DE::EFA(3pm) and the B<-l> option for alternatives.
@@ -708,9 +779,9 @@ All vehicles must be wheelchair accessible.
0 Everything went well
1 Invalid arguments, see error message
2 Network error, unable to send request
- 3 efa.vrr.de did not return any parsable data
- 4 efa.vrr.de error: ambiguous input
- 5 efa.vrr.de error: no connections found
+ 3 EFA did not return any parsable data
+ 4 EFA error: ambiguous input
+ 5 EFA error: no connections found
10 Unknown Travel::Routing::DE::EFA error
255 Other internal error
@@ -769,7 +840,7 @@ to to request via -> to.
=head1 AUTHOR
-Copyright (C) 2009-2020 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>
+Copyright (C) 2009-2023 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
diff --git a/lib/Travel/Routing/DE/EFA.pm b/lib/Travel/Routing/DE/EFA.pm
index a1e07ff..80bfb68 100644
--- a/lib/Travel/Routing/DE/EFA.pm
+++ b/lib/Travel/Routing/DE/EFA.pm
@@ -5,9 +5,7 @@ use warnings;
use 5.010;
use utf8;
-no if $] >= 5.018, warnings => "experimental::smartmatch";
-
-use Carp qw(cluck);
+use Carp qw(cluck);
use Encode qw(encode);
use Travel::Routing::DE::EFA::Route;
use Travel::Routing::DE::EFA::Route::Message;
@@ -42,7 +40,7 @@ use Exception::Class (
},
);
-our $VERSION = '2.19';
+our $VERSION = '2.24';
sub set_time {
my ( $self, %conf ) = @_;
@@ -176,17 +174,19 @@ sub number_of_trips {
sub select_interchange_by {
my ( $self, $prefer ) = @_;
- given ($prefer) {
- when ('speed') { $self->{post}->{routeType} = 'LEASTTIME' }
- when ('waittime') { $self->{post}->{routeType} = 'LEASTINTERCHANGE' }
- when ('distance') { $self->{post}->{routeType} = 'LEASTWALKING' }
- default {
- Travel::Routing::DE::EFA::Exception::Setup->throw(
- option => 'select_interchange_by',
- have => $prefer,
- want => 'speed / waittime / distance',
- );
- }
+ if ( $prefer eq 'speed' ) { $self->{post}->{routeType} = 'LEASTTIME' }
+ elsif ( $prefer eq 'waittime' ) {
+ $self->{post}->{routeType} = 'LEASTINTERCHANGE';
+ }
+ elsif ( $prefer eq 'distance' ) {
+ $self->{post}->{routeType} = 'LEASTWALKING';
+ }
+ else {
+ Travel::Routing::DE::EFA::Exception::Setup->throw(
+ option => 'select_interchange_by',
+ have => $prefer,
+ want => 'speed / waittime / distance',
+ );
}
return;
@@ -195,17 +195,15 @@ sub select_interchange_by {
sub train_type {
my ( $self, $include ) = @_;
- given ($include) {
- when ('local') { $self->{post}->{lineRestriction} = 403 }
- when ('ic') { $self->{post}->{lineRestriction} = 401 }
- when ('ice') { $self->{post}->{lineRestriction} = 400 }
- default {
- Travel::Routing::DE::EFA::Exception::Setup->throw(
- option => 'train_type',
- have => $include,
- want => 'local / ic / ice',
- );
- }
+ if ( $include eq 'local' ) { $self->{post}->{lineRestriction} = 403 }
+ elsif ( $include eq 'ic' ) { $self->{post}->{lineRestriction} = 401 }
+ elsif ( $include eq 'ice' ) { $self->{post}->{lineRestriction} = 400 }
+ else {
+ Travel::Routing::DE::EFA::Exception::Setup->throw(
+ option => 'train_type',
+ have => $include,
+ want => 'local / ic / ice',
+ );
}
return;
@@ -229,7 +227,7 @@ sub use_near_stops {
sub walk_speed {
my ( $self, $walk_speed ) = @_;
- if ( $walk_speed ~~ [ 'normal', 'fast', 'slow' ] ) {
+ if ( $walk_speed =~ m{ ^ (?: normal | fast | slow ) $ }x ) {
$self->{post}->{changeSpeed} = $walk_speed;
}
else {
@@ -305,7 +303,7 @@ sub place {
@{ $self->{post} }{ "place_${which}", "name_${which}" } = ( $place, $stop );
- if ( $type ~~ [qw[address poi stop]] ) {
+ if ( $type =~ m{ ^ (?: address | poi | stop ) $ }x ) {
$self->{post}->{"type_${which}"} = $type;
}
@@ -660,6 +658,8 @@ sub parse_xml_part {
$e_astime //= $e_atime;
my $delay = $e_delay ? $e_delay->getAttribute('delayMinutes') : 0;
+ my $delay_arr
+ = $e_delay ? $e_delay->getAttribute('delayMinutesArr') : 0;
my ( @dep_rms, @dep_sms, @arr_rms, @arr_sms );
@@ -675,17 +675,19 @@ sub parse_xml_part {
}
my $hash = {
- delay => $delay,
departure_date => $self->itddate_str($e_ddate),
+ departure_delay => $delay,
departure_time => $self->itdtime_str($e_dtime),
departure_sdate => $self->itddate_str($e_dsdate),
departure_stime => $self->itdtime_str($e_dstime),
departure_stop => $e_dep->getAttribute('name'),
departure_platform => $e_dep->getAttribute('platformName'),
+ occupancy => $e_dep->getAttribute('occupancy'),
train_line => $e_mot->getAttribute('name'),
train_product => $e_mot->getAttribute('productName'),
train_destination => $e_mot->getAttribute('destination'),
arrival_date => $self->itddate_str($e_adate),
+ arrival_delay => $delay_arr,
arrival_time => $self->itdtime_str($e_atime),
arrival_sdate => $self->itddate_str($e_asdate),
arrival_stime => $self->itdtime_str($e_astime),
@@ -715,8 +717,8 @@ sub parse_xml_part {
$hash->{arrival_stationmaps} = \@arr_sms;
for my $ve ( $e->findnodes($xp_via) ) {
- my $e_vdate = ( $ve->findnodes($xp_date) )[-1];
- my $e_vtime = ( $ve->findnodes($xp_time) )[-1];
+ my $e_vdate = ( $ve->findnodes($xp_date) )[0];
+ my $e_vtime = ( $ve->findnodes($xp_time) )[0];
if ( not( $e_vdate and $e_vtime )
or ( $e_vdate->getAttribute('weekday') == -1 ) )
@@ -724,10 +726,13 @@ sub parse_xml_part {
next;
}
- my $name = $ve->getAttribute('name');
- my $platform = $ve->getAttribute('platformName');
+ my $name = $ve->getAttribute('name');
+ my $platform = $ve->getAttribute('platformName');
+ my $arr_delay = $ve->getAttribute('arrDelay');
- if ( $name ~~ [ $hash->{departure_stop}, $hash->{arrival_stop} ] ) {
+ if ( $name eq $hash->{departure_stop}
+ or $name eq $hash->{arrival_stop} )
+ {
next;
}
@@ -737,7 +742,8 @@ sub parse_xml_part {
$self->itddate_str($e_vdate),
$self->itdtime_str($e_vtime),
$name,
- $platform
+ $platform,
+ $arr_delay,
]
);
}
@@ -819,7 +825,7 @@ sub check_ambiguous_xml {
if ( $s_place eq 'list' ) {
Travel::Routing::DE::EFA::Exception::Ambiguous->throw(
- post_key => 'place',
+ post_key => 'place',
post_value =>
( $e_place->findnodes($xp_place_input) )[0]->textContent,
possibilities => join( q{ | },
@@ -829,7 +835,7 @@ sub check_ambiguous_xml {
}
if ( $s_name eq 'list' ) {
Travel::Routing::DE::EFA::Exception::Ambiguous->throw(
- post_key => 'name',
+ post_key => 'name',
post_value =>
( $e_name->findnodes($xp_name_input) )[0]->textContent,
possibilities => join( q{ | },
@@ -898,40 +904,11 @@ sub get_efa_urls {
name => 'Nahverkehrsgesellschaft Baden-Württemberg',
shortname => 'NVBW',
},
-
- # HTTPS not supported
- {
- url => 'http://efa.svv-info.at/sbs/XSLT_TRIP_REQUEST2',
- name => 'Salzburger Verkehrsverbund',
- shortname => 'SVV',
- },
-
- # HTTPS: invalid certificate
- {
- url =>
- 'http://www.travelineeastmidlands.co.uk/em/XSLT_TRIP_REQUEST2',
- name => 'Traveline East Midlands',
- shortname => 'TLEM',
- },
{
url => 'https://efa.vagfr.de/vagfr3/XSLT_TRIP_REQUEST2',
name => 'Freiburger Verkehrs AG',
shortname => 'VAG',
},
-
- # HTTPS: unsupported protocol
- {
- url => 'http://mobil.vbl.ch/vblmobil/XML_TRIP_REQUEST2',
- name => 'Verkehrsbetriebe Luzern',
- shortname => 'VBL',
- },
-
- # HTTPS not supported
- {
- url => 'http://fahrplan.verbundlinie.at/stv/XSLT_TRIP_REQUEST2',
- name => 'Verkehrsverbund Steiermark',
- shortname => 'Verbundlinie',
- },
{
url => 'https://efa.vgn.de/vgnExt_oeffi/XML_TRIP_REQUEST2',
name => 'Verkehrsverbund Grossraum Nuernberg',
@@ -945,32 +922,22 @@ sub get_efa_urls {
shortname => 'VMV',
},
{
- url => 'https://efa.vor.at/wvb/XSLT_TRIP_REQUEST2',
- name => 'Verkehrsverbund Ost-Region',
- shortname => 'VOR',
- encoding => 'iso-8859-15',
- },
-
- # HTTPS not spported
- {
- url => 'http://fahrplanauskunft.vrn.de/vrn/XML_TRIP_REQUEST2',
+ url => 'https://www.vrn.de/mngvrn/XML_TRIP_REQUEST2',
name => 'Verkehrsverbund Rhein-Neckar',
shortname => 'VRN',
},
{
- url => 'https://efa.vrr.de/vrr/XSLT_TRIP_REQUEST2',
+ url => 'https://app.vrr.de/vrrstd/XML_TRIP_REQUEST2',
name => 'Verkehrsverbund Rhein-Ruhr',
shortname => 'VRR',
},
{
- url => 'https://app.vrr.de/vrrstd/XML_TRIP_REQUEST2',
+ url => 'https://efa.vrr.de/rbgstd3/XSLT_TRIP_REQUEST2',
name => 'Verkehrsverbund Rhein-Ruhr (alternative)',
shortname => 'VRR2',
},
-
- # HTTPS not supported
{
- url => 'http://efa.vvo-online.de:8080/dvb/XSLT_TRIP_REQUEST2',
+ url => 'https://efa.vvo-online.de/VMSSL3/XSLT_TRIP_REQUEST2',
name => 'Verkehrsverbund Oberelbe',
shortname => 'VVO',
},
@@ -995,7 +962,7 @@ Travel::Routing::DE::EFA - unofficial interface to EFA-based itinerary services
use Travel::Routing::DE::EFA;
my $efa = Travel::Routing::DE::EFA->new(
- efa_url => 'http://efa.vrr.de/vrr/XSLT_TRIP_REQUEST2',
+ efa_url => 'https://app.vrr.de/vrrstd/XML_TRIP_REQUEST2';',
origin => [ 'Essen', 'HBf' ],
destination => [ 'Duisburg', 'HBf' ],
);
@@ -1014,7 +981,7 @@ Travel::Routing::DE::EFA - unofficial interface to EFA-based itinerary services
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -1041,47 +1008,9 @@ Valid hash keys and their values are:
=item B<efa_url> => I<efa_url>
Mandatory. Sets the entry point to the EFA itinerary service.
-The following URLs (grouped by country) are known. A service marked with [!]
-is not completely supported yet and may not work at all.
-
-=over
-
-=item * Austria
-
-=over
-
-=item * L<http://efa.ivb.at/ivb/XSLT_TRIP_REQUEST2> (Innsbrucker Verkehrsbetriebe)
-
-=item * L<http://efa.svv-info.at/sbs/XSLT_TRIP_REQUEST2> (Salzburger Verkehrsverbund)
-
-=item * L<http://efa.vor.at/wvb/XSLT_TRIP_REQUEST2> (Verkehrsverbund Ost-Region)
-
-=item * L<http://efaneu.vmobil.at/vvv/XSLT_TRIP_REQUEST2> (Vorarlberger Verkehrsverbund)
-
-=item * L<http://www.linzag.at/static/XSLT_TRIP_REQUEST2> (Linz AG) B<[!]>
-
-=item * The STV / Verkehrsverbund Steiermark is not supported since it returns
-data with broken encoding
-
-=back
-
-=item * Germany
-
-=over
-
-=item * L<http://212.114.197.7/vgnExt_oeffi/XML_TRIP_REQUEST2> (Verkehrsverbund GroE<szlig>raum NE<uuml>rnberg)
-
-=item * L<http://efa.vrr.de/vrr/XSLT_TRIP_REQUEST2> (Verkehrsverbund Rhein-Ruhr)
-
-=item * L<http://app.vrr.de/standard/XML_TRIP_REQUEST2> (Verkehrsverbund Rhein-Ruhr with support for B<--full-route>)
-
-=item * L<http://www2.vvs.de/vvs/XSLT_TRIP_REQUEST2> (Verkehrsverbund Stuttgart)
-
-=back
-
-=back
+See C<< efa --list >> for a list of supported services.
-If you found a URL not listed here, please send it to
+If you found a URL not listed there, please send it to
E<lt>derf@finalrewind.orgE<gt>.
=item B<origin> => B<[> I<city>B<,> I<stop> [ B<,> I<type> ] B<]>
@@ -1285,7 +1214,7 @@ None known.
=head1 AUTHOR
-Copyright (C) 2009-2020 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>
+Copyright (C) 2009-2023 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
diff --git a/lib/Travel/Routing/DE/EFA/Route.pm b/lib/Travel/Routing/DE/EFA/Route.pm
index 61a7c31..87d4e4b 100644
--- a/lib/Travel/Routing/DE/EFA/Route.pm
+++ b/lib/Travel/Routing/DE/EFA/Route.pm
@@ -8,7 +8,7 @@ use parent 'Class::Accessor';
use Travel::Routing::DE::EFA::Route::Part;
-our $VERSION = '2.19';
+our $VERSION = '2.24';
Travel::Routing::DE::EFA::Route->mk_ro_accessors(
qw(duration ticket_text ticket_type fare_adult fare_child vehicle_time));
@@ -52,7 +52,7 @@ Travel::Routing::DE::EFA::Route - Single route (connection) between two points
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -114,7 +114,7 @@ Travel::Routing::DE::EFA(3pm), Travel::Routing::DE::EFA::Route::Part(3pm).
=head1 AUTHOR
-Copyright (C) 2011-2015 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>
+Copyright (C) 2011-2015 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
diff --git a/lib/Travel/Routing/DE/EFA/Route/Message.pm b/lib/Travel/Routing/DE/EFA/Route/Message.pm
index ffa707c..6ae2e19 100644
--- a/lib/Travel/Routing/DE/EFA/Route/Message.pm
+++ b/lib/Travel/Routing/DE/EFA/Route/Message.pm
@@ -6,7 +6,7 @@ use 5.010;
use parent 'Class::Accessor';
-our $VERSION = '2.19';
+our $VERSION = '2.24';
Travel::Routing::DE::EFA::Route::Message->mk_ro_accessors(
qw(is_detailed summary subject subtitle raw_content));
@@ -58,7 +58,7 @@ route or route part.
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -129,7 +129,7 @@ Travel::Routing::DE::EFA(3pm), Travel::Routing::DE::EFA::Route::Part(3pm).
=head1 AUTHOR
-Copyright (C) 2015 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>
+Copyright (C) 2015 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
diff --git a/lib/Travel/Routing/DE/EFA/Route/Part.pm b/lib/Travel/Routing/DE/EFA/Route/Part.pm
index 3a15fc3..7ed2722 100644
--- a/lib/Travel/Routing/DE/EFA/Route/Part.pm
+++ b/lib/Travel/Routing/DE/EFA/Route/Part.pm
@@ -6,17 +6,25 @@ use 5.010;
use parent 'Class::Accessor';
-our $VERSION = '2.19';
+our $VERSION = '2.24';
+
+my %occupancy = (
+ MANY_SEATS => 1,
+ FEW_SEATS => 2,
+ STANDING_ONLY => 3
+);
Travel::Routing::DE::EFA::Route::Part->mk_ro_accessors(
qw(arrival_platform arrival_stop
- arrival_date arrival_time arrival_sdate arrival_stime delay
- departure_platform
+ arrival_date arrival_time arrival_sdate arrival_stime arrival_delay
+ delay
+ departure_platform departure_delay
departure_stop departure_date departure_time departure_sdate
departure_stime
footpath_duration footpath_type
+ occupancy
train_destination train_line train_product
- )
+ )
);
sub new {
@@ -24,6 +32,15 @@ sub new {
my $ref = \%conf;
+ if ( $ref->{occupancy} and exists $occupancy{ $ref->{occupancy} } ) {
+ $ref->{occupancy} = $occupancy{ $ref->{occupancy} };
+ }
+ else {
+ delete $ref->{occupancy};
+ }
+
+ $ref->{delay} = $ref->{departure_delay};
+
return bless( $ref, $obj );
}
@@ -154,7 +171,7 @@ points, without interchanges
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -174,6 +191,10 @@ included in the calculation, "Scheduled" means it isn't.
=over
+=item $part->arrival_delay
+
+arrival delay in minutes, 0 if unknown
+
=item $part->arrival_stop
arrival stop (city name plus station name)
@@ -223,7 +244,11 @@ Returns a list of Travel::Routing::DE::EFA::Route::Message(3pm) objects.
=item $part->delay
-delay in minutes, 0 if unknown
+alias for departure_delay
+
+=item $part->departure_delay
+
+departure delay in minutes, 0 if unknown
=item $part->departure_stop
@@ -302,6 +327,11 @@ Returns true if this part of the route has been cancelled (i.e., the entire
route is probably useless), false otherwise. For unknown reasons, EFA may
sometimes return routes which contain cancelled departures.
+=item $part->occupancy
+
+Returns expected occupancy, if available. Values range from 1 (low occupancy)
+to 3 (very high occupancy).
+
=item $part->regular_notes
Remarks about the line serving this connaction part. Returns a list of
@@ -325,9 +355,10 @@ In those cases, B<train_destination> and B<train_line> are usually empty.
=item $part->via
-Returns a list of C<< [ "DD.MM.YYYY", "HH:MM", stop, platform ] >> arrayrefs
-encoding the stops passed between B<departure_stop> and B<arrival_stop>,
-if supported by the backend. Returns nothing / an empty list otherwise.
+Returns a list of C<< [ "DD.MM.YYYY", "HH:MM", stop, platform, delay ] >>
+arrayrefs encoding the stops passed between B<departure_stop> and
+B<arrival_stop>, if supported by the backend. Returns nothing / an empty list
+otherwise. Date and time refer to schedule data and do not account for delays.
=back
@@ -354,7 +385,7 @@ Class::Accessor(3pm).
=head1 AUTHOR
-Copyright (C) 2011-2017 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>
+Copyright (C) 2011-2021 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
diff --git a/lib/Travel/Routing/DE/VRR.pm b/lib/Travel/Routing/DE/VRR.pm
index 126748c..9e485d6 100644
--- a/lib/Travel/Routing/DE/VRR.pm
+++ b/lib/Travel/Routing/DE/VRR.pm
@@ -4,16 +4,14 @@ use strict;
use warnings;
use 5.010;
-no if $] >= 5.018, warnings => "experimental::smartmatch";
-
-our $VERSION = '2.19';
+our $VERSION = '2.24';
use parent 'Travel::Routing::DE::EFA';
sub new {
my ( $class, %opt ) = @_;
- $opt{efa_url} = 'http://efa.vrr.de/vrr/XSLT_TRIP_REQUEST2';
+ $opt{efa_url} = 'https://app.vrr.de/vrrstd/XML_TRIP_REQUEST2';
return $class->SUPER::new(%opt);
}
@@ -49,7 +47,7 @@ Travel::Routing::DE::VRR - unofficial interface to the efa.vrr.de German itinera
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -102,7 +100,7 @@ None known.
=head1 AUTHOR
-Copyright (C) 2009-2020 by Daniel Friesel E<lt>derf@finalrewind.orgE<gt>
+Copyright (C) 2009-2021 by Birte Kristina Friesel E<lt>derf@finalrewind.orgE<gt>
=head1 LICENSE
diff --git a/scripts/check-efa-urls b/scripts/check-efa-urls
index f7b6b0e..804d949 100755
--- a/scripts/check-efa-urls
+++ b/scripts/check-efa-urls
@@ -7,17 +7,12 @@ IFS=','
checks="BSVG,Braunschweig,Hbf,Volkmarode,Moorhüttenweg
DING,Ulm,Hbf,Ulm,Theodor-Heuss-Platz
KVV,Karlsruhe,Hbf,Karlsruhe,Tivoli
-LinzAG,Linz,Hbf,Linz,Hart
-MVV,München,Hackerbrücke,München,Seeholzenweg
-NVBW,Stuttgart,Brendle (Großmarkt),Stuttgart,Schwabstraße
-SVV,Salzburg,Hbf,Salzburg,Mirabellplatz
-TLEM,London,Waterloo Underground Station,London,St Pancras Way
+LinzAG,Linz/Donau,Hbf,Linz/Donau,Hart
+MVV,München,Donnersbergerbrücke,München,Seeholzenweg
+NVBW,Stuttgart,Brendle (Großmarkt),Stuttgart,Kleinhohenheim
VAG,Schallstadt,Bf,Freiburg im Breisgau,Hbf
-VBL,Luzern,Bf,Luzern,Brüggligasse
-Verbundlinie,Graz,Hbf,Graz,Mariatrost
VGN,Nürnberg,Hbf,Nürnberg,Messe
VMV,Schwerin,Hbf,Schwerin,Dreescher Markt
-VOR,Wien,Hbf,Wien,Praterstern
VRN,Mannheim,Hbf,Ludwigshafen (Rhein),Hbf
VRR,Essen,Hbf,Essen,Rüttenscheider Stern
VRR2,Essen,Hbf,Essen,Rüttenscheider Stern
diff --git a/t/20-vrr.t b/t/20-vrr.t
index e425820..0b9a758 100644
--- a/t/20-vrr.t
+++ b/t/20-vrr.t
@@ -13,208 +13,169 @@ require_ok('Travel::Routing::DE::VRR');
sub efa_conf {
my $ret = {
- efa_url => 'http://efa.vrr.de/vrr/XSLT_TRIP_REQUEST2',
- origin => ['Essen', 'HBf'],
- destination => ['Koeln', 'HBf'],
- rm_base => 'http://efa.vrr.de/vrr/',
- sm_base => 'http://efa.vrr.de/download/envmaps/',
+ efa_url => 'https://app.vrr.de/vrrstd/XML_TRIP_REQUEST2',
+ origin => [ 'Essen', 'HBf' ],
+ destination => [ 'Koeln', 'HBf' ],
+ rm_base => 'https://app.vrr.de/vrrstd/',
+ sm_base => 'https://app.vrr.de/download/envmaps/',
lwp_options => {},
submit => 0,
};
foreach my $p (@_) {
- $ret->{$p->[0]} = $p->[1];
+ $ret->{ $p->[0] } = $p->[1];
}
return $ret;
}
sub efa_new {
- return new_ok(
- 'Travel::Routing::DE::VRR' => [%{efa_conf(@_)}]
- );
+ return new_ok( 'Travel::Routing::DE::VRR' => [ %{ efa_conf(@_) } ] );
}
sub is_efa_post {
- my ($ck, $cv, @post) = @_;
- my $efa = efa_new([$ck, $cv]);
+ my ( $ck, $cv, @post ) = @_;
+ my $efa = efa_new( [ $ck, $cv ] );
my $ok = 1;
is_deeply(
- $efa->{'config'}, efa_conf([$ck, $cv]),
+ $efa->{'config'},
+ efa_conf( [ $ck, $cv ] ),
"$ck => $cv: conf ok",
);
foreach my $ref (@post) {
- my ($key, $value) = @{$ref};
- if (not defined $efa->{'post'}->{"key"} and
- not defined $value) {
+ my ( $key, $value ) = @{$ref};
+ if ( not defined $efa->{'post'}->{"key"}
+ and not defined $value )
+ {
next;
}
- if ($efa->{'post'}->{"$key"} ne $value) {
+ if ( $efa->{'post'}->{"$key"} ne $value ) {
$ok = 0;
last;
}
}
- ok(
- $ok,
- "$ck => $cv: POST okay",
- );
+ ok( $ok, "$ck => $cv: POST okay", );
}
sub is_efa_err {
- my ($ck, $cv, $exception) = @_;
+ my ( $ck, $cv, $exception ) = @_;
isa_ok(
- exception { Travel::Routing::DE::VRR->new(%{efa_conf([$ck, $cv])}) },
+ exception {
+ Travel::Routing::DE::VRR->new( %{ efa_conf( [ $ck, $cv ] ) } )
+ },
$exception,
"$ck => $cv"
);
}
-is_efa_post('ignored', 'ignored');
+is_efa_post( 'ignored', 'ignored' );
my $efa;
is_efa_post(
- 'via', ['MH', 'HBf'],
- ['place_via', 'MH'],
- ['name_via', 'HBf'],
- ['type_via', 'stop'],
+ 'via',
+ [ 'MH', 'HBf' ],
+ [ 'place_via', 'MH' ],
+ [ 'name_via', 'HBf' ],
+ [ 'type_via', 'stop' ],
);
is_efa_post(
- 'origin', ['D', 'Fuerstenwall 232', 'address'],
- ['place_origin', 'D'],
- ['name_origin', 'Fuerstenwall 232'],
- ['type_origin', 'address'],
+ 'origin',
+ [ 'D', 'Fuerstenwall 232', 'address' ],
+ [ 'place_origin', 'D' ],
+ [ 'name_origin', 'Fuerstenwall 232' ],
+ [ 'type_origin', 'address' ],
);
is_efa_post(
'departure_time', '22:23',
- ['itdTripDateTimeDepArr', 'dep'],
- ['itdTimeHour', '22'],
- ['itdTimeMinute', '23'],
+ [ 'itdTripDateTimeDepArr', 'dep' ],
+ [ 'itdTimeHour', '22' ],
+ [ 'itdTimeMinute', '23' ],
);
is_efa_post(
'arrival_time', '16:38',
- ['itdTripDateTimeDepArr', 'arr'],
- ['itdTimeHour', '16'],
- ['itdTimeMinute', '38'],
+ [ 'itdTripDateTimeDepArr', 'arr' ],
+ [ 'itdTimeHour', '16' ],
+ [ 'itdTimeMinute', '38' ],
);
is_efa_post(
'date', '2.10.2009',
- ['itdDateDay', '2'],
- ['itdDateMonth', '10'],
- ['itdDateYear', '2009'],
+ [ 'itdDateDay', '2' ],
+ [ 'itdDateMonth', '10' ],
+ [ 'itdDateYear', '2009' ],
);
is_efa_post(
'date', '26.12.',
- ['itdDateDay', '26'],
- ['itdDateMonth', '12'],
- ['itdDateYear', (localtime(time))[5] + 1900],
+ [ 'itdDateDay', '26' ],
+ [ 'itdDateMonth', '12' ],
+ [ 'itdDateYear', ( localtime(time) )[5] + 1900 ],
);
-is_efa_post(
- 'exclude', [qw[zug]],
- ['inclMOT_0', undef],
-);
+is_efa_post( 'exclude', [qw[zug]], [ 'inclMOT_0', undef ], );
is_efa_post(
'exclude', [qw[stadtbus schiff ast]],
- ['inclMOT_5', undef],
- ['inclMOT_9', undef],
- ['inclMOT_10', undef],
+ [ 'inclMOT_5', undef ],
+ [ 'inclMOT_9', undef ],
+ [ 'inclMOT_10', undef ],
);
-is_efa_post(
- 'select_interchange_by', 'speed',
- ['routeType', 'LEASTTIME'],
-);
+is_efa_post( 'select_interchange_by', 'speed', [ 'routeType', 'LEASTTIME' ], );
-is_efa_post(
- 'select_interchange_by', 'waittime',
- ['routeType', 'LEASTINTERCHANGE'],
+is_efa_post( 'select_interchange_by', 'waittime',
+ [ 'routeType', 'LEASTINTERCHANGE' ],
);
-is_efa_post(
- 'select_interchange_by', 'distance',
- ['routeType', 'LEASTWALKING'],
+is_efa_post( 'select_interchange_by', 'distance',
+ [ 'routeType', 'LEASTWALKING' ],
);
-is_efa_post(
- 'train_type', 'local',
- ['lineRestriction', 403],
-);
+is_efa_post( 'train_type', 'local', [ 'lineRestriction', 403 ], );
-is_efa_post(
- 'train_type', 'ic',
- ['lineRestriction', 401],
-);
+is_efa_post( 'train_type', 'ic', [ 'lineRestriction', 401 ], );
-is_efa_post(
- 'train_type', 'ice',
- ['lineRestriction', 400],
-);
+is_efa_post( 'train_type', 'ice', [ 'lineRestriction', 400 ], );
-is_efa_post(
- 'walk_speed', 'normal',
- ['changeSpeed', 'normal'],
-);
+is_efa_post( 'walk_speed', 'normal', [ 'changeSpeed', 'normal' ], );
-is_efa_post(
- 'max_interchanges', 5,
- ['maxChanges', 5],
-);
+is_efa_post( 'max_interchanges', 5, [ 'maxChanges', 5 ], );
-is_efa_post(
- 'use_near_stops', 1,
- ['useProxFootSearch', 1],
-);
+is_efa_post( 'use_near_stops', 1, [ 'useProxFootSearch', 1 ], );
-is_efa_post(
- 'with_bike', 1,
- ['bikeTakeAlong', 1],
-);
+is_efa_post( 'with_bike', 1, [ 'bikeTakeAlong', 1 ], );
-is_efa_err(
- 'departure_time', '37:00',
+is_efa_err( 'departure_time', '37:00',
'Travel::Routing::DE::EFA::Exception::Setup',
);
-is_efa_err(
- 'departure_time', '07',
+is_efa_err( 'departure_time', '07',
'Travel::Routing::DE::EFA::Exception::Setup',
);
-is_efa_err(
- 'train_type', 'invalid',
+is_efa_err( 'train_type', 'invalid',
'Travel::Routing::DE::EFA::Exception::Setup',
);
-is_efa_err(
- 'walk_speed', 'invalid',
+is_efa_err( 'walk_speed', 'invalid',
'Travel::Routing::DE::EFA::Exception::Setup',
);
-is_efa_err(
- 'select_interchange_by', 'invalid',
+is_efa_err( 'select_interchange_by', 'invalid',
'Travel::Routing::DE::EFA::Exception::Setup',
);
-is_efa_err(
- 'exclude', [qw[sonstige invalid]],
+is_efa_err( 'exclude', [qw[sonstige invalid]],
'Travel::Routing::DE::EFA::Exception::Setup',
);
-is_efa_err(
- 'date', '42.5.2003',
- 'Travel::Routing::DE::EFA::Exception::Setup',
-);
+is_efa_err( 'date', '42.5.2003',
+ 'Travel::Routing::DE::EFA::Exception::Setup', );
-is_efa_err(
- 'date', '7.',
- 'Travel::Routing::DE::EFA::Exception::Setup',
-);
+is_efa_err( 'date', '7.', 'Travel::Routing::DE::EFA::Exception::Setup', );
diff --git a/t/21-vrr.t b/t/21-vrr.t
index d3111b6..691bba9 100644
--- a/t/21-vrr.t
+++ b/t/21-vrr.t
@@ -78,7 +78,7 @@ is( $c0->arrival_time, '14:02', 'r0,0: artime' );
is( $c0->arrival_stime, '14:02', 'r0,0: astime' );
is_deeply( ($c0->via)[0],
- ['27.11.2011', '13:56', 'Essen Florastr.', 'Bstg. 1'],
+ ['27.11.2011', '13:56', 'Essen Florastr.', 'Bstg. 1', undef],
'r0,0: via[0]');
is( $c1->delay, 3, 'r0,1: delay' );