merge elevation changes
This commit is contained in:
parent
d5d938b732
commit
6fddbae157
2 changed files with 147 additions and 1 deletions
|
@ -123,6 +123,152 @@ export const serve_data = {
|
||||||
return res.status(200).send(data);
|
return res.status(200).send(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.get('/:id/elevation/:z/:x/:y',
|
||||||
|
async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const item = repo?.[req.params.id];
|
||||||
|
if (!item) return res.sendStatus(404);
|
||||||
|
if (!item.source) return res.status(404).send('Missing source');
|
||||||
|
if (!item.tileJSON) return res.status(404).send('Missing tileJSON');
|
||||||
|
if (!item.sourceType)
|
||||||
|
return res.status(404).send('Missing sourceType');
|
||||||
|
const { source, tileJSON, sourceType } = item;
|
||||||
|
if (sourceType !== 'pmtiles' && sourceType !== 'mbtiles') {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.send('Invalid sourceType. Must be pmtiles or mbtiles.');
|
||||||
|
}
|
||||||
|
const encoding = tileJSON?.encoding;
|
||||||
|
if (encoding == null) {
|
||||||
|
return res.status(400).send('Missing tileJSON.encoding');
|
||||||
|
} else if (encoding !== 'terrarium' && encoding !== 'mapbox') {
|
||||||
|
return res
|
||||||
|
.status(400)
|
||||||
|
.send('Invalid encoding. Must be terrarium or mapbox.');
|
||||||
|
}
|
||||||
|
const format = tileJSON?.format;
|
||||||
|
if (format == null) {
|
||||||
|
return res.status(400).send('Missing tileJSON.format');
|
||||||
|
} else if (format !== 'webp' && format !== 'png') {
|
||||||
|
return res.status(400).send('Invalid format. Must be webp or png.');
|
||||||
|
}
|
||||||
|
const z = parseInt(req.params.z, 10);
|
||||||
|
const x = parseFloat(req.params.x);
|
||||||
|
const y = parseFloat(req.params.y);
|
||||||
|
if (tileJSON.minzoom == null || tileJSON.maxzoom == null) {
|
||||||
|
return res.status(404).send(JSON.stringify(tileJSON));
|
||||||
|
}
|
||||||
|
const TILE_SIZE = 256;
|
||||||
|
let tileCenter;
|
||||||
|
let xy;
|
||||||
|
if (Number.isInteger(x) && Number.isInteger(y)) {
|
||||||
|
const intX = parseInt(req.params.x, 10);
|
||||||
|
const intY = parseInt(req.params.y, 10);
|
||||||
|
if (
|
||||||
|
z < tileJSON.minzoom ||
|
||||||
|
z > tileJSON.maxzoom ||
|
||||||
|
intX < 0 ||
|
||||||
|
intY < 0 ||
|
||||||
|
intX >= Math.pow(2, z) ||
|
||||||
|
intY >= Math.pow(2, z)
|
||||||
|
) {
|
||||||
|
return res.status(404).send('Out of bounds');
|
||||||
|
}
|
||||||
|
xy = [intX, intY];
|
||||||
|
tileCenter = new SphericalMercator().bbox(intX, intY, z);
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
z < tileJSON.minzoom ||
|
||||||
|
z > tileJSON.maxzoom ||
|
||||||
|
x < -180 ||
|
||||||
|
y < -90 ||
|
||||||
|
x > 180 ||
|
||||||
|
y > 90
|
||||||
|
) {
|
||||||
|
return res.status(404).send('Out of bounds');
|
||||||
|
}
|
||||||
|
tileCenter = [y, x, y + 0.1, x + 0.1];
|
||||||
|
const { minX, minY } = new SphericalMercator().xyz(tileCenter, z);
|
||||||
|
xy = [minX, minY];
|
||||||
|
}
|
||||||
|
const fetchTile = await fetchTileData(
|
||||||
|
source,
|
||||||
|
sourceType,
|
||||||
|
z,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
);
|
||||||
|
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 imgdata = context.getImageData(0, 0, TILE_SIZE, TILE_SIZE);
|
||||||
|
const arrayWidth = imgdata.width;
|
||||||
|
const arrayHeight = imgdata.height;
|
||||||
|
const bytesPerPixel = 4;
|
||||||
|
const xPixel = Math.floor(xy[0]);
|
||||||
|
const yPixel = Math.floor(xy[1]);
|
||||||
|
if (
|
||||||
|
xPixel < 0 ||
|
||||||
|
yPixel < 0 ||
|
||||||
|
xPixel >= arrayWidth ||
|
||||||
|
yPixel >= arrayHeight
|
||||||
|
) {
|
||||||
|
return reject('Out of bounds Pixel');
|
||||||
|
}
|
||||||
|
const index = (yPixel * arrayWidth + xPixel) * bytesPerPixel;
|
||||||
|
const red = imgdata.data[index];
|
||||||
|
const green = imgdata.data[index + 1];
|
||||||
|
const blue = imgdata.data[index + 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,
|
||||||
|
x: xy[0],
|
||||||
|
y: xy[1],
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
blue,
|
||||||
|
latitude: tileCenter[0],
|
||||||
|
longitude: tileCenter[1],
|
||||||
|
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) {
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.header('Content-Type', 'text/plain')
|
||||||
|
.send(err.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
app.get('/:id.json', (req, res) => {
|
app.get('/:id.json', (req, res) => {
|
||||||
const item = repo[req.params.id];
|
const item = repo[req.params.id];
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
|
|
@ -621,7 +621,7 @@ async function start(opts) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
serveTemplate('^/data{/:view}/:id/', 'data', (req) => {
|
serveTemplate('/data{/:view}/:id/', 'data', (req) => {
|
||||||
const { id, view } = req.params;
|
const { id, view } = req.params;
|
||||||
const data = serving.data[id];
|
const data = serving.data[id];
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue