tileserver-gl 4.12.0 → 4.13.1

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,13 +3,16 @@
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';
9
10
  import { server } from './server.js';
10
- import MBTiles from '@mapbox/mbtiles';
11
11
  import { isValidHttpUrl } from './utils.js';
12
12
  import { openPMtiles, getPMtilesInfo } from './pmtiles_adapter.js';
13
+ import { program } from 'commander';
14
+ import { existsP } from './promises.js';
15
+ import { openMbTilesWrapper } from './mbtiles_wrapper.js';
13
16
 
14
17
  const __filename = fileURLToPath(import.meta.url);
15
18
  const __dirname = path.dirname(__filename);
@@ -22,7 +25,6 @@ if (args.length >= 3 && args[2][0] !== '-') {
22
25
  args.splice(2, 0, '--mbtiles');
23
26
  }
24
27
 
25
- import { program } from 'commander';
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: {
@@ -182,62 +184,55 @@ const startWithInputFile = async (inputFile) => {
182
184
  );
183
185
  process.exit(1);
184
186
  }
185
- const instance = new MBTiles(inputFile + '?mode=ro', (err) => {
186
- if (err) {
187
- console.log('ERROR: Unable to open MBTiles.');
188
- console.log(`Make sure ${path.basename(inputFile)} is valid MBTiles.`);
189
- process.exit(1);
190
- }
191
-
192
- instance.getInfo((err, info) => {
193
- if (err || !info) {
194
- console.log('ERROR: Metadata missing in the MBTiles.');
195
- console.log(
196
- `Make sure ${path.basename(inputFile)} is valid MBTiles.`,
197
- );
198
- process.exit(1);
199
- }
200
- const bounds = info.bounds;
187
+ let info;
188
+ try {
189
+ const mbw = await openMbTilesWrapper(inputFile);
190
+ info = await mbw.getInfo();
191
+ if (!info) throw new Error('Metadata missing in the MBTiles.');
192
+ } catch (err) {
193
+ console.log('ERROR: Unable to open MBTiles or read metadata:', err);
194
+ console.log(`Make sure ${path.basename(inputFile)} is valid MBTiles.`);
195
+ process.exit(1);
196
+ }
197
+ const bounds = info.bounds;
201
198
 
202
- if (
203
- info.format === 'pbf' &&
204
- info.name.toLowerCase().indexOf('openmaptiles') > -1
205
- ) {
206
- config['data'][`v3`] = {
207
- mbtiles: path.basename(inputFile),
208
- };
199
+ if (
200
+ info.format === 'pbf' &&
201
+ info.name.toLowerCase().indexOf('openmaptiles') > -1
202
+ ) {
203
+ config['data'][`v3`] = {
204
+ mbtiles: path.basename(inputFile),
205
+ };
209
206
 
210
- const styles = fs.readdirSync(path.resolve(styleDir, 'styles'));
211
- for (const styleName of styles) {
212
- const styleFileRel = styleName + '/style.json';
213
- const styleFile = path.resolve(styleDir, 'styles', styleFileRel);
214
- if (fs.existsSync(styleFile)) {
215
- config['styles'][styleName] = {
216
- style: styleFileRel,
217
- tilejson: {
218
- bounds,
219
- },
220
- };
221
- }
222
- }
223
- } else {
224
- console.log(
225
- `WARN: MBTiles not in "openmaptiles" format. Serving raw data only...`,
226
- );
227
- config['data'][(info.id || 'mbtiles').replace(/[?/:]/g, '_')] = {
228
- mbtiles: path.basename(inputFile),
207
+ const styles = await fsp.readdir(path.resolve(styleDir, 'styles'));
208
+ for (const styleName of styles) {
209
+ const styleFileRel = styleName + '/style.json';
210
+ const styleFile = path.resolve(styleDir, 'styles', styleFileRel);
211
+ if (await existsP(styleFile)) {
212
+ config['styles'][styleName] = {
213
+ style: styleFileRel,
214
+ tilejson: {
215
+ bounds,
216
+ },
229
217
  };
230
218
  }
219
+ }
220
+ } else {
221
+ console.log(
222
+ `WARN: MBTiles not in "openmaptiles" format. Serving raw data only...`,
223
+ );
224
+ config['data'][(info.id || 'mbtiles').replace(/[?/:]/g, '_')] = {
225
+ mbtiles: path.basename(inputFile),
226
+ };
227
+ }
231
228
 
232
- if (opts.verbose) {
233
- console.log(JSON.stringify(config, undefined, 2));
234
- } else {
235
- console.log('Run with --verbose to see the config file here.');
236
- }
229
+ if (opts.verbose) {
230
+ console.log(JSON.stringify(config, undefined, 2));
231
+ } else {
232
+ console.log('Run with --verbose to see the config file here.');
233
+ }
237
234
 
238
- return startServer(null, config);
239
- });
240
- });
235
+ return startServer(null, config);
241
236
  }
242
237
  };
