Unverified Commit d6cca3f8 authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

in transit: show estimated train position and full route (greyed out)

parent 24a01da9
Loading
Loading
Loading
Loading
+64 −6
Original line number Diff line number Diff line
@@ -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;
		}
	);
+6 −3
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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') );
			}
		}
	}
+133 −0
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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}) {
+11 −2
Original line number Diff line number Diff line
@@ -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);
}