gtfs-to-html 2.9.0 → 2.9.2
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/config-sample.json +68 -0
- package/dist/app/index.js +2 -1
- package/dist/app/index.js.map +1 -1
- package/dist/bin/gtfs-to-html.js +16 -9
- package/dist/bin/gtfs-to-html.js.map +1 -1
- package/dist/index.js +16 -9
- package/dist/index.js.map +1 -1
- package/docker/Dockerfile +14 -0
- package/docker/README.md +5 -0
- package/docker/config.json +21 -0
- package/docker/docker-compose.yml +10 -0
- package/examples/stop_attributes.txt +6 -0
- package/examples/timetable_notes.txt +8 -0
- package/examples/timetable_notes_references.txt +8 -0
- package/examples/timetable_pages.txt +3 -0
- package/examples/timetable_stop_order.txt +16 -0
- package/examples/timetables.txt +9 -0
- package/package.json +8 -3
- package/views/default/css/overview_styles.css +198 -0
- package/views/default/css/timetable_pdf_styles.css +69 -0
- package/views/default/css/timetable_styles.css +522 -0
- package/views/default/formatting_functions.pug +104 -0
- package/views/default/js/system-map.js +594 -0
- package/views/default/js/timetable-map.js +753 -0
- package/views/default/js/timetable-menu.js +57 -0
- package/views/default/layout.pug +11 -0
- package/views/default/overview.pug +27 -0
- package/views/default/overview_full.pug +16 -0
- package/views/default/timetable_continuation_as.pug +7 -0
- package/views/default/timetable_continuation_from.pug +7 -0
- package/views/default/timetable_horizontal.pug +42 -0
- package/views/default/timetable_hourly.pug +30 -0
- package/views/default/timetable_map.pug +15 -0
- package/views/default/timetable_menu.pug +48 -0
- package/views/default/timetable_note_symbol.pug +5 -0
- package/views/default/timetable_stop_name.pug +13 -0
- package/views/default/timetable_stoptime.pug +17 -0
- package/views/default/timetable_vertical.pug +67 -0
- package/views/default/timetablepage.pug +64 -0
- package/views/default/timetablepage_full.pug +25 -0
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
/* global window, document, _, $, mapboxgl */
|
|
2
|
+
/* eslint prefer-arrow-callback: "off", no-unused-vars: "off" */
|
|
3
|
+
|
|
4
|
+
const maps = {};
|
|
5
|
+
|
|
6
|
+
function formatRouteColor(route) {
|
|
7
|
+
return route.route_color || '#000000';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function formatRouteTextColor(route) {
|
|
11
|
+
return route.route_text_color || '#FFFFFF';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function formatRoute(route) {
|
|
15
|
+
const html = route.route_url
|
|
16
|
+
? $('<a>').attr('href', route.route_url)
|
|
17
|
+
: $('<div>');
|
|
18
|
+
|
|
19
|
+
html.addClass('map-route-item');
|
|
20
|
+
|
|
21
|
+
// Only add color swatch if route has a color
|
|
22
|
+
const routeItemDivs = [];
|
|
23
|
+
|
|
24
|
+
if (route.route_color) {
|
|
25
|
+
routeItemDivs.push(
|
|
26
|
+
$('<div>')
|
|
27
|
+
.addClass('route-color-swatch')
|
|
28
|
+
.css('backgroundColor', formatRouteColor(route))
|
|
29
|
+
.css('color', formatRouteTextColor(route))
|
|
30
|
+
.text(route.route_short_name ?? ''),
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
routeItemDivs.push(
|
|
34
|
+
$('<div>')
|
|
35
|
+
.addClass('underline-hover')
|
|
36
|
+
.text(route.route_long_name ?? `Route ${route.route_short_name}`),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
html.append(routeItemDivs);
|
|
40
|
+
|
|
41
|
+
return html.prop('outerHTML');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function formatRoutePopup(features) {
|
|
45
|
+
const html = $('<div>');
|
|
46
|
+
|
|
47
|
+
if (features.length > 1) {
|
|
48
|
+
$('<div>').addClass('popup-title').text('Routes').appendTo(html);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
$(html).append(features.map((feature) => formatRoute(feature.properties)));
|
|
52
|
+
|
|
53
|
+
return html.prop('outerHTML');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatStopPopup(feature) {
|
|
57
|
+
const routes = JSON.parse(feature.properties.routes);
|
|
58
|
+
const html = $('<div>');
|
|
59
|
+
|
|
60
|
+
$('<div>')
|
|
61
|
+
.addClass('popup-title')
|
|
62
|
+
.text(feature.properties.stop_name)
|
|
63
|
+
.appendTo(html);
|
|
64
|
+
|
|
65
|
+
if (feature.properties.stop_code ?? false) {
|
|
66
|
+
$('<div>')
|
|
67
|
+
.html([
|
|
68
|
+
$('<label>').addClass('popup-label').text('Stop Code:'),
|
|
69
|
+
$('<strong>').text(feature.properties.stop_code),
|
|
70
|
+
])
|
|
71
|
+
.appendTo(html);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
$('<label>').text('Routes Served:').appendTo(html);
|
|
75
|
+
|
|
76
|
+
$(html).append(
|
|
77
|
+
$('<div>')
|
|
78
|
+
.addClass('route-list')
|
|
79
|
+
.html(routes.map((route) => formatRoute(route))),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
$('<a>')
|
|
83
|
+
.addClass('btn-blue btn-sm')
|
|
84
|
+
.prop(
|
|
85
|
+
'href',
|
|
86
|
+
`https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=${feature.geometry.coordinates[1]},${feature.geometry.coordinates[0]}&heading=0&pitch=0&fov=90`,
|
|
87
|
+
)
|
|
88
|
+
.prop('target', '_blank')
|
|
89
|
+
.prop('rel', 'noopener noreferrer')
|
|
90
|
+
.html('View on Streetview')
|
|
91
|
+
.appendTo(html);
|
|
92
|
+
|
|
93
|
+
return html.prop('outerHTML');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getBounds(geojson) {
|
|
97
|
+
const bounds = new mapboxgl.LngLatBounds();
|
|
98
|
+
for (const feature of geojson.features) {
|
|
99
|
+
if (feature.geometry.type.toLowerCase() === 'point') {
|
|
100
|
+
bounds.extend(feature.geometry.coordinates);
|
|
101
|
+
} else if (feature.geometry.type.toLowerCase() === 'linestring') {
|
|
102
|
+
for (const coordinate of feature.geometry.coordinates) {
|
|
103
|
+
bounds.extend(coordinate);
|
|
104
|
+
}
|
|
105
|
+
} else if (feature.geometry.type.toLowerCase() === 'multilinestring') {
|
|
106
|
+
for (const linestring of feature.geometry.coordinates) {
|
|
107
|
+
for (const coordinate of linestring) {
|
|
108
|
+
bounds.extend(coordinate);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return bounds;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function createSystemMap(id, geojson) {
|
|
118
|
+
const defaultRouteColor = '#000000';
|
|
119
|
+
const lineLayout = {
|
|
120
|
+
'line-join': 'round',
|
|
121
|
+
'line-cap': 'round',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
if (!geojson || geojson.features.length === 0) {
|
|
125
|
+
$('#' + id).hide();
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const bounds = getBounds(geojson);
|
|
130
|
+
const map = new mapboxgl.Map({
|
|
131
|
+
container: id,
|
|
132
|
+
style: 'mapbox://styles/mapbox/light-v11',
|
|
133
|
+
center: bounds.getCenter(),
|
|
134
|
+
zoom: 12,
|
|
135
|
+
});
|
|
136
|
+
const routes = {};
|
|
137
|
+
|
|
138
|
+
for (const feature of geojson.features) {
|
|
139
|
+
routes[feature.properties.route_id] = feature.properties;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
map.scrollZoom.disable();
|
|
143
|
+
map.addControl(new mapboxgl.NavigationControl());
|
|
144
|
+
|
|
145
|
+
map.on('load', () => {
|
|
146
|
+
map.fitBounds(bounds, {
|
|
147
|
+
padding: 20,
|
|
148
|
+
duration: 0,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Turn off Points of Interest labels
|
|
152
|
+
map.setLayoutProperty('poi-label', 'visibility', 'none');
|
|
153
|
+
|
|
154
|
+
// Find the index of the first symbol layer in the map style to put the route lines underneath
|
|
155
|
+
let firstSymbolId;
|
|
156
|
+
for (const layer of map.getStyle().layers) {
|
|
157
|
+
if (layer.type === 'symbol') {
|
|
158
|
+
firstSymbolId = layer.id;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Add route drop shadow outline first
|
|
164
|
+
map.addLayer(
|
|
165
|
+
{
|
|
166
|
+
id: 'route-line-shadows',
|
|
167
|
+
type: 'line',
|
|
168
|
+
source: {
|
|
169
|
+
type: 'geojson',
|
|
170
|
+
data: geojson,
|
|
171
|
+
},
|
|
172
|
+
paint: {
|
|
173
|
+
'line-color': '#000000',
|
|
174
|
+
'line-opacity': 0.3,
|
|
175
|
+
'line-width': {
|
|
176
|
+
base: 12,
|
|
177
|
+
stops: [
|
|
178
|
+
[14, 20],
|
|
179
|
+
[18, 42],
|
|
180
|
+
],
|
|
181
|
+
},
|
|
182
|
+
'line-blur': {
|
|
183
|
+
base: 12,
|
|
184
|
+
stops: [
|
|
185
|
+
[14, 20],
|
|
186
|
+
[18, 42],
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
layout: lineLayout,
|
|
191
|
+
filter: ['!has', 'stop_id'],
|
|
192
|
+
},
|
|
193
|
+
firstSymbolId,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Add highlighted route drop shadow outlines next
|
|
197
|
+
map.addLayer(
|
|
198
|
+
{
|
|
199
|
+
id: 'highlighted-route-line-shadows',
|
|
200
|
+
type: 'line',
|
|
201
|
+
source: {
|
|
202
|
+
type: 'geojson',
|
|
203
|
+
data: geojson,
|
|
204
|
+
},
|
|
205
|
+
paint: {
|
|
206
|
+
'line-color': '#000000',
|
|
207
|
+
'line-opacity': 0.3,
|
|
208
|
+
'line-width': {
|
|
209
|
+
base: 16,
|
|
210
|
+
stops: [
|
|
211
|
+
[14, 24],
|
|
212
|
+
[18, 50],
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
'line-blur': {
|
|
216
|
+
base: 16,
|
|
217
|
+
stops: [
|
|
218
|
+
[14, 24],
|
|
219
|
+
[18, 50],
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
layout: lineLayout,
|
|
224
|
+
filter: ['==', ['get', 'route_id'], 'none'],
|
|
225
|
+
},
|
|
226
|
+
firstSymbolId,
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
// Add white outlines to routes next
|
|
230
|
+
map.addLayer(
|
|
231
|
+
{
|
|
232
|
+
id: `route-outlines`,
|
|
233
|
+
type: 'line',
|
|
234
|
+
source: {
|
|
235
|
+
type: 'geojson',
|
|
236
|
+
data: geojson,
|
|
237
|
+
},
|
|
238
|
+
paint: {
|
|
239
|
+
'line-color': '#FFFFFF',
|
|
240
|
+
'line-opacity': 1,
|
|
241
|
+
'line-width': {
|
|
242
|
+
base: 8,
|
|
243
|
+
stops: [
|
|
244
|
+
[14, 12],
|
|
245
|
+
[18, 32],
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
layout: lineLayout,
|
|
250
|
+
filter: ['has', 'route_id'],
|
|
251
|
+
},
|
|
252
|
+
firstSymbolId,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Add route lines next
|
|
256
|
+
map.addLayer(
|
|
257
|
+
{
|
|
258
|
+
id: 'routes',
|
|
259
|
+
type: 'line',
|
|
260
|
+
source: {
|
|
261
|
+
type: 'geojson',
|
|
262
|
+
data: geojson,
|
|
263
|
+
},
|
|
264
|
+
paint: {
|
|
265
|
+
'line-color': ['coalesce', ['get', 'route_color'], defaultRouteColor],
|
|
266
|
+
'line-opacity': 1,
|
|
267
|
+
'line-width': {
|
|
268
|
+
base: 4,
|
|
269
|
+
stops: [
|
|
270
|
+
[14, 6],
|
|
271
|
+
[18, 16],
|
|
272
|
+
],
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
layout: lineLayout,
|
|
276
|
+
filter: ['has', 'route_id'],
|
|
277
|
+
},
|
|
278
|
+
firstSymbolId,
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
// Add highlighted route white outlines next
|
|
282
|
+
map.addLayer(
|
|
283
|
+
{
|
|
284
|
+
id: `highlighted-route-outlines`,
|
|
285
|
+
type: 'line',
|
|
286
|
+
source: {
|
|
287
|
+
type: 'geojson',
|
|
288
|
+
data: geojson,
|
|
289
|
+
},
|
|
290
|
+
paint: {
|
|
291
|
+
'line-color': '#FFFFFF',
|
|
292
|
+
'line-opacity': 1,
|
|
293
|
+
'line-width': {
|
|
294
|
+
base: 10,
|
|
295
|
+
stops: [
|
|
296
|
+
[14, 16],
|
|
297
|
+
[18, 40],
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
layout: lineLayout,
|
|
302
|
+
filter: ['==', ['get', 'route_id'], 'none'],
|
|
303
|
+
},
|
|
304
|
+
firstSymbolId,
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// Add highlighted route lines next
|
|
308
|
+
map.addLayer(
|
|
309
|
+
{
|
|
310
|
+
id: 'highlighted-routes',
|
|
311
|
+
type: 'line',
|
|
312
|
+
source: {
|
|
313
|
+
type: 'geojson',
|
|
314
|
+
data: geojson,
|
|
315
|
+
},
|
|
316
|
+
paint: {
|
|
317
|
+
'line-color': ['coalesce', ['get', 'route_color'], defaultRouteColor],
|
|
318
|
+
'line-opacity': 1,
|
|
319
|
+
'line-width': {
|
|
320
|
+
base: 6,
|
|
321
|
+
stops: [
|
|
322
|
+
[14, 8],
|
|
323
|
+
[18, 20],
|
|
324
|
+
],
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
layout: lineLayout,
|
|
328
|
+
filter: ['==', ['get', 'route_id'], 'none'],
|
|
329
|
+
},
|
|
330
|
+
firstSymbolId,
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// Add stops when zoomed in
|
|
334
|
+
map.addLayer({
|
|
335
|
+
id: 'stops',
|
|
336
|
+
type: 'circle',
|
|
337
|
+
source: {
|
|
338
|
+
type: 'geojson',
|
|
339
|
+
data: geojson,
|
|
340
|
+
},
|
|
341
|
+
paint: {
|
|
342
|
+
'circle-color': '#fff',
|
|
343
|
+
'circle-radius': {
|
|
344
|
+
base: 1.75,
|
|
345
|
+
stops: [
|
|
346
|
+
[12, 4],
|
|
347
|
+
[22, 100],
|
|
348
|
+
],
|
|
349
|
+
},
|
|
350
|
+
'circle-stroke-color': '#3F4A5C',
|
|
351
|
+
'circle-stroke-width': 2,
|
|
352
|
+
'circle-opacity': ['interpolate', ['linear'], ['zoom'], 13, 0, 13.5, 1],
|
|
353
|
+
'circle-stroke-opacity': [
|
|
354
|
+
'interpolate',
|
|
355
|
+
['linear'],
|
|
356
|
+
['zoom'],
|
|
357
|
+
13,
|
|
358
|
+
0,
|
|
359
|
+
13.5,
|
|
360
|
+
1,
|
|
361
|
+
],
|
|
362
|
+
},
|
|
363
|
+
filter: ['has', 'stop_id'],
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Layer for highlighted stops
|
|
367
|
+
map.addLayer({
|
|
368
|
+
id: 'stops-highlighted',
|
|
369
|
+
type: 'circle',
|
|
370
|
+
source: {
|
|
371
|
+
type: 'geojson',
|
|
372
|
+
data: geojson,
|
|
373
|
+
},
|
|
374
|
+
paint: {
|
|
375
|
+
'circle-color': '#fff',
|
|
376
|
+
'circle-radius': {
|
|
377
|
+
base: 1.75,
|
|
378
|
+
stops: [
|
|
379
|
+
[12, 5],
|
|
380
|
+
[22, 125],
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
'circle-stroke-width': 2,
|
|
384
|
+
'circle-stroke-color': '#3f4a5c',
|
|
385
|
+
'circle-opacity': ['interpolate', ['linear'], ['zoom'], 13, 0, 13.5, 1],
|
|
386
|
+
'circle-stroke-opacity': [
|
|
387
|
+
'interpolate',
|
|
388
|
+
['linear'],
|
|
389
|
+
['zoom'],
|
|
390
|
+
13,
|
|
391
|
+
0,
|
|
392
|
+
13.5,
|
|
393
|
+
1,
|
|
394
|
+
],
|
|
395
|
+
},
|
|
396
|
+
filter: ['==', 'stop_id', ''],
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Add labels
|
|
400
|
+
map.addLayer({
|
|
401
|
+
id: 'route-labels',
|
|
402
|
+
type: 'symbol',
|
|
403
|
+
source: {
|
|
404
|
+
type: 'geojson',
|
|
405
|
+
data: geojson,
|
|
406
|
+
},
|
|
407
|
+
layout: {
|
|
408
|
+
'symbol-placement': 'line',
|
|
409
|
+
'text-field': ['get', 'route_short_name'],
|
|
410
|
+
'text-size': 14,
|
|
411
|
+
},
|
|
412
|
+
paint: {
|
|
413
|
+
'text-color': '#000000',
|
|
414
|
+
'text-halo-width': 2,
|
|
415
|
+
'text-halo-color': '#ffffff',
|
|
416
|
+
},
|
|
417
|
+
filter: ['has', 'route_short_name'],
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
map.on('mousemove', (event) => {
|
|
421
|
+
const features = map.queryRenderedFeatures(event.point, {
|
|
422
|
+
layers: ['routes', 'route-outlines', 'stops-highlighted', 'stops'],
|
|
423
|
+
});
|
|
424
|
+
if (features.length > 0) {
|
|
425
|
+
map.getCanvas().style.cursor = 'pointer';
|
|
426
|
+
highlightRoutes(
|
|
427
|
+
_.compact(
|
|
428
|
+
_.uniq(features.map((feature) => feature.properties.route_id)),
|
|
429
|
+
),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
if (features.some((feature) => feature.layer.id === 'stops')) {
|
|
433
|
+
highlightStop(
|
|
434
|
+
features.find((feature) => feature.layer.id === 'stops').properties
|
|
435
|
+
.stop_id,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
map.getCanvas().style.cursor = '';
|
|
440
|
+
unHighlightRoutes();
|
|
441
|
+
unHighlightStop();
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
map.on('click', (event) => {
|
|
446
|
+
// Set bbox as 5px rectangle area around clicked point
|
|
447
|
+
const bbox = [
|
|
448
|
+
[event.point.x - 5, event.point.y - 5],
|
|
449
|
+
[event.point.x + 5, event.point.y + 5],
|
|
450
|
+
];
|
|
451
|
+
|
|
452
|
+
const stopFeatures = map.queryRenderedFeatures(bbox, {
|
|
453
|
+
layers: ['stops-highlighted', 'stops'],
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
if (stopFeatures && stopFeatures.length > 0) {
|
|
457
|
+
// Get the stop feature and show popup
|
|
458
|
+
const stopFeature = stopFeatures[0];
|
|
459
|
+
|
|
460
|
+
new mapboxgl.Popup()
|
|
461
|
+
.setLngLat(stopFeature.geometry.coordinates)
|
|
462
|
+
.setHTML(formatStopPopup(stopFeature))
|
|
463
|
+
.addTo(map);
|
|
464
|
+
} else {
|
|
465
|
+
const routeFeatures = map.queryRenderedFeatures(bbox, {
|
|
466
|
+
layers: ['routes', 'route-outlines'],
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
if (routeFeatures && routeFeatures.length > 0) {
|
|
470
|
+
const routes = _.orderBy(
|
|
471
|
+
_.uniqBy(
|
|
472
|
+
routeFeatures,
|
|
473
|
+
(feature) => feature.properties.route_short_name,
|
|
474
|
+
),
|
|
475
|
+
(feature) =>
|
|
476
|
+
Number.parseInt(feature.properties.route_short_name, 10),
|
|
477
|
+
);
|
|
478
|
+
|
|
479
|
+
new mapboxgl.Popup()
|
|
480
|
+
.setLngLat(event.lngLat)
|
|
481
|
+
.setHTML(formatRoutePopup(routes))
|
|
482
|
+
.addTo(map);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
function highlightStop(stopId) {
|
|
488
|
+
map.setFilter('stops-highlighted', ['==', 'stop_id', stopId]);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function unHighlightStop() {
|
|
492
|
+
map.setFilter('stops-highlighted', ['==', 'stop_id', '']);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function highlightRoutes(routeIds, zoom) {
|
|
496
|
+
map.setFilter('highlighted-routes', [
|
|
497
|
+
'all',
|
|
498
|
+
['has', 'route_short_name'],
|
|
499
|
+
['in', ['get', 'route_id'], ['literal', routeIds]],
|
|
500
|
+
]);
|
|
501
|
+
map.setFilter('highlighted-route-outlines', [
|
|
502
|
+
'all',
|
|
503
|
+
['has', 'route_short_name'],
|
|
504
|
+
['in', ['get', 'route_id'], ['literal', routeIds]],
|
|
505
|
+
]);
|
|
506
|
+
map.setFilter('highlighted-route-line-shadows', [
|
|
507
|
+
'all',
|
|
508
|
+
['has', 'route_short_name'],
|
|
509
|
+
['in', ['get', 'route_id'], ['literal', routeIds]],
|
|
510
|
+
]);
|
|
511
|
+
|
|
512
|
+
// Show labels only for highlighted route
|
|
513
|
+
map.setFilter('route-labels', [
|
|
514
|
+
'in',
|
|
515
|
+
['get', 'route_id'],
|
|
516
|
+
['literal', routeIds],
|
|
517
|
+
]);
|
|
518
|
+
|
|
519
|
+
const routeLineOpacity = 0.4;
|
|
520
|
+
|
|
521
|
+
// De-emphasize other routes
|
|
522
|
+
map.setPaintProperty('routes', 'line-opacity', routeLineOpacity);
|
|
523
|
+
map.setPaintProperty('route-outlines', 'line-opacity', routeLineOpacity);
|
|
524
|
+
map.setPaintProperty(
|
|
525
|
+
'route-line-shadows',
|
|
526
|
+
'line-opacity',
|
|
527
|
+
routeLineOpacity,
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
const highlightedFeatures = geojson.features.filter((feature) =>
|
|
531
|
+
routeIds.includes(feature.properties.route_id),
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
if (highlightedFeatures.length > 0 && zoom) {
|
|
535
|
+
const zoomBounds = getBounds({
|
|
536
|
+
features: highlightedFeatures,
|
|
537
|
+
});
|
|
538
|
+
map.fitBounds(zoomBounds, {
|
|
539
|
+
padding: 20,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function unHighlightRoutes(zoom) {
|
|
545
|
+
map.setFilter('highlighted-routes', ['==', ['get', 'route_id'], 'none']);
|
|
546
|
+
map.setFilter('highlighted-route-outlines', [
|
|
547
|
+
'==',
|
|
548
|
+
['get', 'route_id'],
|
|
549
|
+
'none',
|
|
550
|
+
]);
|
|
551
|
+
map.setFilter('highlighted-route-line-shadows', [
|
|
552
|
+
'==',
|
|
553
|
+
['get', 'route_id'],
|
|
554
|
+
'none',
|
|
555
|
+
]);
|
|
556
|
+
|
|
557
|
+
// Show labels for all routes
|
|
558
|
+
map.setFilter('route-labels', ['has', 'route_short_name']);
|
|
559
|
+
|
|
560
|
+
const routeLineOpacity = 1;
|
|
561
|
+
|
|
562
|
+
// Re-emphasize other routes
|
|
563
|
+
map.setPaintProperty('routes', 'line-opacity', routeLineOpacity);
|
|
564
|
+
map.setPaintProperty('route-outlines', 'line-opacity', routeLineOpacity);
|
|
565
|
+
map.setPaintProperty(
|
|
566
|
+
'route-line-shadows',
|
|
567
|
+
'line-opacity',
|
|
568
|
+
routeLineOpacity,
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
if (zoom) {
|
|
572
|
+
map.fitBounds(bounds);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// On table hover, highlight route on map
|
|
577
|
+
$(() => {
|
|
578
|
+
$('.overview-list a').hover((event) => {
|
|
579
|
+
const routeIdString = $(event.target).data('route-ids');
|
|
580
|
+
if (routeIdString) {
|
|
581
|
+
const routeIds = routeIdString.toString().split(',');
|
|
582
|
+
highlightRoutes(routeIds, true);
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
$('.overview-list').hover(
|
|
587
|
+
() => {},
|
|
588
|
+
() => unHighlightRoutes(true),
|
|
589
|
+
);
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
maps[id] = map;
|
|
594
|
+
}
|