summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xlib/Travelynx.pm70
-rwxr-xr-xlib/Travelynx/Controller/Traveling.pm9
-rw-r--r--lib/Travelynx/Model/InTransit.pm133
-rw-r--r--templates/_checked_in.html.ep2
-rw-r--r--templates/_map.html.ep13
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);
}