tileserver-gl 4.5.1 → 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/.readthedocs.yaml +18 -0
- package/LICENSE.md +32 -0
- package/docs/config.rst +68 -11
- package/docs/endpoints.rst +17 -8
- package/package.json +8 -6
- package/public/templates/index.tmpl +3 -2
- package/src/main.js +166 -89
- package/src/pmtiles_adapter.js +151 -0
- package/src/serve_data.js +188 -88
- package/src/serve_rendered.js +245 -107
- package/src/serve_style.js +18 -14
- package/src/server.js +114 -62
- package/src/utils.js +12 -1
- package/test/static.js +1 -1
package/src/serve_rendered.js
CHANGED
|
@@ -6,7 +6,7 @@ import path from 'path';
|
|
|
6
6
|
import url from 'url';
|
|
7
7
|
import util from 'util';
|
|
8
8
|
import zlib from 'zlib';
|
|
9
|
-
import sharp from 'sharp'; // sharp has to be required before node-canvas. see https://github.com/lovell/sharp/issues/371
|
|
9
|
+
import sharp from 'sharp'; // sharp has to be required before node-canvas on linux but after it on windows. see https://github.com/lovell/sharp/issues/371
|
|
10
10
|
import { createCanvas, Image } from 'canvas';
|
|
11
11
|
import clone from 'clone';
|
|
12
12
|
import Color from 'color';
|
|
@@ -18,12 +18,22 @@ import MBTiles from '@mapbox/mbtiles';
|
|
|
18
18
|
import polyline from '@mapbox/polyline';
|
|
19
19
|
import proj4 from 'proj4';
|
|
20
20
|
import request from 'request';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
getFontsPbf,
|
|
23
|
+
getTileUrls,
|
|
24
|
+
isValidHttpUrl,
|
|
25
|
+
fixTileJSONCenter,
|
|
26
|
+
} from './utils.js';
|
|
27
|
+
import {
|
|
28
|
+
PMtilesOpen,
|
|
29
|
+
GetPMtilesInfo,
|
|
30
|
+
GetPMtilesTile,
|
|
31
|
+
} from './pmtiles_adapter.js';
|
|
22
32
|
|
|
23
33
|
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
|
|
24
34
|
const PATH_PATTERN =
|
|
25
|
-
/^((fill|stroke|width)\:[^\|]+\|)*(
|
|
26
|
-
const httpTester =
|
|
35
|
+
/^((fill|stroke|width)\:[^\|]+\|)*(enc:.+|-?\d+(\.\d*)?,-?\d+(\.\d*)?(\|-?\d+(\.\d*)?,-?\d+(\.\d*)?)+)/;
|
|
36
|
+
const httpTester = /^\/\//;
|
|
27
37
|
|
|
28
38
|
const mercator = new SphericalMercator();
|
|
29
39
|
const getScale = (scale) => (scale || '@1x').slice(1, 2) | 0;
|
|
@@ -158,10 +168,7 @@ const extractPathsFromQuery = (query, transformer) => {
|
|
|
158
168
|
// Iterate through paths, parse and validate them
|
|
159
169
|
for (const providedPath of providedPaths) {
|
|
160
170
|
// Logic for pushing coords to path when path includes google polyline
|
|
161
|
-
if (
|
|
162
|
-
providedPath.includes('enc:') &&
|
|
163
|
-
PATH_PATTERN.test(decodeURIComponent(providedPath))
|
|
164
|
-
) {
|
|
171
|
+
if (providedPath.includes('enc:') && PATH_PATTERN.test(providedPath)) {
|
|
165
172
|
// +4 because 'enc:' is 4 characters, everything after 'enc:' is considered to be part of the polyline
|
|
166
173
|
const encIndex = providedPath.indexOf('enc:') + 4;
|
|
167
174
|
const coords = polyline
|
|
@@ -279,7 +286,10 @@ const extractMarkersFromQuery = (query, options, transformer) => {
|
|
|
279
286
|
let iconURI = markerParts[1];
|
|
280
287
|
// Check if icon is served via http otherwise marker icons are expected to
|
|
281
288
|
// be provided as filepaths relative to configured icon path
|
|
282
|
-
|
|
289
|
+
const isRemoteURL =
|
|
290
|
+
iconURI.startsWith('http://') || iconURI.startsWith('https://');
|
|
291
|
+
const isDataURL = iconURI.startsWith('data:');
|
|
292
|
+
if (!(isRemoteURL || isDataURL)) {
|
|
283
293
|
// Sanitize URI with sanitize-filename
|
|
284
294
|
// https://www.npmjs.com/package/sanitize-filename#details
|
|
285
295
|
iconURI = sanitize(iconURI);
|
|
@@ -292,7 +302,9 @@ const extractMarkersFromQuery = (query, options, transformer) => {
|
|
|
292
302
|
iconURI = path.resolve(options.paths.icons, iconURI);
|
|
293
303
|
|
|
294
304
|
// When we encounter a remote icon check if the configuration explicitly allows them.
|
|
295
|
-
} else if (options.allowRemoteMarkerIcons !== true) {
|
|
305
|
+
} else if (isRemoteURL && options.allowRemoteMarkerIcons !== true) {
|
|
306
|
+
continue;
|
|
307
|
+
} else if (isDataURL && options.allowInlineMarkerImages !== true) {
|
|
296
308
|
continue;
|
|
297
309
|
}
|
|
298
310
|
|
|
@@ -427,7 +439,7 @@ const drawMarkers = async (ctx, markers, z) => {
|
|
|
427
439
|
* @param {number} z Map zoom level.
|
|
428
440
|
*/
|
|
429
441
|
const drawPath = (ctx, path, query, pathQuery, z) => {
|
|
430
|
-
const splitPaths =
|
|
442
|
+
const splitPaths = pathQuery.split('|');
|
|
431
443
|
|
|
432
444
|
if (!path || path.length < 2) {
|
|
433
445
|
return null;
|
|
@@ -771,6 +783,35 @@ export const serve_rendered = {
|
|
|
771
783
|
composite_array.push({ input: canvas.toBuffer() });
|
|
772
784
|
}
|
|
773
785
|
|
|
786
|
+
if (opt_mode === 'static' && item.staticAttributionText) {
|
|
787
|
+
const canvas = createCanvas(scale * width, scale * height);
|
|
788
|
+
const ctx = canvas.getContext('2d');
|
|
789
|
+
ctx.scale(scale, scale);
|
|
790
|
+
|
|
791
|
+
ctx.font = '10px sans-serif';
|
|
792
|
+
const text = item.staticAttributionText;
|
|
793
|
+
const textMetrics = ctx.measureText(text);
|
|
794
|
+
const textWidth = textMetrics.width;
|
|
795
|
+
const textHeight = 14;
|
|
796
|
+
|
|
797
|
+
const padding = 6;
|
|
798
|
+
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
|
|
799
|
+
ctx.fillRect(
|
|
800
|
+
width - textWidth - padding,
|
|
801
|
+
height - textHeight - padding,
|
|
802
|
+
textWidth + padding,
|
|
803
|
+
textHeight + padding,
|
|
804
|
+
);
|
|
805
|
+
ctx.fillStyle = 'rgba(0,0,0,.8)';
|
|
806
|
+
ctx.fillText(
|
|
807
|
+
item.staticAttributionText,
|
|
808
|
+
width - textWidth - padding / 2,
|
|
809
|
+
height - textHeight + 8,
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
composite_array.push({ input: canvas.toBuffer() });
|
|
813
|
+
}
|
|
814
|
+
|
|
774
815
|
if (composite_array.length > 0) {
|
|
775
816
|
image.composite(composite_array);
|
|
776
817
|
}
|
|
@@ -1176,11 +1217,12 @@ export const serve_rendered = {
|
|
|
1176
1217
|
|
|
1177
1218
|
return Promise.all([fontListingPromise]).then(() => app);
|
|
1178
1219
|
},
|
|
1179
|
-
add: (options, repo, params, id, publicUrl, dataResolver) => {
|
|
1220
|
+
add: async (options, repo, params, id, publicUrl, dataResolver) => {
|
|
1180
1221
|
const map = {
|
|
1181
1222
|
renderers: [],
|
|
1182
1223
|
renderers_static: [],
|
|
1183
1224
|
sources: {},
|
|
1225
|
+
source_types: {},
|
|
1184
1226
|
};
|
|
1185
1227
|
|
|
1186
1228
|
let styleJSON;
|
|
@@ -1189,7 +1231,7 @@ export const serve_rendered = {
|
|
|
1189
1231
|
const renderer = new mlgl.Map({
|
|
1190
1232
|
mode: mode,
|
|
1191
1233
|
ratio: ratio,
|
|
1192
|
-
request: (req, callback) => {
|
|
1234
|
+
request: async (req, callback) => {
|
|
1193
1235
|
const protocol = req.url.split(':')[0];
|
|
1194
1236
|
// console.log('Handling request:', req);
|
|
1195
1237
|
if (protocol === 'sprites') {
|
|
@@ -1216,17 +1258,23 @@ export const serve_rendered = {
|
|
|
1216
1258
|
callback(err, { data: null });
|
|
1217
1259
|
},
|
|
1218
1260
|
);
|
|
1219
|
-
} else if (protocol === 'mbtiles') {
|
|
1261
|
+
} else if (protocol === 'mbtiles' || protocol === 'pmtiles') {
|
|
1220
1262
|
const parts = req.url.split('/');
|
|
1221
1263
|
const sourceId = parts[2];
|
|
1222
1264
|
const source = map.sources[sourceId];
|
|
1265
|
+
const source_type = map.source_types[sourceId];
|
|
1223
1266
|
const sourceInfo = styleJSON.sources[sourceId];
|
|
1267
|
+
|
|
1224
1268
|
const z = parts[3] | 0;
|
|
1225
1269
|
const x = parts[4] | 0;
|
|
1226
1270
|
const y = parts[5].split('.')[0] | 0;
|
|
1227
1271
|
const format = parts[5].split('.')[1];
|
|
1228
|
-
|
|
1229
|
-
|
|
1272
|
+
|
|
1273
|
+
if (source_type === 'pmtiles') {
|
|
1274
|
+
let tileinfo = await GetPMtilesTile(source, z, x, y);
|
|
1275
|
+
let data = tileinfo.data;
|
|
1276
|
+
let headers = tileinfo.header;
|
|
1277
|
+
if (data == undefined) {
|
|
1230
1278
|
if (options.verbose)
|
|
1231
1279
|
console.log('MBTiles error, serving empty', err);
|
|
1232
1280
|
createEmptyResponse(
|
|
@@ -1235,41 +1283,75 @@ export const serve_rendered = {
|
|
|
1235
1283
|
callback,
|
|
1236
1284
|
);
|
|
1237
1285
|
return;
|
|
1238
|
-
}
|
|
1286
|
+
} else {
|
|
1287
|
+
const response = {};
|
|
1288
|
+
response.data = data;
|
|
1289
|
+
if (headers['Last-Modified']) {
|
|
1290
|
+
response.modified = new Date(headers['Last-Modified']);
|
|
1291
|
+
}
|
|
1239
1292
|
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1293
|
+
if (format === 'pbf') {
|
|
1294
|
+
if (options.dataDecoratorFunc) {
|
|
1295
|
+
response.data = options.dataDecoratorFunc(
|
|
1296
|
+
sourceId,
|
|
1297
|
+
'data',
|
|
1298
|
+
response.data,
|
|
1299
|
+
z,
|
|
1300
|
+
x,
|
|
1301
|
+
y,
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1244
1305
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1306
|
+
callback(null, response);
|
|
1307
|
+
}
|
|
1308
|
+
} else if (source_type === 'mbtiles') {
|
|
1309
|
+
source.getTile(z, x, y, (err, data, headers) => {
|
|
1310
|
+
if (err) {
|
|
1311
|
+
if (options.verbose)
|
|
1312
|
+
console.log('MBTiles error, serving empty', err);
|
|
1313
|
+
createEmptyResponse(
|
|
1314
|
+
sourceInfo.format,
|
|
1315
|
+
sourceInfo.color,
|
|
1316
|
+
callback,
|
|
1255
1317
|
);
|
|
1318
|
+
return;
|
|
1256
1319
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
response.data,
|
|
1262
|
-
z,
|
|
1263
|
-
x,
|
|
1264
|
-
y,
|
|
1265
|
-
);
|
|
1320
|
+
|
|
1321
|
+
const response = {};
|
|
1322
|
+
if (headers['Last-Modified']) {
|
|
1323
|
+
response.modified = new Date(headers['Last-Modified']);
|
|
1266
1324
|
}
|
|
1267
|
-
} else {
|
|
1268
|
-
response.data = data;
|
|
1269
|
-
}
|
|
1270
1325
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1326
|
+
if (format === 'pbf') {
|
|
1327
|
+
try {
|
|
1328
|
+
response.data = zlib.unzipSync(data);
|
|
1329
|
+
} catch (err) {
|
|
1330
|
+
console.log(
|
|
1331
|
+
'Skipping incorrect header for tile mbtiles://%s/%s/%s/%s.pbf',
|
|
1332
|
+
id,
|
|
1333
|
+
z,
|
|
1334
|
+
x,
|
|
1335
|
+
y,
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
if (options.dataDecoratorFunc) {
|
|
1339
|
+
response.data = options.dataDecoratorFunc(
|
|
1340
|
+
sourceId,
|
|
1341
|
+
'data',
|
|
1342
|
+
response.data,
|
|
1343
|
+
z,
|
|
1344
|
+
x,
|
|
1345
|
+
y,
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1348
|
+
} else {
|
|
1349
|
+
response.data = data;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
callback(null, response);
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1273
1355
|
} else if (protocol === 'http' || protocol === 'https') {
|
|
1274
1356
|
request(
|
|
1275
1357
|
{
|
|
@@ -1378,87 +1460,143 @@ export const serve_rendered = {
|
|
|
1378
1460
|
dataProjWGStoInternalWGS: null,
|
|
1379
1461
|
lastModified: new Date().toUTCString(),
|
|
1380
1462
|
watermark: params.watermark || options.watermark,
|
|
1463
|
+
staticAttributionText:
|
|
1464
|
+
params.staticAttributionText || options.staticAttributionText,
|
|
1381
1465
|
};
|
|
1382
1466
|
repo[id] = repoobj;
|
|
1383
1467
|
|
|
1384
1468
|
const queue = [];
|
|
1385
1469
|
for (const name of Object.keys(styleJSON.sources)) {
|
|
1470
|
+
let source_type;
|
|
1386
1471
|
let source = styleJSON.sources[name];
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1472
|
+
let url = source.url;
|
|
1473
|
+
if (
|
|
1474
|
+
url &&
|
|
1475
|
+
(url.startsWith('pmtiles://') || url.startsWith('mbtiles://'))
|
|
1476
|
+
) {
|
|
1477
|
+
// found pmtiles or mbtiles source, replace with info from local file
|
|
1391
1478
|
delete source.url;
|
|
1392
1479
|
|
|
1393
|
-
let
|
|
1394
|
-
|
|
1395
|
-
|
|
1480
|
+
let dataId = url.replace('pmtiles://', '').replace('mbtiles://', '');
|
|
1481
|
+
if (dataId.startsWith('{') && dataId.endsWith('}')) {
|
|
1482
|
+
dataId = dataId.slice(1, -1);
|
|
1483
|
+
}
|
|
1396
1484
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1485
|
+
const mapsTo = (params.mapping || {})[dataId];
|
|
1486
|
+
if (mapsTo) {
|
|
1487
|
+
dataId = mapsTo;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
let inputFile;
|
|
1491
|
+
const DataInfo = dataResolver(dataId);
|
|
1492
|
+
if (DataInfo.inputfile) {
|
|
1493
|
+
inputFile = DataInfo.inputfile;
|
|
1494
|
+
source_type = DataInfo.filetype;
|
|
1495
|
+
} else {
|
|
1496
|
+
console.error(`ERROR: data "${inputFile}" not found!`);
|
|
1497
|
+
process.exit(1);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
if (!isValidHttpUrl(inputFile)) {
|
|
1501
|
+
const inputFileStats = fs.statSync(inputFile);
|
|
1502
|
+
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
|
1503
|
+
throw Error(`Not valid PMTiles file: "${inputFile}"`);
|
|
1407
1504
|
}
|
|
1408
1505
|
}
|
|
1409
1506
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1507
|
+
if (source_type === 'pmtiles') {
|
|
1508
|
+
map.sources[name] = PMtilesOpen(inputFile);
|
|
1509
|
+
map.source_types[name] = 'pmtiles';
|
|
1510
|
+
const metadata = await GetPMtilesInfo(map.sources[name]);
|
|
1511
|
+
|
|
1512
|
+
if (!repoobj.dataProjWGStoInternalWGS && metadata.proj4) {
|
|
1513
|
+
// how to do this for multiple sources with different proj4 defs?
|
|
1514
|
+
const to3857 = proj4('EPSG:3857');
|
|
1515
|
+
const toDataProj = proj4(metadata.proj4);
|
|
1516
|
+
repoobj.dataProjWGStoInternalWGS = (xy) =>
|
|
1517
|
+
to3857.inverse(toDataProj.forward(xy));
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
const type = source.type;
|
|
1521
|
+
Object.assign(source, metadata);
|
|
1522
|
+
source.type = type;
|
|
1523
|
+
source.tiles = [
|
|
1524
|
+
// meta url which will be detected when requested
|
|
1525
|
+
`pmtiles://${name}/{z}/{x}/{y}.${metadata.format || 'pbf'}`,
|
|
1526
|
+
];
|
|
1527
|
+
delete source.scheme;
|
|
1528
|
+
|
|
1529
|
+
if (
|
|
1530
|
+
!attributionOverride &&
|
|
1531
|
+
source.attribution &&
|
|
1532
|
+
source.attribution.length > 0
|
|
1533
|
+
) {
|
|
1534
|
+
if (!tileJSON.attribution.includes(source.attribution)) {
|
|
1535
|
+
if (tileJSON.attribution.length > 0) {
|
|
1536
|
+
tileJSON.attribution += ' | ';
|
|
1537
|
+
}
|
|
1538
|
+
tileJSON.attribution += source.attribution;
|
|
1416
1539
|
}
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1540
|
+
}
|
|
1541
|
+
} else {
|
|
1542
|
+
queue.push(
|
|
1543
|
+
new Promise((resolve, reject) => {
|
|
1544
|
+
inputFile = path.resolve(options.paths.mbtiles, inputFile);
|
|
1545
|
+
const inputFileStats = fs.statSync(inputFile);
|
|
1546
|
+
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
|
|
1547
|
+
throw Error(`Not valid MBTiles file: "${inputFile}"`);
|
|
1548
|
+
}
|
|
1549
|
+
map.sources[name] = new MBTiles(inputFile + '?mode=ro', (err) => {
|
|
1550
|
+
map.sources[name].getInfo((err, info) => {
|
|
1551
|
+
if (err) {
|
|
1552
|
+
console.error(err);
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
map.source_types[name] = 'mbtiles';
|
|
1556
|
+
|
|
1557
|
+
if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
|
|
1558
|
+
// how to do this for multiple sources with different proj4 defs?
|
|
1559
|
+
const to3857 = proj4('EPSG:3857');
|
|
1560
|
+
const toDataProj = proj4(info.proj4);
|
|
1561
|
+
repoobj.dataProjWGStoInternalWGS = (xy) =>
|
|
1562
|
+
to3857.inverse(toDataProj.forward(xy));
|
|
1563
|
+
}
|
|
1423
1564
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1565
|
+
const type = source.type;
|
|
1566
|
+
Object.assign(source, info);
|
|
1567
|
+
source.type = type;
|
|
1568
|
+
source.tiles = [
|
|
1569
|
+
// meta url which will be detected when requested
|
|
1570
|
+
`mbtiles://${name}/{z}/{x}/{y}.${info.format || 'pbf'}`,
|
|
1571
|
+
];
|
|
1572
|
+
delete source.scheme;
|
|
1431
1573
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
delete source.scheme;
|
|
1440
|
-
|
|
1441
|
-
if (options.dataDecoratorFunc) {
|
|
1442
|
-
source = options.dataDecoratorFunc(name, 'tilejson', source);
|
|
1443
|
-
}
|
|
1574
|
+
if (options.dataDecoratorFunc) {
|
|
1575
|
+
source = options.dataDecoratorFunc(
|
|
1576
|
+
name,
|
|
1577
|
+
'tilejson',
|
|
1578
|
+
source,
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1444
1581
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1582
|
+
if (
|
|
1583
|
+
!attributionOverride &&
|
|
1584
|
+
source.attribution &&
|
|
1585
|
+
source.attribution.length > 0
|
|
1586
|
+
) {
|
|
1587
|
+
if (!tileJSON.attribution.includes(source.attribution)) {
|
|
1588
|
+
if (tileJSON.attribution.length > 0) {
|
|
1589
|
+
tileJSON.attribution += ' | ';
|
|
1590
|
+
}
|
|
1591
|
+
tileJSON.attribution += source.attribution;
|
|
1453
1592
|
}
|
|
1454
|
-
tileJSON.attribution += source.attribution;
|
|
1455
1593
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1594
|
+
resolve();
|
|
1595
|
+
});
|
|
1458
1596
|
});
|
|
1459
|
-
})
|
|
1460
|
-
|
|
1461
|
-
|
|
1597
|
+
}),
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1462
1600
|
}
|
|
1463
1601
|
}
|
|
1464
1602
|
|
package/src/serve_style.js
CHANGED
|
@@ -9,7 +9,7 @@ import { validate } from '@maplibre/maplibre-gl-style-spec';
|
|
|
9
9
|
|
|
10
10
|
import { getPublicUrl } from './utils.js';
|
|
11
11
|
|
|
12
|
-
const httpTester =
|
|
12
|
+
const httpTester = /^\/\//;
|
|
13
13
|
|
|
14
14
|
const fixUrl = (req, url, publicUrl, opt_nokey) => {
|
|
15
15
|
if (!url || typeof url !== 'string' || url.indexOf('local://') !== 0) {
|
|
@@ -110,20 +110,24 @@ export const serve_style = {
|
|
|
110
110
|
|
|
111
111
|
for (const name of Object.keys(styleJSON.sources)) {
|
|
112
112
|
const source = styleJSON.sources[name];
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
mbtilesFile = mapsTo;
|
|
124
|
-
}
|
|
113
|
+
let url = source.url;
|
|
114
|
+
if (
|
|
115
|
+
url &&
|
|
116
|
+
(url.startsWith('pmtiles://') || url.startsWith('mbtiles://'))
|
|
117
|
+
) {
|
|
118
|
+
const protocol = url.split(':')[0];
|
|
119
|
+
|
|
120
|
+
let dataId = url.replace('pmtiles://', '').replace('mbtiles://', '');
|
|
121
|
+
if (dataId.startsWith('{') && dataId.endsWith('}')) {
|
|
122
|
+
dataId = dataId.slice(1, -1);
|
|
125
123
|
}
|
|
126
|
-
|
|
124
|
+
|
|
125
|
+
const mapsTo = (params.mapping || {})[dataId];
|
|
126
|
+
if (mapsTo) {
|
|
127
|
+
dataId = mapsTo;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const identifier = reportTiles(dataId, protocol);
|
|
127
131
|
if (!identifier) {
|
|
128
132
|
return false;
|
|
129
133
|
}
|