diff --git a/src/serve_data.js b/src/serve_data.js index 1936da6..f64cd05 100644 --- a/src/serve_data.js +++ b/src/serve_data.js @@ -208,30 +208,29 @@ export const serve_data = { return res.status(404).send(JSON.stringify(tileJSON)); } - const TILE_SIZE = 256; - let tileCenter; + const TILE_SIZE = tileJSON.tileSize || 512; + let bbox; let xy; + var zoom = z; 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 || + zoom < tileJSON.minzoom || + zoom > tileJSON.maxzoom || intX < 0 || intY < 0 || - intX >= Math.pow(2, z) || - intY >= Math.pow(2, z) + intX >= Math.pow(2, zoom) || + intY >= Math.pow(2, zoom) ) { return res.status(404).send('Out of bounds'); } xy = [intX, intY]; - tileCenter = new SphericalMercator().bbox(intX, intY, z); + bbox = new SphericalMercator().bbox(intX, intY, zoom); } else { if ( - z < tileJSON.minzoom || - z > tileJSON.maxzoom || x < -180 || y < -90 || x > 180 || @@ -240,19 +239,27 @@ export const serve_data = { 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]; + //no zoom limit with coordinates + if (zoom < tileJSON.minzoom) { + zoom = tileJSON.minzoom; + } + if (zoom > tileJSON.maxzoom) { + zoom = tileJSON.maxzoom; + } + + bbox = [x, y, x + 0.1, y + 0.1]; + const { minX, minY } = new SphericalMercator().xyz(bbox, zoom); + xy = [minX, minY, zoom]; } let data; if (sourceType === 'pmtiles') { - const tileinfo = await getPMtilesTile(source, z, x, y); + const tileinfo = await getPMtilesTile(source, zoom, xy[0], xy[1]); if (!tileinfo?.data) return res.status(204).send(); data = tileinfo.data; } else { data = await new Promise((resolve, reject) => { - source.getTile(z, xy[0], xy[1], (err, tileData) => { + source.getTile(zoom, xy[0], xy[1], (err, tileData) => { if (err) { return /does not exist/.test(err.message) ? resolve(null) @@ -271,51 +278,63 @@ export const serve_data = { 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 long = bbox[0]; + const lat = bbox[1]; - const xPixel = Math.floor(xy[0]); - const yPixel = Math.floor(xy[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 >= arrayWidth || - yPixel >= arrayHeight + xPixel >= TILE_SIZE || + yPixel >= TILE_SIZE ) { - return reject('Out of bounds Pixel'); + return reject('Pixel is out of bounds'); } - const index = (yPixel * arrayWidth + xPixel) * bytesPerPixel; - - const red = imgdata.data[index]; - const green = imgdata.data[index + 1]; - const blue = imgdata.data[index + 2]; + 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; + -10000 + ((red * 256 * 256 + green * 256 + blue) * 0.1); } else if (encoding === 'terrarium') { - elevation = red * 256 + green + blue / 256 - 32768; + elevation = (red * 256 + green + blue / 256) - 32768 } else { elevation = 'invalid encoding'; } resolve( res.status(200).send({ - z, + z: zoom, x: xy[0], y: xy[1], red, green, blue, - latitude: tileCenter[0], - longitude: tileCenter[1], - elevation, + latitude: lat, + longitude: long, + elevation }), ); }; @@ -406,6 +425,7 @@ export const serve_data = { const metadata = await getPMtilesInfo(source); tileJSON['encoding'] = params['encoding']; + tileJSON['tileSize'] = params['tileSize']; tileJSON['name'] = id; tileJSON['format'] = 'pbf'; Object.assign(tileJSON, metadata); @@ -427,6 +447,7 @@ export const serve_data = { const info = await mbw.getInfo(); source = mbw.getMbTiles(); tileJSON['encoding'] = params['encoding']; + tileJSON['tileSize'] = params['tileSize']; tileJSON['name'] = id; tileJSON['format'] = 'pbf';