pixelplanet/src/core/tileserver.js
2020-01-04 07:00:47 +01:00

173 lines
4.9 KiB
JavaScript

/* @flow
*
* creation of tiles
*
*/
import fs from 'fs';
import type { Cell } from './Cell';
import logger from './logger';
import canvases from '../canvases.json';
import Palette from './Palette';
import { TILE_FOLDER } from './config';
import {
TILE_SIZE,
TILE_ZOOM_LEVEL,
} from './constants';
import {
createZoomTileFromChunk,
createZoomedTile,
createTexture,
initializeTiles,
} from './Tile';
import { mod, getChunkOfPixel, getMaxTiledZoom } from './utils';
// Array that holds cells of all changed base zoomlevel tiles
const CanvasUpdaters = {};
class CanvasUpdater {
TileLoadingQueues: Array;
palette: Palette;
id: number;
canvas: Object;
firstZoomtileWidth: number;
canvasTileFolder: string;
constructor(id: number) {
this.updateZoomlevelTiles = this.updateZoomlevelTiles.bind(this);
this.TileLoadingQueues = [];
this.id = id;
this.canvas = canvases[id];
this.canvasTileFolder = `${TILE_FOLDER}/${id}`;
this.palette = new Palette(this.canvas.colors, this.canvas.alpha);
this.firstZoomtileWidth = this.canvas.size / TILE_SIZE / TILE_ZOOM_LEVEL;
this.maxTiledZoom = getMaxTiledZoom(this.canvas.size);
this.startReloadingLoops();
}
/*
* @param zoom tilezoomlevel to update
*/
async updateZoomlevelTiles(zoom: number) {
const queue = this.TileLoadingQueues[zoom];
if (typeof queue === 'undefined') return;
const tile = queue.shift();
if (typeof tile !== 'undefined') {
const width = TILE_ZOOM_LEVEL ** zoom;
const cx = mod(tile, width);
const cy = Math.floor(tile / width);
if (zoom === this.maxTiledZoom - 1) {
await createZoomTileFromChunk(
this.canvas.size,
this.id,
this.canvasTileFolder,
this.palette,
[cx, cy],
);
} else if (zoom !== this.maxTiledZoom) {
await createZoomedTile(
this.canvasTileFolder,
this.palette,
[zoom, cx, cy],
);
}
if (zoom === 0) {
createTexture(this.id, this.canvas.size, this.canvasTileFolder, this.palette);
} else {
const [ucx, ucy] = [cx, cy].map((z) => Math.floor(z / 4));
const upperTile = ucx + ucy * (TILE_ZOOM_LEVEL ** (zoom - 1));
const upperQueue = this.TileLoadingQueues[zoom - 1];
if (~upperQueue.indexOf(upperTile)) return;
upperQueue.push(upperTile);
logger.info(`Tiling: Enqueued ${zoom - 1}, ${ucx}, ${ucy} for reload`);
}
}
}
/*
* register changed chunk, queue corespongind tile to reload
* @param chunk Chunk coordinates
*/
registerChunkChange(chunk: Cell) {
const queue = this.TileLoadingQueues[Math.max(this.maxTiledZoom - 1, 0)];
if (typeof queue === 'undefined') return;
const [cx, cy] = chunk.map((z) => Math.floor(z / 4));
const chunkOffset = cx + cy * this.firstZoomtileWidth;
if (~queue.indexOf(chunkOffset)) return;
queue.push(chunkOffset);
logger.info(`Tiling: Enqueued ${cx}, ${cy} / ${this.id} for basezoom reload`);
}
/*
* register changed pixel, queue corespongind tile to reload
* @param pixel Pixel that got changed
*/
registerPixelChange(pixel: Cell) {
const chunk = getChunkOfPixel(pixel, this.canvas.size);
return this.registerChunkChange(chunk);
}
/*
* initialize queues and start loops for updating tiles
*/
async startReloadingLoops() {
logger.info(`Tiling: Using folder ${this.canvasTileFolder}`);
if (!fs.existsSync(`${this.canvasTileFolder}/0`)) {
if (!fs.existsSync(this.canvasTileFolder)) {
fs.mkdirSync(this.canvasTileFolder);
}
logger.warn(
'Tiling: tiledir empty, will initialize it, this can take some time',
);
await initializeTiles(
this.canvas.size,
this.id,
this.canvasTileFolder,
this.palette,
false,
);
}
for (let c = 0; c < this.maxTiledZoom; c += 1) {
this.TileLoadingQueues.push([]);
const timeout = (8 ** (this.maxTiledZoom - c - 1)) * 5 * 1000;
logger.info(
`Tiling: Set interval for zoomlevel ${c} update to ${timeout / 1000}`,
);
setInterval(this.updateZoomlevelTiles, timeout, c);
}
if (this.maxTiledZoom === 0) {
// in the case of canvasSize == 256
this.TileLoadingQueues.push([]);
setInterval(this.updateZoomlevelTiles, 5 * 60 * 1000, 0);
}
}
}
export function registerChunkChange(canvasId: number, chunk: Cell) {
return CanvasUpdaters[canvasId].registerChunkChange(chunk);
}
export function registerPixelChange(canvasId: number, pixel: Cell) {
return CanvasUpdaters[canvasId].registerPixelChange(pixel);
}
/*
* starting update loops for canvases
*/
export function startAllCanvasLoops() {
if (!fs.existsSync(`${TILE_FOLDER}`)) fs.mkdirSync(`${TILE_FOLDER}`);
const ids = Object.keys(canvases);
for (let i = 0; i < ids.length; i += 1) {
const updater = new CanvasUpdater(parseInt(ids[i], 10));
CanvasUpdaters[ids[i]] = updater;
}
}