tileserver-gl-light 4.6.6 → 4.8.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.
@@ -1,13 +1,24 @@
1
1
  'use strict';
2
2
 
3
+ // SECTION START
4
+ //
5
+ // The order of the two imports below is important.
6
+ // For an unknown reason, if the order is reversed, rendering can crash.
7
+ // This happens on ARM:
8
+ // > terminate called after throwing an instance of 'std::runtime_error'
9
+ // > what(): Cannot read GLX extensions.
10
+ import 'canvas';
11
+ import '@maplibre/maplibre-gl-native';
12
+ //
13
+ // SECTION END
14
+
3
15
  import advancedPool from 'advanced-pool';
4
16
  import fs from 'node:fs';
5
17
  import path from 'path';
6
18
  import url from 'url';
7
19
  import util from 'util';
8
20
  import zlib from 'zlib';
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
- import { createCanvas, Image } from 'canvas';
21
+ import sharp from 'sharp';
11
22
  import clone from 'clone';
12
23
  import Color from 'color';
13
24
  import express from 'express';
@@ -20,15 +31,17 @@ import proj4 from 'proj4';
20
31
  import axios from 'axios';
21
32
  import {
22
33
  getFontsPbf,
34
+ listFonts,
23
35
  getTileUrls,
24
36
  isValidHttpUrl,
25
37
  fixTileJSONCenter,
26
38
  } from './utils.js';
27
39
  import {
28
- PMtilesOpen,
29
- GetPMtilesInfo,
30
- GetPMtilesTile,
40
+ openPMtiles,
41
+ getPMtilesInfo,
42
+ getPMtilesTile,
31
43
  } from './pmtiles_adapter.js';
44
+ import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
32
45
 
33
46
  const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
34
47
  const PATH_PATTERN =
@@ -96,7 +109,7 @@ function createEmptyResponse(format, color, callback) {
96
109
  raw: {
97
110
  width: 1,
98
111
  height: 1,
99
- channels: channels,
112
+ channels,
100
113
  },
101
114
  })
102
115
  .toFormat(format)
@@ -330,263 +343,6 @@ const extractMarkersFromQuery = (query, options, transformer) => {
330
343
  return markers;
331
344
  };
332
345
 
