tileserver-gl-light 5.1.1 → 5.1.3

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/CHANGELOG.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # tileserver-gl changelog
2
2
 
3
+ ## 5.1.3
4
+ * Fix SIGHUP (broken since 5.1.x) (https://github.com/maptiler/tileserver-gl/pull/1452) by @okimiko
5
+
6
+ ## 5.1.2
7
+ * Fix broken light (invalid use of heavy dependencies) (https://github.com/maptiler/tileserver-gl/pull/1449) by @okimiko
8
+
3
9
  ## 5.1.1
4
- * Fix wrong node version in Docker image (https://github.com/maptiler/tileserver-gl/pull/1438) by @acalcutt
10
+ * Fix wrong node version in Docker image (https://github.com/maptiler/tileserver-gl/pull/1442) by @acalcutt
5
11
 
6
12
  ## 5.1.0
7
13
  * Update recommended node to v22 + Update docker images to use node 22 (https://github.com/maptiler/tileserver-gl/pull/1438) by @acalcutt
@@ -1,2 +1,2 @@
1
- * Fix wrong node version in Docker image (https://github.com/maptiler/tileserver-gl/pull/1438) by @acalcutt
1
+ * Fix SIGHUP (broken since 5.1.x) (https://github.com/maptiler/tileserver-gl/pull/1452) by @okimiko
2
2
 
@@ -108,6 +108,8 @@ Source data
108
108
 
109
109
  * the result will be a json object like ``{"z":7,"x":68,"y":45,"red":134,"green":66,"blue":0,"latitude":11.84069,"longitude":46.04798,"elevation":1602}``
110
110
 
111
+ * The elevation api is not available in the ``tileserver-gl-light`` version.
112
+
111
113
  Static files
112
114
  ===========
113
115
  * Static files are served at ``/files/{filename}``
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tileserver-gl-light",
3
- "version": "5.1.1",
3
+ "version": "5.1.3",
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",
@@ -9,7 +9,9 @@
9
9
  <link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl-inspect.css{{&key_query}}" />
10
10
  <script src="{{public_url}}maplibre-gl.js{{&key_query}}"></script>
11
11
  <script src="{{public_url}}maplibre-gl-inspect.js{{&key_query}}"></script>
12
+ {{^is_light}}
12
13
  <script src="{{public_url}}elevation-control.js{{&key_query}}"></script>
14
+ {{/is_light}}
13
15
  <style>
14
16
  body {background:#fff;color:#333;font-family:Arial, sans-serif;}
15
17
  {{^is_terrain}}
@@ -21,7 +23,9 @@
21
23
  h1 {position:absolute;top:5px;right:0;width:240px;margin:0;line-height:20px;font-size:20px;}
22
24
  #layerList {position:absolute;top:35px;right:0;bottom:0;width:240px;overflow:auto;}
23
25
  #layerList div div {width:15px;height:15px;display:inline-block;}
26
+ {{^is_light}}
24
27
  .maplibre-ctrl-elevation { padding-left: 5px; padding-right: 5px; }
28
+ {{/is_light}}
25
29
  </style>
26
30
  {{/use_maplibre}}
27
31
  {{^use_maplibre}}
@@ -135,11 +139,13 @@
135
139
  })
136
140
  );
137
141
 
142
+ {{^is_light}}
138
143
  map.addControl(
139
144
  new ElevationInfoControl({
140
145
  url: "{{public_url}}data/{{id}}/elevation/{z}/{x}/{y}"
141
146
  })
142
147
  );
148
+ {{/is_light}}
143
149
  {{/is_terrain}}
144
150
  {{^is_terrain}}
145
151
 
@@ -124,9 +124,9 @@
124
124
  {{/is_vector}}
125
125
  {{^is_vector}}
126
126
  <a class="btn" href="{{public_url}}data/{{@key}}/{{&../key_query}}{{viewer_hash}}">View</a>
127
- {{#elevation_link}}
127
+ {{#is_terrain}}
128
128
  <a class="btn" href="{{public_url}}data/preview/{{@key}}/{{&../key_query}}{{viewer_hash}}">Preview Terrain</a>
129
- {{/elevation_link}}
129
+ {{/is_terrain}}
130
130
  {{/is_vector}}
131
131
  </div>
132
132
  </div>
package/src/serve_data.js CHANGED
@@ -8,8 +8,6 @@ import express from 'express';
8
8
  import Pbf from 'pbf';
9
9
  import { VectorTile } from '@mapbox/vector-tile';
10
10
  import SphericalMercator from '@mapbox/sphericalmercator';
11
- import { Image, createCanvas } from 'canvas';
12
- import sharp from 'sharp';
13
11
 
14
12
  import {
15
13
  fixTileJSONCenter,
@@ -21,6 +19,20 @@ import { getPMtilesInfo, openPMtiles } from './pmtiles_adapter.js';
21
19
  import { gunzipP, gzipP } from './promises.js';
22
20
  import { openMbTilesWrapper } from './mbtiles_wrapper.js';
23
21
 
22
+ import fs from 'node:fs';
23
+ import { fileURLToPath } from 'url';
24
+ const packageJson = JSON.parse(
25
+ fs.readFileSync(
26
+ path.dirname(fileURLToPath(import.meta.url)) + '/../package.json',
27
+ 'utf8',
28
+ ),
29
+ );
30
+
31
+ const isLight = packageJson.name.slice(-6) === '-light';
32
+ const serve_rendered = (
33
+ await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
34
+ ).serve_rendered;
35
+
24
36
  export const serve_data = {
25
37
  /**
26
38
  * Initializes the serve_data module.
@@ -246,79 +258,20 @@ export const serve_data = {
246
258
  if (fetchTile == null) return res.status(204).send();
247
259
 
248
260
  let data = fetchTile.data;
249
- const image = new Image();
250
- await new Promise(async (resolve, reject) => {
251
- image.onload = async () => {
252
- const canvas = createCanvas(TILE_SIZE, TILE_SIZE);
253
- const context = canvas.getContext('2d');
254
- context.drawImage(image, 0, 0);
255
- const long = bbox[0];
256
- const lat = bbox[1];
257
-
258
- // calculate pixel coordinate of tile,
259
- // see https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
260
- let siny = Math.sin((lat * Math.PI) / 180);
261
- // Truncating to 0.9999 effectively limits latitude to 89.189. This is
262
- // about a third of a tile past the edge of the world tile.
263
- siny = Math.min(Math.max(siny, -0.9999), 0.9999);
264
- const xWorld = TILE_SIZE * (0.5 + long / 360);
265
- const yWorld =
266
- TILE_SIZE *
267
- (0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI));
268
-
269
- const scale = 1 << zoom;
270
-
271
- const xTile = Math.floor((xWorld * scale) / TILE_SIZE);
272
- const yTile = Math.floor((yWorld * scale) / TILE_SIZE);
273
-
274
- const xPixel = Math.floor(xWorld * scale) - xTile * TILE_SIZE;
275
- const yPixel = Math.floor(yWorld * scale) - yTile * TILE_SIZE;
276
- if (
277
- xPixel < 0 ||
278
- yPixel < 0 ||
279
- xPixel >= TILE_SIZE ||
280
- yPixel >= TILE_SIZE
281
- ) {
282
- return reject('Out of bounds Pixel');
283
- }
284
- const imgdata = context.getImageData(xPixel, yPixel, 1, 1);
285
- const red = imgdata.data[0];
286
- const green = imgdata.data[1];
287
- const blue = imgdata.data[2];
288
- let elevation;
289
- if (encoding === 'mapbox') {
290
- elevation = -10000 + (red * 256 * 256 + green * 256 + blue) * 0.1;
291
- } else if (encoding === 'terrarium') {
292
- elevation = red * 256 + green + blue / 256 - 32768;
293
- } else {
294
- elevation = 'invalid encoding';
295
- }
296
- resolve(
297
- res.status(200).send({
298
- z: zoom,
299
- x: xy[0],
300
- y: xy[1],
301
- red,
302
- green,
303
- blue,
304
- latitude: lat,
305
- longitude: long,
306
- elevation,
307
- }),
308
- );
309
- };
310
- image.onerror = (err) => reject(err);
311
- if (format === 'webp') {
312
- try {
313
- const img = await sharp(data).toFormat('png').toBuffer();
314
- image.src = img;
315
- } catch (err) {
316
- reject(err);
317
- }
318
- } else {
319
- image.src = data;
320
- }
321
- });
261
+ var param = {
262
+ long: bbox[0],
263
+ lat: bbox[1],
264
+ encoding,
265
+ format,
266
+ tile_size: TILE_SIZE,
267
+ z: zoom,
268
+ x: xy[0],
269
+ y: xy[1],
270
+ };
271
+
272
+ res
273
+ .status(200)
274
+ .send(await serve_rendered.getTerrainElevation(data, param));
322
275
  } catch (err) {
323
276
  return res
324
277
  .status(500)
@@ -6,4 +6,8 @@ export const serve_rendered = {
6
6
  init: (options, repo, programOpts) => {},
7
7
  add: (options, repo, params, id, programOpts, dataResolver) => {},
8
8
  remove: (repo, id) => {},
9
+ getTerrainElevation: (data, param) => {
10
+ param['elevation'] = 'not supported in light';
11
+ return param;
12
+ },
9
13
  };
@@ -7,7 +7,7 @@
7
7
  // This happens on ARM:
8
8
  // > terminate called after throwing an instance of 'std::runtime_error'
9
9
  // > what(): Cannot read GLX extensions.
10
- import 'canvas';
10
+ import { Image, createCanvas } from 'canvas';
11
11
  import '@maplibre/maplibre-gl-native';
12
12
  //
13
13
  // SECTION END
@@ -1458,4 +1458,76 @@ export const serve_rendered = {
1458
1458
  }
1459
1459
  delete repo[id];
1460
1460
  },
1461
+
1462
+ /**
1463
+ * Get the elevation of terrain tile data by rendering it to a canvas image
1464
+ * @param {object} data The background color (or empty string for transparent).
1465
+ * @param {object} param Required parameters (coordinates e.g.)
1466
+ * @returns {object}
1467
+ */
1468
+ getTerrainElevation: async function (data, param) {
1469
+ return await new Promise(async (resolve, reject) => {
1470
+ const image = new Image();
1471
+ image.onload = async () => {
1472
+ const canvas = createCanvas(param['tile_size'], param['tile_size']);
1473
+ const context = canvas.getContext('2d');
1474
+ context.drawImage(image, 0, 0);
1475
+
1476
+ // calculate pixel coordinate of tile,
1477
+ // see https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
1478
+ let siny = Math.sin((param['lat'] * Math.PI) / 180);
1479
+ // Truncating to 0.9999 effectively limits latitude to 89.189. This is
1480
+ // about a third of a tile past the edge of the world tile.
1481
+ siny = Math.min(Math.max(siny, -0.9999), 0.9999);
1482
+ const xWorld = param['tile_size'] * (0.5 + param['long'] / 360);
1483
+ const yWorld =
1484
+ param['tile_size'] *
1485
+ (0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI));
1486
+
1487
+ const scale = 1 << param['z'];
1488
+
1489
+ const xTile = Math.floor((xWorld * scale) / param['tile_size']);
1490
+ const yTile = Math.floor((yWorld * scale) / param['tile_size']);
1491
+
1492
+ const xPixel = Math.floor(xWorld * scale) - xTile * param['tile_size'];
1493
+ const yPixel = Math.floor(yWorld * scale) - yTile * param['tile_size'];
1494
+ if (
1495
+ xPixel < 0 ||
1496
+ yPixel < 0 ||
1497
+ xPixel >= param['tile_size'] ||
1498
+ yPixel >= param['tile_size']
1499
+ ) {
1500
+ return reject('Out of bounds Pixel');
1501
+ }
1502
+ const imgdata = context.getImageData(xPixel, yPixel, 1, 1);
1503
+ const red = imgdata.data[0];
1504
+ const green = imgdata.data[1];
1505
+ const blue = imgdata.data[2];
1506
+ let elevation;
1507
+ if (param['encoding'] === 'mapbox') {
1508
+ elevation = -10000 + (red * 256 * 256 + green * 256 + blue) * 0.1;
1509
+ } else if (param['encoding'] === 'terrarium') {
1510
+ elevation = red * 256 + green + blue / 256 - 32768;
1511
+ } else {
1512
+ elevation = 'invalid encoding';
1513
+ }
1514
+ param['elevation'] = elevation;
1515
+ param['red'] = red;
1516
+ param['green'] = green;
1517
+ param['blue'] = blue;
1518
+ resolve(param);
1519
+ };
1520
+ image.onerror = (err) => reject(err);
1521
+ if (param['format'] === 'webp') {
1522
+ try {
1523
+ const img = await sharp(data).toFormat('png').toBuffer();
1524
+ image.src = img;
1525
+ } catch (err) {
1526
+ reject(err);
1527
+ }
1528
+ } else {
1529
+ image.src = data;
1530
+ }
1531
+ });
1532
+ },
1461
1533
  };
package/src/server.js CHANGED
@@ -24,13 +24,12 @@ import {
24
24
  } from './utils.js';
25
25
 
26
26
  import { fileURLToPath } from 'url';
27
- const __filename = fileURLToPath(import.meta.url);
28
- const __dirname = path.dirname(__filename);
27
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
29
28
  const packageJson = JSON.parse(
30
29
  fs.readFileSync(__dirname + '/../package.json', 'utf8'),
31
30
  );
32
-
33
31
  const isLight = packageJson.name.slice(-6) === '-light';
32
+
34
33
  const serve_rendered = (
35
34
  await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
36
35
  ).serve_rendered;
@@ -575,11 +574,14 @@ async function start(opts) {
575
574
  tileJSON.encoding === 'terrarium' ||
576
575
  tileJSON.encoding === 'mapbox'
577
576
  ) {
578
- data.elevation_link = getTileUrls(
579
- req,
580
- tileJSON.tiles,
581
- `data/${id}/elevation`,
582
- )[0];
577
+ if (!isLight) {
578
+ data.elevation_link = getTileUrls(
579
+ req,
580
+ tileJSON.tiles,
581
+ `data/${id}/elevation`,
582
+ )[0];
583
+ }
584
+ data.is_terrain = true;
583
585
  }
584
586
  if (center) {
585
587
  const centerPx = mercator.px([center[0], center[1]], center[2]);
@@ -698,6 +700,7 @@ async function start(opts) {
698
700
  is_terrain: is_terrain,
699
701
  is_terrainrgb: data.tileJSON.encoding === 'mapbox',
700
702
  terrain_encoding: data.tileJSON.encoding,
703
+ is_light: isLight,
701
704
  };
702
705
  });
703
706
 
@@ -772,8 +775,8 @@ export async function server(opts) {
772
775
  console.log(`Caught signal ${signal}, refreshing`);
773
776
  console.log('Stopping server and reloading config');
774
777
 
775
- running.server.shutdown(() => {
776
- const restarted = start(opts);
778
+ running.server.shutdown(async () => {
779
+ const restarted = await start(opts);
777
780
  running.server = restarted.server;
778
781
  running.app = restarted.app;
779
782
  });