tileserver-gl-light 5.5.0-pre.4 → 5.5.0-pre.6

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/main.js CHANGED
@@ -82,7 +82,10 @@ program
82
82
  )
83
83
  .option(
84
84
  '-V, --verbose [level]',
85
- 'More verbose output (can specify level 1-3, default 1)',
85
+ 'More verbose output (level 1-3)\n' +
86
+ '\t-V, --verbose, -V 1, or --verbose 1: Important operations\n' +
87
+ '\t-V 2 or --verbose 2: Detailed operations\n' +
88
+ '\t-V 3 or --verbose 3: All requests and debug info',
86
89
  (value) => {
87
90
  // If no value provided, return 1 (boolean true case)
88
91
  if (value === undefined || value === true) return 1;
@@ -92,6 +95,14 @@ program
92
95
  return isNaN(level) ? 1 : Math.min(Math.max(level, 1), 3);
93
96
  },
94
97
  )
98
+ .option(
99
+ '--fetch-timeout <ms>',
100
+ 'External fetch timeout in milliseconds for renderer HTTP requests (default 15000)',
101
+ (value) => {
102
+ const v = parseInt(value, 10);
103
+ return isNaN(v) ? 15000 : v;
104
+ },
105
+ )
95
106
  .option('-s, --silent', 'Less verbose output')
96
107
  .option('-l|--log_file <file>', 'output log file (defaults to standard out)')
97
108
  .option(
@@ -119,6 +130,7 @@ const startServer = (configPath, config) => {
119
130
  silent: opts.silent,
120
131
  logFile: opts.log_file,
121
132
  logFormat: opts.log_format,
133
+ fetchTimeout: opts.fetchTimeout,
122
134
  publicUrl,
123
135
  });
124
136
  };
@@ -246,7 +258,7 @@ const startWithInputFile = async (inputFile) => {
246
258
  }
247
259
  }
248
260
 
