diff --git a/src/core/Palette.js b/src/core/Palette.js index 6dcf527..abf5197 100644 --- a/src/core/Palette.js +++ b/src/core/Palette.js @@ -2,21 +2,14 @@ * Palette */ -export type ColorIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | - 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | - 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | - 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31; -export type Color = string; - - class Palette { - length: number; - rgb: Uint8Array; - colors: Array; - abgr: Uint32Array; - fl: Array; + length; + rgb; + colors; + abgr; + fl; - constructor(colors: Array) { + constructor(colors) { this.length = colors.length; this.rgb = new Uint8Array(this.length * 3); this.colors = new Array(this.length); @@ -42,7 +35,7 @@ class Palette { * @param color Index of color in palette * @return dark True if color is dark */ - isDark(color: number) { + isDark(color) { color *= 3; const r = this.rgb[color++]; const g = this.rgb[color++]; @@ -59,10 +52,10 @@ class Palette { * @return index of color */ getIndexOfColor( - r: number, - g: number, - b: number, - ): ColorIndex { + r, + g, + b, + ) { const { rgb } = this; let i = rgb.length / 3; while (i > 0) { @@ -86,10 +79,10 @@ class Palette { * @return index of color */ getClosestIndexOfColor( - r: number, - g: number, - b: number, - ): ColorIndex { + r, + g, + b, + ) { const { rgb } = this; let i = rgb.length / 3; let closestIndex = 0; @@ -113,10 +106,10 @@ class Palette { * @param chunkBuffer Buffer of indexed pixels * @return ABRG Buffer */ - buffer2ABGR(chunkBuffer: Buffer): Uint32Array { + buffer2ABGR(chunkBuffer) { const { length } = chunkBuffer; const colors = new Uint32Array(length); - let value: number; + let value; const buffer = chunkBuffer; let pos = 0; @@ -132,11 +125,11 @@ class Palette { * @param chunkBuffer Buffer of indexed pixels * @return RGB Buffer */ - buffer2RGB(chunkBuffer: Buffer): Uint8Array { + buffer2RGB(chunkBuffer) { const { length } = chunkBuffer; const colors = new Uint8Array(length * 3); - let color: number; - let value: number; + let color; + let value; const buffer = chunkBuffer; let c = 0; @@ -157,7 +150,7 @@ class Palette { * @param length Length of needed Buffer * @return RGB Buffer of wanted size with just one color */ - oneColorBuffer(color: ColorIndex, length: number) { + oneColorBuffer(color, length) { const buffer = new Uint8Array(length * 3); const r = this.rgb[color * 3]; const g = this.rgb[color * 3 + 1]; @@ -173,7 +166,7 @@ class Palette { } } -export const COLORS_RGB: Uint8Array = new Uint8Array([ +export const COLORS_RGB = new Uint8Array([ 202, 227, 255, // first color is unset pixel in ocean 255, 255, 255, // second color is unset pixel on land 255, 255, 255, // white @@ -209,9 +202,9 @@ export const COLORS_RGB: Uint8Array = new Uint8Array([ ]); export const COLORS_AMOUNT = COLORS_RGB.length / 3; -export const COLORS: Array = new Array(COLORS_AMOUNT); -export const COLORS_ABGR: Uint32Array = new Uint32Array(COLORS_AMOUNT); -export const TRANSPARENT: ColorIndex = 0; +export const COLORS = new Array(COLORS_AMOUNT); +export const COLORS_ABGR = new Uint32Array(COLORS_AMOUNT); +export const TRANSPARENT = 0; export default Palette; diff --git a/src/core/Tile.js b/src/core/Tile.js index eff0193..e845076 100644 --- a/src/core/Tile.js +++ b/src/core/Tile.js @@ -12,6 +12,7 @@ import sharp from 'sharp'; import fs from 'fs'; +import Palette from './Palette'; import { getMaxTiledZoom } from './utils'; import { TILE_SIZE, TILE_ZOOM_LEVEL } from './constants'; @@ -113,22 +114,22 @@ function tileFileName(canvasTileFolder, cell) { } /* - * @param canvasSize dimension of the canvas (pixels width/height) * @param redisClient redis instance * @param canvasId id of the canvas + * @param canvas canvas data * @param canvasTileFolder root folder where to save tiles - * @param palette Palette to use * @param cell tile to create [x, y] * @return true if successfully created tile, false if tile empty */ export async function createZoomTileFromChunk( redisClient, - canvasSize, canvasId, + canvas, canvasTileFolder, - palette, cell, ) { + const palette = new Palette(canvas.colors); + const canvasSize = canvas.size; const [x, y] = cell; const maxTiledZoom = getMaxTiledZoom(canvasSize); const tileRGBBuffer = new Uint8Array( @@ -166,16 +167,23 @@ export async function createZoomTileFromChunk( }); const filename = tileFileName(canvasTileFolder, [maxTiledZoom - 1, x, y]); - await sharp(Buffer.from(tileRGBBuffer.buffer), { - raw: { - width: TILE_SIZE * TILE_ZOOM_LEVEL, - height: TILE_SIZE * TILE_ZOOM_LEVEL, - channels: 3, - }, - }) - .resize(TILE_SIZE) - .png({ options: { compressionLevel: 6 } }) - .toFile(filename); + try { + await sharp(Buffer.from(tileRGBBuffer.buffer), { + raw: { + width: TILE_SIZE * TILE_ZOOM_LEVEL, + height: TILE_SIZE * TILE_ZOOM_LEVEL, + channels: 3, + }, + }) + .resize(TILE_SIZE) + .png({ options: { compressionLevel: 6 } }) + .toFile(filename); + } catch (error) { + console.error( + `Tiling: Error on createZoomTileFromChunk:\n${error.message}`, + ); + return false; + } console.log( // eslint-disable-next-line max-len `Tiling: Created Tile ${filename} with ${na.length} empty chunks in ${Date.now() - startTime}ms`, @@ -186,16 +194,17 @@ export async function createZoomTileFromChunk( } /* + * @param canvas canvas data * @param canvasTileFolder root folder where to save tiles - * @param palette Palette to use * @param cell tile to create [z, x, y] * @return trie if successfully created tile, false if tile empty */ export async function createZoomedTile( + canvas, canvasTileFolder, - palette, cell, ) { + const palette = new Palette(canvas.colors); const tileRGBBuffer = new Uint8Array( TILE_SIZE * TILE_SIZE * TILE_ZOOM_LEVEL * TILE_ZOOM_LEVEL * 3, ); @@ -211,8 +220,15 @@ export async function createZoomedTile( na.push([dx, dy]); continue; } - const chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer(); - addRGBSubtiletoTile(TILE_ZOOM_LEVEL, [dx, dy], chunk, tileRGBBuffer); + try { + const chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer(); + addRGBSubtiletoTile(TILE_ZOOM_LEVEL, [dx, dy], chunk, tileRGBBuffer); + } catch (error) { + console.error( + // eslint-disable-next-line max-len + `Tiling: Error on createZoomedTile on chunk ${chunkfile}:\n${error.message}`, + ); + } } } @@ -222,17 +238,24 @@ export async function createZoomedTile( }); const filename = tileFileName(canvasTileFolder, [z, x, y]); - await sharp( - Buffer.from( - tileRGBBuffer.buffer, - ), { - raw: { - width: TILE_SIZE * TILE_ZOOM_LEVEL, - height: TILE_SIZE * TILE_ZOOM_LEVEL, - channels: 3, + try { + await sharp( + Buffer.from( + tileRGBBuffer.buffer, + ), { + raw: { + width: TILE_SIZE * TILE_ZOOM_LEVEL, + height: TILE_SIZE * TILE_ZOOM_LEVEL, + channels: 3, + }, }, - }, - ).resize(TILE_SIZE).toFile(filename); + ).resize(TILE_SIZE).toFile(filename); + } catch (error) { + console.error( + `Tiling: Error on createZoomedTile:\n${error.message}`, + ); + return false; + } console.log( // eslint-disable-next-line max-len `Tiling: Created tile ${filename} with ${na.length} empty subtiles in ${Date.now() - startTime}ms.`, @@ -247,7 +270,7 @@ export async function createZoomedTile( * @param canvasTileFolder root folder where to save texture * @param palette Palette to use */ -export async function createEmptyTile( +async function createEmptyTile( canvasTileFolder, palette, ) { @@ -265,15 +288,22 @@ export async function createEmptyTile( tileRGBBuffer[i++] = palette.rgb[2]; } const filename = `${canvasTileFolder}/emptytile.png`; - await sharp(Buffer.from(tileRGBBuffer.buffer), { - raw: { - width: TILE_SIZE, - height: TILE_SIZE, - channels: 3, - }, - }) - .png({ options: { compressionLevel: 6 } }) - .toFile(filename); + try { + await sharp(Buffer.from(tileRGBBuffer.buffer), { + raw: { + width: TILE_SIZE, + height: TILE_SIZE, + channels: 3, + }, + }) + .png({ options: { compressionLevel: 6 } }) + .toFile(filename); + } catch (error) { + console.error( + `Tiling: Error on createEmptyTile:\n${error.message}`, + ); + return; + } console.log(`Tiling: Created empty tile at ${filename}`); } @@ -281,18 +311,18 @@ export async function createEmptyTile( * created 4096x4096 texture of default canvas * @param redisClient redis instance * @param canvasId numberical Id of canvas - * @param canvasSize size of canvas + * @param canvas canvas data * @param canvasTileFolder root folder where to save texture - * @param palette Palette to use * */ export async function createTexture( redisClient, canvasId, - canvasSize, + canvas, canvasTileFolder, - palette, ) { + const palette = new Palette(canvas.colors); + const canvasSize = canvas.size; // dont create textures larger than 4096 const targetSize = Math.min(canvasSize, 4096); const amount = targetSize / TILE_SIZE; @@ -310,8 +340,15 @@ export async function createTexture( na.push([dx, dy]); continue; } - chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer(); - addRGBSubtiletoTile(amount, [dx, dy], chunk, textureBuffer); + try { + chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer(); + addRGBSubtiletoTile(amount, [dx, dy], chunk, textureBuffer); + } catch (error) { + console.error( + // eslint-disable-next-line max-len + `Tiling: Error on createTexture in chunk ${chunkfile}:\n${error.message}`, + ); + } } } } else { @@ -339,15 +376,22 @@ export async function createTexture( }); const filename = `${canvasTileFolder}/texture.png`; - await sharp( - Buffer.from(textureBuffer.buffer), { - raw: { - width: targetSize, - height: targetSize, - channels: 3, + try { + await sharp( + Buffer.from(textureBuffer.buffer), { + raw: { + width: targetSize, + height: targetSize, + channels: 3, + }, }, - }, - ).toFile(filename); + ).toFile(filename); + } catch (error) { + console.error( + `Tiling: Error on createTexture:\n${error.message}`, + ); + return; + } console.log( `Tiling: Created texture in ${(Date.now() - startTime) / 1000}s.`, ); @@ -356,24 +400,24 @@ export async function createTexture( /* * Create all tiles * @param redisClient redis instance - * @param canvasSize dimension of the canvas (pixels width/height) * @param canvasId id of the canvas - * @param canvasTileFolder root foler where to save tiles - * @param palette Palette of canvas + * @param canvas canvas data + * @param canvasTileFolder folder for tiles * @param force overwrite existing tiles */ export async function initializeTiles( redisClient, - canvasSize, canvasId, + canvas, canvasTileFolder, - palette, force = false, ) { console.log( `Tiling: Initializing tiles in ${canvasTileFolder}, forceint = ${force}`, ); const startTime = Date.now(); + const palette = new Palette(canvas.colors); + const canvasSize = canvas.size; const maxTiledZoom = getMaxTiledZoom(canvasSize); // empty tile await createEmptyTile(canvasTileFolder, palette); @@ -440,9 +484,8 @@ export async function initializeTiles( await createTexture( redisClient, canvasId, - canvasSize, + canvas, canvasTileFolder, - palette, ); //-- console.log( diff --git a/src/core/tileserver.js b/src/core/tileserver.js index 4092526..8b1fceb 100644 --- a/src/core/tileserver.js +++ b/src/core/tileserver.js @@ -9,7 +9,6 @@ import { Worker } from 'worker_threads'; import logger from './logger'; // eslint-disable-next-line import/no-unresolved import canvases from './canvases.json'; -import Palette from './Palette'; import RedisCanvas from '../data/models/RedisCanvas'; import { TILE_FOLDER } from './config'; @@ -29,7 +28,6 @@ const worker = new Worker('./workers/tilewriter.js'); class CanvasUpdater { TileLoadingQueues; - palette; id; canvas; firstZoomtileWidth; @@ -42,7 +40,6 @@ class CanvasUpdater { this.id = id; this.canvas = canvases[id]; this.canvasTileFolder = `${TILE_FOLDER}/${id}`; - this.palette = new Palette(this.canvas.colors); this.firstZoomtileWidth = this.canvas.size / TILE_SIZE / TILE_ZOOM_LEVEL; this.maxTiledZoom = getMaxTiledZoom(this.canvas.size); this.startReloadingLoops(); @@ -65,10 +62,9 @@ class CanvasUpdater { worker.postMessage({ task: 'createZoomTileFromChunk', args: [ - this.canvas.size, this.id, + this.canvas, this.canvasTileFolder, - this.palette, [cx, cy], ], }); @@ -76,8 +72,8 @@ class CanvasUpdater { worker.postMessage({ task: 'createZoomedTile', args: [ + this.canvas, this.canvasTileFolder, - this.palette, [zoom, cx, cy], ], }); @@ -88,9 +84,8 @@ class CanvasUpdater { task: 'createTexture', args: [ this.id, - this.canvas.size, + this.canvas, this.canvasTileFolder, - this.palette, ], }); } else { @@ -126,10 +121,9 @@ class CanvasUpdater { worker.postMessage({ task: 'initializeTiles', args: [ - this.canvas.size, this.id, + this.canvas, this.canvasTileFolder, - this.palette, false, ], });