Loading lib/Travelynx.pm +64 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -2900,7 +2945,7 @@ sub startup { { polylines => $json->encode( \@station_pairs ), color => '#673ab7', opacity => @polylines opacity => scalar @polylines ? $with_polyline ? 0.4 : 0.6 Loading @@ -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) { Loading @@ -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; } ); Loading lib/Travelynx/Controller/Traveling.pm +6 −3 Original line number Diff line number Diff line Loading @@ -371,6 +371,7 @@ sub homepage { if ( $status->{arr_name} ) { $map_data = $self->journeys_to_map_data( journeys => [$status], show_full_route => 1, ); } my $journey_visibility Loading Loading @@ -461,6 +462,7 @@ sub status_card { if ( $status->{arr_name} ) { $map_data = $self->journeys_to_map_data( journeys => [$status], show_full_route => 1, ); } my $journey_visibility Loading Loading @@ -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') ); } } } Loading lib/Travelynx/Model/InTransit.pm +133 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ use warnings; use 5.020; use DateTime; use GIS::Distance; use JSON; my %visibility_itoa = ( Loading Loading @@ -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 ) { Loading Loading @@ -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; templates/_checked_in.html.ep +1 −1 Original line number Diff line number Diff line Loading @@ -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}) { Loading templates/_map.html.ep +11 −2 Original line number Diff line number Diff line Loading @@ -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] %>]]); % } Loading @@ -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); } Loading Loading
lib/Travelynx.pm +64 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -2900,7 +2945,7 @@ sub startup { { polylines => $json->encode( \@station_pairs ), color => '#673ab7', opacity => @polylines opacity => scalar @polylines ? $with_polyline ? 0.4 : 0.6 Loading @@ -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) { Loading @@ -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; } ); Loading
lib/Travelynx/Controller/Traveling.pm +6 −3 Original line number Diff line number Diff line Loading @@ -371,6 +371,7 @@ sub homepage { if ( $status->{arr_name} ) { $map_data = $self->journeys_to_map_data( journeys => [$status], show_full_route => 1, ); } my $journey_visibility Loading Loading @@ -461,6 +462,7 @@ sub status_card { if ( $status->{arr_name} ) { $map_data = $self->journeys_to_map_data( journeys => [$status], show_full_route => 1, ); } my $journey_visibility Loading Loading @@ -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') ); } } } Loading
lib/Travelynx/Model/InTransit.pm +133 −0 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ use warnings; use 5.020; use DateTime; use GIS::Distance; use JSON; my %visibility_itoa = ( Loading Loading @@ -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 ) { Loading Loading @@ -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;
templates/_checked_in.html.ep +1 −1 Original line number Diff line number Diff line Loading @@ -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}) { Loading
templates/_map.html.ep +11 −2 Original line number Diff line number Diff line Loading @@ -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] %>]]); % } Loading @@ -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); } Loading