From 4b0d51dd80efe7a935ff5ba23e2ff92de7dc8cba Mon Sep 17 00:00:00 2001 From: Maciej Ziarkowski Date: Tue, 17 Sep 2019 18:00:49 +0100 Subject: [PATCH] Add stitch renderer --- app/src/tiles/renderers/stitchRenderer.ts | 67 +++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 app/src/tiles/renderers/stitchRenderer.ts diff --git a/app/src/tiles/renderers/stitchRenderer.ts b/app/src/tiles/renderers/stitchRenderer.ts new file mode 100644 index 00000000..3264fc48 --- /dev/null +++ b/app/src/tiles/renderers/stitchRenderer.ts @@ -0,0 +1,67 @@ +import sharp from 'sharp'; +import { Image } from 'mapnik'; + +import { TileParams, TileRenderer } from "../types"; +import { getBbox, getXYZ, TILE_SIZE, formatParams } from "../util"; + +class StitchRenderer implements TileRenderer { + constructor( + /** Renderer to use when retrieving tiles to be stitched together */ + public tileRenderer: TileRenderer + ) {} + + getTile(tileParams: TileParams, dataParams: any): Promise { + console.log(`Stitching tile ${formatParams(tileParams)}`); + return this.stitchTile(tileParams, dataParams, this.tileRenderer); + } + + private async stitchTile({ tileset, z, x, y, scale }: TileParams, dataParams: any, tileRenderer: TileRenderer) { + const bbox = getBbox(z, x, y); + const nextZ = z + 1; + const nextXY = getXYZ(bbox, nextZ); + const tileSize = TILE_SIZE * scale; + + + const [topLeft, topRight, bottomLeft, bottomRight] = await Promise.all([ + [nextXY.minX, nextXY.minY], + [nextXY.maxX, nextXY.minY], + [nextXY.minX, nextXY.maxY], + [nextXY.maxX, nextXY.maxY] + ].map(([x, y]) => tileRenderer.getTile({ tileset, z: nextZ, x, y, scale }, dataParams))); + + // not possible to chain overlays in a single pipeline, but there may still be a better + // way to create image buffer here (four tiles resize to one at the next zoom level) + // instead of repeatedly creating `sharp` objects, to png, to buffer... + return sharp({ + create: { + width: tileSize * 2, + height: tileSize * 2, + channels: 4, + background: { r: 0, g: 0, b: 0, alpha: 0 } + } + }).overlayWith( + topLeft, { gravity: sharp.gravity.northwest } + ).png().toBuffer().then((buf) => { + return sharp(buf).overlayWith( + topRight, { gravity: sharp.gravity.northeast } + ).png().toBuffer() + }).then((buf) => { + return sharp(buf).overlayWith( + bottomLeft, { gravity: sharp.gravity.southwest } + ).png().toBuffer() + }).then((buf) => { + return sharp(buf).overlayWith( + bottomRight, { gravity: sharp.gravity.southeast } + ).png().toBuffer() + }).then((buf) => { + return sharp(buf + ).resize(tileSize, tileSize, { fit: 'inside' } + ).png().toBuffer() + }) + } + +} + +export { + StitchRenderer +};