diff options
-rwxr-xr-x | lib/Travelynx.pm | 70 | ||||
-rwxr-xr-x | lib/Travelynx/Controller/Traveling.pm | 9 | ||||
-rw-r--r-- | lib/Travelynx/Model/InTransit.pm | 133 | ||||
-rw-r--r-- | templates/_checked_in.html.ep | 2 | ||||
-rw-r--r-- | templates/_map.html.ep | 13 |
5 files changed, 215 insertions, 12 deletions
diff --git a/lib/Travelynx.pm b/lib/Travelynx.pm index 78642ff..7dba658 100755 --- a/lib/Travelynx.pm +++ b/lib/Travelynx.pm @@ -1615,10 +1615,10 @@ sub startup { rt_arrival => ( $stop->[2]{rt_arr} || $stop->[2]{sched_arr} ) ); - if ($stop->[2]{platform}) { + if ( $stop->[2]{platform} ) { $self->in_transit->set_arrival_platform( - uid => $uid, - db => $db, + uid => $uid, + db => $db, arrival_platform => $stop->[2]{platform} ); } @@ -2704,14 +2704,43 @@ sub startup { latlon => $_->{from_latlon} // $_->{dep_latlon} } } @journeys; + my @extra_stations; + + if ( $opt{show_all_stops} ) { + for my $journey (@journeys) { + my @j_stops = map { + { + name => $_->[2], + latlon => [ $_->[1], $_->[0] ] + } + } grep { defined $_->[2] } + @{ $journey->{polyline} // [] }; + @extra_stations + = uniq_by { $_->{name} } ( @extra_stations, @j_stops ); + } + } my @station_coordinates = map { [ $_->{latlon}, $_->{name} ] } @stations; + my @extra_station_coordinates + = map { [ $_->{latlon}, $_->{name} ] } @extra_stations; + + my @now_coordinates = map { + [ + $_->{now_latlon}, + $_->{train_type} . ' ' + . ( $_->{train_line} // $_->{train_no} ) + ] + } @journeys; my @station_pairs; my @polylines; my %seen; + # not part of the travelled route, but trip route before/after the journey. + # Only used if show_full_route is set. + my @extra_polylines; + my @skipped_journeys; my @polyline_journeys = grep { $_->{polyline} } @journeys; my @beeline_journeys = grep { not $_->{polyline} } @journeys; @@ -2787,12 +2816,28 @@ sub startup { if ( $from_index > $to_index ) { ( $to_index, $from_index ) = ( $from_index, $to_index ); } + if ( $opt{show_full_route} ) { + my @pre_polyline = @polyline[ 0 .. $from_index ]; + my @post_polyline = @polyline[ $to_index .. $#polyline ]; + my @pre_polyline_coords; + for my $coord (@pre_polyline) { + push( @pre_polyline_coords, + [ $coord->[1], $coord->[0] ] ); + } + my @post_polyline_coords; + for my $coord (@post_polyline) { + push( @post_polyline_coords, + [ $coord->[1], $coord->[0] ] ); + } + push( @extra_polylines, + ( \@pre_polyline_coords, \@post_polyline_coords ) ); + } @polyline = @polyline[ $from_index .. $to_index ]; my @polyline_coords; for my $coord (@polyline) { push( @polyline_coords, [ $coord->[1], $coord->[0] ] ); } - push( @polylines, [@polyline_coords] ); + push( @polylines, \@polyline_coords ); } for my $journey (@beeline_journeys) { @@ -2900,7 +2945,7 @@ sub startup { { polylines => $json->encode( \@station_pairs ), color => '#673ab7', - opacity => @polylines + opacity => scalar @polylines ? $with_polyline ? 0.4 : 0.6 @@ -2910,8 +2955,14 @@ sub startup { polylines => $json->encode( \@polylines ), color => '#673ab7', opacity => 0.8, - } + }, + { + polylines => $json->encode( \@extra_polylines ), + color => '#665577', + opacity => 0.6, + }, ], + markers => \@now_coordinates, }; if (@station_coordinates) { @@ -2925,6 +2976,13 @@ sub startup { = [ [ $min_lat, $min_lon ], [ $max_lat, $max_lon ] ]; } + if (@extra_station_coordinates) { + $ret->{station_coordinates} = [ + uniq_by { $_->[1] } + ( @station_coordinates, @extra_station_coordinates ) + ]; + } + return $ret; } ); diff --git a/lib/Travelynx/Controller/Traveling.pm b/lib/Travelynx/Controller/Traveling.pm index 6176415..a28ae98 100755 --- a/lib/Travelynx/Controller/Traveling.pm +++ b/lib/Travelynx/Controller/Traveling.pm @@ -370,7 +370,8 @@ sub homepage { my $map_data = {}; if ( $status->{arr_name} ) { $map_data = $self->journeys_to_map_data( - journeys => [$status], + journeys => [$status], + show_full_route => 1, ); } my $journey_visibility @@ -460,7 +461,8 @@ sub status_card { my $map_data = {}; if ( $status->{arr_name} ) { $map_data = $self->journeys_to_map_data( - journeys => [$status], + journeys => [$status], + show_full_route => 1, ); } my $journey_visibility @@ -2492,7 +2494,8 @@ sub edit_journey { $key => $journey->{$key}->strftime('%d.%m.%Y %H:%M:%S') ); } else { - $self->param( $key => $journey->{$key}->strftime('%d.%m.%Y %H:%M') ); + $self->param( + $key => $journey->{$key}->strftime('%d.%m.%Y %H:%M') ); } } } diff --git a/lib/Travelynx/Model/InTransit.pm b/lib/Travelynx/Model/InTransit.pm index 16946ff..40c96c8 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 = ( @@ -633,6 +634,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 ) { @@ -1549,4 +1551,135 @@ 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 ( $i_from and $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 $next_stop; + 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; + + if ( + 0 + and $now <= ( + $route[0][2]{rt_arr} // $route[0][2]{sched_arr} + // $route[0][2]{rt_dep} // $route[0][2]{sched_dep} // 0 + ) + ) + { + return [ $route[0][2]{lat}, $route[0][2]{lon} ]; + } + + 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; + if ( not $next_stop + and $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}, + ); + $next_stop = { + type => 'next', + station => $route[$i], + }; + } + $prev_ts = $ts; + } + + # Actually, the vehicle's position isn't well-known in this case. + #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}); + # 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; diff --git a/templates/_checked_in.html.ep b/templates/_checked_in.html.ep index 91f1ce7..e1cefe6 100644 --- a/templates/_checked_in.html.ep +++ b/templates/_checked_in.html.ep @@ -362,7 +362,7 @@ <span class="card-title">Karte</span> <div id="map" style="height: 70vh;"> </div> - %= include '_map', with_map_header => 0, station_coordinates => stash('station_coordinates'), polyline_groups => stash('polyline_groups') + %= include '_map', with_map_header => 0, station_coordinates => stash('station_coordinates'), polyline_groups => stash('polyline_groups'), markers => stash('markers') </div> </div> % if ($journey->{extra_data}{manual}) { diff --git a/templates/_map.html.ep b/templates/_map.html.ep index 223bd68..93f116a 100644 --- a/templates/_map.html.ep +++ b/templates/_map.html.ep @@ -39,6 +39,15 @@ var pl; % } % } +% for my $marker (@{stash('markers') // []}) { + % if ($marker->[0] and $marker->[0][0] and $marker->[1]) { + { + const marker = L.marker([<%= $marker->[0][0] %>, <%= $marker->[0][1] %>]).addTo(map); + marker.bindPopup('<%= $marker->[1] %>'); + } + % } +% } + % if (my $b = stash('bounds')) { map.fitBounds([[<%= $b->[0][0] %>,<%= $b->[0][1] %>],[<%= $b->[1][0] %>,<%= $b->[1][1] %>]]); % } @@ -48,8 +57,8 @@ for (var station_id in stations) { color: '#f03', opacity: 0.7, fillColor: '#f03', - fillOpacity: 0.5, - radius: 250 + fillOpacity: 0.2, + radius: 200 }).bindPopup(stations[station_id][1]).addTo(map); } |