fix broken light (invalid use of heavy dependencies)
- move elevation calculation to serve_rendered and stub in serve_light due to use of canvas and sharp - elevation api is not available for light
This commit is contained in:
parent
b0a2cefb0e
commit
f55a4791c2
5 changed files with 95 additions and 79 deletions
|
@ -8,19 +8,22 @@ import express from 'express';
|
||||||
import Pbf from 'pbf';
|
import Pbf from 'pbf';
|
||||||
import { VectorTile } from '@mapbox/vector-tile';
|
import { VectorTile } from '@mapbox/vector-tile';
|
||||||
import SphericalMercator from '@mapbox/sphericalmercator';
|
import SphericalMercator from '@mapbox/sphericalmercator';
|
||||||
import { Image, createCanvas } from 'canvas';
|
|
||||||
import sharp from 'sharp';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
fixTileJSONCenter,
|
fixTileJSONCenter,
|
||||||
getTileUrls,
|
getTileUrls,
|
||||||
isValidHttpUrl,
|
isValidHttpUrl,
|
||||||
fetchTileData,
|
fetchTileData,
|
||||||
|
isLight,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
import { getPMtilesInfo, openPMtiles } from './pmtiles_adapter.js';
|
import { getPMtilesInfo, openPMtiles } from './pmtiles_adapter.js';
|
||||||
import { gunzipP, gzipP } from './promises.js';
|
import { gunzipP, gzipP } from './promises.js';
|
||||||
import { openMbTilesWrapper } from './mbtiles_wrapper.js';
|
import { openMbTilesWrapper } from './mbtiles_wrapper.js';
|
||||||
|
|
||||||
|
const serve_rendered = (
|
||||||
|
await import(`${!isLight() ? `./serve_rendered.js` : `./serve_light.js`}`)
|
||||||
|
).serve_rendered;
|
||||||
|
|
||||||
export const serve_data = {
|
export const serve_data = {
|
||||||
/**
|
/**
|
||||||
* Initializes the serve_data module.
|
* Initializes the serve_data module.
|
||||||
|
@ -246,79 +249,9 @@ export const serve_data = {
|
||||||
if (fetchTile == null) return res.status(204).send();
|
if (fetchTile == null) return res.status(204).send();
|
||||||
|
|
||||||
let data = fetchTile.data;
|
let data = fetchTile.data;
|
||||||
const image = new Image();
|
var param = { long: bbox[0], lat: bbox[1], encoding, format, tile_size: TILE_SIZE, z: zoom, x: xy[0], y: xy[1] };
|
||||||
await new Promise(async (resolve, reject) => {
|
|
||||||
image.onload = async () => {
|
|
||||||
const canvas = createCanvas(TILE_SIZE, TILE_SIZE);
|
|
||||||
const context = canvas.getContext('2d');
|
|
||||||
context.drawImage(image, 0, 0);
|
|
||||||
const long = bbox[0];
|
|
||||||
const lat = bbox[1];
|
|
||||||
|
|
||||||
// calculate pixel coordinate of tile,
|
res.status(200).send(await serve_rendered.getTerrainElevation(data, param));
|
||||||
// see https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
|
|
||||||
let siny = Math.sin((lat * Math.PI) / 180);
|
|
||||||
// Truncating to 0.9999 effectively limits latitude to 89.189. This is
|
|
||||||
// about a third of a tile past the edge of the world tile.
|
|
||||||
siny = Math.min(Math.max(siny, -0.9999), 0.9999);
|
|
||||||
const xWorld = TILE_SIZE * (0.5 + long / 360);
|
|
||||||
const yWorld =
|
|
||||||
TILE_SIZE *
|
|
||||||
(0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI));
|
|
||||||
|
|
||||||
const scale = 1 << zoom;
|
|
||||||
|
|
||||||
const xTile = Math.floor((xWorld * scale) / TILE_SIZE);
|
|
||||||
const yTile = Math.floor((yWorld * scale) / TILE_SIZE);
|
|
||||||
|
|
||||||
const xPixel = Math.floor(xWorld * scale) - xTile * TILE_SIZE;
|
|
||||||
const yPixel = Math.floor(yWorld * scale) - yTile * TILE_SIZE;
|
|
||||||
if (
|
|
||||||
xPixel < 0 ||
|
|
||||||
yPixel < 0 ||
|
|
||||||
xPixel >= TILE_SIZE ||
|
|
||||||
yPixel >= TILE_SIZE
|
|
||||||
) {
|
|
||||||
return reject('Out of bounds Pixel');
|
|
||||||
}
|
|
||||||
const imgdata = context.getImageData(xPixel, yPixel, 1, 1);
|
|
||||||
const red = imgdata.data[0];
|
|
||||||
const green = imgdata.data[1];
|
|
||||||
const blue = imgdata.data[2];
|
|
||||||
let elevation;
|
|
||||||
if (encoding === 'mapbox') {
|
|
||||||
elevation = -10000 + (red * 256 * 256 + green * 256 + blue) * 0.1;
|
|
||||||
} else if (encoding === 'terrarium') {
|
|
||||||
elevation = red * 256 + green + blue / 256 - 32768;
|
|
||||||
} else {
|
|
||||||
elevation = 'invalid encoding';
|
|
||||||
}
|
|
||||||
resolve(
|
|
||||||
res.status(200).send({
|
|
||||||
z: zoom,
|
|
||||||
x: xy[0],
|
|
||||||
y: xy[1],
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
blue,
|
|
||||||
latitude: lat,
|
|
||||||
longitude: long,
|
|
||||||
elevation,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
image.onerror = (err) => reject(err);
|
|
||||||
if (format === 'webp') {
|
|
||||||
try {
|
|
||||||
const img = await sharp(data).toFormat('png').toBuffer();
|
|
||||||
image.src = img;
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
image.src = data;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return res
|
return res
|
||||||
.status(500)
|
.status(500)
|
||||||
|
|
|
@ -6,4 +6,5 @@ export const serve_rendered = {
|
||||||
init: (options, repo, programOpts) => {},
|
init: (options, repo, programOpts) => {},
|
||||||
add: (options, repo, params, id, programOpts, dataResolver) => {},
|
add: (options, repo, params, id, programOpts, dataResolver) => {},
|
||||||
remove: (repo, id) => {},
|
remove: (repo, id) => {},
|
||||||
|
getTerrainElevation: (data, param) => { param["elevation"] = "not supported in light"; return param; },
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// This happens on ARM:
|
// This happens on ARM:
|
||||||
// > terminate called after throwing an instance of 'std::runtime_error'
|
// > terminate called after throwing an instance of 'std::runtime_error'
|
||||||
// > what(): Cannot read GLX extensions.
|
// > what(): Cannot read GLX extensions.
|
||||||
import 'canvas';
|
import { Image, createCanvas } from 'canvas';
|
||||||
import '@maplibre/maplibre-gl-native';
|
import '@maplibre/maplibre-gl-native';
|
||||||
//
|
//
|
||||||
// SECTION END
|
// SECTION END
|
||||||
|
@ -1458,4 +1458,76 @@ export const serve_rendered = {
|
||||||
}
|
}
|
||||||
delete repo[id];
|
delete repo[id];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the elevation of terrain tile data by rendering it to a canvas image
|
||||||
|
* @param {object} data `The background color (or empty string for transparent).
|
||||||
|
* @param {object} format A sharp format.
|
||||||
|
* @returns {object}
|
||||||
|
*/
|
||||||
|
getTerrainElevation: async function(data, param) {
|
||||||
|
return await new Promise(async (resolve, reject) => {
|
||||||
|
const image = new Image();
|
||||||
|
image.onload = async () => {
|
||||||
|
const canvas = createCanvas(param["tile_size"], param["tile_size"]);
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
context.drawImage(image, 0, 0);
|
||||||
|
|
||||||
|
// calculate pixel coordinate of tile,
|
||||||
|
// see https://developers.google.com/maps/documentation/javascript/examples/map-coordinates
|
||||||
|
let siny = Math.sin((param["lat"] * Math.PI) / 180);
|
||||||
|
// Truncating to 0.9999 effectively limits latitude to 89.189. This is
|
||||||
|
// about a third of a tile past the edge of the world tile.
|
||||||
|
siny = Math.min(Math.max(siny, -0.9999), 0.9999);
|
||||||
|
const xWorld = param["tile_size"] * (0.5 + param["long"] / 360);
|
||||||
|
const yWorld =
|
||||||
|
param["tile_size"] *
|
||||||
|
(0.5 - Math.log((1 + siny) / (1 - siny)) / (4 * Math.PI));
|
||||||
|
|
||||||
|
const scale = 1 << param["z"];
|
||||||
|
|
||||||
|
const xTile = Math.floor((xWorld * scale) / param["tile_size"]);
|
||||||
|
const yTile = Math.floor((yWorld * scale) / param["tile_size"]);
|
||||||
|
|
||||||
|
const xPixel = Math.floor(xWorld * scale) - xTile * param["tile_size"];
|
||||||
|
const yPixel = Math.floor(yWorld * scale) - yTile * param["tile_size"];
|
||||||
|
if (
|
||||||
|
xPixel < 0 ||
|
||||||
|
yPixel < 0 ||
|
||||||
|
xPixel >= param["tile_size"] ||
|
||||||
|
yPixel >= param["tile_size"]
|
||||||
|
) {
|
||||||
|
return reject('Out of bounds Pixel');
|
||||||
|
}
|
||||||
|
const imgdata = context.getImageData(xPixel, yPixel, 1, 1);
|
||||||
|
const red = imgdata.data[0];
|
||||||
|
const green = imgdata.data[1];
|
||||||
|
const blue = imgdata.data[2];
|
||||||
|
let elevation;
|
||||||
|
if (param["encoding"] === 'mapbox') {
|
||||||
|
elevation = -10000 + (red * 256 * 256 + green * 256 + blue) * 0.1;
|
||||||
|
} else if (param["encoding"] === 'terrarium') {
|
||||||
|
elevation = red * 256 + green + blue / 256 - 32768;
|
||||||
|
} else {
|
||||||
|
elevation = 'invalid encoding';
|
||||||
|
}
|
||||||
|
param["elevation"] = elevation;
|
||||||
|
param["red"] = red;
|
||||||
|
param["green"] = green;
|
||||||
|
param["blue"] = blue;
|
||||||
|
resolve(param);
|
||||||
|
};
|
||||||
|
image.onerror = (err) => reject(err);
|
||||||
|
if (param["format"] === 'webp') {
|
||||||
|
try {
|
||||||
|
const img = await sharp(data).toFormat('png').toBuffer();
|
||||||
|
image.src = img;
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image.src = data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,18 +21,17 @@ import {
|
||||||
getTileUrls,
|
getTileUrls,
|
||||||
getPublicUrl,
|
getPublicUrl,
|
||||||
isValidHttpUrl,
|
isValidHttpUrl,
|
||||||
|
isLight,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const __dirname = path.dirname(__filename);
|
|
||||||
const packageJson = JSON.parse(
|
const packageJson = JSON.parse(
|
||||||
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
|
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isLight = packageJson.name.slice(-6) === '-light';
|
|
||||||
const serve_rendered = (
|
const serve_rendered = (
|
||||||
await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
|
await import(`${!isLight() ? `./serve_rendered.js` : `./serve_light.js`}`)
|
||||||
).serve_rendered;
|
).serve_rendered;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
11
src/utils.js
11
src/utils.js
|
@ -12,6 +12,17 @@ export const allowedSpriteFormats = allowedOptions(['png', 'json']);
|
||||||
|
|
||||||
export const allowedTileSizes = allowedOptions(['256', '512']);
|
export const allowedTileSizes = allowedOptions(['256', '512']);
|
||||||
|
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
export function isLight() {
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
const packageJson = JSON.parse(
|
||||||
|
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
|
||||||
|
);
|
||||||
|
return packageJson.name.slice(-6) === '-light';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restrict user input to an allowed set of options.
|
* Restrict user input to an allowed set of options.
|
||||||
* @param {string[]} opts - An array of allowed option strings.
|
* @param {string[]} opts - An array of allowed option strings.
|
||||||
|
|
Loading…
Reference in a new issue