tileserver-gl-light 4.13.0 → 4.13.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.
- package/.dockerignore +1 -0
- package/docs/config.rst +2 -1
- package/docs/endpoints.rst +13 -1
- package/package.json +2 -2
- package/src/main.js +46 -53
- package/src/mbtiles_wrapper.js +46 -0
- package/src/serve_data.js +16 -30
- package/src/serve_rendered.js +65 -62
- package/src/serve_style.js +14 -1
- package/src/server.js +9 -9
- package/public/resources/fonts/OpenSans-Bold.ttf +0 -0
- package/public/resources/fonts/OpenSans-Italic.ttf +0 -0
- package/public/resources/fonts/OpenSans-Regular.ttf +0 -0
package/.dockerignore
CHANGED
package/docs/config.rst
CHANGED
package/docs/endpoints.rst
CHANGED
|
@@ -77,7 +77,7 @@ Static images
|
|
|
77
77
|
* scales with ``scale`` parameter since image placement is relative to it's size
|
|
78
78
|
* e.g. ``2,-4`` - Image will be moved 2 pixel to the right and 4 pixel in the upwards direction from the provided location
|
|
79
79
|
|
|
80
|
-
* e.g. ``5.9,45.8|marker-
|
|
80
|
+
* e.g. ``5.9,45.8|marker-icon.png|scale:0.5|offset:2,-4``
|
|
81
81
|
* can be provided multiple times
|
|
82
82
|
|
|
83
83
|
* ``padding`` - "percentage" padding for fitted endpoints (area-based and path autofit)
|
|
@@ -100,6 +100,18 @@ Source data
|
|
|
100
100
|
|
|
101
101
|
* TileJSON at ``/data/{id}.json``
|
|
102
102
|
|
|
103
|
+
Static files
|
|
104
|
+
===========
|
|
105
|
+
* Static files are served at ``/files/{filename}``
|
|
106
|
+
|
|
107
|
+
* The source folder can be configured (``options.paths.files``), default is ``public/files``
|
|
108
|
+
|
|
109
|
+
* This feature can be used to serve ``geojson`` files for styles and rendered tiles.
|
|
110
|
+
|
|
111
|
+
* Keep in mind, that each rendered tile loads the whole geojson file, if performance matters a conversion to a tiled format (e.g. with https://github.com/felt/tippecanoe)may be a better approch.
|
|
112
|
+
|
|
113
|
+
* Use ``file://{filename}`` to have matching paths for both endoints
|
|
114
|
+
|
|
103
115
|
TileJSON arrays
|
|
104
116
|
===============
|
|
105
117
|
Array of all TileJSONs is at ``[/{tileSize}]/index.json`` (``[/{tileSize}]/rendered.json``; ``/data.json``)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tileserver-gl-light",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.2",
|
|
4
4
|
"description": "Map tile server for JSON GL styles - serving vector tiles",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"bin": "src/main.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@maplibre/maplibre-gl-style-spec": "20.3.1",
|
|
26
26
|
"@sindresorhus/fnv1a": "3.1.0",
|
|
27
27
|
"advanced-pool": "0.3.3",
|
|
28
|
-
"axios": "^1.7.
|
|
28
|
+
"axios": "^1.7.6",
|
|
29
29
|
"chokidar": "3.6.0",
|
|
30
30
|
"clone": "2.1.2",
|
|
31
31
|
"color": "4.2.3",
|
package/src/main.js
CHANGED
|
@@ -8,9 +8,11 @@ import path from 'path';
|
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import axios from 'axios';
|
|
10
10
|
import { server } from './server.js';
|
|
11
|
-
import MBTiles from '@mapbox/mbtiles';
|
|
12
11
|
import { isValidHttpUrl } from './utils.js';
|
|
13
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';
|
|
14
16
|
|
|
15
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
18
|
const __dirname = path.dirname(__filename);
|
|
@@ -23,8 +25,6 @@ if (args.length >= 3 && args[2][0] !== '-') {
|
|
|
23
25
|
args.splice(2, 0, '--mbtiles');
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
import { program } from 'commander';
|
|
27
|
-
import { existsP } from './promises.js';
|
|
28
28
|
program
|
|
29
29
|
.description('tileserver-gl startup options')
|
|
30
30
|
.usage('tileserver-gl [mbtiles] [options]')
|
|
@@ -184,62 +184,55 @@ const startWithInputFile = async (inputFile) => {
|
|
|
184
184
|
);
|
|
185
185
|
process.exit(1);
|
|
186
186
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
`Make sure ${path.basename(inputFile)} is valid MBTiles.`,
|
|
199
|
-
);
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}
|
|
202
|
-
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;
|
|
203
198
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
199
|
+
if (
|
|
200
|
+
info.format === 'pbf' &&
|
|
201
|
+
info.name.toLowerCase().indexOf('openmaptiles') > -1
|
|
202
|
+
) {
|
|
203
|
+
config['data'][`v3`] = {
|
|
204
|
+
mbtiles: path.basename(inputFile),
|
|
205
|
+
};
|
|
211
206
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
} else {
|
|
226
|
-
console.log(
|
|
227
|
-
`WARN: MBTiles not in "openmaptiles" format. Serving raw data only...`,
|
|
228
|
-
);
|
|
229
|
-
config['data'][(info.id || 'mbtiles').replace(/[?/:]/g, '_')] = {
|
|
230
|
-
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
|
+
},
|
|
231
217
|
};
|
|
232
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
|
+
}
|
|
233
228
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
+
}
|
|
239
234
|
|
|
240
|
-
|
|
241
|
-
});
|
|
242
|
-
});
|
|
235
|
+
return startServer(null, config);
|
|
243
236
|
}
|
|
244
237
|
};
|
|
245
238
|
|
|
@@ -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
|
+
}
|
package/src/serve_data.js
CHANGED
|
@@ -5,7 +5,6 @@ import path from 'path';
|
|
|
5
5
|
|
|
6
6
|
import clone from 'clone';
|
|
7
7
|
import express from 'express';
|
|
8
|
-
import MBTiles from '@mapbox/mbtiles';
|
|
9
8
|
import Pbf from 'pbf';
|
|
10
9
|
import { VectorTile } from '@mapbox/vector-tile';
|
|
11
10
|
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
openPMtiles,
|
|
17
16
|
} from './pmtiles_adapter.js';
|
|
18
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) => {
|
|
@@ -242,39 +242,25 @@ export const serve_data = {
|
|
|
242
242
|
}
|
|
243
243
|
} else if (inputType === 'mbtiles') {
|
|
244
244
|
sourceType = 'mbtiles';
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
262
|
-
delete tileJSON['filesize'];
|
|
263
|
-
delete tileJSON['mtime'];
|
|
264
|
-
delete tileJSON['scheme'];
|
|
251
|
+
Object.assign(tileJSON, info);
|
|
265
252
|
|
|
266
|
-
|
|
267
|
-
|
|
253
|
+
tileJSON['tilejson'] = '2.0.0';
|
|
254
|
+
delete tileJSON['filesize'];
|
|
255
|
+
delete tileJSON['mtime'];
|
|
256
|
+
delete tileJSON['scheme'];
|
|
268
257
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
resolve();
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
});
|
|
258
|
+
Object.assign(tileJSON, params.tilejson || {});
|
|
259
|
+
fixTileJSONCenter(tileJSON);
|
|
276
260
|
|
|
277
|
-
|
|
261
|
+
if (options.dataDecoratorFunc) {
|
|
262
|
+
tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
|
|
263
|
+
}
|
|
278
264
|
}
|
|
279
265
|
|
|
280
266
|
repo[id] = {
|
package/src/serve_rendered.js
CHANGED
|
@@ -24,7 +24,6 @@ import express from 'express';
|
|
|
24
24
|
import sanitize from 'sanitize-filename';
|
|
25
25
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
|
26
26
|
import mlgl from '@maplibre/maplibre-gl-native';
|
|
27
|
-
import MBTiles from '@mapbox/mbtiles';
|
|
28
27
|
import polyline from '@mapbox/polyline';
|
|
29
28
|
import proj4 from 'proj4';
|
|
30
29
|
import axios from 'axios';
|
|
@@ -42,7 +41,8 @@ import {
|
|
|
42
41
|
} from './pmtiles_adapter.js';
|
|
43
42
|
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
|
|
44
43
|
import fsp from 'node:fs/promises';
|
|
45
|
-
import { gunzipP } from './promises.js';
|
|
44
|
+
import { existsP, gunzipP } from './promises.js';
|
|
45
|
+
import { openMbTilesWrapper } from './mbtiles_wrapper.js';
|
|
46
46
|
|
|
47
47
|
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
|
48
48
|
const PATH_PATTERN =
|
|
@@ -893,13 +893,15 @@ export const serve_rendered = {
|
|
|
893
893
|
// console.log('Handling request:', req);
|
|
894
894
|
if (protocol === 'sprites') {
|
|
895
895
|
const dir = options.paths[protocol];
|
|
896
|
-
const file =
|
|
896
|
+
const file = decodeURIComponent(req.url).substring(
|
|
897
|
+
protocol.length + 3,
|
|
898
|
+
);
|
|
897
899
|
fs.readFile(path.join(dir, file), (err, data) => {
|
|
898
900
|
callback(err, { data: data });
|
|
899
901
|
});
|
|
900
902
|
} else if (protocol === 'fonts') {
|
|
901
903
|
const parts = req.url.split('/');
|
|
902
|
-
const fontstack =
|
|
904
|
+
const fontstack = decodeURIComponent(parts[2]);
|
|
903
905
|
const range = parts[3].split('.')[0];
|
|
904
906
|
|
|
905
907
|
try {
|
|
@@ -1039,6 +1041,25 @@ export const serve_rendered = {
|
|
|
1039
1041
|
const format = extensionToFormat[extension] || '';
|
|
1040
1042
|
createEmptyResponse(format, '', callback);
|
|
1041
1043
|
}
|
|
1044
|
+
} else if (protocol === 'file') {
|
|
1045
|
+
const name = decodeURI(req.url).substring(protocol.length + 3);
|
|
1046
|
+
const file = path.join(options.paths['files'], name);
|
|
1047
|
+
if (await existsP(file)) {
|
|
1048
|
+
const inputFileStats = await fsp.stat(file);
|
|
1049
|
+
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
|
1050
|
+
throw Error(
|
|
1051
|
+
`File is not valid: "${req.url}" - resolved to "${file}"`,
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
fs.readFile(file, (err, data) => {
|
|
1056
|
+
callback(err, { data: data });
|
|
1057
|
+
});
|
|
1058
|
+
} else {
|
|
1059
|
+
throw Error(
|
|
1060
|
+
`File does not exist: "${req.url}" - resolved to "${file}"`,
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
1042
1063
|
}
|
|
1043
1064
|
},
|
|
1044
1065
|
});
|
|
@@ -1131,7 +1152,6 @@ export const serve_rendered = {
|
|
|
1131
1152
|
};
|
|
1132
1153
|
repo[id] = repoobj;
|
|
1133
1154
|
|
|
1134
|
-
const queue = [];
|
|
1135
1155
|
for (const name of Object.keys(styleJSON.sources)) {
|
|
1136
1156
|
let sourceType;
|
|
1137
1157
|
let source = styleJSON.sources[name];
|
|
@@ -1205,69 +1225,52 @@ export const serve_rendered = {
|
|
|
1205
1225
|
}
|
|
1206
1226
|
}
|
|
1207
1227
|
} else {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
map.sources[name].getInfo((err, info) => {
|
|
1217
|
-
if (err) {
|
|
1218
|
-
console.error(err);
|
|
1219
|
-
return;
|
|
1220
|
-
}
|
|
1221
|
-
map.sourceTypes[name] = 'mbtiles';
|
|
1222
|
-
|
|
1223
|
-
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
|
|
1224
|
-
// how to do this for multiple sources with different proj4 defs?
|
|
1225
|
-
const to3857 = proj4('EPSG:3857');
|
|
1226
|
-
const toDataProj = proj4(info.proj4);
|
|
1227
|
-
repoobj.dataProjWGStoInternalWGS = (xy) =>
|
|
1228
|
-
to3857.inverse(toDataProj.forward(xy));
|
|
1229
|
-
}
|
|
1228
|
+
const inputFileStats = await fsp.stat(inputFile);
|
|
1229
|
+
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
|
1230
|
+
throw Error(`Not valid MBTiles file: "${inputFile}"`);
|
|
1231
|
+
}
|
|
1232
|
+
const mbw = await openMbTilesWrapper(inputFile);
|
|
1233
|
+
const info = await mbw.getInfo();
|
|
1234
|
+
map.sources[name] = mbw.getMbTiles();
|
|
1235
|
+
map.sourceTypes[name] = 'mbtiles';
|
|
1230
1236
|
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
delete source.scheme;
|
|
1239
|
-
|
|
1240
|
-
if (options.dataDecoratorFunc) {
|
|
1241
|
-
source = options.dataDecoratorFunc(
|
|
1242
|
-
name,
|
|
1243
|
-
'tilejson',
|
|
1244
|
-
source,
|
|
1245
|
-
);
|
|
1246
|
-
}
|
|
1237
|
+
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
|
|
1238
|
+
// how to do this for multiple sources with different proj4 defs?
|
|
1239
|
+
const to3857 = proj4('EPSG:3857');
|
|
1240
|
+
const toDataProj = proj4(info.proj4);
|
|
1241
|
+
repoobj.dataProjWGStoInternalWGS = (xy) =>
|
|
1242
|
+
to3857.inverse(toDataProj.forward(xy));
|
|
1243
|
+
}
|
|
1247
1244
|
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1245
|
+
const type = source.type;
|
|
1246
|
+
Object.assign(source, info);
|
|
1247
|
+
source.type = type;
|
|
1248
|
+
source.tiles = [
|
|
1249
|
+
// meta url which will be detected when requested
|
|
1250
|
+
`mbtiles://${name}/{z}/{x}/{y}.${info.format || 'pbf'}`,
|
|
1251
|
+
];
|
|
1252
|
+
delete source.scheme;
|
|
1253
|
+
|
|
1254
|
+
if (options.dataDecoratorFunc) {
|
|
1255
|
+
source = options.dataDecoratorFunc(name, 'tilejson', source);
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
if (
|
|
1259
|
+
!attributionOverride &&
|
|
1260
|
+
source.attribution &&
|
|
1261
|
+
source.attribution.length > 0
|
|
1262
|
+
) {
|
|
1263
|
+
if (!tileJSON.attribution.includes(source.attribution)) {
|
|
1264
|
+
if (tileJSON.attribution.length > 0) {
|
|
1265
|
+
tileJSON.attribution += ' | ';
|
|
1266
|
+
}
|
|
1267
|
+
tileJSON.attribution += source.attribution;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1265
1270
|
}
|
|
1266
1271
|
}
|
|
1267
1272
|
}
|
|
1268
1273
|
|
|
1269
|
-
await Promise.all(queue);
|
|
1270
|
-
|
|
1271
1274
|
// standard and @2x tiles are much more usual -> default to larger pools
|
|
1272
1275
|
const minPoolSizes = options.minRendererPoolSizes || [8, 4, 2];
|
|
1273
1276
|
const maxPoolSizes = options.maxRendererPoolSizes || [16, 8, 4];
|
package/src/serve_style.js
CHANGED
|
@@ -26,6 +26,9 @@ export const serve_style = {
|
|
|
26
26
|
for (const name of Object.keys(styleJSON_.sources)) {
|
|
27
27
|
const source = styleJSON_.sources[name];
|
|
28
28
|
source.url = fixUrl(req, source.url, item.publicUrl);
|
|
29
|
+
if (typeof source.data == 'string') {
|
|
30
|
+
source.data = fixUrl(req, source.data, item.publicUrl);
|
|
31
|
+
}
|
|
29
32
|
}
|
|
30
33
|
// mapbox-gl-js viewer cannot handle sprite urls with query
|
|
31
34
|
if (styleJSON_.sprite) {
|
|
@@ -89,7 +92,7 @@ export const serve_style = {
|
|
|
89
92
|
try {
|
|
90
93
|
styleFileData = fs.readFileSync(styleFile); // TODO: could be made async if this function was
|
|
91
94
|
} catch (e) {
|
|
92
|
-
console.log(
|
|
95
|
+
console.log(`Error reading style file "${params.style}"`);
|
|
93
96
|
return false;
|
|
94
97
|
}
|
|
95
98
|
|
|
@@ -128,6 +131,16 @@ export const serve_style = {
|
|
|
128
131
|
}
|
|
129
132
|
source.url = `local://data/${identifier}.json`;
|
|
130
133
|
}
|
|
134
|
+
|
|
135
|
+
let data = source.data;
|
|
136
|
+
if (data && typeof data == 'string' && data.startsWith('file://')) {
|
|
137
|
+
source.data =
|
|
138
|
+
'local://files' +
|
|
139
|
+
path.resolve(
|
|
140
|
+
'/',
|
|
141
|
+
data.replace('file://', '').replace(options.paths.files, ''),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
for (const obj of styleJSON.layers) {
|
package/src/server.js
CHANGED
|
@@ -94,24 +94,23 @@ function start(opts) {
|
|
|
94
94
|
paths.sprites = path.resolve(paths.root, paths.sprites || '');
|
|
95
95
|
paths.mbtiles = path.resolve(paths.root, paths.mbtiles || '');
|
|
96
96
|
paths.pmtiles = path.resolve(paths.root, paths.pmtiles || '');
|
|
97
|
-
paths.icons =
|
|
97
|
+
paths.icons = paths.icons
|
|
98
|
+
? path.resolve(paths.root, paths.icons)
|
|
99
|
+
: path.resolve(__dirname, '../public/resources/images');
|
|
100
|
+
paths.files = paths.files
|
|
101
|
+
? path.resolve(paths.root, paths.files)
|
|
102
|
+
: path.resolve(__dirname, '../public/files');
|
|
98
103
|
|
|
99
104
|
const startupPromises = [];
|
|
100
105
|
|
|
101
|
-
const
|
|
106
|
+
for (const type of Object.keys(paths)) {
|
|
102
107
|
if (!fs.existsSync(paths[type])) {
|
|
103
108
|
console.error(
|
|
104
109
|
`The specified path for "${type}" does not exist (${paths[type]}).`,
|
|
105
110
|
);
|
|
106
111
|
process.exit(1);
|
|
107
112
|
}
|
|
108
|
-
}
|
|
109
|
-
checkPath('styles');
|
|
110
|
-
checkPath('fonts');
|
|
111
|
-
checkPath('sprites');
|
|
112
|
-
checkPath('mbtiles');
|
|
113
|
-
checkPath('pmtiles');
|
|
114
|
-
checkPath('icons');
|
|
113
|
+
}
|
|
115
114
|
|
|
116
115
|
/**
|
|
117
116
|
* Recursively get all files within a directory.
|
|
@@ -161,6 +160,7 @@ function start(opts) {
|
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
app.use('/data/', serve_data.init(options, serving.data));
|
|
163
|
+
app.use('/files/', express.static(paths.files));
|
|
164
164
|
app.use('/styles/', serve_style.init(options, serving.styles));
|
|
165
165
|
if (!isLight) {
|
|
166
166
|
startupPromises.push(
|
|
Binary file
|
|
Binary file
|
|
Binary file
|