summaryrefslogtreecommitdiff
path: root/lib/Travelynx/Model/InTransit.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Travelynx/Model/InTransit.pm')
-rw-r--r--lib/Travelynx/Model/InTransit.pm337
1 files changed, 286 insertions, 51 deletions
diff --git a/lib/Travelynx/Model/InTransit.pm b/lib/Travelynx/Model/InTransit.pm
index b67b716..8324027 100644
--- a/lib/Travelynx/Model/InTransit.pm
+++ b/lib/Travelynx/Model/InTransit.pm
@@ -10,6 +10,7 @@ use warnings;
use 5.020;
use DateTime;
+use GIS::Distance;
use JSON;
my %visibility_itoa = (
@@ -101,6 +102,7 @@ sub add {
my $journey = $opt{journey};
my $stop = $opt{stop};
my $stopover = $opt{stopover};
+ my $manual = $opt{manual};
my $checkin_station_id = $opt{departure_eva};
my $route = $opt{route};
my $data = $opt{data};
@@ -129,7 +131,7 @@ sub add {
messages => $json->encode(
[ map { [ $_->[0]->epoch, $_->[1] ] } $train->messages ]
),
- data => JSON->new->encode(
+ data => $json->encode(
{
rt => $train->departure_has_realtime ? 1
: 0,
@@ -152,19 +154,22 @@ sub add {
$j_stop->full_name,
$j_stop->id_num,
{
- sched_arr => _epoch( $j_stop->sched_arr ),
- sched_dep => _epoch( $j_stop->sched_dep ),
- rt_arr => _epoch( $j_stop->rt_arr ),
- rt_dep => _epoch( $j_stop->rt_dep ),
- isCancelled => $j_stop->is_cancelled,
- arr_delay => $j_stop->arr_delay,
- dep_delay => $j_stop->dep_delay,
- efa_load => $j_stop->occupancy,
- lat => $j_stop->latlon->[0],
- lon => $j_stop->latlon->[1],
+ sched_arr => _epoch( $j_stop->sched_arr ),
+ sched_dep => _epoch( $j_stop->sched_dep ),
+ rt_arr => _epoch( $j_stop->rt_arr ),
+ rt_dep => _epoch( $j_stop->rt_dep ),
+ arr_delay => $j_stop->arr_delay,
+ dep_delay => $j_stop->dep_delay,
+ platform => $j_stop->platform,
+ efa_load => $j_stop->occupancy,
+ lat => $j_stop->latlon->[0],
+ lon => $j_stop->latlon->[1],
}
]
);
+ if ( $j_stop->is_cancelled ) {
+ $route[-1][2]{isCancelled} = 1;
+ }
}
$persistent_data->{operator} = $journey->operator;
$db->insert(
@@ -182,13 +187,13 @@ sub add {
sched_departure => $stop->sched_dep,
real_departure => $stop->rt_dep // $stop->sched_dep,
route => $json->encode( \@route ),
- data => JSON->new->encode(
+ data => $json->encode(
{
rt => $stop->rt_dep ? 1 : 0,
%{ $data // {} }
}
),
- user_data => JSON->new->encode($persistent_data),
+ user_data => $json->encode($persistent_data),
backend_id => $backend_id,
}
);
@@ -213,6 +218,7 @@ sub add {
rt_dep => _epoch( $j_stop->rt_dep ),
arr_delay => $j_stop->arr_delay,
dep_delay => $j_stop->dep_delay,
+ platform => $j_stop->platform,
load => $j_stop->load,
lat => $j_stop->loc->lat,
lon => $j_stop->loc->lon,
@@ -243,13 +249,13 @@ sub add {
sched_departure => $stop->{sched_dep},
real_departure => $stop->{rt_dep} // $stop->{sched_dep},
route => $json->encode( \@route ),
- data => JSON->new->encode(
+ data => $json->encode(
{
rt => $stop->{rt_dep} ? 1 : 0,
%{ $data // {} }
}
),
- user_data => JSON->new->encode($persistent_data),
+ user_data => $json->encode($persistent_data),
backend_id => $backend_id,
}
);
@@ -258,7 +264,11 @@ sub add {
and $stop
and ref($journey) eq 'Travel::Status::DE::DBRIS::Journey' )
{
- my $number = $journey->train_no // $journey->number // $train_suffix;
+ my $trip_no
+ = $journey->trip_no_at( $stop->eva,
+ $stop->sched_dep ? $stop->sched_dep->epoch : undef )
+ // $journey->train_no;
+ my $number = $trip_no // $journey->number // $train_suffix;
my $line;
if ( defined $journey->line_no and $journey->line_no ne $number ) {
@@ -276,14 +286,14 @@ sub add {
$j_stop->name,
$j_stop->eva,
{
- sched_arr => _epoch( $j_stop->sched_arr ),
- sched_dep => _epoch( $j_stop->sched_dep ),
- rt_arr => _epoch( $j_stop->rt_arr ),
- rt_dep => _epoch( $j_stop->rt_dep ),
- isCancelled => $j_stop->is_cancelled,
- arr_delay => $j_stop->arr_delay,
- dep_delay => $j_stop->dep_delay,
- load => {
+ sched_arr => _epoch( $j_stop->sched_arr ),
+ sched_dep => _epoch( $j_stop->sched_dep ),
+ rt_arr => _epoch( $j_stop->rt_arr ),
+ rt_dep => _epoch( $j_stop->rt_dep ),
+ arr_delay => $j_stop->arr_delay,
+ dep_delay => $j_stop->dep_delay,
+ platform => $j_stop->platform,
+ load => {
FIRST => $j_stop->occupancy_first,
SECOND => $j_stop->occupancy_second
},
@@ -292,6 +302,12 @@ sub add {
}
]
);
+ if ( $j_stop->is_additional ) {
+ $route[-1][2]{isAdditional} = 1;
+ }
+ if ( $j_stop->is_cancelled ) {
+ $route[-1][2]{isCancelled} = 1;
+ }
}
my @messages;
for my $msg ( $journey->messages ) {
@@ -313,6 +329,12 @@ sub add {
);
}
}
+ if ( scalar $journey->admin_ids ) {
+ $persistent_data->{admin_ids} = [ $journey->admin_ids ];
+ }
+ if ( scalar $journey->operators ) {
+ $persistent_data->{operators} = [ $journey->operators ];
+ }
$db->insert(
'in_transit',
{
@@ -330,13 +352,13 @@ sub add {
sched_departure => $stop->sched_dep,
real_departure => $stop->rt_dep // $stop->sched_dep,
route => $json->encode( \@route ),
- data => JSON->new->encode(
+ data => $json->encode(
{
rt => $stop->{rt_dep} ? 1 : 0,
%{ $data // {} }
}
),
- user_data => JSON->new->encode($persistent_data),
+ user_data => $json->encode($persistent_data),
backend_id => $backend_id,
}
);
@@ -363,6 +385,7 @@ sub add {
_epoch( $journey_stopover->realtime_departure ),
arr_delay => $journey_stopover->arrival_delay,
dep_delay => $journey_stopover->departure_delay,
+ platform => $journey_stopover->track,
lat => $journey_stopover->stop->lat,
lon => $journey_stopover->stop->lon,
}
@@ -389,17 +412,50 @@ sub add {
sched_departure => $stopover->scheduled_departure,
real_departure => $stopover->departure,
route => $json->encode( \@route ),
- data => JSON->new->encode(
+ data => $json->encode(
{
rt => $stopover->{is_realtime} ? 1 : 0,
%{ $data // {} }
}
),
- user_data => JSON->new->encode($persistent_data),
+ user_data => $json->encode($persistent_data),
backend_id => $backend_id,
}
);
}
+ elsif ($manual) {
+ if ( $manual->{comment} ) {
+ $persistent_data->{comment} = $manual->{comment};
+ }
+ $db->insert(
+ 'in_transit',
+ {
+ user_id => $uid,
+ cancelled => 0,
+ checkin_station_id => $manual->{dep_id},
+ checkout_station_id => $manual->{arr_id},
+ checkin_time => DateTime->now( time_zone => 'Europe/Berlin' ),
+ train_type => $manual->{train_type},
+ train_no => $manual->{train_no} || q{},
+ train_id => 'manual',
+ train_line => $manual->{train_line} || undef,
+ sched_departure => $manual->{sched_departure},
+ real_departure => $manual->{sched_departure},
+ sched_arrival => $manual->{sched_arrival},
+ real_arrival => $manual->{sched_arrival},
+ route => $json->encode( $manual->{route} // [] ),
+ data => $json->encode(
+ {
+ manual => \1,
+ %{ $data // {} }
+ }
+ ),
+ user_data => $json->encode($persistent_data),
+ backend_id => $backend_id,
+ }
+ );
+ return;
+ }
else {
die('invalid arguments / argument types passed to InTransit->add');
}
@@ -478,6 +534,14 @@ sub postprocess {
$ret->{comment} = $ret->{user_data}{comment};
$ret->{wagongroups} = $ret->{user_data}{wagongroups};
+ if ( $ret->{sched_dep_ts} and $ret->{real_dep_ts} ) {
+ $ret->{dep_delay} = $ret->{real_dep_ts} - $ret->{sched_dep_ts};
+ }
+
+ if ( $ret->{sched_arr_ts} and $ret->{real_arr_ts} ) {
+ $ret->{arr_delay} = $ret->{real_arr_ts} - $ret->{sched_arr_ts};
+ }
+
$ret->{platform_type} = 'Gleis';
if ( $ret->{train_type} and $ret->{train_type} =~ m{ ast | bus | ruf }ix ) {
$ret->{platform_type} = 'Steig';
@@ -595,6 +659,7 @@ sub get {
if ( $opt{with_polyline} and $ret ) {
$ret->{dep_latlon} = [ $ret->{dep_lat}, $ret->{dep_lon} ];
$ret->{arr_latlon} = [ $ret->{arr_lat}, $ret->{arr_lon} ];
+ $ret->{now_latlon} = $self->estimate_trip_position($ret);
}
if ( $opt{with_visibility} and $ret ) {
@@ -736,6 +801,22 @@ sub set_arrival_eva {
);
}
+sub set_arrival_platform {
+ my ( $self, %opt ) = @_;
+
+ my $uid = $opt{uid};
+ my $db = $opt{db} // $self->{pg}->db;
+ my $platform = $opt{arrival_platform};
+
+ $db->update(
+ 'in_transit',
+ {
+ arr_platform => $platform,
+ },
+ { user_id => $uid }
+ );
+}
+
sub set_arrival_times {
my ( $self, %opt ) = @_;
@@ -1193,15 +1274,15 @@ sub update_arrival_dbris {
$j_stop->name,
$j_stop->eva,
{
- sched_arr => _epoch( $j_stop->sched_arr ),
- sched_dep => _epoch( $j_stop->sched_dep ),
- rt_arr => _epoch( $j_stop->rt_arr ),
- rt_dep => _epoch( $j_stop->rt_dep ),
- platform => $j_stop->platform,
- isCancelled => $j_stop->is_cancelled,
- arr_delay => $j_stop->arr_delay,
- dep_delay => $j_stop->dep_delay,
- load => {
+ sched_arr => _epoch( $j_stop->sched_arr ),
+ sched_dep => _epoch( $j_stop->sched_dep ),
+ rt_arr => _epoch( $j_stop->rt_arr ),
+ rt_dep => _epoch( $j_stop->rt_dep ),
+ platform => $j_stop->platform,
+ arr_delay => $j_stop->arr_delay,
+ dep_delay => $j_stop->dep_delay,
+ platform => $j_stop->platform,
+ load => {
FIRST => $j_stop->occupancy_first,
SECOND => $j_stop->occupancy_second
},
@@ -1210,6 +1291,12 @@ sub update_arrival_dbris {
}
]
);
+ if ( $j_stop->is_additional ) {
+ $route[-1][2]{isAdditional} = 1;
+ }
+ if ( $j_stop->is_cancelled ) {
+ $route[-1][2]{isCancelled} = 1;
+ }
}
# selecting on user_id and train_no avoids a race condition if a user checks
@@ -1218,8 +1305,8 @@ sub update_arrival_dbris {
$db->update(
'in_transit',
{
- real_arrival => $stop->{rt_arr},
- arr_platform => $stop->{platform},
+ real_arrival => $stop->rt_arr,
+ arr_platform => $stop->platform,
route => $json->encode( [@route] ),
data => $json->encode($ephemeral_data),
user_data => $json->encode($persistent_data),
@@ -1261,21 +1348,27 @@ sub update_arrival_efa {
$j_stop->full_name,
$j_stop->id_num,
{
- sched_arr => _epoch( $j_stop->sched_arr ),
- sched_dep => _epoch( $j_stop->sched_dep ),
- rt_arr => _epoch( $j_stop->rt_arr ),
- rt_dep => _epoch( $j_stop->rt_dep ),
- isCancelled => $j_stop->is_cancelled,
- arr_delay => $j_stop->arr_delay,
- dep_delay => $j_stop->dep_delay,
- efa_load => $j_stop->occupancy,
- lat => $j_stop->latlon->[0],
- lon => $j_stop->latlon->[1],
+ sched_arr => _epoch( $j_stop->sched_arr ),
+ sched_dep => _epoch( $j_stop->sched_dep ),
+ rt_arr => _epoch( $j_stop->rt_arr ),
+ rt_dep => _epoch( $j_stop->rt_dep ),
+ arr_delay => $j_stop->arr_delay,
+ dep_delay => $j_stop->dep_delay,
+ platform => $j_stop->platform,
+ efa_load => $j_stop->occupancy,
+ lat => $j_stop->latlon->[0],
+ lon => $j_stop->latlon->[1],
}
]
);
+ if ( $j_stop->is_cancelled ) {
+ $route[-1][2]{isCancelled} = 1;
+ }
}
+ # TODO set efa_load from old route entry if missing in current route entry
+ # (at least in VVO, occupancy data is only provided for future stops)
+
# selecting on user_id and train_no avoids a race condition if a user checks
# into a new train while we are fetching data for their previous journey. In
# this case, the new train would receive data from the previous journey.
@@ -1284,6 +1377,7 @@ sub update_arrival_efa {
{
data => $json->encode($ephemeral_data),
real_arrival => $stop->rt_arr,
+ arr_platform => $stop->platform,
route => $json->encode( [@route] ),
},
{
@@ -1321,6 +1415,7 @@ sub update_arrival_motis {
rt_dep => _epoch( $journey_stopover->realtime_departure ),
arr_delay => $journey_stopover->arrival_delay,
dep_delay => $journey_stopover->departure_delay,
+ platform => $journey_stopover->track,
lat => $journey_stopover->stop->lat,
lon => $journey_stopover->stop->lon,
}
@@ -1334,7 +1429,8 @@ sub update_arrival_motis {
$db->update(
'in_transit',
{
- real_arrival => $stopover->{realtime_arrival},
+ real_arrival => $stopover->realtime_arrival,
+ arr_platform => $stopover->track,
route => $json->encode( [@route] ),
},
{
@@ -1380,6 +1476,7 @@ sub update_arrival_hafas {
rt_dep => _epoch( $j_stop->rt_dep ),
arr_delay => $j_stop->arr_delay,
dep_delay => $j_stop->dep_delay,
+ platform => $j_stop->platform,
load => $j_stop->load,
lat => $j_stop->loc->lat,
lon => $j_stop->loc->lon,
@@ -1406,7 +1503,8 @@ sub update_arrival_hafas {
'in_transit',
{
data => $json->encode($ephemeral_data),
- real_arrival => $stop->{rt_arr},
+ real_arrival => $stop->rt_arr,
+ arr_platform => $stop->platform,
route => $json->encode( [@route] ),
},
{
@@ -1488,4 +1586,141 @@ sub update_visibility {
);
}
+sub estimate_trip_position_between_stops {
+ my ( $self, %opt ) = @_;
+
+ my $time_complete = $opt{now} - $opt{from_ts};
+ my $time_total = $opt{to_ts} - $opt{from_ts};
+ my $ratio = $time_complete / $time_total;
+
+ my $distance = GIS::Distance->new;
+ my $polyline = $opt{polyline};
+ my ( $i_from, $i_to );
+
+ for my $i ( 0 .. $#{$polyline} ) {
+ if ( not defined $i_from
+ and $polyline->[$i][2]
+ and $polyline->[$i][2] == $opt{from}[1] )
+ {
+ $i_from = $i;
+ }
+ elsif ( not defined $i_to
+ and $polyline->[$i][2]
+ and $polyline->[$i][2] == $opt{to}[1] )
+ {
+ $i_to = $i;
+ last;
+ }
+ }
+ if ( defined $i_from and defined $i_to ) {
+ my $total_distance = 0;
+ for my $i ( $i_from + 1 .. $i_to ) {
+ my $prev = $polyline->[ $i - 1 ];
+ my $this = $polyline->[$i];
+ if ( $prev and $this ) {
+ $total_distance
+ += $distance->distance_metal( $prev->[1], $prev->[0],
+ $this->[1], $this->[0] );
+ }
+ }
+
+ my $marker_distance = $total_distance * $ratio;
+ $total_distance = 0;
+ for my $i ( $i_from + 1 .. $i_to ) {
+ my $prev = $polyline->[ $i - 1 ];
+ my $this = $polyline->[$i];
+ if ( $prev and $this ) {
+ my $prev_distance = $total_distance;
+ $total_distance
+ += $distance->distance_metal( $prev->[1], $prev->[0],
+ $this->[1], $this->[0] );
+ if ( $total_distance > $marker_distance ) {
+ my $sub_ratio = 1;
+ if ( $total_distance != $prev_distance ) {
+ $sub_ratio = ( $marker_distance - $prev_distance )
+ / ( $total_distance - $prev_distance );
+ }
+ return (
+ $prev->[1] + ( $this->[1] - $prev->[1] ) * $sub_ratio,
+ $prev->[0] + ( $this->[0] - $prev->[0] ) * $sub_ratio,
+ );
+ }
+ }
+ }
+ }
+ return (
+ $opt{from}[2]{lat} + ( $opt{to}[2]{lat} - $opt{from}[2]{lat} ) * $ratio,
+ $opt{from}[2]{lon} + ( $opt{to}[2]{lon} - $opt{from}[2]{lon} ) * $ratio
+ );
+}
+
+sub estimate_trip_position {
+ my ( $self, $in_transit ) = @_;
+
+ my @now_latlon;
+ my @route = @{ $in_transit->{route} };
+
+ # estimate_train_position runs before postprocess, so all route
+ # timestamps are provided in UNIX seconds and not as DateTime objects.
+ my $now = DateTime->now( time_zone => 'Europe/Berlin' )->epoch;
+
+ my $prev_ts;
+ for my $i ( 0 .. $#route ) {
+ my $ts = $route[$i][2]{rt_arr} // $route[$i][2]{sched_arr}
+ // $route[$i][2]{rt_dep} // $route[$i][2]{sched_dep} // 0;
+ my $ts_dep = $route[$i][2]{rt_dep} // $route[$i][2]{sched_dep}
+ // $route[$i][2]{rt_arr} // $route[$i][2]{sched_arr} // 0;
+ if ( $ts and $ts_dep and $now >= $ts and $now <= $ts_dep ) {
+
+ # Currently at a stop
+ @now_latlon = ( $route[$i][2]{lat}, $route[$i][2]{lon} );
+ last;
+ }
+ if ( $ts
+ and $prev_ts
+ and $now > $prev_ts
+ and $now < $ts )
+ {
+ @now_latlon = $self->estimate_trip_position_between_stops(
+ now => $now,
+ from => $route[ $i - 1 ],
+ from_ts => $prev_ts,
+ to => $route[$i],
+ to_ts => $ts,
+ polyline => $in_transit->{polyline},
+ );
+ last;
+ }
+ $prev_ts = $ts_dep;
+ }
+
+ if ( not @now_latlon
+ and $in_transit->{sched_dep_ts}
+ and $in_transit->{sched_arr_ts} )
+ {
+ my $time_complete = $now
+ - ( $in_transit->{real_dep_ts} // $in_transit->{sched_dep_ts} );
+ my $time_total
+ = ( $in_transit->{real_arr_ts} // $in_transit->{sched_arr_ts} )
+ - ( $in_transit->{real_dep_ts} // $in_transit->{sched_dep_ts} );
+
+ if ( $time_total == 0 ) {
+ return [ $in_transit->{dep_lat}, $in_transit->{dep_lon} ];
+ }
+
+ my $completion = $time_complete / $time_total;
+ $completion = $completion < 0 ? 0 : $completion > 1 ? 1 : $completion;
+ @now_latlon = (
+ $in_transit->{dep_lat}
+ + ( $in_transit->{arr_lat} - $in_transit->{dep_lat} )
+ * $completion,
+ $in_transit->{dep_lon}
+ + ( $in_transit->{arr_lon} - $in_transit->{dep_lon} )
+ * $completion,
+ );
+ }
+
+ return \@now_latlon;
+}
+
1;