333
- /**
334
- * Transforms coordinates to pixels.
335
- * @param {List[Number]} ll Longitude/Latitude coordinate pair.
336
- * @param {number} zoom Map zoom level.
337
- */
338
- const precisePx = (ll, zoom) => {
339
- const px = mercator.px(ll, 20);
340
- const scale = Math.pow(2, zoom - 20);
341
- return [px[0] * scale, px[1] * scale];
342
- };
343
-
344
- /**
345
- * Draws a marker in cavans context.
346
- * @param {object} ctx Canvas context object.
347
- * @param {object} marker Marker object parsed by extractMarkersFromQuery.
348
- * @param {number} z Map zoom level.
349
- */
350
- const drawMarker = (ctx, marker, z) => {
351
- return new Promise((resolve) => {
352
- const img = new Image();
353
- const pixelCoords = precisePx(marker.location, z);
354
-
355
- const getMarkerCoordinates = (imageWidth, imageHeight, scale) => {
356
- // Images are placed with their top-left corner at the provided location
357
- // within the canvas but we expect icons to be centered and above it.
358
-
359
- // Substract half of the images width from the x-coordinate to center
360
- // the image in relation to the provided location
361
- let xCoordinate = pixelCoords[0] - imageWidth / 2;
362
- // Substract the images height from the y-coordinate to place it above
363
- // the provided location
364
- let yCoordinate = pixelCoords[1] - imageHeight;
365
-
366
- // Since image placement is dependent on the size offsets have to be
367
- // scaled as well. Additionally offsets are provided as either positive or
368
- // negative values so we always add them
369
- if (marker.offsetX) {
370
- xCoordinate = xCoordinate + marker.offsetX * scale;
371
- }
372
- if (marker.offsetY) {
373
- yCoordinate = yCoordinate + marker.offsetY * scale;
374
- }
375
-
376
- return {
377
- x: xCoordinate,
378
- y: yCoordinate,
379
- };
380
- };
381
-
382
- const drawOnCanvas = () => {
383
- // Check if the images should be resized before beeing drawn
384
- const defaultScale = 1;
385
- const scale = marker.scale ? marker.scale : defaultScale;
386
-
387
- // Calculate scaled image sizes
388
- const imageWidth = img.width * scale;
389
- const imageHeight = img.height * scale;
390
-
391
- // Pass the desired sizes to get correlating coordinates
392
- const coords = getMarkerCoordinates(imageWidth, imageHeight, scale);
393
-
394
- // Draw the image on canvas
395
- if (scale != defaultScale) {
396
- ctx.drawImage(img, coords.x, coords.y, imageWidth, imageHeight);
397
- } else {
398
- ctx.drawImage(img, coords.x, coords.y);
399
- }
400
- // Resolve the promise when image has been drawn
401
- resolve();
402
- };
403
-
404
- img.onload = drawOnCanvas;
405
- img.onerror = (err) => {
406
- throw err;
407
- };
408
- img.src = marker.icon;
409
- });
410
- };
411
-
412
- /**
413
- * Draws a list of markers onto a canvas.
414
- * Wraps drawing of markers into list of promises and awaits them.
415
- * It's required because images are expected to load asynchronous in canvas js
416
- * even when provided from a local disk.
417
- * @param {object} ctx Canvas context object.
418
- * @param {List[Object]} markers Marker objects parsed by extractMarkersFromQuery.
419
- * @param {number} z Map zoom level.
420
- */
421
- const drawMarkers = async (ctx, markers, z) => {
422
- const markerPromises = [];
423
-
424
- for (const marker of markers) {
425
- // Begin drawing marker
426
- markerPromises.push(drawMarker(ctx, marker, z));
427
- }
428
-
429
- // Await marker drawings before continuing
430
- await Promise.all(markerPromises);
431
- };
432
-
433
- /**
434
- * Draws a list of coordinates onto a canvas and styles the resulting path.
435
- * @param {object} ctx Canvas context object.
436
- * @param {List[Number]} path List of coordinates.
437
- * @param {object} query Request query parameters.
438
- * @param {string} pathQuery Path query parameter.
439
- * @param {number} z Map zoom level.
440
- */
441
- const drawPath = (ctx, path, query, pathQuery, z) => {
442
- const splitPaths = pathQuery.split('|');
443
-
444
- if (!path || path.length < 2) {
445
- return null;
446
- }
447
-
448
- ctx.beginPath();
449
-
450
- // Transform coordinates to pixel on canvas and draw lines between points
451
- for (const pair of path) {
452
- const px = precisePx(pair, z);
453
- ctx.lineTo(px[0], px[1]);
454
- }
455
-
456
- // Check if first coordinate matches last coordinate
457
- if (
458
- path[0][0] === path[path.length - 1][0] &&
459
- path[0][1] === path[path.length - 1][1]
460
- ) {
461
- ctx.closePath();
462
- }
463
-
464
- // Optionally fill drawn shape with a rgba color from query
465
- const pathHasFill = splitPaths.filter((x) => x.startsWith('fill')).length > 0;
466
- if (query.fill !== undefined || pathHasFill) {
467
- if ('fill' in query) {
468
- ctx.fillStyle = query.fill || 'rgba(255,255,255,0.4)';
469
- }
470
- if (pathHasFill) {
471
- ctx.fillStyle = splitPaths
472
- .find((x) => x.startsWith('fill:'))
473
- .replace('fill:', '');
474
- }
475
- ctx.fill();
476
- }
477
-
478
- // Get line width from query and fall back to 1 if not provided
479
- const pathHasWidth =
480
- splitPaths.filter((x) => x.startsWith('width')).length > 0;
481
- if (query.width !== undefined || pathHasWidth) {
482
- let lineWidth = 1;
483
- // Get line width from query
484
- if ('width' in query) {
485
- lineWidth = Number(query.width);
486
- }
487
- // Get line width from path in query
488
- if (pathHasWidth) {
489
- lineWidth = Number(
490
- splitPaths.find((x) => x.startsWith('width:')).replace('width:', ''),
491
- );
492
- }
493
- // Get border width from query and fall back to 10% of line width
494
- const borderWidth =
495
- query.borderwidth !== undefined
496
- ? parseFloat(query.borderwidth)
497
- : lineWidth * 0.1;
498
-
499
- // Set rendering style for the start and end points of the path
500
- // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineCap
501
- ctx.lineCap = query.linecap || 'butt';
502
-
503
- // Set rendering style for overlapping segments of the path with differing directions
504
- // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin
505
- ctx.lineJoin = query.linejoin || 'miter';
506
-
507
- // In order to simulate a border we draw the path two times with the first
508
- // beeing the wider border part.
509
- if (query.border !== undefined && borderWidth > 0) {
510
- // We need to double the desired border width and add it to the line width
511
- // in order to get the desired border on each side of the line.
512
- ctx.lineWidth = lineWidth + borderWidth * 2;
513
- // Set border style as rgba
514
- ctx.strokeStyle = query.border;
515
- ctx.stroke();
516
- }
517
- ctx.lineWidth = lineWidth;
518
- }
519
-
520
- const pathHasStroke =
521
- splitPaths.filter((x) => x.startsWith('stroke')).length > 0;
522
- if (query.stroke !== undefined || pathHasStroke) {
523
- if ('stroke' in query) {
524
- ctx.strokeStyle = query.stroke;
525
- }
526
- // Path Stroke gets higher priority
527
- if (pathHasStroke) {
528
- ctx.strokeStyle = splitPaths
529
- .find((x) => x.startsWith('stroke:'))
530
- .replace('stroke:', '');
531
- }
532
- } else {
533
- ctx.strokeStyle = 'rgba(0,64,255,0.7)';
534
- }
535
- ctx.stroke();
536
- };
537
-
538
- const renderOverlay = async (
539
- z,
540
- x,
541
- y,
542
- bearing,
543
- pitch,
544
- w,
545
- h,
546
- scale,
547
- paths,
548
- markers,
549
- query,
550
- ) => {
551
- if ((!paths || paths.length === 0) && (!markers || markers.length === 0)) {
552
- return null;
553
- }
554
-
555
- const center = precisePx([x, y], z);
556
-
557
- const mapHeight = 512 * (1 << z);
558
- const maxEdge = center[1] + h / 2;
559
- const minEdge = center[1] - h / 2;
560
- if (maxEdge > mapHeight) {
561
- center[1] -= maxEdge - mapHeight;
562
- } else if (minEdge < 0) {
563
- center[1] -= minEdge;
564
- }
565
-
566
- const canvas = createCanvas(scale * w, scale * h);
567
- const ctx = canvas.getContext('2d');
568
- ctx.scale(scale, scale);
569
- if (bearing) {
570
- ctx.translate(w / 2, h / 2);
571
- ctx.rotate((-bearing / 180) * Math.PI);
572
- ctx.translate(-center[0], -center[1]);
573
- } else {
574
- // optimized path
575
- ctx.translate(-center[0] + w / 2, -center[1] + h / 2);
576
- }
577
-
578
- // Draw provided paths if any
579
- paths.forEach((path, i) => {
580
- const pathQuery = Array.isArray(query.path) ? query.path.at(i) : query.path;
581
- drawPath(ctx, path, query, pathQuery, z);
582
- });
583
-
584
- // Await drawing of markers before rendering the canvas
585
- await drawMarkers(ctx, markers, z);
586
-
587
- return canvas.toBuffer();
588
- };
589
-
590
346
  const calcZForBBox = (bbox, w, h, query) => {
591
347
  let z = 25;
592
348
 
@@ -608,237 +364,172 @@ const calcZForBBox = (bbox, w, h, query) => {
608
364
  return z;
609
365
  };
610
366
 
611
- const existingFonts = {};
612
- let maxScaleFactor = 2;
613
-
614
- export const serve_rendered = {
615
- init: (options, repo) => {
616
- const fontListingPromise = new Promise((resolve, reject) => {
617
- fs.readdir(options.paths.fonts, (err, files) => {
618
- if (err) {
619
- reject(err);
620
- return;
621
- }
622
- for (const file of files) {
623
- fs.stat(path.join(options.paths.fonts, file), (err, stats) => {
624
- if (err) {
625
- reject(err);
626
- return;
627
- }
628
- if (stats.isDirectory()) {
629
- existingFonts[path.basename(file)] = true;
630
- }
631
- });
632
- }
633
- resolve();
634
- });
635
- });
367
+ const respondImage = (
368
+ options,
369
+ item,
370
+ z,
371
+ lon,
372
+ lat,
373
+ bearing,
374
+ pitch,
375
+ width,
376
+ height,
377
+ scale,
378
+ format,
379
+ res,
380
+ overlay = null,
381
+ mode = 'tile',
382
+ ) => {
383
+ if (
384
+ Math.abs(lon) > 180 ||
385
+ Math.abs(lat) > 85.06 ||
386
+ lon !== lon ||
387
+ lat !== lat
388
+ ) {
389
+ return res.status(400).send('Invalid center');
390
+ }
636
391
 
637
- maxScaleFactor = Math.min(Math.floor(options.maxScaleFactor || 3), 9);
638
- let scalePattern = '';
639
- for (let i = 2; i <= maxScaleFactor; i++) {
640
- scalePattern += i.toFixed();
641
- }
642
- scalePattern = `@[${scalePattern}]x`;
392
+ if (
393
+ Math.min(width, height) <= 0 ||
394
+ Math.max(width, height) * scale > (options.maxSize || 2048) ||
395
+ width !== width ||
396
+ height !== height
397
+ ) {
398
+ return res.status(400).send('Invalid size');
399
+ }
643
400
 
644
- const app = express().disable('x-powered-by');
401
+ if (format === 'png' || format === 'webp') {
402
+ } else if (format === 'jpg' || format === 'jpeg') {
403
+ format = 'jpeg';
404
+ } else {
405
+ return res.status(400).send('Invalid format');
406
+ }
645
407
 
646
- const respondImage = (
647
- item,
648
- z,
649
- lon,
650
- lat,
408
+ const tileMargin = Math.max(options.tileMargin || 0, 0);
409
+ let pool;
410
+ if (mode === 'tile' && tileMargin === 0) {
411
+ pool = item.map.renderers[scale];
412
+ } else {
413
+ pool = item.map.renderersStatic[scale];
414
+ }
415
+ pool.acquire((err, renderer) => {
416
+ const mlglZ = Math.max(0, z - 1);
417
+ const params = {
418
+ zoom: mlglZ,
419
+ center: [lon, lat],
651
420
  bearing,
652
421
  pitch,
653
422
  width,
654
423
  height,
655
- scale,
656
- format,
657
- res,
658
- next,
659
- opt_overlay,
660
- opt_mode = 'tile',
661
- ) => {
662
- if (
663
- Math.abs(lon) > 180 ||
664
- Math.abs(lat) > 85.06 ||
665
- lon !== lon ||
666
- lat !== lat
667
- ) {
668
- return res.status(400).send('Invalid center');
669
- }
424
+ };
670
425
 
671
- if (
672
- Math.min(width, height) <= 0 ||
673
- Math.max(width, height) * scale > (options.maxSize || 2048) ||
674
- width !== width ||
675
- height !== height
676
- ) {
677
- return res.status(400).send('Invalid size');
678
- }
426
+ if (z === 0) {
427
+ params.width *= 2;
428
+ params.height *= 2;
429
+ }
679
430
 
680
- if (format === 'png' || format === 'webp') {
681
- } else if (format === 'jpg' || format === 'jpeg') {
682
- format = 'jpeg';
683
- } else {
684
- return res.status(400).send('Invalid format');
685
- }
431
+ if (z > 2 && tileMargin > 0) {
432
+ params.width += tileMargin * 2;
433
+ params.height += tileMargin * 2;
434
+ }
686
435
 
687
- const tileMargin = Math.max(options.tileMargin || 0, 0);
688
- let pool;
689
- if (opt_mode === 'tile' && tileMargin === 0) {
690
- pool = item.map.renderers[scale];
691
- } else {
692
- pool = item.map.renderers_static[scale];
436
+ renderer.render(params, (err, data) => {
437
+ pool.release(renderer);
438
+ if (err) {
439
+ console.error(err);
440
+ return res.status(500).header('Content-Type', 'text/plain').send(err);
693
441
  }
694
- pool.acquire((err, renderer) => {
695
- const mlglZ = Math.max(0, z - 1);
696
- const params = {
697
- zoom: mlglZ,
698
- center: [lon, lat],
699
- bearing: bearing,
700
- pitch: pitch,
701
- width: width,
702
- height: height,
703
- };
704
-
705
- if (z === 0) {
706
- params.width *= 2;
707
- params.height *= 2;
708
- }
709
442
 
710
- if (z > 2 && tileMargin > 0) {
711
- params.width += tileMargin * 2;
712
- params.height += tileMargin * 2;
713
- }
714
-
715
- renderer.render(params, (err, data) => {
716
- pool.release(renderer);
717
- if (err) {
718
- console.error(err);
719
- return res
720
- .status(500)
721
- .header('Content-Type', 'text/plain')
722
- .send(err);
723
- }
443
+ const image = sharp(data, {
444
+ raw: {
445
+ premultiplied: true,
446
+ width: params.width * scale,
447
+ height: params.height * scale,
448
+ channels: 4,
449
+ },
450
+ });
724
451
 
725
- // Fix semi-transparent outlines on raw, premultiplied input
726
- // https://github.com/maptiler/tileserver-gl/issues/350#issuecomment-477857040
727
- for (let i = 0; i < data.length; i += 4) {
728
- const alpha = data[i + 3];
729
- const norm = alpha / 255;
730
- if (alpha === 0) {
731
- data[i] = 0;
732
- data[i + 1] = 0;
733
- data[i + 2] = 0;
734
- } else {
735
- data[i] = data[i] / norm;
736
- data[i + 1] = data[i + 1] / norm;
737
- data[i + 2] = data[i + 2] / norm;
738
- }
739
- }
452
+ if (z > 2 && tileMargin > 0) {
453
+ const [_, y] = mercator.px(params.center, z);
454
+ let yoffset = Math.max(
455
+ Math.min(0, y - 128 - tileMargin),
456
+ y + 128 + tileMargin - Math.pow(2, z + 8),
457
+ );
458
+ image.extract({
459
+ left: tileMargin * scale,
460
+ top: (tileMargin + yoffset) * scale,
461
+ width: width * scale,
462
+ height: height * scale,
463
+ });
464
+ }
740
465
 
741
- const image = sharp(data, {
742
- raw: {
743
- width: params.width * scale,
744
- height: params.height * scale,
745
- channels: 4,
746
- },
747
- });
748
-
749
- if (z > 2 && tileMargin > 0) {
750
- const [_, y] = mercator.px(params.center, z);
751
- let yoffset = Math.max(
752
- Math.min(0, y - 128 - tileMargin),
753
- y + 128 + tileMargin - Math.pow(2, z + 8),
754
- );
755
- image.extract({
756
- left: tileMargin * scale,
757
- top: (tileMargin + yoffset) * scale,
758
- width: width * scale,
759
- height: height * scale,
760
- });
761
- }
466
+ if (z === 0) {
467
+ // HACK: when serving zoom 0, resize the 0 tile from 512 to 256
468
+ image.resize(width * scale, height * scale);
469
+ }
762
470
 
763
- if (z === 0) {
764
- // HACK: when serving zoom 0, resize the 0 tile from 512 to 256
765
- image.resize(width * scale, height * scale);
766
- }
471
+ const composites = [];
472
+ if (overlay) {
473
+ composites.push({ input: overlay });
474
+ }
475
+ if (item.watermark) {
476
+ const canvas = renderWatermark(width, height, scale, item.watermark);
767
477
 
768
- var composite_array = [];
769
- if (opt_overlay) {
770
- composite_array.push({ input: opt_overlay });
771
- }
772
- if (item.watermark) {
773
- const canvas = createCanvas(scale * width, scale * height);
774
- const ctx = canvas.getContext('2d');
775
- ctx.scale(scale, scale);
776
- ctx.font = '10px sans-serif';
777
- ctx.strokeWidth = '1px';
778
- ctx.strokeStyle = 'rgba(255,255,255,.4)';
779
- ctx.strokeText(item.watermark, 5, height - 5);
780
- ctx.fillStyle = 'rgba(0,0,0,.4)';
781
- ctx.fillText(item.watermark, 5, height - 5);
782
-
783
- composite_array.push({ input: canvas.toBuffer() });
784
- }
478
+ composites.push({ input: canvas.toBuffer() });
479
+ }
785
480
 
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
- );
481
+ if (mode === 'static' && item.staticAttributionText) {
482
+ const canvas = renderAttribution(
483
+ width,
484
+ height,
485
+ scale,
486
+ item.staticAttributionText,
487
+ );
811
488
 
812
- composite_array.push({ input: canvas.toBuffer() });
813
- }
489
+ composites.push({ input: canvas.toBuffer() });
490
+ }
814
491
 
815
- if (composite_array.length > 0) {
816
- image.composite(composite_array);
817
- }
492
+ if (composites.length > 0) {
493
+ image.composite(composites);
494
+ }
818
495
 
819
- const formatQuality = (options.formatQuality || {})[format];
496
+ const formatQuality = (options.formatQuality || {})[format];
820
497
 
821
- if (format === 'png') {
822
- image.png({ adaptiveFiltering: false });
823
- } else if (format === 'jpeg') {
824
- image.jpeg({ quality: formatQuality || 80 });
825
- } else if (format === 'webp') {
826
- image.webp({ quality: formatQuality || 90 });
827
- }
828
- image.toBuffer((err, buffer, info) => {
829
- if (!buffer) {
830
- return res.status(404).send('Not found');
831
- }
498
+ if (format === 'png') {
499
+ image.png({ adaptiveFiltering: false });
500
+ } else if (format === 'jpeg') {
501
+ image.jpeg({ quality: formatQuality || 80 });
502
+ } else if (format === 'webp') {
503
+ image.webp({ quality: formatQuality || 90 });
504
+ }
505
+ image.toBuffer((err, buffer, info) => {
506
+ if (!buffer) {
507
+ return res.status(404).send('Not found');
508
+ }
832
509
 
833
- res.set({
834
- 'Last-Modified': item.lastModified,
835
- 'Content-Type': `image/${format}`,
836
- });
837
- return res.status(200).send(buffer);
838
- });
510
+ res.set({
511
+ 'Last-Modified': item.lastModified,
512
+ 'Content-Type': `image/${format}`,
839
513
  });
514
+ return res.status(200).send(buffer);
840
515
  });
841
- };
516
+ });
517
+ });
518
+ };
519
+
520
+ const existingFonts = {};
521
+ let maxScaleFactor = 2;
522
+
523
+ export const serve_rendered = {
524
+ init: async (options, repo) => {
525
+ maxScaleFactor = Math.min(Math.floor(options.maxScaleFactor || 3), 9);
526
+ let scalePattern = '';
527
+ for (let i = 2; i <= maxScaleFactor; i++) {
528
+ scalePattern += i.toFixed();
529
+ }
530
+ scalePattern = `@[${scalePattern}]x`;
531
+
532
+ const app = express().disable('x-powered-by');
842
533
 
843
534
  app.get(
844
535
  `/:id/:z(\\d+)/:x(\\d+)/:y(\\d+):scale(${scalePattern})?.:format([\\w]+)`,
@@ -879,19 +570,10 @@ export const serve_rendered = {
879
570
  ],
880
571
  z,
881
572
  );
573
+
574
+ // prettier-ignore
882
575
  return respondImage(
883
- item,
884
- z,
885
- tileCenter[0],
886
- tileCenter[1],
887
- 0,
888
- 0,
889
- tileSize,
890
- tileSize,
891
- scale,
892
- format,
893
- res,
894
- next,
576
+ options, item, z, tileCenter[0], tileCenter[1], 0, 0, tileSize, tileSize, scale, format, res,
895
577
  );
896
578
  },
897
579
  );
@@ -947,35 +629,15 @@ export const serve_rendered = {
947
629
  options,
948
630
  transformer,
949
631
  );
632
+
633
+ // prettier-ignore
950
634
  const overlay = await renderOverlay(
951
- z,
952
- x,
953
- y,
954
- bearing,
955
- pitch,
956
- w,
957
- h,
958
- scale,
959
- paths,
960
- markers,
961
- req.query,
635
+ z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query,
962
636
  );
963
637
 
638
+ // prettier-ignore
964
639
  return respondImage(
965
- item,
966
- z,
967
- x,
968
- y,
969
- bearing,
970
- pitch,
971
- w,
972
- h,
973
- scale,
974
- format,
975
- res,
976
- next,
977
- overlay,
978
- 'static',
640
+ options, item, z, x, y, bearing, pitch, w, h, scale, format, res, overlay, 'static',
979
641
  );
980
642
  } catch (e) {
981
643
  next(e);
@@ -1029,34 +691,15 @@ export const serve_rendered = {
1029
691
  options,
1030
692
  transformer,
1031
693
  );
694
+
695
+ // prettier-ignore
1032
696
  const overlay = await renderOverlay(
1033
- z,
1034
- x,
1035
- y,
1036
- bearing,
1037
- pitch,
1038
- w,
1039
- h,
1040
- scale,
1041
- paths,
1042
- markers,
1043
- req.query,
697
+ z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query,
1044
698
  );
699
+
700
+ // prettier-ignore
1045
701
  return respondImage(
1046
- item,
1047
- z,
1048
- x,
1049
- y,
1050
- bearing,
1051
- pitch,
1052
- w,
1053
- h,
1054
- scale,
1055
- format,
1056
- res,
1057
- next,
1058
- overlay,
1059
- 'static',
702
+ options, item, z, x, y, bearing, pitch, w, h, scale, format, res, overlay, 'static',
1060
703
  );
1061
704
  } catch (e) {
1062
705
  next(e);
@@ -1162,35 +805,14 @@ export const serve_rendered = {
1162
805
  const x = center[0];
1163
806
  const y = center[1];
1164
807
 
808
+ // prettier-ignore
1165
809
  const overlay = await renderOverlay(
1166
- z,
1167
- x,
1168
- y,
1169
- bearing,
1170
- pitch,
1171
- w,
1172
- h,
1173
- scale,
1174
- paths,
1175
- markers,
1176
- req.query,
810
+ z, x, y, bearing, pitch, w, h, scale, paths, markers, req.query,
1177
811
  );
1178
812
 
813
+ // prettier-ignore
1179
814
  return respondImage(
1180
- item,
1181
- z,
1182
- x,
1183
- y,
1184
- bearing,
1185
- pitch,
1186
- w,
1187
- h,
1188
- scale,
1189
- format,
1190
- res,
1191
- next,
1192
- overlay,
1193
- 'static',
815
+ options, item, z, x, y, bearing, pitch, w, h, scale, format, res, overlay, 'static',
1194
816
  );
1195
817
  } catch (e) {
1196
818
  next(e);
@@ -1215,22 +837,24 @@ export const serve_rendered = {
1215
837
  return res.send(info);
1216
838
  });
1217
839
 
1218
- return Promise.all([fontListingPromise]).then(() => app);
840
+ const fonts = await listFonts(options.paths.fonts);
841
+ Object.assign(existingFonts, fonts);
842
+ return app;
1219
843
  },
1220
844
  add: async (options, repo, params, id, publicUrl, dataResolver) => {
1221
845
  const map = {
1222
846
  renderers: [],
1223
- renderers_static: [],
847
+ renderersStatic: [],
1224
848
  sources: {},
1225
- source_types: {},
849
+ sourceTypes: {},
1226
850
  };
1227
851
 
1228
852
  let styleJSON;
1229
853
  const createPool = (ratio, mode, min, max) => {
1230
854
  const createRenderer = (ratio, createCallback) => {
1231
855
  const renderer = new mlgl.Map({
1232
- mode: mode,
1233
- ratio: ratio,
856
+ mode,
857
+ ratio,
1234
858
  request: async (req, callback) => {
1235
859
  const protocol = req.url.split(':')[0];
1236
860
  // console.log('Handling request:', req);
@@ -1244,25 +868,24 @@ export const serve_rendered = {
1244
868
  const parts = req.url.split('/');
1245
869
  const fontstack = unescape(parts[2]);
1246
870
  const range = parts[3].split('.')[0];
1247
- getFontsPbf(
1248
- null,
1249
- options.paths[protocol],
1250
- fontstack,
1251
- range,
1252
- existingFonts,
1253
- ).then(
1254
- (concated) => {
1255
- callback(null, { data: concated });
1256
- },
1257
- (err) => {
1258
- callback(err, { data: null });
1259
- },
1260
- );
871
+
872
+ try {
873
+ const concatenated = await getFontsPbf(
874
+ null,
875
+ options.paths[protocol],
876
+ fontstack,
877
+ range,
878
+ existingFonts,
879
+ );
880
+ callback(null, { data: concatenated });
881
+ } catch (err) {
882
+ callback(err, { data: null });
883
+ }
1261
884
  } else if (protocol === 'mbtiles' || protocol === 'pmtiles') {
1262
885
  const parts = req.url.split('/');
1263
886
  const sourceId = parts[2];
1264
887
  const source = map.sources[sourceId];
1265
- const source_type = map.source_types[sourceId];
888
+ const sourceType = map.sourceTypes[sourceId];
1266
889
  const sourceInfo = styleJSON.sources[sourceId];
1267
890
 
1268
891
  const z = parts[3] | 0;
@@ -1270,8 +893,8 @@ export const serve_rendered = {
1270
893
  const y = parts[5].split('.')[0] | 0;
1271
894
  const format = parts[5].split('.')[1];
1272
895
 
1273
- if (source_type === 'pmtiles') {
1274
- let tileinfo = await GetPMtilesTile(source, z, x, y);
896
+ if (sourceType === 'pmtiles') {
897
+ let tileinfo = await getPMtilesTile(source, z, x, y);
1275
898
  let data = tileinfo.data;
1276
899
  let headers = tileinfo.header;
1277
900
  if (data == undefined) {
@@ -1305,7 +928,7 @@ export const serve_rendered = {
1305
928
 
1306
929
  callback(null, response);
1307
930
  }
1308
- } else if (source_type === 'mbtiles') {
931
+ } else if (sourceType === 'mbtiles') {
1309
932
  source.getTile(z, x, y, (err, data, headers) => {
1310
933
  if (err) {
1311
934
  if (options.verbose)
@@ -1390,8 +1013,8 @@ export const serve_rendered = {
1390
1013
  createCallback(null, renderer);
1391
1014
  };
1392
1015
  return new advancedPool.Pool({
1393
- min: min,
1394
- max: max,
1016
+ min,
1017
+ max,
1395
1018
  create: createRenderer.bind(null, ratio),
1396
1019
  destroy: (renderer) => {
1397
1020
  renderer.release();
@@ -1466,7 +1089,7 @@ export const serve_rendered = {
1466
1089
 
1467
1090
  const queue = [];
1468
1091
  for (const name of Object.keys(styleJSON.sources)) {
1469
- let source_type;
1092
+ let sourceType;
1470
1093
  let source = styleJSON.sources[name];
1471
1094
  let url = source.url;
1472
1095
  if (
@@ -1487,10 +1110,10 @@ export const serve_rendered = {
1487
1110
  }
1488
1111
 
1489
1112
  let inputFile;
1490
- const DataInfo = dataResolver(dataId);
1491
- if (DataInfo.inputfile) {
1492
- inputFile = DataInfo.inputfile;
1493
- source_type = DataInfo.filetype;
1113
+ const dataInfo = dataResolver(dataId);
1114
+ if (dataInfo.inputFile) {
1115
+ inputFile = dataInfo.inputFile;
1116
+ sourceType = dataInfo.fileType;
1494
1117
  } else {
1495
1118
  console.error(`ERROR: data "${inputFile}" not found!`);
1496
1119
  process.exit(1);
@@ -1503,10 +1126,10 @@ export const serve_rendered = {
1503
1126
  }
1504
1127
  }
1505
1128
 
1506
- if (source_type === 'pmtiles') {
1507
- map.sources[name] = PMtilesOpen(inputFile);
1508
- map.source_types[name] = 'pmtiles';
1509
- const metadata = await GetPMtilesInfo(map.sources[name]);
1129
+ if (sourceType === 'pmtiles') {
1130
+ map.sources[name] = openPMtiles(inputFile);
1131
+ map.sourceTypes[name] = 'pmtiles';
1132
+ const metadata = await getPMtilesInfo(map.sources[name]);
1510
1133
 
1511
1134
  if (!repoobj.dataProjWGStoInternalWGS && metadata.proj4) {
1512
1135
  // how to do this for multiple sources with different proj4 defs?
@@ -1551,7 +1174,7 @@ export const serve_rendered = {
1551
1174
  console.error(err);
1552
1175
  return;
1553
1176
  }
1554
- map.source_types[name] = 'mbtiles';
1177
+ map.sourceTypes[name] = 'mbtiles';
1555
1178
 
1556
1179
  if (!repoobj.dataProjWGStoInternalWGS && info.proj4) {
1557
1180
  // how to do this for multiple sources with different proj4 defs?
@@ -1599,26 +1222,24 @@ export const serve_rendered = {
1599
1222
  }
1600
1223
  }
1601
1224
 
1602
- const renderersReadyPromise = Promise.all(queue).then(() => {
1603
- // standard and @2x tiles are much more usual -> default to larger pools
1604
- const minPoolSizes = options.minRendererPoolSizes || [8, 4, 2];
1605
- const maxPoolSizes = options.maxRendererPoolSizes || [16, 8, 4];
1606
- for (let s = 1; s <= maxScaleFactor; s++) {
1607
- const i = Math.min(minPoolSizes.length - 1, s - 1);
1608
- const j = Math.min(maxPoolSizes.length - 1, s - 1);
1609
- const minPoolSize = minPoolSizes[i];
1610
- const maxPoolSize = Math.max(minPoolSize, maxPoolSizes[j]);
1611
- map.renderers[s] = createPool(s, 'tile', minPoolSize, maxPoolSize);
1612
- map.renderers_static[s] = createPool(
1613
- s,
1614
- 'static',
1615
- minPoolSize,
1616
- maxPoolSize,
1617
- );
1618
- }
1619
- });
1620
-
1621
- return Promise.all([renderersReadyPromise]);
1225
+ await Promise.all(queue);
1226
+
1227
+ // standard and @2x tiles are much more usual -> default to larger pools
1228
+ const minPoolSizes = options.minRendererPoolSizes || [8, 4, 2];
1229
+ const maxPoolSizes = options.maxRendererPoolSizes || [16, 8, 4];
1230
+ for (let s = 1; s <= maxScaleFactor; s++) {
1231
+ const i = Math.min(minPoolSizes.length - 1, s - 1);
1232
+ const j = Math.min(maxPoolSizes.length - 1, s - 1);
1233
+ const minPoolSize = minPoolSizes[i];
1234
+ const maxPoolSize = Math.max(minPoolSize, maxPoolSizes[j]);
1235
+ map.renderers[s] = createPool(s, 'tile', minPoolSize, maxPoolSize);
1236
+ map.renderersStatic[s] = createPool(
1237
+ s,
1238
+ 'static',
1239
+ minPoolSize,
1240
+ maxPoolSize,
1241
+ );
1242
+ }
1622
1243
  },
1623
1244
  remove: (repo, id) => {
1624
1245
  const item = repo[id];
@@ -1626,7 +1247,7 @@ export const serve_rendered = {
1626
1247
  item.map.renderers.forEach((pool) => {
1627
1248
  pool.close();
1628
1249
  });
1629
- item.map.renderers_static.forEach((pool) => {
1250
+ item.map.renderersStatic.forEach((pool) => {
1630
1251
  pool.close();
1631
1252
  });
1632
1253
  }