249
- if (opts.verbose) {
261
+ if (opts.verbose >= 1) {
250
262
  console.log(JSON.stringify(config, undefined, 2));
251
263
  } else {
252
264
  console.log('Run with --verbose to see the config file here.');
@@ -304,7 +316,7 @@ const startWithInputFile = async (inputFile) => {
304
316
  };
305
317
  }
306
318
 
307
- if (opts.verbose) {
319
+ if (opts.verbose >= 1) {
308
320
  console.log(JSON.stringify(config, undefined, 2));
309
321
  } else {
310
322
  console.log('Run with --verbose to see the config file here.');
@@ -18,7 +18,7 @@ class S3Source {
18
18
  * @param {boolean} [configRequestPayer] - Optional flag from config for requester pays buckets.
19
19
  * @param {string} [configRegion] - Optional AWS region from config.
20
20
  * @param {string} [s3UrlFormat] - Optional S3 URL format from config: 'aws' or 'custom'.
21
- * @param {boolean} [verbose] - Whether to show verbose logging.
21
+ * @param {number} [verbose] - Verbosity level (1-3). 1=important, 2=detailed, 3=debug/all requests.
22
22
  */
23
23
  constructor(
24
24
  s3Url,
@@ -147,7 +147,7 @@ class S3Source {
147
147
  * @param {string|null} endpoint - The custom endpoint URL, or null for default AWS S3.
148
148
  * @param {string} region - The AWS region.
149
149
  * @param {string} [profile] - Optional AWS credential profile name.
150
- * @param {boolean} [verbose] - Whether to show verbose logging.
150
+ * @param {number} [verbose] - Verbosity level (1-3). 1=important, 2=detailed, 3=debug/all requests.
151
151
  * @returns {S3Client} - Configured S3Client instance.
152
152
  */
153
153
  createS3Client(endpoint, region, profile, verbose) {
@@ -308,14 +308,18 @@ async function readFileBytes(fd, buffer, offset) {
308
308
  });
309
309
  }
310
310
 
311
+ // Cache for PMTiles objects to avoid creating multiple instances for the same URL
312
+ const pmtilesCache = new Map();
313
+
311
314
  /**
312
315
  * Opens a PMTiles file from local filesystem, HTTP URL, or S3 URL.
316
+ * Uses caching to avoid creating multiple PMTiles instances for the same file.
313
317
  * @param {string} filePath - The path to the PMTiles file.
314
318
  * @param {string} [s3Profile] - Optional AWS credential profile name.
315
319
  * @param {boolean} [requestPayer] - Optional flag for requester pays buckets.
316
320
  * @param {string} [s3Region] - Optional AWS region.
317
321
  * @param {string} [s3UrlFormat] - Optional S3 URL format: 'aws' or 'custom'.
318
- * @param {boolean} [verbose] - Whether to show verbose logging.
322
+ * @param {number} [verbose] - Verbosity level (1-3). 1=important, 2=detailed, 3=debug/all requests.
319
323
  * @returns {PMTiles} - A PMTiles instance.
320
324
  */
321
325
  export function openPMtiles(
@@ -326,6 +330,23 @@ export function openPMtiles(
326
330
  s3UrlFormat,
327
331
  verbose = 0,
328
332
  ) {
333
+ // Create a cache key that includes all parameters that affect the source
334
+ const cacheKey = JSON.stringify({
335
+ filePath,
336
+ s3Profile,
337
+ requestPayer,
338
+ s3Region,
339
+ s3UrlFormat,
340
+ });
341
+
342
+ // Check if we already have a PMTiles object for this configuration
343
+ if (pmtilesCache.has(cacheKey)) {
344
+ if (verbose >= 2) {
345
+ console.log(`Using cached PMTiles instance for: ${filePath}`);
346
+ }
347
+ return pmtilesCache.get(cacheKey);
348
+ }
349
+
329
350
  let pmtiles = undefined;
330
351
 
331
352
  if (isS3Url(filePath)) {
@@ -357,6 +378,9 @@ export function openPMtiles(
357
378
  pmtiles = new PMTiles(source);
358
379
  }
359
380
 
381
+ // Cache the PMTiles object
382
+ pmtilesCache.set(cacheKey, pmtiles);
383
+
360
384
  return pmtiles;
361
385
  }
362
386
 
package/src/serve_data.js CHANGED
@@ -58,7 +58,7 @@ export const serve_data = {
58
58
  * @returns {Promise<void>}
59
59
  */
60
60
  app.get('/:id/:z/:x/:y.:format', async (req, res) => {
61
- if (verbose) {
61
+ if (verbose >= 1) {
62
62
  console.log(
63
63
  `Handling tile request for: /data/%s/%s/%s/%s.%s`,
64
64
  String(req.params.id).replace(/\n|\r/g, ''),
@@ -109,10 +109,10 @@ export const serve_data = {
109
109
  x,
110
110
  y,
111
111
  );
112
- if (fetchTile == null && item.tileJSON.sparse) {
113
- return res.status(410).send();
114
- } else if (fetchTile == null) {
115
- return res.status(204).send();
112
+ if (fetchTile == null) {
113
+ // sparse=true (default) -> 404 (allows overzoom)
114
+ // sparse=false -> 204 (empty tile, no overzoom)
115
+ return res.status(item.sparse ? 404 : 204).send();
116
116
  }
117
117
 
118
118
  let data = fetchTile.data;
@@ -180,7 +180,7 @@ export const serve_data = {
180
180
  */
181
181
  app.get('/:id/elevation/:z/:x/:y', async (req, res, next) => {
182
182
  try {
183
- if (verbose) {
183
+ if (verbose >= 1) {
184
184
  console.log(
185
185
  `Handling elevation request for: /data/%s/elevation/%s/%s/%s`,
186
186
  String(req.params.id).replace(/\n|\r/g, ''),
@@ -261,7 +261,11 @@ export const serve_data = {
261
261
  xy[0],
262
262
  xy[1],
263
263
  );
264
- if (fetchTile == null) return res.status(204).send();
264
+ if (fetchTile == null) {
265
+ // sparse=true (default) -> 404 (allows overzoom)
266
+ // sparse=false -> 204 (empty tile, no overzoom)
267
+ return res.status(item.sparse ? 404 : 204).send();
268
+ }
265
269
 
266
270
  let data = fetchTile.data;
267
271
  var param = {
@@ -294,7 +298,7 @@ export const serve_data = {
294
298
  * @returns {Promise<void>}
295
299
  */
296
300
  app.get('/:id.json', (req, res) => {
297
- if (verbose) {
301
+ if (verbose >= 1) {
298
302
  console.log(
299
303
  `Handling tilejson request for: /data/%s.json`,
300
304
  String(req.params.id).replace(/\n|\r/g, ''),
@@ -331,7 +335,7 @@ export const serve_data = {
331
335
  * @param {string} id ID of the data source.
332
336
  * @param {object} programOpts - An object containing the program options
333
337
  * @param {string} programOpts.publicUrl Public URL for the data.
334
- * @param {boolean} programOpts.verbose Whether verbose logging should be used.
338
+ * @param {number} programOpts.verbose Verbosity level (1-3). 1=important, 2=detailed, 3=debug/all requests.
335
339
  * @returns {Promise<void>}
336
340
  */
337
341
  add: async function (options, repo, params, id, programOpts) {
@@ -359,7 +363,7 @@ export const serve_data = {
359
363
  }
360
364
  }
361
365
 
362
- if (verbose && verbose >= 1) {
366
+ if (verbose >= 1) {
363
367
  console.log(`[INFO] Loading data source '${id}' from: ${inputFile}`);
364
368
  }
365
369
 
@@ -381,7 +385,6 @@ export const serve_data = {
381
385
  tileJSON['format'] = 'pbf';
382
386
  tileJSON['encoding'] = params['encoding'];
383
387
  tileJSON['tileSize'] = params['tileSize'];
384
- tileJSON['sparse'] = params['sparse'];
385
388
 
386
389
  if (inputType === 'pmtiles') {
387
390
  source = openPMtiles(
@@ -415,12 +418,20 @@ export const serve_data = {
415
418
  tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
416
419
  }
417
420
 
421
+ // Determine sparse: per-source overrides global, then format-based default
422
+ // sparse=true -> 404 (allows overzoom)
423
+ // sparse=false -> 204 (empty tile, no overzoom)
424
+ // Default: vector tiles (pbf) -> false, raster tiles -> true
425
+ const isVector = tileJSON.format === 'pbf';
426
+ const sparse = params.sparse ?? options.sparse ?? !isVector;
427
+
418
428
  // eslint-disable-next-line security/detect-object-injection -- id is from config file data source names
419
429
  repo[id] = {
420
430
  tileJSON,
421
431
  publicUrl,
422
432
  source,
423
433
  sourceType,
434
+ sparse,
424
435
  };
425
436
  },
426
437
  };
package/src/serve_font.js CHANGED
@@ -36,7 +36,7 @@ export async function serve_font(options, allowedFonts, programOpts) {
36
36
  '',
37
37
  );
38
38
 
39
- if (verbose) {
39
+ if (verbose >= 1) {
40
40
  console.log(
41
41
  `Handling font request for: /fonts/%s/%s.pbf`,
42
42
  sFontStack,
@@ -86,7 +86,7 @@ export async function serve_font(options, allowedFonts, programOpts) {
86
86
  * @returns {void}
87
87
  */
88
88
  app.get('/fonts.json', (req, res) => {
89
- if (verbose) {
89
+ if (verbose >= 1) {
90
90
  console.log('Handling list font request for /fonts.json');
91
91
  }
92
92
  res.header('Content-type', 'application/json');