tileserver-gl-light 4.5.2 → 4.6.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/src/server.js CHANGED
@@ -6,7 +6,7 @@ process.env.UV_THREADPOOL_SIZE = Math.ceil(Math.max(4, os.cpus().length * 1.5));
6
6
 
7
7
  import fs from 'node:fs';
8
8
  import path from 'path';
9
-
9
+ import fnv1a from '@sindresorhus/fnv1a';
10
10
  import chokidar from 'chokidar';
11
11
  import clone from 'clone';
12
12
  import cors from 'cors';
@@ -19,7 +19,7 @@ import morgan from 'morgan';
19
19
  import { serve_data } from './serve_data.js';
20
20
  import { serve_style } from './serve_style.js';
21
21
  import { serve_font } from './serve_font.js';
22
- import { getTileUrls, getPublicUrl } from './utils.js';
22
+ import { getTileUrls, getPublicUrl, isValidHttpUrl } from './utils.js';
23
23
 
24
24
  import { fileURLToPath } from 'url';
25
25
  const __filename = fileURLToPath(import.meta.url);
@@ -93,6 +93,7 @@ function start(opts) {
93
93
  paths.fonts = path.resolve(paths.root, paths.fonts || '');
94
94
  paths.sprites = path.resolve(paths.root, paths.sprites || '');
95
95
  paths.mbtiles = path.resolve(paths.root, paths.mbtiles || '');
96
+ paths.pmtiles = path.resolve(paths.root, paths.pmtiles || '');
96
97
  paths.icons = path.resolve(paths.root, paths.icons || '');
97
98
 
98
99
  const startupPromises = [];
@@ -109,6 +110,7 @@ function start(opts) {
109
110
  checkPath('fonts');
110
111
  checkPath('sprites');
111
112
  checkPath('mbtiles');
113
+ checkPath('pmtiles');
112
114
  checkPath('icons');
113
115
 
114
116
  /**
@@ -181,34 +183,43 @@ function start(opts) {
181
183
  item,
182
184
  id,
183
185
  opts.publicUrl,
184
- (mbtiles, fromData) => {
186
+ (StyleSourceId, protocol) => {
185
187
  let dataItemId;
186
188
  for (const id of Object.keys(data)) {
187
- if (fromData) {
188
- if (id === mbtiles) {
189
- dataItemId = id;
190
- }
189
+ if (id === StyleSourceId) {
190
+ // Style id was found in data ids, return that id
191
+ dataItemId = id;
191
192
  } else {
192
- if (data[id].mbtiles === mbtiles) {
193
+ const fileType = Object.keys(data[id])[0];
194
+ if (data[id][fileType] === StyleSourceId) {
195
+ // Style id was found in data filename, return the id that filename belong to
193
196
  dataItemId = id;
194
197
  }
195
198
  }
196
199
  }
197
200
  if (dataItemId) {
198
- // mbtiles exist in the data config
201
+ // input files exists in the data config, return found id
199
202
  return dataItemId;
200
203
  } else {
201
- if (fromData || !allowMoreData) {
204
+ if (!allowMoreData) {
202
205
  console.log(
203
- `ERROR: style "${item.style}" using unknown mbtiles "${mbtiles}"! Skipping...`,
206
+ `ERROR: style "${item.style}" using unknown file "${StyleSourceId}"! Skipping...`,
204
207
  );
205
208
  return undefined;
206
209
  } else {
207
- let id = mbtiles.substr(0, mbtiles.lastIndexOf('.')) || mbtiles;
208
- while (data[id]) id += '_';
210
+ let id =
211
+ StyleSourceId.substr(0, StyleSourceId.lastIndexOf('.')) ||
212
+ StyleSourceId;
213
+ if (isValidHttpUrl(StyleSourceId)) {
214
+ id =
215
+ fnv1a(StyleSourceId) + '_' + id.replace(/^.*\/(.*)$/, '$1');
216
+ }
217
+ while (data[id]) id += '_'; //if the data source id already exists, add a "_" untill it doesn't
218
+ //Add the new data source to the data array.
209
219
  data[id] = {
210
- mbtiles: mbtiles,
220
+ [protocol]: StyleSourceId,
211
221
  };
222
+
212
223
  return id;
213
224
  }
214
225
  }
@@ -229,14 +240,24 @@ function start(opts) {
229
240
  item,
230
241
  id,
231
242
  opts.publicUrl,
232
- (mbtiles) => {
233
- let mbtilesFile;
243
+ (StyleSourceId) => {
244
+ let fileType;
245
+ let inputFile;
234
246
  for (const id of Object.keys(data)) {
235
- if (id === mbtiles) {
236
- mbtilesFile = data[id].mbtiles;
247
+ fileType = Object.keys(data[id])[0];
248
+ if (StyleSourceId == id) {
249
+ inputFile = data[id][fileType];
250
+ break;
251
+ } else if (data[id][fileType] == StyleSourceId) {
252
+ inputFile = data[id][fileType];
253
+ break;
237
254
  }
238
255
  }
239
- return mbtilesFile;
256
+ if (!isValidHttpUrl(inputFile)) {
257
+ inputFile = path.resolve(options.paths[fileType], inputFile);
258
+ }
259
+
260
+ return { inputfile: inputFile, filetype: fileType };
240
261
  },
241
262
  ),
242
263
  );
@@ -264,8 +285,11 @@ function start(opts) {
264
285
 
265
286
  for (const id of Object.keys(data)) {
266
287
  const item = data[id];
267
- if (!item.mbtiles || item.mbtiles.length === 0) {
268
- console.log(`Missing "mbtiles" property for ${id}`);
288
+ const fileType = Object.keys(data[id])[0];
289
+ if (!fileType || !(fileType === 'pmtiles' || fileType === 'mbtiles')) {
290
+ console.log(
291
+ `Missing "pmtiles" or "mbtiles" property for ${id} data source`,
292
+ );
269
293
  continue;
270
294
  }
271
295
 
@@ -424,14 +448,16 @@ function start(opts) {
424
448
  };
425
449
 
426
450
  serveTemplate('/$', 'index', (req) => {
427
- const styles = clone(serving.styles || {});
428
- for (const id of Object.keys(styles)) {
429
- const style = styles[id];
430
- style.name = (serving.styles[id] || serving.rendered[id] || {}).name;
431
- style.serving_data = serving.styles[id];
432
- style.serving_rendered = serving.rendered[id];
451
+ let styles = {};
452
+ for (const id of Object.keys(serving.styles || {})) {
453
+ let style = {
454
+ ...serving.styles[id],
455
+ serving_data: serving.styles[id],
456
+ serving_rendered: serving.rendered[id],
457
+ };
458
+
433
459
  if (style.serving_rendered) {
434
- const center = style.serving_rendered.tileJSON.center;
460
+ const { center } = style.serving_rendered.tileJSON;
435
461
  if (center) {
436
462
  style.viewer_hash = `#${center[2]}/${center[1].toFixed(
437
463
  5,
@@ -451,40 +477,46 @@ function start(opts) {
451
477
  opts.publicUrl,
452
478
  )[0];
453
479
  }
480
+
481
+ styles[id] = style;
454
482
  }
455
- const data = clone(serving.data || {});
456
- for (const id of Object.keys(data)) {
457
- const data_ = data[id];
458
- const tilejson = data[id].tileJSON;
459
- const center = tilejson.center;
483
+
484
+ let datas = {};
485
+ for (const id of Object.keys(serving.data || {})) {
486
+ let data = Object.assign({}, serving.data[id]);
487
+
488
+ const { tileJSON } = serving.data[id];
489
+ const { center } = tileJSON;
490
+
460
491
  if (center) {
461
- data_.viewer_hash = `#${center[2]}/${center[1].toFixed(
492
+ data.viewer_hash = `#${center[2]}/${center[1].toFixed(
462
493
  5,
463
494
  )}/${center[0].toFixed(5)}`;
464
495
  }
465
- data_.is_vector = tilejson.format === 'pbf';
466
- if (!data_.is_vector) {
496
+
497
+ data.is_vector = tileJSON.format === 'pbf';
498
+ if (!data.is_vector) {
467
499
  if (center) {
468
500
  const centerPx = mercator.px([center[0], center[1]], center[2]);
469
- data_.thumbnail = `${center[2]}/${Math.floor(
501
+ data.thumbnail = `${center[2]}/${Math.floor(
470
502
  centerPx[0] / 256,
471
- )}/${Math.floor(centerPx[1] / 256)}.${data_.tileJSON.format}`;
503
+ )}/${Math.floor(centerPx[1] / 256)}.${tileJSON.format}`;
472
504
  }
473
505
 
474
- data_.xyz_link = getTileUrls(
506
+ data.xyz_link = getTileUrls(
475
507
  req,
476
- tilejson.tiles,
508
+ tileJSON.tiles,
477
509
  `data/${id}`,
478
- tilejson.format,
510
+ tileJSON.format,
479
511
  opts.publicUrl,
480
512
  {
481
513
  pbf: options.pbfAlias,
482
514
  },
483
515
  )[0];
484
516
  }
485
- if (data_.filesize) {
517
+ if (data.filesize) {
486
518
  let suffix = 'kB';
487
- let size = parseInt(data_.filesize, 10) / 1024;
519
+ let size = parseInt(tileJSON.filesize, 10) / 1024;
488
520
  if (size > 1024) {
489
521
  suffix = 'MB';
490
522
  size /= 1024;
@@ -493,26 +525,33 @@ function start(opts) {
493
525
  suffix = 'GB';
494
526
  size /= 1024;
495
527
  }
496
- data_.formatted_filesize = `${size.toFixed(2)} ${suffix}`;
528
+ data.formatted_filesize = `${size.toFixed(2)} ${suffix}`;
497
529
  }
530
+
531
+ datas[id] = data;
498
532
  }
533
+
499
534
  return {
500
535
  styles: Object.keys(styles).length ? styles : null,
501
- data: Object.keys(data).length ? data : null,
536
+ data: Object.keys(datas).length ? datas : null,
502
537
  };
503
538
  });
504
539
 
505
540
  serveTemplate('/styles/:id/$', 'viewer', (req) => {
506
- const id = req.params.id;
541
+ const { id } = req.params;
507
542
  const style = clone(((serving.styles || {})[id] || {}).styleJSON);
543
+
508
544
  if (!style) {
509
545
  return null;
510
546
  }
511
- style.id = id;
512
- style.name = (serving.styles[id] || serving.rendered[id]).name;
513
- style.serving_data = serving.styles[id];
514
- style.serving_rendered = serving.rendered[id];
515
- return style;
547
+
548
+ return {
549
+ id,
550
+ name: (serving.styles[id] || serving.rendered[id]).name,
551
+ serving_data: serving.styles[id],
552
+ serving_rendered: serving.rendered[id],
553
+ ...style,
554
+ };
516
555
  });
517
556
 
518
557
  /*
@@ -521,37 +560,49 @@ function start(opts) {
521
560
  });
522
561
  */
523
562
  serveTemplate('/styles/:id/wmts.xml', 'wmts', (req) => {
524
- const id = req.params.id;
563
+ const { id } = req.params;
525
564
  const wmts = clone((serving.styles || {})[id]);
565
+
526
566
  if (!wmts) {
527
567
  return null;
528
568
  }
569
+
529
570
  if (wmts.hasOwnProperty('serve_rendered') && !wmts.serve_rendered) {
530
571
  return null;
531
572
  }
532
- wmts.id = id;
533
- wmts.name = (serving.styles[id] || serving.rendered[id]).name;
573
+
574
+ let baseUrl;
534
575
  if (opts.publicUrl) {
535
- wmts.baseUrl = opts.publicUrl;
576
+ baseUrl = opts.publicUrl;
536
577
  } else {
537
- wmts.baseUrl = `${
578
+ baseUrl = `${
538
579
  req.get('X-Forwarded-Protocol')
539
580
  ? req.get('X-Forwarded-Protocol')
540
581
  : req.protocol
541
582
  }://${req.get('host')}/`;
542
583
  }
543
- return wmts;
584
+
585
+ return {
586
+ id,
587
+ name: (serving.styles[id] || serving.rendered[id]).name,
588
+ baseUrl,
589
+ ...wmts,
590
+ };
544
591
  });
545
592
 
546
593
  serveTemplate('/data/:id/$', 'data', (req) => {
547
- const id = req.params.id;
548
- const data = clone(serving.data[id]);
594
+ const { id } = req.params;
595
+ const data = serving.data[id];
596
+
549
597
  if (!data) {
550
598
  return null;
551
599
  }
552
- data.id = id;
553
- data.is_vector = data.tileJSON.format === 'pbf';
554
- return data;
600
+
601
+ return {
602
+ id,
603
+ is_vector: data.tileJSON.format === 'pbf',
604
+ ...data,
605
+ };
555
606
  });
556
607
 
557
608
  let startupComplete = false;
@@ -559,6 +610,7 @@ function start(opts) {
559
610
  console.log('Startup complete');
560
611
  startupComplete = true;
561
612
  });
613
+
562
614
  app.get('/health', (req, res, next) => {
563
615
  if (startupComplete) {
564
616
  return res.status(200).send('OK');
package/src/utils.js CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  import path from 'path';
4
4
  import fs from 'node:fs';
5
-
6
5
  import clone from 'clone';
7
6
  import glyphCompose from '@mapbox/glyph-pbf-composite';
8
7
 
@@ -163,3 +162,15 @@ export const getFontsPbf = (
163
162
 
164
163
  return Promise.all(queue).then((values) => glyphCompose.combine(values));
165
164
  };
165
+
166
+ export const isValidHttpUrl = (string) => {
167
+ let url;
168
+
169
+ try {
170
+ url = new URL(string);
171
+ } catch (_) {
172
+ return false;
173
+ }
174
+
175
+ return url.protocol === 'http:' || url.protocol === 'https:';
176
+ };