243
238
 
@@ -254,10 +249,10 @@ fs.stat(path.resolve(opts.config), async (err, stats) => {
254
249
  return startWithInputFile(inputFile);
255
250
  } else {
256
251
  // try to find in the cwd
257
- const files = fs.readdirSync(process.cwd());
252
+ const files = await fsp.readdir(process.cwd());
258
253
  for (const filename of files) {
259
254
  if (filename.endsWith('.mbtiles') || filename.endsWith('.pmtiles')) {
260
- const inputFilesStats = fs.statSync(filename);
255
+ const inputFilesStats = await fsp.stat(filename);
261
256
  if (inputFilesStats.isFile() && inputFilesStats.size > 0) {
262
257
  inputFile = filename;
263
258
  break;
@@ -0,0 +1,46 @@
1
+ import MBTiles from '@mapbox/mbtiles';
2
+ import util from 'node:util';
3
+
4
+ /**
5
+ * Promise-ful wrapper around the MBTiles class.
6
+ */
7
+ class MBTilesWrapper {
8
+ constructor(mbtiles) {
9
+ this._mbtiles = mbtiles;
10
+ this._getInfoP = util.promisify(mbtiles.getInfo.bind(mbtiles));
11
+ }
12
+
13
+ /**
14
+ * Get the underlying MBTiles object.
15
+ * @returns {MBTiles}
16
+ */
17
+ getMbTiles() {
18
+ return this._mbtiles;
19
+ }
20
+
21
+ /**
22
+ * Get the MBTiles metadata object.
23
+ * @returns {Promise<object>}
24
+ */
25
+ getInfo() {
26
+ return this._getInfoP();
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Open the given MBTiles file and return a promise that resolves with a
32
+ * MBTilesWrapper instance.
33
+ * @param inputFile Input file
34
+ * @returns {Promise<MBTilesWrapper>}
35
+ */
36
+ export function openMbTilesWrapper(inputFile) {
37
+ return new Promise((resolve, reject) => {
38
+ const mbtiles = new MBTiles(inputFile + '?mode=ro', (err) => {
39
+ if (err) {
40
+ reject(err);
41
+ return;
42
+ }
43
+ resolve(new MBTilesWrapper(mbtiles));
44
+ });
45
+ });
46
+ }
@@ -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,21 +1,21 @@
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';
9
- import MBTiles from '@mapbox/mbtiles';
10
8
  import Pbf from 'pbf';
11
9
  import { VectorTile } from '@mapbox/vector-tile';
12
10
 
13
- import { getTileUrls, isValidHttpUrl, fixTileJSONCenter } from './utils.js';
11
+ import { fixTileJSONCenter, getTileUrls, isValidHttpUrl } from './utils.js';
14
12
  import {
15
- openPMtiles,
16
13
  getPMtilesInfo,
17
14
  getPMtilesTile,
15
+ openPMtiles,
18
16
  } from './pmtiles_adapter.js';
17
+ import { gunzipP, gzipP } from './promises.js';
18
+ import { openMbTilesWrapper } from './mbtiles_wrapper.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
  }
@@ -242,39 +242,25 @@ export const serve_data = {
242
242
  }
243
243
  } else if (inputType === 'mbtiles') {
244
244
  sourceType = 'mbtiles';
245
- const sourceInfoPromise = new Promise((resolve, reject) => {
246
- source = new MBTiles(inputFile + '?mode=ro', (err) => {
247
- if (err) {
248
- reject(err);
249
- return;
250
- }
251
- source.getInfo((err, info) => {
252
- if (err) {
253
- reject(err);
254
- return;
255
- }
256
- tileJSON['name'] = id;
257
- tileJSON['format'] = 'pbf';
258
-
259
- Object.assign(tileJSON, info);
245
+ const mbw = await openMbTilesWrapper(inputFile);
246
+ const info = await mbw.getInfo();
247
+ source = mbw.getMbTiles();
248
+ tileJSON['name'] = id;
249
+ tileJSON['format'] = 'pbf';
260
250
 
261
- tileJSON['tilejson'] = '2.0.0';
262
- delete tileJSON['filesize'];
263
- delete tileJSON['mtime'];
264
- delete tileJSON['scheme'];
251
+ Object.assign(tileJSON, info);
265
252
 
266
- Object.assign(tileJSON, params.tilejson || {});
267
- fixTileJSONCenter(tileJSON);
253
+ tileJSON['tilejson'] = '2.0.0';
254
+ delete tileJSON['filesize'];
255
+ delete tileJSON['mtime'];
256
+ delete tileJSON['scheme'];
268
257
 
269
- if (options.dataDecoratorFunc) {
270
- tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
271
- }
272
- resolve();
273
- });
274
- });
275
- });
258
+ Object.assign(tileJSON, params.tilejson || {});
259
+ fixTileJSONCenter(tileJSON);
276
260
 
277
- await sourceInfoPromise;
261
+ if (options.dataDecoratorFunc) {
262
+ tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
263
+ }
278
264
  }
279
265
 
280
266
  repo[id] = {
@@ -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';
@@ -25,7 +24,6 @@ import express from 'express';
25
24
  import sanitize from 'sanitize-filename';
26
25
  import SphericalMercator from '@mapbox/sphericalmercator';
27
26
  import mlgl from '@maplibre/maplibre-gl-native';
28
- import MBTiles from '@mapbox/mbtiles';
29
27
  import polyline from '@mapbox/polyline';
30
28
  import proj4 from 'proj4';
31
29
  import axios from 'axios';
@@ -42,6 +40,9 @@ import {
42
40
  getPMtilesTile,
43
41
  } from './pmtiles_adapter.js';
44
42
  import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
43
+ import fsp from 'node:fs/promises';
44
+ import { gunzipP } from './promises.js';
45
+ import { openMbTilesWrapper } from './mbtiles_wrapper.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;
@@ -1112,7 +1131,6 @@ export const serve_rendered = {
1112
1131
  };
1113
1132
  repo[id] = repoobj;
1114
1133
 
1115
- const queue = [];
1116
1134
  for (const name of Object.keys(styleJSON.sources)) {
1117
1135
  let sourceType;
1118
1136
  let source = styleJSON.sources[name];
@@ -1145,7 +1163,7 @@ export const serve_rendered = {
1145
1163
  }
1146
1164
 
1147
1165
  if (!isValidHttpUrl(inputFile)) {
1148
- const inputFileStats = fs.statSync(inputFile);
1166
+ const inputFileStats = await fsp.stat(inputFile);
1149
1167
  if (!inputFileStats.isFile() || inputFileStats.size === 0) {
1150
1168
  throw Error(`Not valid PMTiles file: "${inputFile}"`);
1151
1169
  }
@@ -1186,69 +1204,52 @@ export const serve_rendered = {
1186
1204
  }
1187
1205
  }
1188
1206
  } else {
1189
- queue.push(
1190
- new Promise((resolve, reject) => {
1191
- inputFile = path.resolve(options.paths.mbtiles, inputFile);
1192
- const inputFileStats = fs.statSync(inputFile);
1193
- if (!inputFileStats.isFile() || inputFileStats.size === 0) {
1194
- throw Error(`Not valid MBTiles file: "${inputFile}"`);
1195
- }
1196
- map.sources[name] = new MBTiles(inputFile + '?mode=ro', (err) => {
1197
- map.sources[name].getInfo((err, info) => {
1198
- if (err) {
1199
- console.error(err);
1200
- return;
1201
- }
1202
- map.sourceTypes[name] = 'mbtiles';
1203
-
1204
- if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
1205
- // how to do this for multiple sources with different proj4 defs?
1206
- const to3857 = proj4('EPSG:3857');
1207
- const toDataProj = proj4(info.proj4);
1208
- repoobj.dataProjWGStoInternalWGS = (xy) =>
1209
- to3857.inverse(toDataProj.forward(xy));
1210
- }
1207
+ const inputFileStats = await fsp.stat(inputFile);
1208
+ if (!inputFileStats.isFile() || inputFileStats.size === 0) {
1209
+ throw Error(`Not valid MBTiles file: "${inputFile}"`);
1210
+ }
1211
+ const mbw = await openMbTilesWrapper(inputFile);
1212
+ const info = await mbw.getInfo();
1213
+ map.sources[name] = mbw.getMbTiles();
1214
+ map.sourceTypes[name] = 'mbtiles';
1211
1215
 
1212
- const type = source.type;
1213
- Object.assign(source, info);
1214
- source.type = type;
1215
- source.tiles = [
1216
- // meta url which will be detected when requested
1217
- `mbtiles://${name}/{z}/{x}/{y}.${info.format || 'pbf'}`,
1218
- ];
1219
- delete source.scheme;
1220
-
1221
- if (options.dataDecoratorFunc) {
1222
- source = options.dataDecoratorFunc(
1223
- name,
1224
- 'tilejson',
1225
- source,
1226
- );
1227
- }
1216
+ if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
1217
+ // how to do this for multiple sources with different proj4 defs?
1218
+ const to3857 = proj4('EPSG:3857');
1219
+ const toDataProj = proj4(info.proj4);
1220
+ repoobj.dataProjWGStoInternalWGS = (xy) =>
1221
+ to3857.inverse(toDataProj.forward(xy));
1222
+ }
1228
1223
 
1229
- if (
1230
- !attributionOverride &&
1231
- source.attribution &&
1232
- source.attribution.length > 0
1233
- ) {
1234
- if (!tileJSON.attribution.includes(source.attribution)) {
1235
- if (tileJSON.attribution.length > 0) {
1236
- tileJSON.attribution += ' | ';
1237
- }
1238
- tileJSON.attribution += source.attribution;
1239
- }
1240
- }
1241
- resolve();
1242
- });
1243
- });
1244
- }),
1245
- );
1224
+ const type = source.type;
1225
+ Object.assign(source, info);
1226
+ source.type = type;
1227
+ source.tiles = [
1228
+ // meta url which will be detected when requested
1229
+ `mbtiles://${name}/{z}/{x}/{y}.${info.format || 'pbf'}`,
1230
+ ];
1231
+ delete source.scheme;
1232
+
1233
+ if (options.dataDecoratorFunc) {
1234
+ source = options.dataDecoratorFunc(name, 'tilejson', source);
1235
+ }
1236
+
1237
+ if (
1238
+ !attributionOverride &&
1239
+ source.attribution &&
1240
+ source.attribution.length > 0
1241
+ ) {
1242
+ if (!tileJSON.attribution.includes(source.attribution)) {
1243
+ if (tileJSON.attribution.length > 0) {
1244
+ tileJSON.attribution += ' | ';
1245
+ }
1246
+ tileJSON.attribution += source.attribution;
1247
+ }
1248
+ }
1246
1249
  }
1247
1250
  }
1248
1251
  }
1249
1252
 
1250
- await Promise.all(queue);
1251
-
1252
1253
  // standard and @2x tiles are much more usual -> default to larger pools
1253
1254
  const minPoolSizes = options.minRendererPoolSizes || [8, 4, 2];
1254
1255
  const maxPoolSizes = options.maxRendererPoolSizes || [16, 8, 4];
@@ -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
  }