tileserver-gl-light 4.4.10 → 4.5.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.
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/codeql.yml +1 -1
- package/.github/workflows/ct.yml +2 -2
- package/.github/workflows/pipeline.yml +3 -3
- package/.github/workflows/release.yml +1 -1
- package/docs/deployment.rst +3 -3
- package/docs/endpoints.rst +8 -7
- package/package.json +4 -4
- package/public/resources/maplibre-gl.css +1 -1
- package/public/resources/maplibre-gl.js +8 -4
- package/public/resources/maplibre-gl.js.map +1 -1
- package/public/templates/data.tmpl +43 -42
- package/public/templates/viewer.tmpl +21 -4
- package/src/serve_rendered.js +292 -299
- package/src/server.js +0 -2
- package/src/utils.js +0 -1
- package/test/static.js +12 -0
- package/public/resources/maplibre-gl-compat.css +0 -1
- package/public/resources/maplibre-gl-compat.js +0 -45
- package/public/resources/maplibre-gl-compat.js.map +0 -1
- package/public/resources/maplibre-gl-inspect-compat.min.js +0 -1
package/src/serve_rendered.js
CHANGED
|
@@ -54,7 +54,6 @@ const cachedEmptyResponses = {
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Create an appropriate mlgl response for http errors.
|
|
57
|
-
*
|
|
58
57
|
* @param {string} format The format (a sharp format or 'pbf').
|
|
59
58
|
* @param {string} color The background color (or empty string for transparent).
|
|
60
59
|
* @param {Function} callback The mlgl callback.
|
|
@@ -102,7 +101,6 @@ function createEmptyResponse(format, color, callback) {
|
|
|
102
101
|
/**
|
|
103
102
|
* Parses coordinate pair provided to pair of floats and ensures the resulting
|
|
104
103
|
* pair is a longitude/latitude combination depending on lnglat query parameter.
|
|
105
|
-
*
|
|
106
104
|
* @param {List} coordinatePair Coordinate pair.
|
|
107
105
|
* @param coordinates
|
|
108
106
|
* @param {object} query Request query parameters.
|
|
@@ -127,7 +125,6 @@ const parseCoordinatePair = (coordinates, query) => {
|
|
|
127
125
|
|
|
128
126
|
/**
|
|
129
127
|
* Parses a coordinate pair from query arguments and optionally transforms it.
|
|
130
|
-
*
|
|
131
128
|
* @param {List} coordinatePair Coordinate pair.
|
|
132
129
|
* @param {object} query Request query parameters.
|
|
133
130
|
* @param {Function} transformer Optional transform function.
|
|
@@ -145,7 +142,6 @@ const parseCoordinates = (coordinatePair, query, transformer) => {
|
|
|
145
142
|
|
|
146
143
|
/**
|
|
147
144
|
* Parses paths provided via query into a list of path objects.
|
|
148
|
-
*
|
|
149
145
|
* @param {object} query Request query parameters.
|
|
150
146
|
* @param {Function} transformer Optional transform function.
|
|
151
147
|
*/
|
|
@@ -166,21 +162,12 @@ const extractPathsFromQuery = (query, transformer) => {
|
|
|
166
162
|
providedPath.includes('enc:') &&
|
|
167
163
|
PATH_PATTERN.test(decodeURIComponent(providedPath))
|
|
168
164
|
) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
!x.startsWith('fill') &&
|
|
176
|
-
!x.startsWith('stroke') &&
|
|
177
|
-
!x.startsWith('width'),
|
|
178
|
-
)
|
|
179
|
-
.join('')
|
|
180
|
-
.replace('enc:', '');
|
|
181
|
-
const coords = polyline.decode(line).map(([lat, lng]) => [lng, lat]);
|
|
182
|
-
paths.push(coords);
|
|
183
|
-
}
|
|
165
|
+
// +4 because 'enc:' is 4 characters, everything after 'enc:' is considered to be part of the polyline
|
|
166
|
+
const encIndex = providedPath.indexOf('enc:') + 4;
|
|
167
|
+
const coords = polyline
|
|
168
|
+
.decode(providedPath.substring(encIndex))
|
|
169
|
+
.map(([lat, lng]) => [lng, lat]);
|
|
170
|
+
paths.push(coords);
|
|
184
171
|
} else {
|
|
185
172
|
// Iterate through paths, parse and validate them
|
|
186
173
|
const currentPath = [];
|
|
@@ -220,7 +207,6 @@ const extractPathsFromQuery = (query, transformer) => {
|
|
|
220
207
|
* on marker object.
|
|
221
208
|
* Options adhere to the following format
|
|
222
209
|
* [optionName]:[optionValue]
|
|
223
|
-
*
|
|
224
210
|
* @param {List[String]} optionsList List of option strings.
|
|
225
211
|
* @param {object} marker Marker object to configure.
|
|
226
212
|
*/
|
|
@@ -255,7 +241,6 @@ const parseMarkerOptions = (optionsList, marker) => {
|
|
|
255
241
|
|
|
256
242
|
/**
|
|
257
243
|
* Parses markers provided via query into a list of marker objects.
|
|
258
|
-
*
|
|
259
244
|
* @param {object} query Request query parameters.
|
|
260
245
|
* @param {object} options Configuration options.
|
|
261
246
|
* @param {Function} transformer Optional transform function.
|
|
@@ -335,7 +320,6 @@ const extractMarkersFromQuery = (query, options, transformer) => {
|
|
|
335
320
|
|
|
336
321
|
/**
|
|
337
322
|
* Transforms coordinates to pixels.
|
|
338
|
-
*
|
|
339
323
|
* @param {List[Number]} ll Longitude/Latitude coordinate pair.
|
|
340
324
|
* @param {number} zoom Map zoom level.
|
|
341
325
|
*/
|
|
@@ -347,7 +331,6 @@ const precisePx = (ll, zoom) => {
|
|
|
347
331
|
|
|
348
332
|
/**
|
|
349
333
|
* Draws a marker in cavans context.
|
|
350
|
-
*
|
|
351
334
|
* @param {object} ctx Canvas context object.
|
|
352
335
|
* @param {object} marker Marker object parsed by extractMarkersFromQuery.
|
|
353
336
|
* @param {number} z Map zoom level.
|
|
@@ -419,7 +402,6 @@ const drawMarker = (ctx, marker, z) => {
|
|
|
419
402
|
* Wraps drawing of markers into list of promises and awaits them.
|
|
420
403
|
* It's required because images are expected to load asynchronous in canvas js
|
|
421
404
|
* even when provided from a local disk.
|
|
422
|
-
*
|
|
423
405
|
* @param {object} ctx Canvas context object.
|
|
424
406
|
* @param {List[Object]} markers Marker objects parsed by extractMarkersFromQuery.
|
|
425
407
|
* @param {number} z Map zoom level.
|
|
@@ -438,117 +420,107 @@ const drawMarkers = async (ctx, markers, z) => {
|
|
|
438
420
|
|
|
439
421
|
/**
|
|
440
422
|
* Draws a list of coordinates onto a canvas and styles the resulting path.
|
|
441
|
-
*
|
|
442
423
|
* @param {object} ctx Canvas context object.
|
|
443
424
|
* @param {List[Number]} path List of coordinates.
|
|
444
425
|
* @param {object} query Request query parameters.
|
|
426
|
+
* @param {string} pathQuery Path query parameter.
|
|
445
427
|
* @param {number} z Map zoom level.
|
|
446
428
|
*/
|
|
447
|
-
const drawPath = (ctx, path, query, z) => {
|
|
448
|
-
const
|
|
449
|
-
if (!path || path.length < 2) {
|
|
450
|
-
return null;
|
|
451
|
-
}
|
|
429
|
+
const drawPath = (ctx, path, query, pathQuery, z) => {
|
|
430
|
+
const splitPaths = decodeURIComponent(pathQuery).split('|');
|
|
452
431
|
|
|
453
|
-
|
|
432
|
+
if (!path || path.length < 2) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
454
435
|
|
|
455
|
-
|
|
456
|
-
for (const pair of path) {
|
|
457
|
-
const px = precisePx(pair, z);
|
|
458
|
-
ctx.lineTo(px[0], px[1]);
|
|
459
|
-
}
|
|
436
|
+
ctx.beginPath();
|
|
460
437
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
ctx.closePath();
|
|
467
|
-
}
|
|
438
|
+
// Transform coordinates to pixel on canvas and draw lines between points
|
|
439
|
+
for (const pair of path) {
|
|
440
|
+
const px = precisePx(pair, z);
|
|
441
|
+
ctx.lineTo(px[0], px[1]);
|
|
442
|
+
}
|
|
468
443
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
if (pathHasFill) {
|
|
477
|
-
ctx.fillStyle = splitPaths
|
|
478
|
-
.find((x) => x.startsWith('fill:'))
|
|
479
|
-
.replace('fill:', '');
|
|
480
|
-
}
|
|
481
|
-
ctx.fill();
|
|
482
|
-
}
|
|
444
|
+
// Check if first coordinate matches last coordinate
|
|
445
|
+
if (
|
|
446
|
+
path[0][0] === path[path.length - 1][0] &&
|
|
447
|
+
path[0][1] === path[path.length - 1][1]
|
|
448
|
+
) {
|
|
449
|
+
ctx.closePath();
|
|
450
|
+
}
|
|
483
451
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (
|
|
488
|
-
|
|
489
|
-
// Get line width from query
|
|
490
|
-
if ('width' in query) {
|
|
491
|
-
lineWidth = Number(query.width);
|
|
492
|
-
}
|
|
493
|
-
// Get line width from path in query
|
|
494
|
-
if (pathHasWidth) {
|
|
495
|
-
lineWidth = Number(
|
|
496
|
-
splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
// Get border width from query and fall back to 10% of line width
|
|
500
|
-
const borderWidth =
|
|
501
|
-
query.borderwidth !== undefined
|
|
502
|
-
? parseFloat(query.borderwidth)
|
|
503
|
-
: lineWidth * 0.1;
|
|
504
|
-
|
|
505
|
-
// Set rendering style for the start and end points of the path
|
|
506
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
|
|
507
|
-
ctx.lineCap = query.linecap || 'butt';
|
|
508
|
-
|
|
509
|
-
// Set rendering style for overlapping segments of the path with differing directions
|
|
510
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
|
|
511
|
-
ctx.lineJoin = query.linejoin || 'miter';
|
|
512
|
-
|
|
513
|
-
// In order to simulate a border we draw the path two times with the first
|
|
514
|
-
// beeing the wider border part.
|
|
515
|
-
if (query.border !== undefined && borderWidth > 0) {
|
|
516
|
-
// We need to double the desired border width and add it to the line width
|
|
517
|
-
// in order to get the desired border on each side of the line.
|
|
518
|
-
ctx.lineWidth = lineWidth + borderWidth * 2;
|
|
519
|
-
// Set border style as rgba
|
|
520
|
-
ctx.strokeStyle = query.border;
|
|
521
|
-
ctx.stroke();
|
|
522
|
-
}
|
|
523
|
-
ctx.lineWidth = lineWidth;
|
|
452
|
+
// Optionally fill drawn shape with a rgba color from query
|
|
453
|
+
const pathHasFill = splitPaths.filter((x) => x.startsWith('fill')).length > 0;
|
|
454
|
+
if (query.fill !== undefined || pathHasFill) {
|
|
455
|
+
if ('fill' in query) {
|
|
456
|
+
ctx.fillStyle = query.fill || 'rgba(255,255,255,0.4)';
|
|
524
457
|
}
|
|
458
|
+
if (pathHasFill) {
|
|
459
|
+
ctx.fillStyle = splitPaths
|
|
460
|
+
.find((x) => x.startsWith('fill:'))
|
|
461
|
+
.replace('fill:', '');
|
|
462
|
+
}
|
|
463
|
+
ctx.fill();
|
|
464
|
+
}
|
|
525
465
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
ctx.strokeStyle = splitPaths
|
|
535
|
-
.find((x) => x.startsWith('stroke:'))
|
|
536
|
-
.replace('stroke:', '');
|
|
537
|
-
}
|
|
538
|
-
} else {
|
|
539
|
-
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
|
|
466
|
+
// Get line width from query and fall back to 1 if not provided
|
|
467
|
+
const pathHasWidth =
|
|
468
|
+
splitPaths.filter((x) => x.startsWith('width')).length > 0;
|
|
469
|
+
if (query.width !== undefined || pathHasWidth) {
|
|
470
|
+
let lineWidth = 1;
|
|
471
|
+
// Get line width from query
|
|
472
|
+
if ('width' in query) {
|
|
473
|
+
lineWidth = Number(query.width);
|
|
540
474
|
}
|
|
541
|
-
|
|
542
|
-
|
|
475
|
+
// Get line width from path in query
|
|
476
|
+
if (pathHasWidth) {
|
|
477
|
+
lineWidth = Number(
|
|
478
|
+
splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
// Get border width from query and fall back to 10% of line width
|
|
482
|
+
const borderWidth =
|
|
483
|
+
query.borderwidth !== undefined
|
|
484
|
+
? parseFloat(query.borderwidth)
|
|
485
|
+
: lineWidth * 0.1;
|
|
486
|
+
|
|
487
|
+
// Set rendering style for the start and end points of the path
|
|
488
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
|
|
489
|
+
ctx.lineCap = query.linecap || 'butt';
|
|
490
|
+
|
|
491
|
+
// Set rendering style for overlapping segments of the path with differing directions
|
|
492
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
|
|
493
|
+
ctx.lineJoin = query.linejoin || 'miter';
|
|
494
|
+
|
|
495
|
+
// In order to simulate a border we draw the path two times with the first
|
|
496
|
+
// beeing the wider border part.
|
|
497
|
+
if (query.border !== undefined && borderWidth > 0) {
|
|
498
|
+
// We need to double the desired border width and add it to the line width
|
|
499
|
+
// in order to get the desired border on each side of the line.
|
|
500
|
+
ctx.lineWidth = lineWidth + borderWidth * 2;
|
|
501
|
+
// Set border style as rgba
|
|
502
|
+
ctx.strokeStyle = query.border;
|
|
503
|
+
ctx.stroke();
|
|
504
|
+
}
|
|
505
|
+
ctx.lineWidth = lineWidth;
|
|
506
|
+
}
|
|
543
507
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
508
|
+
const pathHasStroke =
|
|
509
|
+
splitPaths.filter((x) => x.startsWith('stroke')).length > 0;
|
|
510
|
+
if (query.stroke !== undefined || pathHasStroke) {
|
|
511
|
+
if ('stroke' in query) {
|
|
512
|
+
ctx.strokeStyle = query.stroke;
|
|
513
|
+
}
|
|
514
|
+
// Path Stroke gets higher priority
|
|
515
|
+
if (pathHasStroke) {
|
|
516
|
+
ctx.strokeStyle = splitPaths
|
|
517
|
+
.find((x) => x.startsWith('stroke:'))
|
|
518
|
+
.replace('stroke:', '');
|
|
548
519
|
}
|
|
549
520
|
} else {
|
|
550
|
-
|
|
521
|
+
ctx.strokeStyle = 'rgba(0,64,255,0.7)';
|
|
551
522
|
}
|
|
523
|
+
ctx.stroke();
|
|
552
524
|
};
|
|
553
525
|
|
|
554
526
|
const renderOverlay = async (
|
|
@@ -592,9 +564,10 @@ const renderOverlay = async (
|
|
|
592
564
|
}
|
|
593
565
|
|
|
594
566
|
// Draw provided paths if any
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
567
|
+
paths.forEach((path, i) => {
|
|
568
|
+
const pathQuery = Array.isArray(query.path) ? query.path.at(i) : query.path;
|
|
569
|
+
drawPath(ctx, path, query, pathQuery, z);
|
|
570
|
+
});
|
|
598
571
|
|
|
599
572
|
// Await drawing of markers before rendering the canvas
|
|
600
573
|
await drawMarkers(ctx, markers, z);
|
|
@@ -780,8 +753,9 @@ export const serve_rendered = {
|
|
|
780
753
|
image.resize(width * scale, height * scale);
|
|
781
754
|
}
|
|
782
755
|
|
|
756
|
+
var composite_array = [];
|
|
783
757
|
if (opt_overlay) {
|
|
784
|
-
|
|
758
|
+
composite_array.push({ input: opt_overlay });
|
|
785
759
|
}
|
|
786
760
|
if (item.watermark) {
|
|
787
761
|
const canvas = createCanvas(scale * width, scale * height);
|
|
@@ -794,7 +768,11 @@ export const serve_rendered = {
|
|
|
794
768
|
ctx.fillStyle = 'rgba(0,0,0,.4)';
|
|
795
769
|
ctx.fillText(item.watermark, 5, height - 5);
|
|
796
770
|
|
|
797
|
-
|
|
771
|
+
composite_array.push({ input: canvas.toBuffer() });
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (composite_array.length > 0) {
|
|
775
|
+
image.composite(composite_array);
|
|
798
776
|
}
|
|
799
777
|
|
|
800
778
|
const formatQuality = (options.formatQuality || {})[format];
|
|
@@ -892,35 +870,118 @@ export const serve_rendered = {
|
|
|
892
870
|
app.get(
|
|
893
871
|
util.format(staticPattern, centerPattern),
|
|
894
872
|
async (req, res, next) => {
|
|
873
|
+
try {
|
|
874
|
+
const item = repo[req.params.id];
|
|
875
|
+
if (!item) {
|
|
876
|
+
return res.sendStatus(404);
|
|
877
|
+
}
|
|
878
|
+
const raw = req.params.raw;
|
|
879
|
+
const z = +req.params.z;
|
|
880
|
+
let x = +req.params.x;
|
|
881
|
+
let y = +req.params.y;
|
|
882
|
+
const bearing = +(req.params.bearing || '0');
|
|
883
|
+
const pitch = +(req.params.pitch || '0');
|
|
884
|
+
const w = req.params.width | 0;
|
|
885
|
+
const h = req.params.height | 0;
|
|
886
|
+
const scale = getScale(req.params.scale);
|
|
887
|
+
const format = req.params.format;
|
|
888
|
+
|
|
889
|
+
if (z < 0) {
|
|
890
|
+
return res.status(404).send('Invalid zoom');
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
const transformer = raw
|
|
894
|
+
? mercator.inverse.bind(mercator)
|
|
895
|
+
: item.dataProjWGStoInternalWGS;
|
|
896
|
+
|
|
897
|
+
if (transformer) {
|
|
898
|
+
const ll = transformer([x, y]);
|
|
899
|
+
x = ll[0];
|
|
900
|
+
y = ll[1];
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
const paths = extractPathsFromQuery(req.query, transformer);
|
|
904
|
+
const markers = extractMarkersFromQuery(
|
|
905
|
+
req.query,
|
|
906
|
+
options,
|
|
907
|
+
transformer,
|
|
908
|
+
);
|
|
909
|
+
const overlay = await renderOverlay(
|
|
910
|
+
z,
|
|
911
|
+
x,
|
|
912
|
+
y,
|
|
913
|
+
bearing,
|
|
914
|
+
pitch,
|
|
915
|
+
w,
|
|
916
|
+
h,
|
|
917
|
+
scale,
|
|
918
|
+
paths,
|
|
919
|
+
markers,
|
|
920
|
+
req.query,
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
return respondImage(
|
|
924
|
+
item,
|
|
925
|
+
z,
|
|
926
|
+
x,
|
|
927
|
+
y,
|
|
928
|
+
bearing,
|
|
929
|
+
pitch,
|
|
930
|
+
w,
|
|
931
|
+
h,
|
|
932
|
+
scale,
|
|
933
|
+
format,
|
|
934
|
+
res,
|
|
935
|
+
next,
|
|
936
|
+
overlay,
|
|
937
|
+
'static',
|
|
938
|
+
);
|
|
939
|
+
} catch (e) {
|
|
940
|
+
next(e);
|
|
941
|
+
}
|
|
942
|
+
},
|
|
943
|
+
);
|
|
944
|
+
|
|
945
|
+
const serveBounds = async (req, res, next) => {
|
|
946
|
+
try {
|
|
895
947
|
const item = repo[req.params.id];
|
|
896
948
|
if (!item) {
|
|
897
949
|
return res.sendStatus(404);
|
|
898
950
|
}
|
|
899
951
|
const raw = req.params.raw;
|
|
900
|
-
const
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
const scale = getScale(req.params.scale);
|
|
908
|
-
const format = req.params.format;
|
|
909
|
-
|
|
910
|
-
if (z < 0) {
|
|
911
|
-
return res.status(404).send('Invalid zoom');
|
|
912
|
-
}
|
|
952
|
+
const bbox = [
|
|
953
|
+
+req.params.minx,
|
|
954
|
+
+req.params.miny,
|
|
955
|
+
+req.params.maxx,
|
|
956
|
+
+req.params.maxy,
|
|
957
|
+
];
|
|
958
|
+
let center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
|
|
913
959
|
|
|
914
960
|
const transformer = raw
|
|
915
961
|
? mercator.inverse.bind(mercator)
|
|
916
962
|
: item.dataProjWGStoInternalWGS;
|
|
917
963
|
|
|
918
964
|
if (transformer) {
|
|
919
|
-
const
|
|
920
|
-
|
|
921
|
-
|
|
965
|
+
const minCorner = transformer(bbox.slice(0, 2));
|
|
966
|
+
const maxCorner = transformer(bbox.slice(2));
|
|
967
|
+
bbox[0] = minCorner[0];
|
|
968
|
+
bbox[1] = minCorner[1];
|
|
969
|
+
bbox[2] = maxCorner[0];
|
|
970
|
+
bbox[3] = maxCorner[1];
|
|
971
|
+
center = transformer(center);
|
|
922
972
|
}
|
|
923
973
|
|
|
974
|
+
const w = req.params.width | 0;
|
|
975
|
+
const h = req.params.height | 0;
|
|
976
|
+
const scale = getScale(req.params.scale);
|
|
977
|
+
const format = req.params.format;
|
|
978
|
+
|
|
979
|
+
const z = calcZForBBox(bbox, w, h, req.query);
|
|
980
|
+
const x = center[0];
|
|
981
|
+
const y = center[1];
|
|
982
|
+
const bearing = 0;
|
|
983
|
+
const pitch = 0;
|
|
984
|
+
|
|
924
985
|
const paths = extractPathsFromQuery(req.query, transformer);
|
|
925
986
|
const markers = extractMarkersFromQuery(
|
|
926
987
|
req.query,
|
|
@@ -940,7 +1001,6 @@ export const serve_rendered = {
|
|
|
940
1001
|
markers,
|
|
941
1002
|
req.query,
|
|
942
1003
|
);
|
|
943
|
-
|
|
944
1004
|
return respondImage(
|
|
945
1005
|
item,
|
|
946
1006
|
z,
|
|
@@ -957,83 +1017,9 @@ export const serve_rendered = {
|
|
|
957
1017
|
overlay,
|
|
958
1018
|
'static',
|
|
959
1019
|
);
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
const serveBounds = async (req, res, next) => {
|
|
964
|
-
const item = repo[req.params.id];
|
|
965
|
-
if (!item) {
|
|
966
|
-
return res.sendStatus(404);
|
|
1020
|
+
} catch (e) {
|
|
1021
|
+
next(e);
|
|
967
1022
|
}
|
|
968
|
-
const raw = req.params.raw;
|
|
969
|
-
const bbox = [
|
|
970
|
-
+req.params.minx,
|
|
971
|
-
+req.params.miny,
|
|
972
|
-
+req.params.maxx,
|
|
973
|
-
+req.params.maxy,
|
|
974
|
-
];
|
|
975
|
-
let center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
|
|
976
|
-
|
|
977
|
-
const transformer = raw
|
|
978
|
-
? mercator.inverse.bind(mercator)
|
|
979
|
-
: item.dataProjWGStoInternalWGS;
|
|
980
|
-
|
|
981
|
-
if (transformer) {
|
|
982
|
-
const minCorner = transformer(bbox.slice(0, 2));
|
|
983
|
-
const maxCorner = transformer(bbox.slice(2));
|
|
984
|
-
bbox[0] = minCorner[0];
|
|
985
|
-
bbox[1] = minCorner[1];
|
|
986
|
-
bbox[2] = maxCorner[0];
|
|
987
|
-
bbox[3] = maxCorner[1];
|
|
988
|
-
center = transformer(center);
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
const w = req.params.width | 0;
|
|
992
|
-
const h = req.params.height | 0;
|
|
993
|
-
const scale = getScale(req.params.scale);
|
|
994
|
-
const format = req.params.format;
|
|
995
|
-
|
|
996
|
-
const z = calcZForBBox(bbox, w, h, req.query);
|
|
997
|
-
const x = center[0];
|
|
998
|
-
const y = center[1];
|
|
999
|
-
const bearing = 0;
|
|
1000
|
-
const pitch = 0;
|
|
1001
|
-
|
|
1002
|
-
const paths = extractPathsFromQuery(req.query, transformer);
|
|
1003
|
-
const markers = extractMarkersFromQuery(
|
|
1004
|
-
req.query,
|
|
1005
|
-
options,
|
|
1006
|
-
transformer,
|
|
1007
|
-
);
|
|
1008
|
-
const overlay = await renderOverlay(
|
|
1009
|
-
z,
|
|
1010
|
-
x,
|
|
1011
|
-
y,
|
|
1012
|
-
bearing,
|
|
1013
|
-
pitch,
|
|
1014
|
-
w,
|
|
1015
|
-
h,
|
|
1016
|
-
scale,
|
|
1017
|
-
paths,
|
|
1018
|
-
markers,
|
|
1019
|
-
req.query,
|
|
1020
|
-
);
|
|
1021
|
-
return respondImage(
|
|
1022
|
-
item,
|
|
1023
|
-
z,
|
|
1024
|
-
x,
|
|
1025
|
-
y,
|
|
1026
|
-
bearing,
|
|
1027
|
-
pitch,
|
|
1028
|
-
w,
|
|
1029
|
-
h,
|
|
1030
|
-
scale,
|
|
1031
|
-
format,
|
|
1032
|
-
res,
|
|
1033
|
-
next,
|
|
1034
|
-
overlay,
|
|
1035
|
-
'static',
|
|
1036
|
-
);
|
|
1037
1023
|
};
|
|
1038
1024
|
|
|
1039
1025
|
const boundsPattern = util.format(
|
|
@@ -1073,97 +1059,101 @@ export const serve_rendered = {
|
|
|
1073
1059
|
app.get(
|
|
1074
1060
|
util.format(staticPattern, autoPattern),
|
|
1075
1061
|
async (req, res, next) => {
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1062
|
+
try {
|
|
1063
|
+
const item = repo[req.params.id];
|
|
1064
|
+
if (!item) {
|
|
1065
|
+
return res.sendStatus(404);
|
|
1066
|
+
}
|
|
1067
|
+
const raw = req.params.raw;
|
|
1068
|
+
const w = req.params.width | 0;
|
|
1069
|
+
const h = req.params.height | 0;
|
|
1070
|
+
const bearing = 0;
|
|
1071
|
+
const pitch = 0;
|
|
1072
|
+
const scale = getScale(req.params.scale);
|
|
1073
|
+
const format = req.params.format;
|
|
1074
|
+
|
|
1075
|
+
const transformer = raw
|
|
1076
|
+
? mercator.inverse.bind(mercator)
|
|
1077
|
+
: item.dataProjWGStoInternalWGS;
|
|
1078
|
+
|
|
1079
|
+
const paths = extractPathsFromQuery(req.query, transformer);
|
|
1080
|
+
const markers = extractMarkersFromQuery(
|
|
1081
|
+
req.query,
|
|
1082
|
+
options,
|
|
1083
|
+
transformer,
|
|
1084
|
+
);
|
|
1087
1085
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1086
|
+
// Extract coordinates from markers
|
|
1087
|
+
const markerCoordinates = [];
|
|
1088
|
+
for (const marker of markers) {
|
|
1089
|
+
markerCoordinates.push(marker.location);
|
|
1090
|
+
}
|
|
1091
1091
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
req.query,
|
|
1095
|
-
options,
|
|
1096
|
-
transformer,
|
|
1097
|
-
);
|
|
1092
|
+
// Create array with coordinates from markers and path
|
|
1093
|
+
const coords = [].concat(paths.flat()).concat(markerCoordinates);
|
|
1098
1094
|
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
}
|
|
1095
|
+
// Check if we have at least one coordinate to calculate a bounding box
|
|
1096
|
+
if (coords.length < 1) {
|
|
1097
|
+
return res.status(400).send('No coordinates provided');
|
|
1098
|
+
}
|
|
1104
1099
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1100
|
+
const bbox = [Infinity, Infinity, -Infinity, -Infinity];
|
|
1101
|
+
for (const pair of coords) {
|
|
1102
|
+
bbox[0] = Math.min(bbox[0], pair[0]);
|
|
1103
|
+
bbox[1] = Math.min(bbox[1], pair[1]);
|
|
1104
|
+
bbox[2] = Math.max(bbox[2], pair[0]);
|
|
1105
|
+
bbox[3] = Math.max(bbox[3], pair[1]);
|
|
1106
|
+
}
|
|
1107
1107
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1108
|
+
const bbox_ = mercator.convert(bbox, '900913');
|
|
1109
|
+
const center = mercator.inverse([
|
|
1110
|
+
(bbox_[0] + bbox_[2]) / 2,
|
|
1111
|
+
(bbox_[1] + bbox_[3]) / 2,
|
|
1112
|
+
]);
|
|
1113
|
+
|
|
1114
|
+
// Calculate zoom level
|
|
1115
|
+
const maxZoom = parseFloat(req.query.maxzoom);
|
|
1116
|
+
let z = calcZForBBox(bbox, w, h, req.query);
|
|
1117
|
+
if (maxZoom > 0) {
|
|
1118
|
+
z = Math.min(z, maxZoom);
|
|
1119
|
+
}
|
|
1112
1120
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1121
|
+
const x = center[0];
|
|
1122
|
+
const y = center[1];
|
|
1123
|
+
|
|
1124
|
+
const overlay = await renderOverlay(
|
|
1125
|
+
z,
|
|
1126
|
+
x,
|
|
1127
|
+
y,
|
|
1128
|
+
bearing,
|
|
1129
|
+
pitch,
|
|
1130
|
+
w,
|
|
1131
|
+
h,
|
|
1132
|
+
scale,
|
|
1133
|
+
paths,
|
|
1134
|
+
markers,
|
|
1135
|
+
req.query,
|
|
1136
|
+
);
|
|
1120
1137
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1138
|
+
return respondImage(
|
|
1139
|
+
item,
|
|
1140
|
+
z,
|
|
1141
|
+
x,
|
|
1142
|
+
y,
|
|
1143
|
+
bearing,
|
|
1144
|
+
pitch,
|
|
1145
|
+
w,
|
|
1146
|
+
h,
|
|
1147
|
+
scale,
|
|
1148
|
+
format,
|
|
1149
|
+
res,
|
|
1150
|
+
next,
|
|
1151
|
+
overlay,
|
|
1152
|
+
'static',
|
|
1153
|
+
);
|
|
1154
|
+
} catch (e) {
|
|
1155
|
+
next(e);
|
|
1132
1156
|
}
|
|
1133
|
-
|
|
1134
|
-
const x = center[0];
|
|
1135
|
-
const y = center[1];
|
|
1136
|
-
|
|
1137
|
-
const overlay = await renderOverlay(
|
|
1138
|
-
z,
|
|
1139
|
-
x,
|
|
1140
|
-
y,
|
|
1141
|
-
bearing,
|
|
1142
|
-
pitch,
|
|
1143
|
-
w,
|
|
1144
|
-
h,
|
|
1145
|
-
scale,
|
|
1146
|
-
paths,
|
|
1147
|
-
markers,
|
|
1148
|
-
req.query,
|
|
1149
|
-
);
|
|
1150
|
-
|
|
1151
|
-
return respondImage(
|
|
1152
|
-
item,
|
|
1153
|
-
z,
|
|
1154
|
-
x,
|
|
1155
|
-
y,
|
|
1156
|
-
bearing,
|
|
1157
|
-
pitch,
|
|
1158
|
-
w,
|
|
1159
|
-
h,
|
|
1160
|
-
scale,
|
|
1161
|
-
format,
|
|
1162
|
-
res,
|
|
1163
|
-
next,
|
|
1164
|
-
overlay,
|
|
1165
|
-
'static',
|
|
1166
|
-
);
|
|
1167
1157
|
},
|
|
1168
1158
|
);
|
|
1169
1159
|
}
|
|
@@ -1374,6 +1364,9 @@ export const serve_rendered = {
|
|
|
1374
1364
|
type: 'baselayer',
|
|
1375
1365
|
};
|
|
1376
1366
|
const attributionOverride = params.tilejson && params.tilejson.attribution;
|
|
1367
|
+
if (styleJSON.center && styleJSON.zoom) {
|
|
1368
|
+
tileJSON.center = styleJSON.center.concat(Math.round(styleJSON.zoom));
|
|
1369
|
+
}
|
|
1377
1370
|
Object.assign(tileJSON, params.tilejson || {});
|
|
1378
1371
|
tileJSON.tiles = params.domains || options.domains;
|
|
1379
1372
|
fixTileJSONCenter(tileJSON);
|