tileserver-gl-light 4.12.0 → 4.13.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.
@@ -13,17 +13,39 @@
13
13
  el.setSelectionRange(0, el.value.length);
14
14
  return false;
15
15
  }
16
+
17
+ function filter() {
18
+ var filter = document.getElementById('filter').value.toLowerCase();
19
+ var items = document.getElementsByClassName('item');
20
+ for (var i = 0; i < items.length; i++) {
21
+ var item = items[i];
22
+ var dataName = item.getAttribute('data-name')?.toLowerCase() ?? '';
23
+ var dataIdentifier = item.getAttribute('data-id')?.toLowerCase() ?? '';
24
+ item.hidden = !(dataName.indexOf(filter) > -1 || dataIdentifier.indexOf(filter) > -1);
25
+ }
26
+ }
16
27
  </script>
17
28
  </head>
18
29
  <body>
19
30
  <section>
20
31
  <h1 class="title {{#if is_light}}light{{/if}}"><img src="{{public_url}}images/logo.png{{&key_query}}" alt="TileServer GL" /></h1>
21
32
  <h2 class="subtitle">Vector {{#if is_light}}<s>and raster</s>{{else}}and raster{{/if}} maps with GL styles</h2>
33
+ <h2 class="box-header">Filter</h2>
34
+ <!-- filter box -->
35
+ <div class="box">
36
+ <div class="filter-item">
37
+ <div class="filter-details">
38
+ <h3>Filter styles and data by name or identifier</h3>
39
+ <!-- filter input , needs to call filter() when content changes...-->
40
+ <input id="filter" type="text" oninput="filter()" placeholder="Start typing name or identifier" />
41
+ </div>
42
+ </div>
43
+ </div>
22
44
  {{#if styles}}
23
45
  <h2 class="box-header">Styles</h2>
24
46
  <div class="box">
25
47
  {{#each styles}}
26
- <div class="item">
48
+ <div class="item" data-id="{{@key}}" data-name="{{name}}">
27
49
  {{#if thumbnail}}
28
50
  <img src="{{public_url}}styles/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" />
29
51
  {{else}}
@@ -70,7 +92,7 @@
70
92
  <h2 class="box-header">Data</h2>
71
93
  <div class="box">
72
94
  {{#each data}}
73
- <div class="item">
95
+ <div class="item" data-id="{{@key}}" data-name="{{tileJSON.name}}">
74
96
  {{#if thumbnail}}
75
97
  <img src="{{public_url}}data/{{@key}}/{{thumbnail}}{{&../key_query}}" alt="{{name}} preview" />
76
98
  {{else}}
package/src/main.js CHANGED
@@ -3,6 +3,7 @@
3
3
  'use strict';
4
4
 
5
5
  import fs from 'node:fs';
6
+ import fsp from 'node:fs/promises';
6
7
  import path from 'path';
7
8
  import { fileURLToPath } from 'url';
8
9
  import axios from 'axios';
@@ -23,6 +24,7 @@ if (args.length >= 3 && args[2][0] !== '-') {
23
24
  }
24
25
 
25
26
  import { program } from 'commander';
27
+ import { existsP } from './promises.js';
26
28
  program
27
29
  .description('tileserver-gl startup options')
28
30
  .usage('tileserver-gl [mbtiles] [options]')
@@ -95,7 +97,7 @@ const startWithInputFile = async (inputFile) => {
95
97
  inputFile = path.resolve(process.cwd(), inputFile);
96
98
  inputFilePath = path.dirname(inputFile);
97
99
 
98
- const inputFileStats = fs.statSync(inputFile);
100
+ const inputFileStats = await fsp.stat(inputFile);
99
101
  if (!inputFileStats.isFile() || inputFileStats.size === 0) {
100
102
  console.log(`ERROR: Not a valid input file: `);
101
103
  process.exit(1);
@@ -140,11 +142,11 @@ const startWithInputFile = async (inputFile) => {
140
142
  };
141
143
  }
142
144
 
143
- const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
145
+ const styles = await fsp.readdir(path.resolve(styleDir, 'styles'));
144
146
  for (const styleName of styles) {
145
147
  const styleFileRel = styleName + '/style.json';
146
148
  const styleFile = path.resolve(styleDir, 'styles', styleFileRel);
147
- if (fs.existsSync(styleFile)) {
149
+ if (await existsP(styleFile)) {
148
150
  config['styles'][styleName] = {
149
151
  style: styleFileRel,
150
152
  tilejson: {
@@ -189,7 +191,7 @@ const startWithInputFile = async (inputFile) => {
189
191
  process.exit(1);
190
192
  }
191
193
 
192
- instance.getInfo((err, info) => {
194
+ instance.getInfo(async (err, info) => {
193
195
  if (err || !info) {
194
196
  console.log('ERROR: Metadata missing in the MBTiles.');
195
197
  console.log(
@@ -207,11 +209,11 @@ const startWithInputFile = async (inputFile) => {
207
209
  mbtiles: path.basename(inputFile),
208
210
  };
209
211
 
210
- const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
212
+ const styles = await fsp.readdir(path.resolve(styleDir, 'styles'));
211
213
  for (const styleName of styles) {
212
214
  const styleFileRel = styleName + '/style.json';
213
215
  const styleFile = path.resolve(styleDir, 'styles', styleFileRel);
214
- if (fs.existsSync(styleFile)) {
216
+ if (await existsP(styleFile)) {
215
217
  config['styles'][styleName] = {
216
218
  style: styleFileRel,
217
219
  tilejson: {
@@ -254,10 +256,10 @@ fs.stat(path.resolve(opts.config), async (err, stats) => {
254
256
  return startWithInputFile(inputFile);
255
257
  } else {
256
258
  // try to find in the cwd
257
- const files = fs.readdirSync(process.cwd());
259
+ const files = await fsp.readdir(process.cwd());
258
260
  for (const filename of files) {
259
261
  if (filename.endsWith('.mbtiles') || filename.endsWith('.pmtiles')) {
260
- const inputFilesStats = fs.statSync(filename);
262
+ const inputFilesStats = await fsp.stat(filename);
261
263
  if (inputFilesStats.isFile() && inputFilesStats.size > 0) {
262
264
  inputFile = filename;
263
265
  break;
@@ -0,0 +1,14 @@
1
+ import util from 'node:util';
2
+ import fsp from 'node:fs/promises';
3
+ import zlib from 'zlib';
4
+
5
+ export const gzipP = util.promisify(zlib.gzip);
6
+ export const gunzipP = util.promisify(zlib.gunzip);
7
+ export const existsP = async (path) => {
8
+ try {
9
+ await fsp.access(path); // Defaults to F_OK: indicating that the file is visible to the calling process
10
+ return true;
11
+ } catch (err) {
12
+ return false;
13
+ }
14
+ };
package/src/serve_data.js CHANGED
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
- import fs from 'node:fs';
3
+ import fsp from 'node:fs/promises';
4
4
  import path from 'path';
5
- import zlib from 'zlib';
6
5
 
7
6
  import clone from 'clone';
8
7
  import express from 'express';
@@ -10,12 +9,13 @@ import MBTiles from '@mapbox/mbtiles';
10
9
  import Pbf from 'pbf';
11
10
  import { VectorTile } from '@mapbox/vector-tile';
12
11
 
13
- import { getTileUrls, isValidHttpUrl, fixTileJSONCenter } from './utils.js';
12
+ import { fixTileJSONCenter, getTileUrls, isValidHttpUrl } from './utils.js';
14
13
  import {
15
- openPMtiles,
16
14
  getPMtilesInfo,
17
15
  getPMtilesTile,
16
+ openPMtiles,
18
17
  } from './pmtiles_adapter.js';
18
+ import { gunzipP, gzipP } from './promises.js';
19
19
 
20
20
  export const serve_data = {
21
21
  init: (options, repo) => {
@@ -89,12 +89,12 @@ export const serve_data = {
89
89
  headers['Content-Encoding'] = 'gzip';
90
90
  res.set(headers);
91
91
 
92
- data = zlib.gzipSync(data);
92
+ data = await gzipP(data);
93
93
 
94
94
  return res.status(200).send(data);
95
95
  }
96
96
  } else if (item.sourceType === 'mbtiles') {
97
- item.source.getTile(z, x, y, (err, data, headers) => {
97
+ item.source.getTile(z, x, y, async (err, data, headers) => {
98
98
  let isGzipped;
99
99
  if (err) {
100
100
  if (/does not exist/.test(err.message)) {
@@ -114,7 +114,7 @@ export const serve_data = {
114
114
  data.slice(0, 2).indexOf(Buffer.from([0x1f, 0x8b])) === 0;
115
115
  if (options.dataDecoratorFunc) {
116
116
  if (isGzipped) {
117
- data = zlib.unzipSync(data);
117
+ data = await gunzipP(data);
118
118
  isGzipped = false;
119
119
  }
120
120
  data = options.dataDecoratorFunc(id, 'data', data, z, x, y);
@@ -126,7 +126,7 @@ export const serve_data = {
126
126
  headers['Content-Type'] = 'application/json';
127
127
 
128
128
  if (isGzipped) {
129
- data = zlib.unzipSync(data);
129
+ data = await gunzipP(data);
130
130
  isGzipped = false;
131
131
  }
132
132
 
@@ -151,7 +151,7 @@ export const serve_data = {
151
151
  res.set(headers);
152
152
 
153
153
  if (!isGzipped) {
154
- data = zlib.gzipSync(data);
154
+ data = await gzipP(data);
155
155
  }
156
156
 
157
157
  return res.status(200).send(data);
@@ -212,7 +212,7 @@ export const serve_data = {
212
212
  };
213
213
 
214
214
  if (!isValidHttpUrl(inputFile)) {
215
- const inputFileStats = fs.statSync(inputFile);
215
+ const inputFileStats = await fsp.stat(inputFile);
216
216
  if (!inputFileStats.isFile() || inputFileStats.size === 0) {
217
217
  throw Error(`Not valid input file: "${inputFile}"`);
218
218
  }
@@ -17,7 +17,6 @@ import fs from 'node:fs';
17
17
  import path from 'path';
18
18
  import url from 'url';
19
19
  import util from 'util';
20
- import zlib from 'zlib';
21
20
  import sharp from 'sharp';
22
21
  import clone from 'clone';
23
22
  import Color from 'color';
@@ -42,6 +41,8 @@ import {
42
41
  getPMtilesTile,
43
42
  } from './pmtiles_adapter.js';
44
43
  import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
44
+ import fsp from 'node:fs/promises';
45
+ import { gunzipP } from './promises.js';
45
46
 
46
47
  const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
47
48
  const PATH_PATTERN =
@@ -503,14 +504,32 @@ const respondImage = (
503
504
  image.composite(composites);
504
505
  }
505
506
 
506
- const formatQuality = (options.formatQuality || {})[format];
507
+ // Legacy formatQuality is deprecated but still works
508
+ const formatQualities = options.formatQuality || {};
509
+ if (Object.keys(formatQualities).length !== 0) {
510
+ console.log(
511
+ 'WARNING: The formatQuality option is deprecated and has been replaced with formatOptions. Please see the documentation. The values from formatQuality will be used if a quality setting is not provided via formatOptions.',
512
+ );
513
+ }
514
+ const formatQuality = formatQualities[format];
515
+
516
+ const formatOptions = (options.formatOptions || {})[format] || {};
507
517
 
508
518
  if (format === 'png') {
509
- image.png({ adaptiveFiltering: false });
519
+ image.png({
520
+ progressive: formatOptions.progressive,
521
+ compressionLevel: formatOptions.compressionLevel,
522
+ adaptiveFiltering: formatOptions.adaptiveFiltering,
523
+ palette: formatOptions.palette,
524
+ quality: formatOptions.quality,
525
+ effort: formatOptions.effort,
526
+ colors: formatOptions.colors,
527
+ dither: formatOptions.dither,
528
+ });
510
529
  } else if (format === 'jpeg') {
511
- image.jpeg({ quality: formatQuality || 80 });
530
+ image.jpeg({ quality: formatOptions.quality || formatQuality || 80 });
512
531
  } else if (format === 'webp') {
513
- image.webp({ quality: formatQuality || 90 });
532
+ image.webp({ quality: formatOptions.quality || formatQuality || 90 });
514
533
  }
515
534
  image.toBuffer((err, buffer, info) => {
516
535
  if (!buffer) {
@@ -943,7 +962,7 @@ export const serve_rendered = {
943
962
  callback(null, response);
944
963
  }
945
964
  } else if (sourceType === 'mbtiles') {
946
- source.getTile(z, x, y, (err, data, headers) => {
965
+ source.getTile(z, x, y, async (err, data, headers) => {
947
966
  if (err) {
948
967
  if (options.verbose)
949
968
  console.log('MBTiles error, serving empty', err);
@@ -962,7 +981,7 @@ export const serve_rendered = {
962
981
 
963
982
  if (format === 'pbf') {
964
983
  try {
965
- response.data = zlib.unzipSync(data);
984
+ response.data = await gunzipP(data);
966
985
  } catch (err) {
967
986
  console.log(
968
987
  'Skipping incorrect header for tile mbtiles://%s/%s/%s/%s.pbf',
@@ -1039,7 +1058,7 @@ export const serve_rendered = {
1039
1058
  const styleFile = params.style;
1040
1059
  const styleJSONPath = path.resolve(options.paths.styles, styleFile);
1041
1060
  try {
1042
- styleJSON = JSON.parse(fs.readFileSync(styleJSONPath));
1061
+ styleJSON = JSON.parse(await fsp.readFile(styleJSONPath));
1043
1062
  } catch (e) {
1044
1063
  console.log('Error parsing style file');
1045
1064
  return false;
@@ -1145,7 +1164,7 @@ export const serve_rendered = {
1145
1164
  }
1146
1165
 
1147
1166
  if (!isValidHttpUrl(inputFile)) {
1148
- const inputFileStats = fs.statSync(inputFile);
1167
+ const inputFileStats = await fsp.stat(inputFile);
1149
1168
  if (!inputFileStats.isFile() || inputFileStats.size === 0) {
1150
1169
  throw Error(`Not valid PMTiles file: "${inputFile}"`);
1151
1170
  }
@@ -1187,9 +1206,9 @@ export const serve_rendered = {
1187
1206
  }
1188
1207
  } else {
1189
1208
  queue.push(
1190
- new Promise((resolve, reject) => {
1209
+ new Promise(async (resolve, reject) => {
1191
1210
  inputFile = path.resolve(options.paths.mbtiles, inputFile);
1192
- const inputFileStats = fs.statSync(inputFile);
1211
+ const inputFileStats = await fsp.stat(inputFile);
1193
1212
  if (!inputFileStats.isFile() || inputFileStats.size === 0) {
1194
1213
  throw Error(`Not valid MBTiles file: "${inputFile}"`);
1195
1214
  }
@@ -87,7 +87,7 @@ export const serve_style = {
87
87
 
88
88
  let styleFileData;
89
89
  try {
90
- styleFileData = fs.readFileSync(styleFile);
90
+ styleFileData = fs.readFileSync(styleFile); // TODO: could be made async if this function was
91
91
  } catch (e) {
92
92
  console.log('Error reading style file');
93
93
  return false;
package/src/utils.js CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  import path from 'path';
4
4
  import fsPromises from 'fs/promises';
5
- import fs, { existsSync } from 'node:fs';
5
+ import fs from 'node:fs';
6
6
  import clone from 'clone';
7
7
  import { combine } from '@jsse/pbfont';
8
+ import { existsP } from './promises.js';
8
9
 
9
10
  /**
10
11
  * Restrict user input to an allowed set of options.
@@ -225,7 +226,7 @@ export const listFonts = async (fontPath) => {
225
226
  const stats = await fsPromises.stat(path.join(fontPath, file));
226
227
  if (
227
228
  stats.isDirectory() &&
228
- existsSync(path.join(fontPath, file, '0-255.pbf'))
229
+ (await existsP(path.join(fontPath, file, '0-255.pbf')))
229
230
  ) {
230
231
  existingFonts[path.basename(file)] = true;
231
232
  }