gtfs-to-html 2.3.5 → 2.4.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.
@@ -63,6 +63,10 @@ function getBounds(geojson) {
63
63
 
64
64
  function createMap(id, geojson, routes) {
65
65
  const defaultRouteColor = '#FF4728';
66
+ const lineLayout = {
67
+ 'line-join': 'round',
68
+ 'line-cap': 'round',
69
+ };
66
70
 
67
71
  if (!geojson || geojson.features.length === 0) {
68
72
  $('#map_' + id).hide();
@@ -92,6 +96,9 @@ function createMap(id, geojson, routes) {
92
96
  duration: 0,
93
97
  });
94
98
 
99
+ // Turn of Points of Interest labels
100
+ map.setLayoutProperty('poi-label', 'visibility', 'none');
101
+
95
102
  // Find the index of the first symbol layer in the map style
96
103
  let firstSymbolId;
97
104
  for (const layer of map.getStyle().layers) {
@@ -101,7 +108,40 @@ function createMap(id, geojson, routes) {
101
108
  }
102
109
  }
103
110
 
104
- // Add route line outline first
111
+ // Add route drop shadow outline first
112
+ map.addLayer(
113
+ {
114
+ id: 'route-line-shadow',
115
+ type: 'line',
116
+ source: {
117
+ type: 'geojson',
118
+ data: geojson,
119
+ },
120
+ paint: {
121
+ 'line-color': '#000000',
122
+ 'line-opacity': 0.3,
123
+ 'line-width': {
124
+ base: 12,
125
+ stops: [
126
+ [14, 20],
127
+ [18, 42],
128
+ ],
129
+ },
130
+ 'line-blur': {
131
+ base: 12,
132
+ stops: [
133
+ [14, 20],
134
+ [18, 42],
135
+ ],
136
+ },
137
+ },
138
+ layout: lineLayout,
139
+ filter: ['!has', 'stop_id'],
140
+ },
141
+ firstSymbolId
142
+ );
143
+
144
+ // Add route line outline
105
145
  map.addLayer(
106
146
  {
107
147
  id: 'route-line-outline',
@@ -113,17 +153,21 @@ function createMap(id, geojson, routes) {
113
153
  paint: {
114
154
  'line-color': '#FFFFFF',
115
155
  'line-opacity': 1,
116
- 'line-width': 6,
117
- },
118
- layout: {
119
- 'line-join': 'round',
120
- 'line-cap': 'round',
156
+ 'line-width': {
157
+ base: 8,
158
+ stops: [
159
+ [14, 12],
160
+ [18, 32],
161
+ ],
162
+ },
121
163
  },
164
+ layout: lineLayout,
122
165
  filter: ['!has', 'stop_id'],
123
166
  },
124
167
  firstSymbolId
125
168
  );
126
169
 
170
+ // Add route line
127
171
  map.addLayer(
128
172
  {
129
173
  id: 'route-line',
@@ -135,17 +179,21 @@ function createMap(id, geojson, routes) {
135
179
  paint: {
136
180
  'line-color': ['to-color', ['get', 'route_color'], defaultRouteColor],
137
181
  'line-opacity': 1,
138
- 'line-width': 2,
139
- },
140
- layout: {
141
- 'line-join': 'round',
142
- 'line-cap': 'round',
182
+ 'line-width': {
183
+ base: 4,
184
+ stops: [
185
+ [14, 6],
186
+ [18, 16],
187
+ ],
188
+ },
143
189
  },
190
+ layout: lineLayout,
144
191
  filter: ['!has', 'stop_id'],
145
192
  },
146
193
  firstSymbolId
147
194
  );
148
195
 
196
+ // Add stops
149
197
  map.addLayer(
150
198
  {
151
199
  id: 'stops',
@@ -155,22 +203,23 @@ function createMap(id, geojson, routes) {
155
203
  data: geojson,
156
204
  },
157
205
  paint: {
206
+ 'circle-color': '#fff',
158
207
  'circle-radius': {
208
+ base: 1.75,
159
209
  stops: [
160
- [9, 2],
161
- [13, 4],
162
- [15, 6],
210
+ [12, 4],
211
+ [22, 100],
163
212
  ],
164
213
  },
165
- 'circle-stroke-width': 1,
166
- 'circle-stroke-color': '#363636',
167
- 'circle-color': '#363636',
214
+ 'circle-stroke-color': '#3f4a5c',
215
+ 'circle-stroke-width': 2,
168
216
  },
169
217
  filter: ['has', 'stop_id'],
170
218
  },
171
219
  firstSymbolId
172
220
  );
173
221
 
222
+ // Layer for highlighted stops
174
223
  map.addLayer(
175
224
  {
176
225
  id: 'stops-highlighted',
@@ -180,16 +229,16 @@ function createMap(id, geojson, routes) {
180
229
  data: geojson,
181
230
  },
182
231
  paint: {
232
+ 'circle-color': '#fff',
183
233
  'circle-radius': {
234
+ base: 1.75,
184
235
  stops: [
185
- [9, 3],
186
- [13, 4],
187
- [15, 7],
236
+ [12, 5],
237
+ [22, 125],
188
238
  ],
189
239
  },
190
240
  'circle-stroke-width': 2,
191
- 'circle-stroke-color': '#666666',
192
- 'circle-color': '#888888',
241
+ 'circle-stroke-color': '#3f4a5c',
193
242
  },
194
243
  filter: ['==', 'stop_id', ''],
195
244
  },
@@ -216,7 +265,7 @@ function createMap(id, geojson, routes) {
216
265
  [event.point.x + 5, event.point.y + 5],
217
266
  ];
218
267
  const features = map.queryRenderedFeatures(bbox, {
219
- layers: ['stops'],
268
+ layers: ['stops-highlighted', 'stops'],
220
269
  });
221
270
 
222
271
  if (!features || features.length === 0) {
@@ -234,14 +283,10 @@ function createMap(id, geojson, routes) {
234
283
 
235
284
  function highlightStop(stopId) {
236
285
  map.setFilter('stops-highlighted', ['==', 'stop_id', stopId]);
237
- map.setPaintProperty('stops', 'circle-opacity', 0.5);
238
- map.setPaintProperty('stops', 'circle-stroke-opacity', 0.5);
239
286
  }
240
287
 
241
288
  function unHighlightStop() {
242
289
  map.setFilter('stops-highlighted', ['==', 'stop_id', '']);
243
- map.setPaintProperty('stops', 'circle-opacity', 1);
244
- map.setPaintProperty('stops', 'circle-stroke-opacity', 1);
245
290
  }
246
291
 
247
292
  // On table hover, highlight stop on map
@@ -3,12 +3,13 @@ html
3
3
  head
4
4
  title= title
5
5
  meta(charset="utf-8")
6
- link(rel="stylesheet" href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" crossorigin="anonymous")
7
6
  link(rel="stylesheet" href=`${config.assetPath}css/timetable_styles.css`)
8
7
  if config.outputFormat === 'pdf'
9
8
  link(rel="stylesheet" href=`${config.assetPath}css/timetable_pdf_styles.css`)
10
9
  meta(name="viewport" content="initial-scale=1.0, width=device-width")
11
10
 
11
+ script(src="https://cdn.tailwindcss.com")
12
+
12
13
  block extraHeader
13
14
 
14
15
  body
@@ -10,13 +10,13 @@ include formatting_functions.pug
10
10
  h1.text-2xl.pt-4.pb-2= `${formatAgencyName(timetablePageGroup.agency)} Routes`
11
11
  each timetablePage in timetablePageGroup.timetablePages
12
12
  if config.allowEmptyTimetables || timetablePage.consolidatedTimetables.length > 0
13
- a.overview-menu-item.mb-3(href=`${timetablePage.relativePath}` data-route-ids=`${timetablePage.route_ids ? timetablePage.route_ids.join(',') : ''}`)
13
+ a.block.p-2.border-b.border-slate-200(class="hover:bg-slate-200 hover:no-underline" href=`${timetablePage.relativePath}` data-route-ids=`${timetablePage.route_ids ? timetablePage.route_ids.join(',') : ''}`)
14
14
  .text-lg.text-gray-800.leading-none= timetablePage.timetable_page_label
15
15
  each route in _.uniqBy(_.flatMap(timetablePage.consolidatedTimetables, timetable => timetable.routes), 'route_id')
16
16
  .flex.my-1
17
17
  .route-color-swatch.flex-none.mr-2(style=`background-color: #${route.route_color}; color: #${route.route_text_color};`)= route.route_short_name || ''
18
18
  .mt-1.text-gray-600.leading-none= formatRouteName(route)
19
- .text-gray-600= timetablePage.dayList
19
+ .inline-flex.items-center.justify-center.px-2.py-1.text-xs.font-bold.leading-none.text-slate-800.bg-slate-200.rounded-full= timetablePage.dayList
20
20
  if config.showMap
21
21
  .map.ml-4.h-full.w-full(id="system_map")
22
22
 
@@ -14,7 +14,7 @@ include formatting_functions.pug
14
14
  include timetable_menu.pug
15
15
 
16
16
  each timetable in timetablePage.consolidatedTimetables
17
- .timetable.mb-5(id=`timetable_id_${formatHtmlId(timetable.timetable_id)}` data-day-list=timetable.dayList data-direction-name=timetable.direction_name data-timetable-id=timetable.timetable_id data-direction-id=timetable.direction_id data-route-id=timetable.route_ids.join('_'))
17
+ .timetable.mb-10(id=`timetable_id_${formatHtmlId(timetable.timetable_id)}` data-day-list=timetable.dayList data-direction-name=timetable.direction_name data-timetable-id=timetable.timetable_id data-direction-id=timetable.direction_id data-route-id=timetable.route_ids.join('_'))
18
18
  if config.showRouteTitle
19
19
  h2.text-xl= `${timetable.timetable_label} | ${timetable.dayListLong}`
20
20
  each note in getNotesForTimetableLabel(timetable.notes)
@@ -490,7 +490,7 @@ The default trip-sorting algorithm is `common`.
490
490
 
491
491
  ### sqlitePath
492
492
 
493
- {String} A path to an SQLite database. Optional, defaults to using an in-memory database with a value of `:memory:`. If you want the data imported to persist, you need to specify a value for `sqlitePath`
493
+ {String} A path to an SQLite database. Optional, defaults to using an in-memory database with a value of `:memory:`. If you want the data imported to persist, you need to specify a value for `sqlitePath`. Supports tilde as part of the path, like `~/Documents/gtfs`.
494
494
 
495
495
  ```
496
496
  "sqlitePath": "/tmp/gtfs"
@@ -29,7 +29,6 @@ Skips importing GTFS into SQLite. Useful if you are rerunning with an unchanged
29
29
 
30
30
  gtfs-to-html --skipImport
31
31
 
32
-
33
32
  ### Processing very large GTFS files.
34
33
 
35
34
  By default, node has a memory limit of 512 MB or 1 GB. If you have a very large GTFS file and want to use the option `showOnlyTimepoint` = `false` you may need to allocate more memory. Use the `max-old-space-size` option. For example to allocate 4 GB:
@@ -44,17 +43,17 @@ You can use both [`docker`](https://docker.com) and [`docker-compose`](https://d
44
43
 
45
44
  A `Dockerfile` is available in the `docker` directory.
46
45
 
47
- * Create a `config.json` file and save in the same directory as your `Dockerfile`. You can use `config-sample.json` from the project root as a starting point.
46
+ - Create a `config.json` file and save in the same directory as your `Dockerfile`. You can use `config-sample.json` from the project root as a starting point. For Docker usage, remove any `sqlitePath` and `templatePath` values from the config.json.
48
47
 
49
- * Build the docker image:
48
+ - Build the docker image:
50
49
 
51
50
  docker build -t gtfs-to-html .
52
51
 
53
- * Run the docker image:
52
+ - Run the docker image:
54
53
 
55
54
  docker run gtfs-to-html
56
55
 
57
- * Copy the generated HTML out of the docker container
56
+ - Copy the generated HTML out of the docker container
58
57
 
59
58
  // Figure out what your container ID is
60
59
  docker container ls -a
@@ -69,17 +68,16 @@ A `Dockerfile` is available in the `docker` directory.
69
68
 
70
69
  Docker compose is used for multi-container Docker applications. In this context, it is just a convenient way to manage volumes. This allows (_i_) to get the generated HTML out of the docker container without explicitly copying with `docker cp`, and (_ii_) to tweak and run a new configuration without rebuilding the container from scratch.
71
70
 
72
- * Create a `config.json` file and save in the same directory as your `Dockerfile` and `docker-compose.yml`;
71
+ - Create a `config.json` file and save in the same directory as your `Dockerfile` and `docker-compose.yml`;
73
72
 
74
- * build and run the container:
73
+ - build and run the container:
75
74
 
76
75
  docker-compose up
77
76
 
78
- * the generated HTML will be available in the folder `html` next to docker files.
77
+ - the generated HTML will be available in the folder `html` next to docker files.
79
78
 
80
79
  Do you want to change something? Just delete the old HTML, change your `config.json`, and finally run `docker-compose up` again.
81
80
 
82
-
83
81
  ## Usage as a node module
84
82
 
85
83
  If you are using this as a node module as part of an application, you can include it in your project's `package.json` file.
@@ -87,22 +85,25 @@ If you are using this as a node module as part of an application, you can includ
87
85
  ### Code example
88
86
 
89
87
  ```javascript
90
- import gtfsToHtml from 'gtfs-to-html';
91
- import { readFile } from 'fs/promises';
92
- const config = JSON.parse(await readFile(new URL('./config.json', import.meta.url)));
93
-
94
- gtfsToHtml(config)
95
- .then(() => {
96
- console.log('HTML Generation Successful');
97
- process.exit();
98
- })
99
- .catch(err => {
100
- console.error(err);
101
- process.exit(1);
102
- });
88
+ import gtfsToHtml from 'gtfs-to-html';
89
+ import { readFile } from 'fs/promises';
90
+ const config = JSON.parse(
91
+ await readFile(new URL('./config.json', import.meta.url))
92
+ );
93
+
94
+ gtfsToHtml(config)
95
+ .then(() => {
96
+ console.log('HTML Generation Successful');
97
+ process.exit();
98
+ })
99
+ .catch((err) => {
100
+ console.error(err);
101
+ process.exit(1);
102
+ });
103
103
  ```
104
104
 
105
105
  ### Example Application
106
+
106
107
  An example Express application that uses `gtfs-to-html` is included in the `app` folder. After an initial run of `gtfs-to-html`, the GTFS data will be downloaded and loaded into SQLite.
107
108
 
108
109
  You can view an individual route HTML on demand by running the included Express app:
@@ -115,17 +116,16 @@ By default, `gtfs-to-html` will look for a `config.json` file in the project roo
115
116
 
116
117
  Once running, you can view the HTML in your browser at [localhost:3000](http://localhost:3000)
117
118
 
118
-
119
119
  ## Usage as a hosted web app
120
120
 
121
121
  A [hosted version of GTFS-to-HTML as a service](https://run.gtfstohtml.com) allows you to use it entirely within your browser - no downloads or command line necessary.
122
122
 
123
123
  It provides:
124
124
 
125
- * a web-based interface for finding GTFS feeds or ability to enter your own URL
126
- * support for adding [custom configuration](/docs/configuration) as JSON
127
- * creation of HTML timetables as a downloadable .zip file
128
- * a preview of all timetables generated directly in your browser
125
+ - a web-based interface for finding GTFS feeds or ability to enter your own URL
126
+ - support for adding [custom configuration](/docs/configuration) as JSON
127
+ - creation of HTML timetables as a downloadable .zip file
128
+ - a preview of all timetables generated directly in your browser
129
129
 
130
130
  [run.gtfstohtml.com](https://run.gtfstohtml.com)
131
131
 
@@ -136,8 +136,8 @@ Currently, it is limited to relatively small GTFS files and doesn't offer suppor
136
136
  ### SQLite3 unable to be installed with `Failed to exec install script`
137
137
 
138
138
  For an error like:
139
- ```lifecycle sqlite3@5.0.0~install: Failed to exec install script```
139
+ `lifecycle sqlite3@5.0.0~install: Failed to exec install script`
140
140
 
141
141
  Try installing `gtfs-to-html` using the following flags:
142
142
 
143
- ```npm install --unsafe-perm --allow-root -g```
143
+ `npm install --unsafe-perm --allow-root -g`
@@ -33,3 +33,8 @@ The [GTFS Text-to-Speech Tester](https://github.com/BlinkTagInc/node-gtfs-tts) i
33
33
 
34
34
  [`https://github.com/BlinkTagInc/node-gtfs-tts`](https://github.com/BlinkTagInc/node-gtfs-tts)
35
35
 
36
+ ## `node-gtfs-realtime`
37
+
38
+ [GTFS-realtime](https://developers.google.com/transit/gtfs-realtime) transit data is in [protobuf format](https://developers.google.com/protocol-buffers) which means its not human-readable by default. `node-GTFS-Realtime` aims to make it fast and easy to inspect GTFS-realtime data by providing a one-line command for downloading [GTFS-realtime format](https://developers.google.com/transit/gtfs-realtime) data and converting to JSON. Try it by running `npx gtfs-realtime http://api.bart.gov/gtfsrt/tripupdate.aspx` in your terminal.
39
+
40
+ [`https://github.com/BlinkTagInc/node-gtfs-realtime`](https://github.com/BlinkTagInc/node-gtfs-realtime)
package/www/package.json CHANGED
@@ -9,11 +9,11 @@
9
9
  "deploy": "docusaurus deploy"
10
10
  },
11
11
  "dependencies": {
12
- "@docusaurus/core": "^2.0.0-beta.18",
13
- "@docusaurus/preset-classic": "^2.0.0-beta.18",
14
- "clsx": "^1.1.1",
15
- "react": "^18.0.0",
16
- "react-dom": "^18.0.0"
12
+ "@docusaurus/core": "^2.0.0-rc.1",
13
+ "@docusaurus/preset-classic": "^2.0.0-rc.1",
14
+ "clsx": "^1.2.1",
15
+ "react": "^18.2.0",
16
+ "react-dom": "^18.2.0"
17
17
  },
18
18
  "browserslist": {
19
19
  "production": [
@@ -7,7 +7,6 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
7
7
  import CodeBlock from '@theme/CodeBlock';
8
8
  import useBaseUrl from '@docusaurus/useBaseUrl';
9
9
  import styles from './styles.module.css';
10
- import { hsl } from 'chalk';
11
10
 
12
11
  const features = [
13
12
  {
@@ -15,28 +14,32 @@ const features = [
15
14
  imageUrl: 'img/undraw_proud_coder.svg',
16
15
  description: (
17
16
  <>
18
- Most transit agencies have schedule data in GTFS format but need to show each route's schedule to users on a website.
17
+ Most transit agencies have schedule data in GTFS format but need to show
18
+ each route's schedule to users on a website.
19
19
  </>
20
- )
20
+ ),
21
21
  },
22
22
  {
23
23
  title: <>What?</>,
24
24
  imageUrl: 'img/undraw_spreadsheets.svg',
25
25
  description: (
26
26
  <>
27
- This tool automates the process of creating nicely formatted HTML timetables for inclusion on a transit agency website.
27
+ This tool automates the process of creating nicely formatted HTML
28
+ timetables for inclusion on a transit agency website.
28
29
  </>
29
- )
30
+ ),
30
31
  },
31
32
  {
32
33
  title: <>Automate schedule changes</>,
33
34
  imageUrl: 'img/undraw_happy_music.svg',
34
35
  description: (
35
36
  <>
36
- Automating timetable creation means that timetables can be kept up to date and accurate when schedule changes happen and the likelihood of errors is reduced.
37
+ Automating timetable creation means that timetables can be kept up to
38
+ date and accurate when schedule changes happen and the likelihood of
39
+ errors is reduced.
37
40
  </>
38
- )
39
- }
41
+ ),
42
+ },
40
43
  ];
41
44
 
42
45
  function Feature({ imageUrl, title, description }) {
@@ -60,25 +63,35 @@ function Home() {
60
63
  return (
61
64
  <Layout
62
65
  title="GTFS-to-HTML"
63
- description="GTFS-to-HTML creates human-readable, user-friendly transit timetables in HTML format directly from GTFS transit data.">
66
+ description="GTFS-to-HTML creates human-readable, user-friendly transit timetables in HTML format directly from GTFS transit data."
67
+ >
64
68
  <header className={clsx('hero hero--dark', styles.heroBanner)}>
65
69
  <div className="container">
66
70
  <div className="row">
67
71
  <div className="col col--4">
68
- <img src="/img/gtfs-to-html-logo.svg" style={{ maxWidth: '150px' }} alt="" />
72
+ <img
73
+ src="/img/gtfs-to-html-logo.svg"
74
+ style={{ maxWidth: '150px' }}
75
+ alt=""
76
+ />
69
77
  </div>
70
78
 
71
79
  <div className="col col--8">
72
80
  <h1 className="hero__title">{siteConfig.title}</h1>
73
81
  <p className="hero__subtitle">{siteConfig.tagline}</p>
74
- <img alt="npm" src="https://img.shields.io/npm/v/gtfs-to-html?color=%2325c2a0&amp;label=stable&amp;style=for-the-badge" className="margin-bottom--sm" />
82
+ <img
83
+ alt="npm"
84
+ src="https://img.shields.io/npm/v/gtfs-to-html?color=%2325c2a0&amp;label=stable&amp;style=for-the-badge"
85
+ className="margin-bottom--sm"
86
+ />
75
87
  <div className={styles.buttons}>
76
88
  <Link
77
89
  className={clsx(
78
90
  'button button--outline button--secondary button--lg',
79
91
  styles.heroButton
80
92
  )}
81
- to={useBaseUrl('docs/')}>
93
+ to={useBaseUrl('docs/')}
94
+ >
82
95
  Get Started
83
96
  </Link>
84
97
  </div>
@@ -99,19 +112,44 @@ function Home() {
99
112
  </section>
100
113
  )}
101
114
 
102
- <section style={{backgroundColor: 'hsl(0, 0%, 85%)'}} className="padding--lg">
115
+ <section
116
+ style={{ backgroundColor: 'hsl(0, 0%, 85%)' }}
117
+ className="padding--lg"
118
+ >
103
119
  <div className="container">
104
120
  <div className="row">
105
121
  <div className="col"></div>
106
122
  <div className="col">
107
123
  <div className="avatar avatar--vertical margin-bottom--sm">
108
124
  <div className="avatar__photo avatar__photo--xl">
109
- <img src="https://avatars.githubusercontent.com/u/46612183?v=4" alt="Brody" width="200" height="200" style={{width: '100%', height: 'auto', maxWidth: '100%', marginBottom: '-4px'}} /></div>
110
- <div className="avatar__intro padding-top--sm"><div className="avatar__name">Brody</div>
111
- <small className="avatar__subtitle"><a href="https://github.com/transcollines">Transcollines</a></small>
125
+ <img
126
+ src="https://avatars.githubusercontent.com/u/46612183?v=4"
127
+ alt="Brody"
128
+ width="200"
129
+ height="200"
130
+ style={{
131
+ width: '100%',
132
+ height: 'auto',
133
+ maxWidth: '100%',
134
+ marginBottom: '-4px',
135
+ }}
136
+ />
137
+ </div>
138
+ <div className="avatar__intro padding-top--sm">
139
+ <div className="avatar__name">Brody</div>
140
+ <small className="avatar__subtitle">
141
+ <a href="https://github.com/transcollines">
142
+ Transcollines
143
+ </a>
144
+ </small>
112
145
  </div>
113
146
  </div>
114
- <p className="text--center text--italic padding-horiz--md">It's been a huge success. Not only do the timetables look fantastic, but I've had customer service agents tell me the interactive timetables are a total game changer and they make their jobs way easier.</p>
147
+ <p className="text--center text--italic padding-horiz--md">
148
+ It's been a huge success. Not only do the timetables look
149
+ fantastic, but I've had customer service agents tell me the
150
+ interactive timetables are a total game changer and they make
151
+ their jobs way easier.
152
+ </p>
115
153
  </div>
116
154
  <div className="col"></div>
117
155
  </div>