Skip to content

Commit

Permalink
fix broken light (invalid use of heavy dependencies)
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Miko committed Jan 16, 2025
1 parent b0a2cef commit f55a479
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 80 deletions.
83 changes: 8 additions & 75 deletions src/serve_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,22 @@ import express from 'express';
import Pbf from 'pbf';
import { VectorTile } from '@mapbox/vector-tile';
import SphericalMercator from '@mapbox/sphericalmercator';
import { Image, createCanvas } from 'canvas';
import sharp from 'sharp';

import {
fixTileJSONCenter,
getTileUrls,
isValidHttpUrl,
fetchTileData,
isLight,
} from './utils.js';
import { getPMtilesInfo, openPMtiles } from './pmtiles_adapter.js';
import { gunzipP, gzipP } from './promises.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 = {
/**
* Initializes the serve_data module.
Expand Down Expand Up @@ -246,79 +249,9 @@ export const serve_data = {
if (fetchTile == null) return res.status(204).send();

let data = fetchTile.data;
const image = new Image();
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,
// 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;
}
});
var param = { long: bbox[0], lat: bbox[1], encoding, format, tile_size: TILE_SIZE, z: zoom, x: xy[0], y: xy[1] };

res.status(200).send(await serve_rendered.getTerrainElevation(data, param));
} catch (err) {
return res
.status(500)
Expand Down
1 change: 1 addition & 0 deletions src/serve_light.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export const serve_rendered = {
init: (options, repo, programOpts) => {},
add: (options, repo, params, id, programOpts, dataResolver) => {},
remove: (repo, id) => {},
getTerrainElevation: (data, param) => { param["elevation"] = "not supported in light"; return param; },
};
74 changes: 73 additions & 1 deletion src/serve_rendered.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// This happens on ARM:
// > terminate called after throwing an instance of 'std::runtime_error'
// > what(): Cannot read GLX extensions.
import 'canvas';
import { Image, createCanvas } from 'canvas';
import '@maplibre/maplibre-gl-native';
//
// SECTION END
Expand Down Expand Up @@ -1458,4 +1458,76 @@ export const serve_rendered = {
}
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;
}
});
},
};
7 changes: 3 additions & 4 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ import {
getTileUrls,
getPublicUrl,
isValidHttpUrl,
isLight,
} from './utils.js';

import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packageJson = JSON.parse(
fs.readFileSync(__dirname + '/../package.json', 'utf8'),
);

const isLight = packageJson.name.slice(-6) === '-light';
const serve_rendered = (
await import(`${!isLight ? `./serve_rendered.js` : `./serve_light.js`}`)
await import(`${!isLight() ? `./serve_rendered.js` : `./serve_light.js`}`)
).serve_rendered;

/**
Expand Down
11 changes: 11 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ export const allowedSpriteFormats = allowedOptions(['png', 'json']);

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.
* @param {string[]} opts - An array of allowed option strings.
Expand Down

0 comments on commit f55a479

Please sign in to comment.