summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.mailmap3
-rw-r--r--Changelog29
-rw-r--r--README.md33
-rwxr-xr-xbin/efa155
-rw-r--r--lib/Travel/Routing/DE/EFA.pm174
-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.pm32
-rw-r--r--lib/Travel/Routing/DE/VRR.pm10
-rwxr-xr-xscripts/check-efa-urls9
-rw-r--r--t/20-vrr.t177
-rw-r--r--t/21-vrr.t2
12 files changed, 297 insertions, 339 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 9f0c130..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,22 +239,13 @@ 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{};
@@ -242,15 +261,6 @@ sub display_connection {
}
}
- 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 );
- }
- }
-
if ( $opt->{maps} ) {
for my $m ( $c->departure_routemaps, $c->departure_stationmaps ) {
say "# $m";
@@ -258,22 +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 %-30s %s\n",
- $c->arrival_time, $c->arrival_stop_and_platform, $occupancy );
print "\n";
if ( $opt->{'extended-info'}
@@ -340,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;
}
}
@@ -405,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 "
@@ -472,7 +523,7 @@ efa - Command line client for EFA-based public transit routing services
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -551,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.
@@ -728,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
@@ -789,7 +840,7 @@ to to request via -> to.
=head1 AUTHOR
-Copyright (C) 2009-2021 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 7d1e551..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,8 +675,8 @@ 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),
@@ -687,6 +687,7 @@ sub parse_xml_part {
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),
@@ -716,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 ) )
@@ -725,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;
}
@@ -738,7 +742,8 @@ sub parse_xml_part {
$self->itddate_str($e_vdate),
$self->itdtime_str($e_vtime),
$name,
- $platform
+ $platform,
+ $arr_delay,
]
);
}
@@ -820,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{ | },
@@ -830,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{ | },
@@ -899,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',
@@ -946,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',
},
@@ -996,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' ],
);
@@ -1015,7 +981,7 @@ Travel::Routing::DE::EFA - unofficial interface to EFA-based itinerary services
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -1042,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<]>
@@ -1286,7 +1214,7 @@ None known.
=head1 AUTHOR
-Copyright (C) 2009-2021 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 2c9df16..7ed2722 100644
--- a/lib/Travel/Routing/DE/EFA/Route/Part.pm
+++ b/lib/Travel/Routing/DE/EFA/Route/Part.pm
@@ -6,7 +6,7 @@ use 5.010;
use parent 'Class::Accessor';
-our $VERSION = '2.19';
+our $VERSION = '2.24';
my %occupancy = (
MANY_SEATS => 1,
@@ -16,14 +16,15 @@ my %occupancy = (
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 {
@@ -38,6 +39,8 @@ sub new {
delete $ref->{occupancy};
}
+ $ref->{delay} = $ref->{departure_delay};
+
return bless( $ref, $obj );
}
@@ -168,7 +171,7 @@ points, without interchanges
=head1 VERSION
-version 2.19
+version 2.24
=head1 DESCRIPTION
@@ -188,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)
@@ -237,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
@@ -344,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
@@ -373,7 +385,7 @@ Class::Accessor(3pm).
=head1 AUTHOR
-Copyright (C) 2011-2021 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 0ff291a..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-2021 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 be3e4a4..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
+LinzAG,Linz/Donau,Hbf,Linz/Donau,Hart
MVV,München,Donnersbergerbrü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
+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' );