gtfs-to-html 2.9.14 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/config-sample.json +0 -1
- package/dist/app/index.js +104 -55
- package/dist/app/index.js.map +1 -1
- package/dist/bin/gtfs-to-html.js +29 -17
- package/dist/bin/gtfs-to-html.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +29 -17
- package/dist/index.js.map +1 -1
- package/package.json +10 -11
- package/views/default/css/overview_styles.css +11 -9
- package/views/default/css/timetable_styles.css +17 -15
- package/views/default/formatting_functions.pug +2 -1
- package/views/default/js/system-map.js +492 -400
- package/views/default/js/timetable-map.js +390 -286
- package/views/default/overview.pug +3 -4
- package/views/default/overview_full.pug +4 -4
- package/views/default/timetablepage.pug +1 -1
- package/views/default/timetablepage_full.pug +2 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* global document, jQuery,
|
|
1
|
+
/* global document, jQuery, maplibregl, Pbf, mapStyleUrl, stopData, routeData, routeIds, tripIds, geojsons, gtfsRealtimeUrls */
|
|
2
2
|
/* eslint prefer-arrow-callback: "off", no-unused-vars: "off" */
|
|
3
3
|
|
|
4
4
|
const maps = {};
|
|
@@ -93,13 +93,83 @@ function getStopPopupHtml(feature, stop) {
|
|
|
93
93
|
if (stop.stop_code ?? false) {
|
|
94
94
|
jQuery('<div>')
|
|
95
95
|
.html([
|
|
96
|
-
jQuery('<
|
|
96
|
+
jQuery('<div>').addClass('popup-label').text('Stop Code:'),
|
|
97
97
|
jQuery('<strong>').text(stop.stop_code),
|
|
98
98
|
])
|
|
99
99
|
.appendTo(html);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
if (tripUpdates) {
|
|
103
|
+
const stopTimeUpdates = {
|
|
104
|
+
0: [],
|
|
105
|
+
1: [],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
for (const tripUpdate of tripUpdates) {
|
|
109
|
+
const stopTimeUpdatesForStop =
|
|
110
|
+
tripUpdate.trip_update.stop_time_update.filter(
|
|
111
|
+
(stopTimeUpdate) =>
|
|
112
|
+
stopTimeUpdate.stop_id === stop.stop_id &&
|
|
113
|
+
(stopTimeUpdate.departure !== null ||
|
|
114
|
+
stopTimeUpdate.arrival !== null) &&
|
|
115
|
+
stopTimeUpdate.schedule_relationship !== 3,
|
|
116
|
+
);
|
|
117
|
+
if (stopTimeUpdatesForStop.length > 0) {
|
|
118
|
+
stopTimeUpdates[tripUpdate.trip_update.trip.direction_id].push(
|
|
119
|
+
...stopTimeUpdatesForStop,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
stopTimeUpdates['0'].sort((a, b) => {
|
|
125
|
+
const timeA = a.departure ? a.departure.time : a.arrival.time;
|
|
126
|
+
const timeB = b.departure ? b.departure.time : b.arrival.time;
|
|
127
|
+
return timeA - timeB;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
stopTimeUpdates['1'].sort((a, b) => {
|
|
131
|
+
const timeA = a.departure ? a.departure.time : a.arrival.time;
|
|
132
|
+
const timeB = b.departure ? b.departure.time : b.arrival.time;
|
|
133
|
+
return timeA - timeB;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (stopTimeUpdates['0'].length > 0 || stopTimeUpdates['1'].length > 0) {
|
|
137
|
+
jQuery('<div>')
|
|
138
|
+
.addClass('popup-label')
|
|
139
|
+
.text('Upcoming Departures:')
|
|
140
|
+
.appendTo(html);
|
|
141
|
+
|
|
142
|
+
for (const direction of ['0', '1']) {
|
|
143
|
+
if (stopTimeUpdates[direction].length > 0) {
|
|
144
|
+
const directionName = jQuery(
|
|
145
|
+
`.timetable[data-direction-id="${direction}"]`,
|
|
146
|
+
).data('direction-name');
|
|
147
|
+
const departureTimes = stopTimeUpdates[direction].map(
|
|
148
|
+
(stopTimeUpdate) =>
|
|
149
|
+
Math.round(
|
|
150
|
+
((stopTimeUpdate.departure
|
|
151
|
+
? stopTimeUpdate.departure.time
|
|
152
|
+
: stopTimeUpdate.arrival.time) -
|
|
153
|
+
Date.now() / 1000) /
|
|
154
|
+
60,
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Only use the next 4 departures
|
|
159
|
+
const formattedDepartures = new Intl.ListFormat('en', {
|
|
160
|
+
style: 'long',
|
|
161
|
+
type: 'conjunction',
|
|
162
|
+
}).format(departureTimes.slice(0, 4).map((time) => `<b>${time}</b>`));
|
|
163
|
+
|
|
164
|
+
jQuery('<div>')
|
|
165
|
+
.html(`<b>${directionName}</b> in ${formattedDepartures} min`)
|
|
166
|
+
.appendTo(html);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
jQuery('<div>').addClass('popup-label').text('Routes Served:').appendTo(html);
|
|
103
173
|
|
|
104
174
|
jQuery(html).append(
|
|
105
175
|
jQuery('<div>')
|
|
@@ -122,7 +192,7 @@ function getStopPopupHtml(feature, stop) {
|
|
|
122
192
|
}
|
|
123
193
|
|
|
124
194
|
function getBounds(geojson) {
|
|
125
|
-
const bounds = new
|
|
195
|
+
const bounds = new maplibregl.LngLatBounds();
|
|
126
196
|
for (const feature of geojson.features) {
|
|
127
197
|
if (feature.geometry.type.toLowerCase() === 'point') {
|
|
128
198
|
bounds.extend(feature.geometry.coordinates);
|
|
@@ -389,7 +459,10 @@ function addVehicleMarker(vehiclePosition, vehicleTripUpdate) {
|
|
|
389
459
|
];
|
|
390
460
|
|
|
391
461
|
// Add marker to map
|
|
392
|
-
const vehicleMarker = new
|
|
462
|
+
const vehicleMarker = new maplibregl.Marker({
|
|
463
|
+
element: el,
|
|
464
|
+
anchor: 'center',
|
|
465
|
+
})
|
|
393
466
|
.setLngLat(coordinates)
|
|
394
467
|
.addTo(maps[visibleTimetableId]);
|
|
395
468
|
|
|
@@ -502,10 +575,7 @@ async function updateArrivals() {
|
|
|
502
575
|
|
|
503
576
|
jQuery('.vehicle-legend-item').show();
|
|
504
577
|
|
|
505
|
-
vehiclePositions = latestVehiclePositions
|
|
506
|
-
tripUpdates = latestTripUpdates;
|
|
507
|
-
|
|
508
|
-
const routeVehiclePositions = vehiclePositions.filter((vehiclePosition) => {
|
|
578
|
+
vehiclePositions = latestVehiclePositions.filter((vehiclePosition) => {
|
|
509
579
|
if (
|
|
510
580
|
!vehiclePosition ||
|
|
511
581
|
!vehiclePosition.vehicle ||
|
|
@@ -534,7 +604,19 @@ async function updateArrivals() {
|
|
|
534
604
|
return tripIds.includes(vehiclePosition.vehicle.trip.trip_id);
|
|
535
605
|
});
|
|
536
606
|
|
|
537
|
-
|
|
607
|
+
tripUpdates = latestTripUpdates.filter((tripUpdate) => {
|
|
608
|
+
if (
|
|
609
|
+
!tripUpdate ||
|
|
610
|
+
!tripUpdate.trip_update ||
|
|
611
|
+
!tripUpdate.trip_update.trip
|
|
612
|
+
) {
|
|
613
|
+
return false;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return tripIds.includes(tripUpdate.trip_update.trip.trip_id);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
for (const vehiclePosition of vehiclePositions) {
|
|
538
620
|
const vehicleId = vehiclePosition.vehicle.vehicle.id;
|
|
539
621
|
|
|
540
622
|
let vehicleTripUpdate = tripUpdates?.find(
|
|
@@ -575,7 +657,7 @@ async function updateArrivals() {
|
|
|
575
657
|
// Remove vehicles not in the feed
|
|
576
658
|
for (const vehicleId of Object.keys(vehicleMarkers)) {
|
|
577
659
|
if (
|
|
578
|
-
!
|
|
660
|
+
!vehiclePositions.find(
|
|
579
661
|
(vehiclePosition) => vehiclePosition.vehicle.vehicle.id === vehicleId,
|
|
580
662
|
)
|
|
581
663
|
) {
|
|
@@ -630,319 +712,332 @@ function createMap(id) {
|
|
|
630
712
|
}
|
|
631
713
|
|
|
632
714
|
const bounds = getBounds(geojson);
|
|
633
|
-
const map = new
|
|
715
|
+
const map = new maplibregl.Map({
|
|
634
716
|
container: `map_timetable_id_${id}`,
|
|
635
|
-
style:
|
|
717
|
+
style: mapStyleUrl,
|
|
636
718
|
center: bounds.getCenter(),
|
|
637
719
|
zoom: 12,
|
|
638
720
|
preserveDrawingBuffer: true,
|
|
639
721
|
});
|
|
640
722
|
|
|
641
|
-
map.initialize = () =>
|
|
642
|
-
map.fitBounds(bounds, {
|
|
643
|
-
padding: {
|
|
644
|
-
top: 40,
|
|
645
|
-
bottom: 40,
|
|
646
|
-
left: 20,
|
|
647
|
-
right: 40,
|
|
648
|
-
},
|
|
649
|
-
duration: 0,
|
|
650
|
-
});
|
|
723
|
+
map.initialize = () => fitMapToBounds(map, bounds);
|
|
651
724
|
|
|
652
725
|
map.scrollZoom.disable();
|
|
653
|
-
map.addControl(new
|
|
726
|
+
map.addControl(new maplibregl.NavigationControl());
|
|
727
|
+
map.addControl(new maplibregl.FullscreenControl());
|
|
654
728
|
|
|
655
729
|
map.on('load', () => {
|
|
656
|
-
map
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
right: 40,
|
|
662
|
-
},
|
|
663
|
-
duration: 0,
|
|
664
|
-
});
|
|
730
|
+
fitMapToBounds(map, bounds);
|
|
731
|
+
disablePointsOfInterest(map);
|
|
732
|
+
addMapLayers(map, geojson, defaultRouteColor, lineLayout);
|
|
733
|
+
setupEventListeners(map, id);
|
|
734
|
+
});
|
|
665
735
|
|
|
666
|
-
|
|
667
|
-
|
|
736
|
+
return map;
|
|
737
|
+
}
|
|
668
738
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
}
|
|
676
|
-
}
|
|
739
|
+
function fitMapToBounds(map, bounds) {
|
|
740
|
+
map.fitBounds(bounds, {
|
|
741
|
+
padding: { top: 40, bottom: 40, left: 20, right: 40 },
|
|
742
|
+
duration: 0,
|
|
743
|
+
});
|
|
744
|
+
}
|
|
677
745
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
paint: {
|
|
688
|
-
'line-color': '#000000',
|
|
689
|
-
'line-opacity': 0.3,
|
|
690
|
-
'line-width': {
|
|
691
|
-
base: 12,
|
|
692
|
-
stops: [
|
|
693
|
-
[14, 20],
|
|
694
|
-
[18, 42],
|
|
695
|
-
],
|
|
696
|
-
},
|
|
697
|
-
'line-blur': {
|
|
698
|
-
base: 12,
|
|
699
|
-
stops: [
|
|
700
|
-
[14, 20],
|
|
701
|
-
[18, 42],
|
|
702
|
-
],
|
|
703
|
-
},
|
|
704
|
-
},
|
|
705
|
-
layout: lineLayout,
|
|
706
|
-
filter: ['!has', 'stop_id'],
|
|
707
|
-
},
|
|
708
|
-
firstSymbolId,
|
|
709
|
-
);
|
|
746
|
+
function disablePointsOfInterest(map) {
|
|
747
|
+
const layers = map.getStyle().layers;
|
|
748
|
+
const poiLayerIds = layers
|
|
749
|
+
.filter((layer) => layer.id.startsWith('poi'))
|
|
750
|
+
?.map((layer) => layer.id);
|
|
751
|
+
poiLayerIds.forEach((layerId) => {
|
|
752
|
+
map.setLayoutProperty(layerId, 'visibility', 'none');
|
|
753
|
+
});
|
|
754
|
+
}
|
|
710
755
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
'line-width': {
|
|
724
|
-
base: 8,
|
|
725
|
-
stops: [
|
|
726
|
-
[14, 12],
|
|
727
|
-
[18, 32],
|
|
728
|
-
],
|
|
729
|
-
},
|
|
730
|
-
},
|
|
731
|
-
layout: lineLayout,
|
|
732
|
-
filter: ['!has', 'stop_id'],
|
|
733
|
-
},
|
|
734
|
-
firstSymbolId,
|
|
735
|
-
);
|
|
756
|
+
function addMapLayers(map, geojson, defaultRouteColor, lineLayout) {
|
|
757
|
+
const layers = map.getStyle().layers;
|
|
758
|
+
const firstLabelLayerId = layers.find(
|
|
759
|
+
(layer) => layer.type === 'symbol' && layer.id.includes('label'),
|
|
760
|
+
)?.id;
|
|
761
|
+
|
|
762
|
+
addRouteLineShadow(map, geojson, lineLayout, firstLabelLayerId);
|
|
763
|
+
addRouteLineOutline(map, geojson, lineLayout, firstLabelLayerId);
|
|
764
|
+
addRouteLine(map, geojson, defaultRouteColor, lineLayout, firstLabelLayerId);
|
|
765
|
+
addStops(map, geojson);
|
|
766
|
+
addHighlightedStops(map, geojson);
|
|
767
|
+
}
|
|
736
768
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
769
|
+
function addRouteLineShadow(map, geojson, lineLayout, firstSymbolId) {
|
|
770
|
+
map.addLayer(
|
|
771
|
+
{
|
|
772
|
+
id: 'route-line-shadow',
|
|
773
|
+
type: 'line',
|
|
774
|
+
source: { type: 'geojson', data: geojson },
|
|
775
|
+
paint: {
|
|
776
|
+
'line-color': '#000000',
|
|
777
|
+
'line-opacity': 0.3,
|
|
778
|
+
'line-width': {
|
|
779
|
+
base: 12,
|
|
780
|
+
stops: [
|
|
781
|
+
[14, 20],
|
|
782
|
+
[18, 42],
|
|
783
|
+
],
|
|
745
784
|
},
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
[14, 6],
|
|
753
|
-
[18, 16],
|
|
754
|
-
],
|
|
755
|
-
},
|
|
785
|
+
'line-blur': {
|
|
786
|
+
base: 12,
|
|
787
|
+
stops: [
|
|
788
|
+
[14, 20],
|
|
789
|
+
[18, 42],
|
|
790
|
+
],
|
|
756
791
|
},
|
|
757
|
-
layout: lineLayout,
|
|
758
|
-
filter: ['!has', 'stop_id'],
|
|
759
792
|
},
|
|
760
|
-
|
|
761
|
-
|
|
793
|
+
layout: lineLayout,
|
|
794
|
+
filter: ['!has', 'stop_id'],
|
|
795
|
+
},
|
|
796
|
+
firstSymbolId,
|
|
797
|
+
);
|
|
798
|
+
}
|
|
762
799
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
data: geojson,
|
|
770
|
-
},
|
|
800
|
+
function addRouteLineOutline(map, geojson, lineLayout, firstSymbolId) {
|
|
801
|
+
map.addLayer(
|
|
802
|
+
{
|
|
803
|
+
id: 'route-line-outline',
|
|
804
|
+
type: 'line',
|
|
805
|
+
source: { type: 'geojson', data: geojson },
|
|
771
806
|
paint: {
|
|
772
|
-
'
|
|
773
|
-
'
|
|
774
|
-
|
|
807
|
+
'line-color': '#FFFFFF',
|
|
808
|
+
'line-opacity': 1,
|
|
809
|
+
'line-width': {
|
|
810
|
+
base: 8,
|
|
775
811
|
stops: [
|
|
776
|
-
[
|
|
777
|
-
[
|
|
812
|
+
[14, 12],
|
|
813
|
+
[18, 32],
|
|
778
814
|
],
|
|
779
815
|
},
|
|
780
|
-
'circle-stroke-color': '#3f4a5c',
|
|
781
|
-
'circle-stroke-width': 2,
|
|
782
816
|
},
|
|
783
|
-
|
|
784
|
-
|
|
817
|
+
layout: lineLayout,
|
|
818
|
+
filter: ['!has', 'stop_id'],
|
|
819
|
+
},
|
|
820
|
+
firstSymbolId,
|
|
821
|
+
);
|
|
822
|
+
}
|
|
785
823
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
824
|
+
function addRouteLine(
|
|
825
|
+
map,
|
|
826
|
+
geojson,
|
|
827
|
+
defaultRouteColor,
|
|
828
|
+
lineLayout,
|
|
829
|
+
firstSymbolId,
|
|
830
|
+
) {
|
|
831
|
+
map.addLayer(
|
|
832
|
+
{
|
|
833
|
+
id: 'route-line',
|
|
834
|
+
type: 'line',
|
|
835
|
+
source: { type: 'geojson', data: geojson },
|
|
794
836
|
paint: {
|
|
795
|
-
'
|
|
796
|
-
'
|
|
797
|
-
|
|
837
|
+
'line-color': ['to-color', ['get', 'route_color'], defaultRouteColor],
|
|
838
|
+
'line-opacity': 1,
|
|
839
|
+
'line-width': {
|
|
840
|
+
base: 4,
|
|
798
841
|
stops: [
|
|
799
|
-
[
|
|
800
|
-
[
|
|
842
|
+
[14, 6],
|
|
843
|
+
[18, 16],
|
|
801
844
|
],
|
|
802
845
|
},
|
|
803
|
-
'circle-stroke-width': 2,
|
|
804
|
-
'circle-stroke-color': '#3f4a5c',
|
|
805
846
|
},
|
|
806
|
-
|
|
807
|
-
|
|
847
|
+
layout: lineLayout,
|
|
848
|
+
filter: ['!has', 'stop_id'],
|
|
849
|
+
},
|
|
850
|
+
firstSymbolId,
|
|
851
|
+
);
|
|
852
|
+
}
|
|
808
853
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
854
|
+
function addStops(map, geojson) {
|
|
855
|
+
map.addLayer({
|
|
856
|
+
id: 'stops',
|
|
857
|
+
type: 'circle',
|
|
858
|
+
source: { type: 'geojson', data: geojson },
|
|
859
|
+
paint: {
|
|
860
|
+
'circle-color': '#ffffff',
|
|
861
|
+
'circle-radius': {
|
|
862
|
+
base: 1.75,
|
|
863
|
+
stops: [
|
|
864
|
+
[12, 4],
|
|
865
|
+
[22, 100],
|
|
866
|
+
],
|
|
867
|
+
},
|
|
868
|
+
'circle-stroke-color': '#3f4a5c',
|
|
869
|
+
'circle-stroke-width': 2,
|
|
870
|
+
},
|
|
871
|
+
filter: ['has', 'stop_id'],
|
|
872
|
+
});
|
|
873
|
+
}
|
|
819
874
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
875
|
+
function addHighlightedStops(map, geojson) {
|
|
876
|
+
map.addLayer({
|
|
877
|
+
id: 'stops-highlighted',
|
|
878
|
+
type: 'circle',
|
|
879
|
+
source: { type: 'geojson', data: geojson },
|
|
880
|
+
paint: {
|
|
881
|
+
'circle-color': '#f8f8b9',
|
|
882
|
+
'circle-radius': {
|
|
883
|
+
base: 1.75,
|
|
884
|
+
stops: [
|
|
885
|
+
[12, 6],
|
|
886
|
+
[22, 150],
|
|
887
|
+
],
|
|
888
|
+
},
|
|
889
|
+
'circle-stroke-width': 2,
|
|
890
|
+
'circle-stroke-color': '#3f4a5c',
|
|
891
|
+
},
|
|
892
|
+
filter: ['==', 'stop_id', ''],
|
|
893
|
+
});
|
|
894
|
+
}
|
|
826
895
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
];
|
|
833
|
-
const features = map.queryRenderedFeatures(bbox, {
|
|
834
|
-
layers: ['stops-highlighted', 'stops'],
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
if (!features || features.length === 0) {
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
896
|
+
function setupEventListeners(map, id) {
|
|
897
|
+
map.on('mousemove', (event) => handleMouseMove(event, map, id));
|
|
898
|
+
map.on('click', (event) => handleClick(event, map));
|
|
899
|
+
setupTableHoverListeners(id, map);
|
|
900
|
+
}
|
|
840
901
|
|
|
841
|
-
|
|
842
|
-
|
|
902
|
+
function handleMouseMove(event, map, id) {
|
|
903
|
+
const features = map.queryRenderedFeatures(event.point, {
|
|
904
|
+
layers: ['stops'],
|
|
905
|
+
});
|
|
906
|
+
if (features.length > 0) {
|
|
907
|
+
map.getCanvas().style.cursor = 'pointer';
|
|
908
|
+
const stopIds = [features[0].properties.stop_id];
|
|
909
|
+
if (features[0].properties.parent_station) {
|
|
910
|
+
stopIds.push(features[0].properties.parent_station);
|
|
911
|
+
}
|
|
912
|
+
highlightStop(map, id, stopIds);
|
|
913
|
+
} else {
|
|
914
|
+
map.getCanvas().style.cursor = '';
|
|
915
|
+
unHighlightStop(map, id);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
843
918
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
919
|
+
function handleClick(event, map) {
|
|
920
|
+
const bbox = [
|
|
921
|
+
[event.point.x - 5, event.point.y - 5],
|
|
922
|
+
[event.point.x + 5, event.point.y + 5],
|
|
923
|
+
];
|
|
924
|
+
const features = map.queryRenderedFeatures(bbox, {
|
|
925
|
+
layers: ['stops-highlighted', 'stops'],
|
|
926
|
+
});
|
|
851
927
|
|
|
852
|
-
|
|
853
|
-
map.setFilter('stops-highlighted', [
|
|
854
|
-
'any',
|
|
855
|
-
['in', 'stop_id', ...stopIds],
|
|
856
|
-
['in', 'parent_station', ...stopIds],
|
|
857
|
-
]);
|
|
928
|
+
if (!features || features.length === 0) return;
|
|
858
929
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
const columnIndexes = [];
|
|
863
|
-
const stopIdSelectors = stopIds
|
|
864
|
-
.map(
|
|
865
|
-
(stopId) =>
|
|
866
|
-
`#timetable_id_${id} table colgroup col[data-stop-id="${stopId}"]`,
|
|
867
|
-
)
|
|
868
|
-
.join(',');
|
|
869
|
-
jQuery(stopIdSelectors).each((index, col) => {
|
|
870
|
-
columnIndexes.push(
|
|
871
|
-
jQuery(`#timetable_id_${id} table colgroup col`).index(col),
|
|
872
|
-
);
|
|
873
|
-
});
|
|
930
|
+
const feature = features[0];
|
|
931
|
+
showStopPopup(map, feature);
|
|
932
|
+
}
|
|
874
933
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
jQuery(`#timetable_id_${id} table .trip-row`).each((index, row) => {
|
|
882
|
-
jQuery('.stop-time', row).each((index, el) => {
|
|
883
|
-
if (columnIndexes.includes(index)) {
|
|
884
|
-
jQuery(el).addClass('highlighted');
|
|
885
|
-
}
|
|
886
|
-
});
|
|
887
|
-
});
|
|
934
|
+
function showStopPopup(map, feature) {
|
|
935
|
+
new maplibregl.Popup()
|
|
936
|
+
.setLngLat(feature.geometry.coordinates)
|
|
937
|
+
.setHTML(getStopPopupHtml(feature, stopData[feature.properties.stop_id]))
|
|
938
|
+
.addTo(map);
|
|
939
|
+
}
|
|
888
940
|
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
});
|
|
896
|
-
} else {
|
|
897
|
-
jQuery(`#timetable_id_${id} table .stop-row`).removeClass(
|
|
898
|
-
'highlighted',
|
|
899
|
-
);
|
|
900
|
-
const stopIdSelectors = stopIds
|
|
901
|
-
.map((stopId) => `#timetable_id_${id} table #stop_id_${stopId}`)
|
|
902
|
-
.join(',');
|
|
903
|
-
jQuery(stopIdSelectors).addClass('highlighted');
|
|
904
|
-
}
|
|
905
|
-
}
|
|
941
|
+
function highlightStop(map, id, stopIds) {
|
|
942
|
+
map.setFilter('stops-highlighted', [
|
|
943
|
+
'any',
|
|
944
|
+
['in', 'stop_id', ...stopIds],
|
|
945
|
+
['in', 'parent_station', ...stopIds],
|
|
946
|
+
]);
|
|
906
947
|
|
|
907
|
-
|
|
908
|
-
|
|
948
|
+
highlightTimetableStops(id, stopIds);
|
|
949
|
+
}
|
|
909
950
|
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
'highlighted',
|
|
915
|
-
);
|
|
916
|
-
jQuery(`#timetable_id_${id} table thead .stop-header`).removeClass(
|
|
917
|
-
'highlighted',
|
|
918
|
-
);
|
|
919
|
-
} else {
|
|
920
|
-
jQuery(`#timetable_id_${id} table .stop-row`).removeClass(
|
|
921
|
-
'highlighted',
|
|
922
|
-
);
|
|
923
|
-
}
|
|
924
|
-
}
|
|
951
|
+
function unHighlightStop(map, id) {
|
|
952
|
+
map.setFilter('stops-highlighted', ['==', 'stop_id', '']);
|
|
953
|
+
unHighlightTimetableStops(id);
|
|
954
|
+
}
|
|
925
955
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
956
|
+
function highlightTimetableStops(id, stopIds) {
|
|
957
|
+
const table = jQuery(`#timetable_id_${id} table`);
|
|
958
|
+
const isVertical = table.data('orientation') === 'vertical';
|
|
959
|
+
|
|
960
|
+
if (isVertical) {
|
|
961
|
+
highlightVerticalTimetableStops(id, stopIds);
|
|
962
|
+
} else {
|
|
963
|
+
highlightHorizontalTimetableStops(id, stopIds);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
936
966
|
|
|
937
|
-
|
|
938
|
-
|
|
967
|
+
function highlightVerticalTimetableStops(id, stopIds) {
|
|
968
|
+
const table = jQuery(`#timetable_id_${id} table`);
|
|
969
|
+
const columnIndexes = [];
|
|
970
|
+
const stopIdSelectors = stopIds
|
|
971
|
+
.map(
|
|
972
|
+
(stopId) =>
|
|
973
|
+
`#timetable_id_${id} table colgroup col[data-stop-id="${stopId}"]`,
|
|
974
|
+
)
|
|
975
|
+
.join(',');
|
|
976
|
+
|
|
977
|
+
jQuery(stopIdSelectors).each((index, col) => {
|
|
978
|
+
columnIndexes.push(
|
|
979
|
+
jQuery(`#timetable_id_${id} table colgroup col`).index(col),
|
|
980
|
+
);
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
table.find('.stop-time, thead .stop-header').removeClass('highlighted');
|
|
984
|
+
table.find('.trip-row').each((index, row) => {
|
|
985
|
+
jQuery('.stop-time', row).each((index, el) => {
|
|
986
|
+
if (columnIndexes.includes(index)) {
|
|
987
|
+
jQuery(el).addClass('highlighted');
|
|
939
988
|
}
|
|
989
|
+
});
|
|
990
|
+
});
|
|
940
991
|
|
|
941
|
-
|
|
942
|
-
|
|
992
|
+
table.find('thead').each((index, thead) => {
|
|
993
|
+
jQuery('.stop-header', thead).each((index, el) => {
|
|
994
|
+
if (columnIndexes.includes(index)) {
|
|
995
|
+
jQuery(el).addClass('highlighted');
|
|
996
|
+
}
|
|
997
|
+
});
|
|
943
998
|
});
|
|
999
|
+
}
|
|
944
1000
|
|
|
945
|
-
|
|
1001
|
+
function highlightHorizontalTimetableStops(id, stopIds) {
|
|
1002
|
+
const table = jQuery(`#timetable_id_${id} table`);
|
|
1003
|
+
table.find('.stop-row').removeClass('highlighted');
|
|
1004
|
+
const stopIdSelectors = stopIds
|
|
1005
|
+
.map((stopId) => `#timetable_id_${id} table #stop_id_${stopId}`)
|
|
1006
|
+
.join(',');
|
|
1007
|
+
jQuery(stopIdSelectors).addClass('highlighted');
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
function unHighlightTimetableStops(id) {
|
|
1011
|
+
const table = jQuery(`#timetable_id_${id} table`);
|
|
1012
|
+
const isVertical = table.data('orientation') === 'vertical';
|
|
1013
|
+
|
|
1014
|
+
if (isVertical) {
|
|
1015
|
+
table.find('.stop-time, thead .stop-header').removeClass('highlighted');
|
|
1016
|
+
} else {
|
|
1017
|
+
table.find('.stop-row').removeClass('highlighted');
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function setupTableHoverListeners(id, map) {
|
|
1022
|
+
jQuery('th, td', jQuery(`#timetable_id_${id} table`)).hover(
|
|
1023
|
+
(event) => {
|
|
1024
|
+
const stopId = getStopIdFromTableCell(event.target);
|
|
1025
|
+
if (stopId !== undefined) {
|
|
1026
|
+
highlightStop(map, id, [stopId.toString()]);
|
|
1027
|
+
}
|
|
1028
|
+
},
|
|
1029
|
+
() => unHighlightStop(map, id),
|
|
1030
|
+
);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
function getStopIdFromTableCell(cell) {
|
|
1034
|
+
const table = jQuery(cell).closest('table');
|
|
1035
|
+
if (table.data('orientation') === 'vertical') {
|
|
1036
|
+
const index = jQuery(cell).index();
|
|
1037
|
+
return jQuery('colgroup col', table).eq(index).data('stop-id');
|
|
1038
|
+
} else {
|
|
1039
|
+
return jQuery(cell).closest('tr').data('stop-id');
|
|
1040
|
+
}
|
|
946
1041
|
}
|
|
947
1042
|
|
|
948
1043
|
function createMaps() {
|
|
@@ -956,18 +1051,27 @@ function createMaps() {
|
|
|
956
1051
|
gtfsRealtimeUrls?.realtimeVehiclePositions?.url
|
|
957
1052
|
) {
|
|
958
1053
|
// Popup for realtime vehicle locations
|
|
959
|
-
|
|
1054
|
+
const markerHeight = 20;
|
|
1055
|
+
const markerRadius = 10;
|
|
1056
|
+
const linearOffset = 15;
|
|
1057
|
+
vehiclePopup = new maplibregl.Popup({
|
|
960
1058
|
closeOnClick: false,
|
|
961
1059
|
className: 'vehicle-popup',
|
|
962
1060
|
offset: {
|
|
963
|
-
top: [0,
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
'
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1061
|
+
top: [0, 0],
|
|
1062
|
+
'top-left': [0, 0],
|
|
1063
|
+
'top-right': [0, 0],
|
|
1064
|
+
bottom: [0, -markerHeight],
|
|
1065
|
+
'bottom-left': [
|
|
1066
|
+
linearOffset,
|
|
1067
|
+
(markerHeight - markerRadius + linearOffset) * -1,
|
|
1068
|
+
],
|
|
1069
|
+
'bottom-right': [
|
|
1070
|
+
-linearOffset,
|
|
1071
|
+
(markerHeight - markerRadius + linearOffset) * -1,
|
|
1072
|
+
],
|
|
1073
|
+
left: [markerRadius, (markerHeight - markerRadius) * -1],
|
|
1074
|
+
right: [-markerRadius, (markerHeight - markerRadius) * -1],
|
|
971
1075
|
},
|
|
972
1076
|
});
|
|
973
1077
|
|