gtfs-to-html 2.3.5 → 2.4.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/CHANGELOG.md +11 -0
- package/lib/geojson-utils.js +6 -1
- package/package.json +9 -9
- package/public/css/timetable_styles.css +4 -16
- package/public/js/system-map.js +408 -80
- package/public/js/timetable-map.js +72 -27
- package/views/default/layout.pug +2 -1
- package/views/default/overview.pug +2 -2
- package/views/default/timetablepage.pug +1 -1
- package/www/docs/related-libraries.md +5 -0
- package/www/package.json +4 -4
- package/www/yarn.lock +1143 -1101
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.4.0] - 2022-06-07
|
|
9
|
+
|
|
10
|
+
### Updated
|
|
11
|
+
|
|
12
|
+
- Dependency updates
|
|
13
|
+
- Add route labels and stops to system map
|
|
14
|
+
- Turn off points of interest labels on all maps
|
|
15
|
+
- Better map data styles
|
|
16
|
+
- Improved system map route and stop highlighting
|
|
17
|
+
- Updated to tailwindcss 3
|
|
18
|
+
|
|
8
19
|
## [2.3.5] - 2022-04-26
|
|
9
20
|
|
|
10
21
|
### Updated
|
package/lib/geojson-utils.js
CHANGED
|
@@ -91,6 +91,11 @@ export async function getTimetableGeoJSON(timetable, config) {
|
|
|
91
91
|
* Get the geoJSON for an agency (all routes and stops).
|
|
92
92
|
*/
|
|
93
93
|
export async function getAgencyGeoJSON(config) {
|
|
94
|
-
const
|
|
94
|
+
const [shapesGeojsons, stopsGeojsons] = await Promise.all([
|
|
95
|
+
getShapesAsGeoJSON(),
|
|
96
|
+
getStopsAsGeoJSON(),
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
const geojson = mergeGeojson(shapesGeojsons, stopsGeojsons);
|
|
95
100
|
return simplifyGeoJSON(geojson, config);
|
|
96
101
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gtfs-to-html",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Build human readable transit timetables as HTML, PDF or CSV from GTFS",
|
|
6
6
|
"keywords": [
|
|
@@ -41,28 +41,28 @@
|
|
|
41
41
|
"chalk": "^5.0.1",
|
|
42
42
|
"cli-table": "^0.3.11",
|
|
43
43
|
"copy-dir": "^1.3.0",
|
|
44
|
-
"csv-stringify": "^6.0
|
|
45
|
-
"express": "^4.18.
|
|
46
|
-
"gtfs": "^3.3.
|
|
44
|
+
"csv-stringify": "^6.1.0",
|
|
45
|
+
"express": "^4.18.1",
|
|
46
|
+
"gtfs": "^3.3.1",
|
|
47
47
|
"js-beautify": "^1.14.3",
|
|
48
48
|
"lodash-es": "^4.17.21",
|
|
49
49
|
"moment": "^2.29.3",
|
|
50
50
|
"morgan": "^1.10.0",
|
|
51
51
|
"pretty-error": "^4.0.0",
|
|
52
52
|
"pug": "^3.0.2",
|
|
53
|
-
"puppeteer": "^
|
|
53
|
+
"puppeteer": "^14.2.1",
|
|
54
54
|
"sanitize-filename": "^1.6.3",
|
|
55
55
|
"sqlstring": "^2.3.3",
|
|
56
56
|
"timer-machine": "^1.1.0",
|
|
57
57
|
"toposort": "^2.0.2",
|
|
58
58
|
"untildify": "^4.0.0",
|
|
59
|
-
"yargs": "^17.
|
|
59
|
+
"yargs": "^17.5.1"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"eslint": "^8.
|
|
62
|
+
"eslint": "^8.17.0",
|
|
63
63
|
"eslint-config-prettier": "^8.5.0",
|
|
64
|
-
"eslint-config-xo": "^0.
|
|
65
|
-
"husky": "^
|
|
64
|
+
"eslint-config-xo": "^0.41.0",
|
|
65
|
+
"husky": "^8.0.1",
|
|
66
66
|
"prettier": "^2.6.2",
|
|
67
67
|
"pretty-quick": "^3.1.3"
|
|
68
68
|
},
|
|
@@ -70,7 +70,7 @@ a:hover {
|
|
|
70
70
|
|
|
71
71
|
.timetable .table-container {
|
|
72
72
|
overflow-x: scroll;
|
|
73
|
-
margin
|
|
73
|
+
margin: 20px 0;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
.timetable .table-container .table {
|
|
@@ -93,11 +93,12 @@ a:hover {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
.timetable thead tr {
|
|
96
|
-
background: #
|
|
96
|
+
background: #aed9fb;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
.timetable thead tr,
|
|
99
100
|
.timetable thead tr a {
|
|
100
|
-
color: #
|
|
101
|
+
color: #222222;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
.timetable th {
|
|
@@ -257,16 +258,3 @@ a:hover {
|
|
|
257
258
|
align-items: center;
|
|
258
259
|
line-height: 1;
|
|
259
260
|
}
|
|
260
|
-
|
|
261
|
-
/* Overview page */
|
|
262
|
-
|
|
263
|
-
.overview-menu-item {
|
|
264
|
-
display: block;
|
|
265
|
-
text-decoration: none;
|
|
266
|
-
padding: 5px;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
.overview-menu-item:hover {
|
|
270
|
-
text-decoration: none;
|
|
271
|
-
background: #bdd5fa;
|
|
272
|
-
}
|
package/public/js/system-map.js
CHANGED
|
@@ -36,6 +36,28 @@ function formatRoutePopup(features) {
|
|
|
36
36
|
return html.prop('outerHTML');
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function formatStopPopup(feature) {
|
|
40
|
+
const routes = JSON.parse(feature.properties.routes);
|
|
41
|
+
const html = $('<div>');
|
|
42
|
+
|
|
43
|
+
$('<div>')
|
|
44
|
+
.addClass('popup-title')
|
|
45
|
+
.text(feature.properties.stop_name)
|
|
46
|
+
.appendTo(html);
|
|
47
|
+
|
|
48
|
+
if (feature.properties.stop_code ?? false) {
|
|
49
|
+
$('<label>').addClass('mr-1').text('Stop Code:').appendTo(html);
|
|
50
|
+
|
|
51
|
+
$('<strong>').text(feature.properties.stop_code).appendTo(html);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
$('<div>').text('Routes Served:').appendTo(html);
|
|
55
|
+
|
|
56
|
+
$(html).append(routes.map((route) => formatRoute(route)));
|
|
57
|
+
|
|
58
|
+
return html.prop('outerHTML');
|
|
59
|
+
}
|
|
60
|
+
|
|
39
61
|
function getBounds(geojson) {
|
|
40
62
|
const bounds = new mapboxgl.LngLatBounds();
|
|
41
63
|
for (const feature of geojson.features) {
|
|
@@ -53,8 +75,10 @@ function getBounds(geojson) {
|
|
|
53
75
|
|
|
54
76
|
function createSystemMap(id, geojson) {
|
|
55
77
|
const defaultRouteColor = '#FF4728';
|
|
56
|
-
const
|
|
57
|
-
|
|
78
|
+
const lineLayout = {
|
|
79
|
+
'line-join': 'round',
|
|
80
|
+
'line-cap': 'round',
|
|
81
|
+
};
|
|
58
82
|
|
|
59
83
|
if (!geojson || geojson.features.length === 0) {
|
|
60
84
|
$('#' + id).hide();
|
|
@@ -83,6 +107,9 @@ function createSystemMap(id, geojson) {
|
|
|
83
107
|
duration: 0,
|
|
84
108
|
});
|
|
85
109
|
|
|
110
|
+
// Turn of Points of Interest labels
|
|
111
|
+
map.setLayoutProperty('poi-label', 'visibility', 'none');
|
|
112
|
+
|
|
86
113
|
// Find the index of the first symbol layer in the map style
|
|
87
114
|
let firstSymbolId;
|
|
88
115
|
for (const layer of map.getStyle().layers) {
|
|
@@ -92,120 +119,400 @@ function createSystemMap(id, geojson) {
|
|
|
92
119
|
}
|
|
93
120
|
}
|
|
94
121
|
|
|
95
|
-
// Add
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
type: '
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
// Add route drop shadow outline first
|
|
123
|
+
map.addLayer(
|
|
124
|
+
{
|
|
125
|
+
id: 'route-line-shadows',
|
|
126
|
+
type: 'line',
|
|
127
|
+
source: {
|
|
128
|
+
type: 'geojson',
|
|
129
|
+
data: geojson,
|
|
130
|
+
},
|
|
131
|
+
paint: {
|
|
132
|
+
'line-color': '#000000',
|
|
133
|
+
'line-opacity': 0.3,
|
|
134
|
+
'line-width': {
|
|
135
|
+
base: 12,
|
|
136
|
+
stops: [
|
|
137
|
+
[14, 20],
|
|
138
|
+
[18, 42],
|
|
139
|
+
],
|
|
105
140
|
},
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
141
|
+
'line-blur': {
|
|
142
|
+
base: 12,
|
|
143
|
+
stops: [
|
|
144
|
+
[14, 20],
|
|
145
|
+
[18, 42],
|
|
146
|
+
],
|
|
110
147
|
},
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
148
|
+
},
|
|
149
|
+
layout: lineLayout,
|
|
150
|
+
filter: ['!has', 'stop_id'],
|
|
151
|
+
},
|
|
152
|
+
firstSymbolId
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Add highlighted route drop shadow outlines next
|
|
156
|
+
map.addLayer(
|
|
157
|
+
{
|
|
158
|
+
id: 'highlighted-route-line-shadows',
|
|
159
|
+
type: 'line',
|
|
160
|
+
source: {
|
|
161
|
+
type: 'geojson',
|
|
162
|
+
data: geojson,
|
|
163
|
+
},
|
|
164
|
+
paint: {
|
|
165
|
+
'line-color': '#000000',
|
|
166
|
+
'line-opacity': 0.3,
|
|
167
|
+
'line-width': {
|
|
168
|
+
base: 16,
|
|
169
|
+
stops: [
|
|
170
|
+
[14, 24],
|
|
171
|
+
[18, 50],
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
'line-blur': {
|
|
175
|
+
base: 16,
|
|
176
|
+
stops: [
|
|
177
|
+
[14, 24],
|
|
178
|
+
[18, 50],
|
|
179
|
+
],
|
|
114
180
|
},
|
|
115
|
-
filter: ['==', 'route_id', routeId],
|
|
116
181
|
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
182
|
+
layout: lineLayout,
|
|
183
|
+
filter: ['==', ['get', 'route_id'], 'none'],
|
|
184
|
+
},
|
|
185
|
+
firstSymbolId
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// Add white outlines to routes next
|
|
189
|
+
map.addLayer(
|
|
190
|
+
{
|
|
191
|
+
id: `route-outlines`,
|
|
192
|
+
type: 'line',
|
|
193
|
+
source: {
|
|
194
|
+
type: 'geojson',
|
|
195
|
+
data: geojson,
|
|
196
|
+
},
|
|
197
|
+
paint: {
|
|
198
|
+
'line-color': '#FFFFFF',
|
|
199
|
+
'line-opacity': 1,
|
|
200
|
+
'line-width': {
|
|
201
|
+
base: 8,
|
|
202
|
+
stops: [
|
|
203
|
+
[14, 12],
|
|
204
|
+
[18, 32],
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
layout: lineLayout,
|
|
209
|
+
filter: ['has', 'route_id'],
|
|
210
|
+
},
|
|
211
|
+
firstSymbolId
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// Add highlighted route white outlines next
|
|
215
|
+
map.addLayer(
|
|
216
|
+
{
|
|
217
|
+
id: `highlighted-route-outlines`,
|
|
218
|
+
type: 'line',
|
|
219
|
+
source: {
|
|
220
|
+
type: 'geojson',
|
|
221
|
+
data: geojson,
|
|
222
|
+
},
|
|
223
|
+
paint: {
|
|
224
|
+
'line-color': '#FFFFFF',
|
|
225
|
+
'line-opacity': 1,
|
|
226
|
+
'line-width': {
|
|
227
|
+
base: 10,
|
|
228
|
+
stops: [
|
|
229
|
+
[14, 16],
|
|
230
|
+
[18, 40],
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
layout: lineLayout,
|
|
235
|
+
filter: ['==', ['get', 'route_id'], 'none'],
|
|
236
|
+
},
|
|
237
|
+
firstSymbolId
|
|
238
|
+
);
|
|
120
239
|
|
|
121
240
|
// Add route lines next
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
241
|
+
map.addLayer(
|
|
242
|
+
{
|
|
243
|
+
id: 'routes',
|
|
244
|
+
type: 'line',
|
|
245
|
+
source: {
|
|
246
|
+
type: 'geojson',
|
|
247
|
+
data: geojson,
|
|
248
|
+
},
|
|
249
|
+
paint: {
|
|
250
|
+
'line-color': ['coalesce', ['get', 'route_color'], defaultRouteColor],
|
|
251
|
+
'line-opacity': 1,
|
|
252
|
+
'line-width': {
|
|
253
|
+
base: 4,
|
|
254
|
+
stops: [
|
|
255
|
+
[14, 6],
|
|
256
|
+
[18, 16],
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
layout: lineLayout,
|
|
261
|
+
filter: ['has', 'route_id'],
|
|
262
|
+
},
|
|
263
|
+
firstSymbolId
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Add highlighted route lines next
|
|
267
|
+
map.addLayer(
|
|
268
|
+
{
|
|
269
|
+
id: 'highlighted-routes',
|
|
270
|
+
type: 'line',
|
|
271
|
+
source: {
|
|
272
|
+
type: 'geojson',
|
|
273
|
+
data: geojson,
|
|
274
|
+
},
|
|
275
|
+
paint: {
|
|
276
|
+
'line-color': ['coalesce', ['get', 'route_color'], defaultRouteColor],
|
|
277
|
+
'line-opacity': 1,
|
|
278
|
+
'line-width': {
|
|
279
|
+
base: 6,
|
|
280
|
+
stops: [
|
|
281
|
+
[14, 8],
|
|
282
|
+
[18, 20],
|
|
283
|
+
],
|
|
132
284
|
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
285
|
+
},
|
|
286
|
+
layout: lineLayout,
|
|
287
|
+
filter: ['==', ['get', 'route_id'], 'none'],
|
|
288
|
+
},
|
|
289
|
+
firstSymbolId
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Add stops when zoomed in
|
|
293
|
+
map.addLayer(
|
|
294
|
+
{
|
|
295
|
+
id: 'stops',
|
|
296
|
+
type: 'circle',
|
|
297
|
+
source: {
|
|
298
|
+
type: 'geojson',
|
|
299
|
+
data: geojson,
|
|
300
|
+
},
|
|
301
|
+
paint: {
|
|
302
|
+
'circle-color': '#fff',
|
|
303
|
+
'circle-radius': {
|
|
304
|
+
base: 1.75,
|
|
305
|
+
stops: [
|
|
306
|
+
[12, 4],
|
|
307
|
+
[22, 100],
|
|
308
|
+
],
|
|
137
309
|
},
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
310
|
+
'circle-stroke-color': '#3F4A5C',
|
|
311
|
+
'circle-stroke-width': 2,
|
|
312
|
+
'circle-opacity': [
|
|
313
|
+
'interpolate',
|
|
314
|
+
['linear'],
|
|
315
|
+
['zoom'],
|
|
316
|
+
13,
|
|
317
|
+
0,
|
|
318
|
+
13.5,
|
|
319
|
+
1,
|
|
320
|
+
],
|
|
321
|
+
'circle-stroke-opacity': [
|
|
322
|
+
'interpolate',
|
|
323
|
+
['linear'],
|
|
324
|
+
['zoom'],
|
|
325
|
+
13,
|
|
326
|
+
0,
|
|
327
|
+
13.5,
|
|
328
|
+
1,
|
|
329
|
+
],
|
|
330
|
+
},
|
|
331
|
+
filter: ['has', 'stop_id'],
|
|
332
|
+
},
|
|
333
|
+
firstSymbolId
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Layer for highlighted stops
|
|
337
|
+
map.addLayer(
|
|
338
|
+
{
|
|
339
|
+
id: 'stops-highlighted',
|
|
340
|
+
type: 'circle',
|
|
341
|
+
source: {
|
|
342
|
+
type: 'geojson',
|
|
343
|
+
data: geojson,
|
|
344
|
+
},
|
|
345
|
+
paint: {
|
|
346
|
+
'circle-color': '#fff',
|
|
347
|
+
'circle-radius': {
|
|
348
|
+
base: 1.75,
|
|
349
|
+
stops: [
|
|
350
|
+
[12, 5],
|
|
351
|
+
[22, 125],
|
|
352
|
+
],
|
|
141
353
|
},
|
|
142
|
-
|
|
354
|
+
'circle-stroke-width': 2,
|
|
355
|
+
'circle-stroke-color': '#3f4a5c',
|
|
356
|
+
'circle-opacity': [
|
|
357
|
+
'interpolate',
|
|
358
|
+
['linear'],
|
|
359
|
+
['zoom'],
|
|
360
|
+
13,
|
|
361
|
+
0,
|
|
362
|
+
13.5,
|
|
363
|
+
1,
|
|
364
|
+
],
|
|
365
|
+
'circle-stroke-opacity': [
|
|
366
|
+
'interpolate',
|
|
367
|
+
['linear'],
|
|
368
|
+
['zoom'],
|
|
369
|
+
13,
|
|
370
|
+
0,
|
|
371
|
+
13.5,
|
|
372
|
+
1,
|
|
373
|
+
],
|
|
143
374
|
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
375
|
+
filter: ['==', 'stop_id', ''],
|
|
376
|
+
},
|
|
377
|
+
firstSymbolId
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
// Add labels
|
|
381
|
+
map.addLayer({
|
|
382
|
+
id: 'route-labels',
|
|
383
|
+
type: 'symbol',
|
|
384
|
+
source: {
|
|
385
|
+
type: 'geojson',
|
|
386
|
+
data: geojson,
|
|
387
|
+
},
|
|
388
|
+
layout: {
|
|
389
|
+
'symbol-placement': 'line',
|
|
390
|
+
'text-field': ['get', 'route_short_name'],
|
|
391
|
+
'text-size': 14,
|
|
392
|
+
},
|
|
393
|
+
paint: {
|
|
394
|
+
'text-color': '#000000',
|
|
395
|
+
'text-halo-width': 2,
|
|
396
|
+
'text-halo-color': '#ffffff',
|
|
397
|
+
},
|
|
398
|
+
filter: ['has', 'route_short_name'],
|
|
399
|
+
});
|
|
147
400
|
|
|
148
401
|
map.on('mousemove', (event) => {
|
|
149
402
|
const features = map.queryRenderedFeatures(event.point, {
|
|
150
|
-
layers: [
|
|
403
|
+
layers: ['routes', 'route-outlines', 'stops-highlighted', 'stops'],
|
|
151
404
|
});
|
|
152
405
|
if (features.length > 0) {
|
|
153
406
|
map.getCanvas().style.cursor = 'pointer';
|
|
154
407
|
highlightRoutes(
|
|
155
|
-
_.
|
|
408
|
+
_.compact(
|
|
409
|
+
_.uniq(features.map((feature) => feature.properties.route_id))
|
|
410
|
+
)
|
|
156
411
|
);
|
|
412
|
+
|
|
413
|
+
if (features.some((feature) => feature.layer.id === 'stops')) {
|
|
414
|
+
highlightStop(
|
|
415
|
+
features.find((feature) => feature.layer.id === 'stops').properties
|
|
416
|
+
.stop_id
|
|
417
|
+
);
|
|
418
|
+
}
|
|
157
419
|
} else {
|
|
158
420
|
map.getCanvas().style.cursor = '';
|
|
159
421
|
unHighlightRoutes();
|
|
422
|
+
unHighlightStop();
|
|
160
423
|
}
|
|
161
424
|
});
|
|
162
425
|
|
|
163
426
|
map.on('click', (event) => {
|
|
164
|
-
// Set bbox as 5px
|
|
427
|
+
// Set bbox as 5px rectangle area around clicked point
|
|
165
428
|
const bbox = [
|
|
166
429
|
[event.point.x - 5, event.point.y - 5],
|
|
167
430
|
[event.point.x + 5, event.point.y + 5],
|
|
168
431
|
];
|
|
169
|
-
|
|
170
|
-
|
|
432
|
+
|
|
433
|
+
const stopFeatures = map.queryRenderedFeatures(bbox, {
|
|
434
|
+
layers: ['stops-highlighted', 'stops'],
|
|
171
435
|
});
|
|
172
436
|
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
437
|
+
if (stopFeatures && stopFeatures.length > 0) {
|
|
438
|
+
// Get the stop feature and show popup
|
|
439
|
+
const stopFeature = stopFeatures[0];
|
|
176
440
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
441
|
+
new mapboxgl.Popup()
|
|
442
|
+
.setLngLat(stopFeature.geometry.coordinates)
|
|
443
|
+
.setHTML(formatStopPopup(stopFeature))
|
|
444
|
+
.addTo(map);
|
|
445
|
+
} else {
|
|
446
|
+
const routeFeatures = map.queryRenderedFeatures(bbox, {
|
|
447
|
+
layers: ['routes', 'route-outlines'],
|
|
448
|
+
});
|
|
181
449
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
450
|
+
if (routeFeatures && routeFeatures.length > 0) {
|
|
451
|
+
const routes = _.orderBy(
|
|
452
|
+
_.uniqBy(
|
|
453
|
+
routeFeatures,
|
|
454
|
+
(feature) => feature.properties.route_short_name
|
|
455
|
+
),
|
|
456
|
+
(feature) =>
|
|
457
|
+
Number.parseInt(feature.properties.route_short_name, 10)
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
new mapboxgl.Popup()
|
|
461
|
+
.setLngLat(event.lngLat)
|
|
462
|
+
.setHTML(formatRoutePopup(routes))
|
|
463
|
+
.addTo(map);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
186
466
|
});
|
|
187
467
|
|
|
468
|
+
function highlightStop(stopId) {
|
|
469
|
+
map.setFilter('stops-highlighted', ['==', 'stop_id', stopId]);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function unHighlightStop() {
|
|
473
|
+
map.setFilter('stops-highlighted', ['==', 'stop_id', '']);
|
|
474
|
+
}
|
|
475
|
+
|
|
188
476
|
function highlightRoutes(routeIds, zoom) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
477
|
+
map.setFilter('highlighted-routes', [
|
|
478
|
+
'all',
|
|
479
|
+
['has', 'route_short_name'],
|
|
480
|
+
['in', ['get', 'route_id'], ['literal', routeIds]],
|
|
481
|
+
]);
|
|
482
|
+
map.setFilter('highlighted-route-outlines', [
|
|
483
|
+
'all',
|
|
484
|
+
['has', 'route_short_name'],
|
|
485
|
+
['in', ['get', 'route_id'], ['literal', routeIds]],
|
|
486
|
+
]);
|
|
487
|
+
map.setFilter('highlighted-route-line-shadows', [
|
|
488
|
+
'all',
|
|
489
|
+
['has', 'route_short_name'],
|
|
490
|
+
['in', ['get', 'route_id'], ['literal', routeIds]],
|
|
491
|
+
]);
|
|
492
|
+
|
|
493
|
+
// Show labels only for highlighted route
|
|
494
|
+
map.setFilter('route-labels', [
|
|
495
|
+
'in',
|
|
496
|
+
['get', 'route_id'],
|
|
497
|
+
['literal', routeIds],
|
|
498
|
+
]);
|
|
499
|
+
|
|
500
|
+
const routeLineOpacity = 0.4;
|
|
501
|
+
|
|
502
|
+
// De-emphasize other routes
|
|
503
|
+
map.setPaintProperty('routes', 'line-opacity', routeLineOpacity);
|
|
504
|
+
map.setPaintProperty('route-outlines', 'line-opacity', routeLineOpacity);
|
|
505
|
+
map.setPaintProperty(
|
|
506
|
+
'route-line-shadows',
|
|
507
|
+
'line-opacity',
|
|
508
|
+
routeLineOpacity
|
|
509
|
+
);
|
|
199
510
|
|
|
200
511
|
const highlightedFeatures = geojson.features.filter((feature) =>
|
|
201
512
|
routeIds.includes(feature.properties.route_id)
|
|
202
513
|
);
|
|
203
514
|
|
|
204
|
-
if (highlightedFeatures.length
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (zoom) {
|
|
515
|
+
if (highlightedFeatures.length > 0 && zoom) {
|
|
209
516
|
const zoomBounds = getBounds({
|
|
210
517
|
features: highlightedFeatures,
|
|
211
518
|
});
|
|
@@ -216,10 +523,31 @@ function createSystemMap(id, geojson) {
|
|
|
216
523
|
}
|
|
217
524
|
|
|
218
525
|
function unHighlightRoutes(zoom) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
526
|
+
map.setFilter('highlighted-routes', ['==', ['get', 'route_id'], 'none']);
|
|
527
|
+
map.setFilter('highlighted-route-outlines', [
|
|
528
|
+
'==',
|
|
529
|
+
['get', 'route_id'],
|
|
530
|
+
'none',
|
|
531
|
+
]);
|
|
532
|
+
map.setFilter('highlighted-route-line-shadows', [
|
|
533
|
+
'==',
|
|
534
|
+
['get', 'route_id'],
|
|
535
|
+
'none',
|
|
536
|
+
]);
|
|
537
|
+
|
|
538
|
+
// Show labels for all routes
|
|
539
|
+
map.setFilter('route-labels', ['has', 'route_short_name']);
|
|
540
|
+
|
|
541
|
+
const routeLineOpacity = 1;
|
|
542
|
+
|
|
543
|
+
// Re-emphasize other routes
|
|
544
|
+
map.setPaintProperty('routes', 'line-opacity', routeLineOpacity);
|
|
545
|
+
map.setPaintProperty('route-outlines', 'line-opacity', routeLineOpacity);
|
|
546
|
+
map.setPaintProperty(
|
|
547
|
+
'route-line-shadows',
|
|
548
|
+
'line-opacity',
|
|
549
|
+
routeLineOpacity
|
|
550
|
+
);
|
|
223
551
|
|
|
224
552
|
if (zoom) {
|
|
225
553
|
map.fitBounds(bounds);
|
|
@@ -229,7 +557,7 @@ function createSystemMap(id, geojson) {
|
|
|
229
557
|
// On table hover, highlight route on map
|
|
230
558
|
$(() => {
|
|
231
559
|
$('.overview-list a').hover((event) => {
|
|
232
|
-
const routeIdString = $(event.target).
|
|
560
|
+
const routeIdString = $(event.target).data('route-ids');
|
|
233
561
|
if (routeIdString) {
|
|
234
562
|
const routeIds = routeIdString.toString().split(',');
|
|
235
563
|
highlightRoutes(routeIds, true);
|