summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/DBInfoscreen/Controller/Map.pm176
-rw-r--r--templates/route_map.html.ep15
2 files changed, 176 insertions, 15 deletions
diff --git a/lib/DBInfoscreen/Controller/Map.pm b/lib/DBInfoscreen/Controller/Map.pm
index 7654d44..ccffa04 100644
--- a/lib/DBInfoscreen/Controller/Map.pm
+++ b/lib/DBInfoscreen/Controller/Map.pm
@@ -4,7 +4,9 @@ use Mojo::Base 'Mojolicious::Controller';
use Mojo::JSON qw(decode_json);
use Mojo::Promise;
+use DateTime;
use DateTime::Format::Strptime;
+use Geo::Distance;
my $dbf_version = qx{git describe --dirty} || 'experimental';
@@ -75,7 +77,11 @@ sub route {
my @polyline = @{ $pl->{polyline} };
my @line_pairs;
my @station_coordinates;
+ my @route;
+ my @markers;
+
+ my $now = DateTime->now( time_zone => 'Europe/Berlin' );
my $strp = DateTime::Format::Strptime->new(
pattern => '%Y-%m-%dT%H:%M:%S.000%z',
time_zone => 'Europe/Berlin',
@@ -93,30 +99,28 @@ sub route {
for my $stop ( @{ $pl->{raw}{stopovers} // [] } ) {
my @stop_lines = ( $stop->{stop}{name} );
- my $platform;
+ my ( $platform, $arr, $dep, $arr_delay, $dep_delay );
- if ( $stop->{arrival}
- and my $arrival
- = $strp->parse_datetime( $stop->{arrival} ) )
+ if ( $stop->{arrival}
+ and $arr = $strp->parse_datetime( $stop->{arrival} ) )
{
- my $delay = $stop->{arrivalDelay} // 0;
+ $arr_delay = ( $stop->{arrivalDelay} // 0 ) / 60;
$platform //= $stop->{arrivalPlatform};
- my $arr_line = $arrival->strftime('Ankunft: %H:%M');
- if ($delay) {
- $arr_line .= sprintf( ' (%+d)', $delay / 60 );
+ my $arr_line = $arr->strftime('Ankunft: %H:%M');
+ if ($arr_delay) {
+ $arr_line .= sprintf( ' (%+d)', $arr_delay );
}
push( @stop_lines, $arr_line );
}
- if ( $stop->{departure}
- and my $departure
- = $strp->parse_datetime( $stop->{departure} ) )
+ if ( $stop->{departure}
+ and $dep = $strp->parse_datetime( $stop->{departure} ) )
{
- my $delay = $stop->{departureDelay} // 0;
+ $dep_delay = ( $stop->{departureDelay} // 0 ) / 60;
$platform //= $stop->{departurePlatform};
- my $dep_line = $departure->strftime('Abfahrt: %H:%M');
- if ($delay) {
- $dep_line .= sprintf( ' (%+d)', $delay / 60 );
+ my $dep_line = $dep->strftime('Abfahrt: %H:%M');
+ if ($dep_delay) {
+ $dep_line .= sprintf( ' (%+d)', $dep_delay );
}
push( @stop_lines, $dep_line );
}
@@ -135,6 +139,147 @@ sub route {
[@stop_lines],
]
);
+ push(
+ @route,
+ {
+ lat => $stop->{stop}{location}{latitude},
+ lon => $stop->{stop}{location}{longitude},
+ name => $stop->{stop}{name},
+ arr => $arr,
+ dep => $dep,
+ arr_delay => $arr_delay,
+ dep_delay => $dep_delay,
+ }
+ );
+
+ }
+
+ for my $i ( 1 .. $#route ) {
+ if ( $route[$i]{arr}
+ and $route[ $i - 1 ]{dep}
+ and $now > $route[ $i - 1 ]{dep}
+ and $now < $route[$i]{arr} )
+ {
+ my $from_dt = $route[ $i - 1 ]{dep};
+ my $to_dt = $route[$i]{arr};
+ my $from_name = $route[ $i - 1 ]{name};
+ my $to_name = $route[$i]{name};
+
+ my $route_part_completion_ratio
+ = ( $now->epoch - $from_dt->epoch )
+ / ( $to_dt->epoch - $from_dt->epoch );
+
+ my $title = $pl->{name};
+ if ( $route[$i]{arr_delay} ) {
+ $title .= sprintf( ' (%+d)', $route[$i]{arr_delay} );
+ }
+
+ my $geo = Geo::Distance->new;
+ my ( $from_index, $to_index );
+
+ for my $i ( 0 .. $#{ $pl->{raw}{polyline}{features} } ) {
+ my $this_point = $pl->{raw}{polyline}{features}[$i];
+ if ( not defined $from_index
+ and $this_point->{properties}{type}
+ and $this_point->{properties}{type} eq 'stop'
+ and $this_point->{properties}{name} eq $from_name )
+ {
+ $from_index = $i;
+ }
+ elsif ( $this_point->{properties}{type}
+ and $this_point->{properties}{type} eq 'stop'
+ and $this_point->{properties}{name} eq $to_name )
+ {
+ $to_index = $i;
+ last;
+ }
+ }
+ if ( $from_index and $to_index ) {
+ my $total_distance = 0;
+ for my $i ( $from_index + 1 .. $to_index ) {
+ my $prev = $pl->{raw}{polyline}{features}[ $i - 1 ]
+ {geometry}{coordinates};
+ my $this
+ = $pl->{raw}{polyline}{features}[$i]{geometry}
+ {coordinates};
+ if ( $prev and $this ) {
+ $total_distance += $geo->distance(
+ 'kilometer', $prev->[0], $prev->[1],
+ $this->[0], $this->[1]
+ );
+ }
+ }
+ my $marker_distance
+ = $total_distance * $route_part_completion_ratio;
+ $total_distance = 0;
+ for my $i ( $from_index + 1 .. $to_index ) {
+ my $prev = $pl->{raw}{polyline}{features}[ $i - 1 ]
+ {geometry}{coordinates};
+ my $this
+ = $pl->{raw}{polyline}{features}[$i]{geometry}
+ {coordinates};
+ if ( $prev and $this ) {
+ $total_distance += $geo->distance(
+ 'kilometer', $prev->[0], $prev->[1],
+ $this->[0], $this->[1]
+ );
+ }
+ if ( $total_distance > $marker_distance ) {
+ push(
+ @markers,
+ {
+ lon => $this->[0],
+ lat => $this->[1],
+ title => $title
+ }
+ );
+ last;
+ }
+ }
+ }
+ else {
+ push(
+ @markers,
+ {
+ lat => $route[ $i - 1 ]{lat} + (
+ ( $route[$i]{lat} - $route[ $i - 1 ]{lat} )
+ * $route_part_completion_ratio
+ ),
+ lon => $route[ $i - 1 ]{lon} + (
+ ( $route[$i]{lon} - $route[ $i - 1 ]{lon} )
+ * $route_part_completion_ratio
+ ),
+ title => $title
+ }
+ );
+ }
+ last;
+ }
+ if ( $route[ $i - 1 ]{dep} and $now <= $route[ $i - 1 ]{dep} ) {
+ my $title = $pl->{name};
+ if ( $route[$i]{arr_delay} ) {
+ $title .= sprintf( ' (%+d)', $route[$i]{arr_delay} );
+ }
+ push(
+ @markers,
+ {
+ lat => $route[ $i - 1 ]{lat},
+ lon => $route[ $i - 1 ]{lon},
+ title => $title
+ }
+ );
+ last;
+ }
+ }
+ if ( not @markers ) {
+ push(
+ @markers,
+ {
+ lat => $route[-1]{lat},
+ lon => $route[-1]{lon},
+ title => $route[-1]{name} . ' - Endstation',
+ }
+ );
}
$self->render(
@@ -163,6 +308,7 @@ sub route {
}
],
station_coordinates => [@station_coordinates],
+ markers => [@markers],
);
}
)->catch(
diff --git a/templates/route_map.html.ep b/templates/route_map.html.ep
index 4ba5917..4b38fc4 100644
--- a/templates/route_map.html.ep
+++ b/templates/route_map.html.ep
@@ -46,4 +46,19 @@ for (var station_id in stations) {
}).bindPopup(stations[station_id][1].join('<br/>')).addTo(map);
}
+var marker;
+% for my $marker (@{stash('markers') // [] } ) {
+ marker = L.marker([<%= $marker->{lat} %>,<%= $marker->{lon} %>]).addTo(map);
+ % if ($marker->{title}) {
+ marker.bindPopup('<%= $marker->{title} %>');
+ % }
+% }
+
</script>
+
+<div class="container" style="margin-top: 1ex; margin-bottom: 1ex; color: #555;">
+Die Zugposition auf der Karte ist eine Schätzung und kann erheblich von der
+tatsächlichen Position des Zugs abweichen.
+Live-Tracking mit automatischer Kartenaktualisierung wird noch nicht
+unterstützt.
+</div>