|
@@ -1,11 +1,12 @@ |
|
|
import {NextApiHandler} from 'next'; |
|
|
import {NextApiHandler} from 'next'; |
|
|
import * as fs from 'fs/promises'; |
|
|
import * as fs from 'fs/promises'; |
|
|
import {PNG} from 'pngjs'; |
|
|
import {PNG} from 'pngjs'; |
|
|
import {MapType, Projection, WaterType, WorldData} from '../../../utils/types'; |
|
|
|
|
|
|
|
|
import {MapType, Projection, WorldData} from '../../../utils/types'; |
|
|
import {Stats} from 'fs'; |
|
|
import {Stats} from 'fs'; |
|
|
import sharp from 'sharp'; |
|
|
import sharp from 'sharp'; |
|
|
import {ProjectionServiceImpl} from '../../../backend/services/Projection.service'; |
|
|
|
|
|
|
|
|
import ProjectionService, {ProjectionServiceImpl} from '../../../backend/services/Projection.service'; |
|
|
import {FileSystemServiceImpl} from '../../../backend/services/FileSystem.service'; |
|
|
import {FileSystemServiceImpl} from '../../../backend/services/FileSystem.service'; |
|
|
|
|
|
import TileService, {TileServiceImpl} from '../../../backend/services/Tile.service'; |
|
|
|
|
|
|
|
|
const generateProjectedBaseData = async (t: MapType, p: Projection) => { |
|
|
const generateProjectedBaseData = async (t: MapType, p: Projection) => { |
|
|
const destPath = `public/generated/base/${p}-${t}.png`; |
|
|
const destPath = `public/generated/base/${p}-${t}.png`; |
|
@@ -25,7 +26,7 @@ const generateProjectedBaseData = async (t: MapType, p: Projection) => { |
|
|
if (shouldGenerateFile) { |
|
|
if (shouldGenerateFile) { |
|
|
const inputBuffer = await fs.readFile(`src/assets/data/000/${t}.png`) |
|
|
const inputBuffer = await fs.readFile(`src/assets/data/000/${t}.png`) |
|
|
const inputPng = PNG.sync.read(inputBuffer); |
|
|
const inputPng = PNG.sync.read(inputBuffer); |
|
|
const projectionService = new ProjectionServiceImpl() |
|
|
|
|
|
|
|
|
const projectionService: ProjectionService = new ProjectionServiceImpl() |
|
|
const outputPng = projectionService.project(inputPng, p) as PNG; |
|
|
const outputPng = projectionService.project(inputPng, p) as PNG; |
|
|
const outputBuffer = PNG.sync.write(outputPng); |
|
|
const outputBuffer = PNG.sync.write(outputPng); |
|
|
await fs.writeFile(destPath, outputBuffer); |
|
|
await fs.writeFile(destPath, outputBuffer); |
|
@@ -64,307 +65,17 @@ const resizeBaseData = async (t: MapType, p: Projection, size: number, isWidthDi |
|
|
.toBuffer(); |
|
|
.toBuffer(); |
|
|
|
|
|
|
|
|
await fs.writeFile(destPath, buffer); |
|
|
await fs.writeFile(destPath, buffer); |
|
|
return destPath.replace('public/', '/'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
enum LandType { |
|
|
|
|
|
MESA = 0, |
|
|
|
|
|
DESERT = 1, |
|
|
|
|
|
FOREST = 2, |
|
|
|
|
|
PLAINS = 3, |
|
|
|
|
|
ICE = 4, |
|
|
|
|
|
BEACH = 5, |
|
|
|
|
|
MUD = 6, |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const rgb = (r: number, g: number, b: number) => { |
|
|
|
|
|
return (r << 16) + (g << 8) + b |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const checkBiomeLandType = (biomePngData: Buffer, x: number, y: number, width: number) => { |
|
|
|
|
|
const dataIndex = ((y * width) + x) * 4; |
|
|
|
|
|
switch (rgb( |
|
|
|
|
|
biomePngData[dataIndex], |
|
|
|
|
|
biomePngData[dataIndex + 1], |
|
|
|
|
|
biomePngData[dataIndex + 2], |
|
|
|
|
|
)) { |
|
|
|
|
|
case 0xfa9418: |
|
|
|
|
|
case 0xffbc40: |
|
|
|
|
|
return LandType.DESERT; |
|
|
|
|
|
case 0x606060: |
|
|
|
|
|
case 0xd25f12: |
|
|
|
|
|
case 0xd94515: |
|
|
|
|
|
case 0xca8c65: |
|
|
|
|
|
return LandType.MESA; |
|
|
|
|
|
case 0x056621: |
|
|
|
|
|
case 0x0b6659: |
|
|
|
|
|
case 0x537b09: |
|
|
|
|
|
case 0x2c4205: |
|
|
|
|
|
case 0x628b17: |
|
|
|
|
|
case 0x507050: |
|
|
|
|
|
case 0x2d8e49: |
|
|
|
|
|
case 0x8ab33f: |
|
|
|
|
|
case 0x687942: |
|
|
|
|
|
return LandType.FOREST; |
|
|
|
|
|
case 0x596651: |
|
|
|
|
|
case 0x454f3e: |
|
|
|
|
|
case 0x338e81: |
|
|
|
|
|
// return LandType.MUD; |
|
|
|
|
|
// case 0xfade55: |
|
|
|
|
|
// return LandType.BEACH; |
|
|
|
|
|
case 0xffffff: |
|
|
|
|
|
case 0xa0a0a0: |
|
|
|
|
|
case 0xfaf0c0: |
|
|
|
|
|
case 0x597d72: |
|
|
|
|
|
case 0x202070: |
|
|
|
|
|
case 0x202038: |
|
|
|
|
|
case 0x404090: |
|
|
|
|
|
case 0x163933: |
|
|
|
|
|
return LandType.ICE; |
|
|
|
|
|
case 0xfade55: |
|
|
|
|
|
case 0x8db360: |
|
|
|
|
|
case 0x07f9b2: |
|
|
|
|
|
case 0xbdb25f: |
|
|
|
|
|
case 0xa79d64: |
|
|
|
|
|
case 0xb5db88: |
|
|
|
|
|
case 0x000070: |
|
|
|
|
|
case 0x0000ff: |
|
|
|
|
|
case 0x0000ac: |
|
|
|
|
|
case 0x000090: |
|
|
|
|
|
case 0x000050: |
|
|
|
|
|
case 0x000040: |
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return LandType.PLAINS; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const isMountainBiome = (biomePngData: Buffer, x: number, y: number, width: number) => { |
|
|
|
|
|
const dataIndex = ((y * width) + x) * 4; |
|
|
|
|
|
switch (rgb( |
|
|
|
|
|
(biomePngData[dataIndex]), |
|
|
|
|
|
(biomePngData[dataIndex + 1]), |
|
|
|
|
|
(biomePngData[dataIndex + 2]), |
|
|
|
|
|
)) { |
|
|
|
|
|
case 0x606060: |
|
|
|
|
|
case 0xa0a0a0: |
|
|
|
|
|
case 0x507050: |
|
|
|
|
|
case 0x338e81: |
|
|
|
|
|
case 0x597d72: |
|
|
|
|
|
return true |
|
|
|
|
|
default: |
|
|
|
|
|
break |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const isHillBiome = (biomePngData: Buffer, x: number, y: number, width: number) => { |
|
|
|
|
|
const dataIndex = ((y * width) + x) * 4; |
|
|
|
|
|
switch (rgb( |
|
|
|
|
|
(biomePngData[dataIndex]), |
|
|
|
|
|
(biomePngData[dataIndex + 1]), |
|
|
|
|
|
(biomePngData[dataIndex + 2]), |
|
|
|
|
|
)) { |
|
|
|
|
|
case 0xd25f12: |
|
|
|
|
|
case 0x163933: |
|
|
|
|
|
case 0x2c4205: |
|
|
|
|
|
case 0x454f3e: |
|
|
|
|
|
case 0x687942: |
|
|
|
|
|
return true |
|
|
|
|
|
default: |
|
|
|
|
|
break |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return false |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const isWoodedBiome = (biomePngData: Buffer, x: number, y: number, width: number) => { |
|
|
|
|
|
const dataIndex = ((y * width) + x) * 4; |
|
|
|
|
|
switch (rgb( |
|
|
|
|
|
(biomePngData[dataIndex]), |
|
|
|
|
|
(biomePngData[dataIndex + 1]), |
|
|
|
|
|
(biomePngData[dataIndex + 2]), |
|
|
|
|
|
)) { |
|
|
|
|
|
case 0x056621: |
|
|
|
|
|
case 0x537b09: |
|
|
|
|
|
case 0x596651: |
|
|
|
|
|
case 0x2d8e49: |
|
|
|
|
|
return true; |
|
|
|
|
|
default: |
|
|
|
|
|
break |
|
|
|
|
|
} |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const index = (x: number, y: number, width: number) => { |
|
|
|
|
|
return ((y * width) + x) * 4; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const landThreshold = 0x80 |
|
|
|
|
|
const SHALLOW_WATER_THRESHOLD = 0xE0 |
|
|
|
|
|
|
|
|
|
|
|
const checkNeighboringLands = (waterMaskPngData: Buffer, x: number, y: number, width: number, height: number) => { |
|
|
|
|
|
const ny = y === 0 ? 0 : y - 1; |
|
|
|
|
|
const wx = (x + width - 1) % width; |
|
|
|
|
|
const ex = (x + 1) % width ; |
|
|
|
|
|
const sy = y === height - 1 ? height - 1 : y + 1; |
|
|
|
|
|
|
|
|
|
|
|
return { |
|
|
|
|
|
nw: waterMaskPngData[index(wx, ny, width)] < landThreshold, |
|
|
|
|
|
n: waterMaskPngData[index(x, ny, width)] < landThreshold, |
|
|
|
|
|
ne: waterMaskPngData[index(ex, ny, width)] < landThreshold, |
|
|
|
|
|
w: waterMaskPngData[index(wx, y, width)] < landThreshold, |
|
|
|
|
|
i: waterMaskPngData[index(x, y, width)], |
|
|
|
|
|
e: waterMaskPngData[index(ex, y, width)] < landThreshold, |
|
|
|
|
|
sw: waterMaskPngData[index(wx, sy, width)] < landThreshold, |
|
|
|
|
|
s: waterMaskPngData[index(x, sy, width)] < landThreshold, |
|
|
|
|
|
se: waterMaskPngData[index(ex, sy, width)] < landThreshold, |
|
|
|
|
|
}; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const determineBackgroundTileType = (landFlags: ReturnType<typeof checkNeighboringLands>) => { |
|
|
|
|
|
const islandThreshold = 0xE0; |
|
|
|
|
|
if (landFlags.i < landThreshold) { |
|
|
|
|
|
return 1; // 1 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( |
|
|
|
|
|
landFlags.i < islandThreshold |
|
|
|
|
|
&& !( |
|
|
|
|
|
landFlags.nw |
|
|
|
|
|
|| landFlags.n |
|
|
|
|
|
|| landFlags.ne |
|
|
|
|
|
|| landFlags.w |
|
|
|
|
|
|| landFlags.e |
|
|
|
|
|
|| landFlags.sw |
|
|
|
|
|
|| landFlags.s |
|
|
|
|
|
|| landFlags.se |
|
|
|
|
|
) |
|
|
|
|
|
) { |
|
|
|
|
|
return 29; // 29 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let cursor = 0; |
|
|
|
|
|
if (landFlags.n) { |
|
|
|
|
|
cursor += 12; // 12 |
|
|
|
|
|
if (landFlags.e) { |
|
|
|
|
|
cursor += 12; // 24 |
|
|
|
|
|
if (landFlags.s) { |
|
|
|
|
|
cursor += 14; // 38 |
|
|
|
|
|
if (landFlags.w) { |
|
|
|
|
|
cursor -= 10; // 28 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.w) { |
|
|
|
|
|
cursor += 16; // 40 |
|
|
|
|
|
} else if (landFlags.sw) { |
|
|
|
|
|
cursor += 12; // 36 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.w) { |
|
|
|
|
|
cursor += 15; // 27 |
|
|
|
|
|
if (landFlags.s) { |
|
|
|
|
|
cursor += 12; // 39 |
|
|
|
|
|
} else if (landFlags.se) { |
|
|
|
|
|
cursor += 10; // 37 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.s) { |
|
|
|
|
|
cursor += 31; // 43 |
|
|
|
|
|
} else if (landFlags.se) { |
|
|
|
|
|
cursor += 8; // 20 |
|
|
|
|
|
if (landFlags.sw) { |
|
|
|
|
|
cursor += 12; // 32 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.sw) { |
|
|
|
|
|
cursor += 11; // 23 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.s) { |
|
|
|
|
|
cursor += 13; // 13 |
|
|
|
|
|
if (landFlags.e) { |
|
|
|
|
|
cursor += 12; // 25 |
|
|
|
|
|
if (landFlags.w) { |
|
|
|
|
|
cursor += 16; // 41 |
|
|
|
|
|
} else if (landFlags.nw) { |
|
|
|
|
|
cursor += 10; // 35 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.w) { |
|
|
|
|
|
cursor += 13; // 26 |
|
|
|
|
|
if (landFlags.ne) { |
|
|
|
|
|
cursor += 8; // 34 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.ne) { |
|
|
|
|
|
cursor += 8; // 21 |
|
|
|
|
|
if (landFlags.nw) { |
|
|
|
|
|
cursor += 20; // 33 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.nw) { |
|
|
|
|
|
cursor += 9; // 22 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.w) { |
|
|
|
|
|
cursor += 14; // 14 |
|
|
|
|
|
if (landFlags.e) { |
|
|
|
|
|
cursor += 28; // 42 |
|
|
|
|
|
} else if (landFlags.se) { |
|
|
|
|
|
cursor += 4; // 18 |
|
|
|
|
|
if (landFlags.ne) { |
|
|
|
|
|
cursor += 13; // 31 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.ne) { |
|
|
|
|
|
cursor += 5; // 19 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.e) { |
|
|
|
|
|
cursor += 15; // 15 |
|
|
|
|
|
if (landFlags.nw) { |
|
|
|
|
|
cursor += 1 // 16 |
|
|
|
|
|
if (landFlags.sw) { |
|
|
|
|
|
cursor += 14; // 30 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.sw) { |
|
|
|
|
|
cursor += 2; // 17 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.ne) { |
|
|
|
|
|
cursor += 2; // 2 |
|
|
|
|
|
if (landFlags.nw) { |
|
|
|
|
|
cursor += 4; // 6 |
|
|
|
|
|
if (landFlags.sw) { |
|
|
|
|
|
cursor += 42; // 48 |
|
|
|
|
|
if (landFlags.se) { |
|
|
|
|
|
cursor -= 4; // 44 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.se) { |
|
|
|
|
|
cursor += 39; // 45 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.se) { |
|
|
|
|
|
cursor += 7; // 9 |
|
|
|
|
|
if (landFlags.sw) { |
|
|
|
|
|
cursor += 37; // 46 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.sw) { |
|
|
|
|
|
cursor += 8; // 10 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.nw) { |
|
|
|
|
|
cursor += 3; // 3 |
|
|
|
|
|
if (landFlags.sw) { |
|
|
|
|
|
cursor += 5; // 8 |
|
|
|
|
|
if (landFlags.se) { |
|
|
|
|
|
cursor += 39; // 47 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.se) { |
|
|
|
|
|
cursor += 8; // 11 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.sw) { |
|
|
|
|
|
cursor += 4; // 4 |
|
|
|
|
|
if (landFlags.se) { |
|
|
|
|
|
cursor += 3; // 7 |
|
|
|
|
|
} |
|
|
|
|
|
} else if (landFlags.se) { |
|
|
|
|
|
cursor += 5; // 5 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return cursor; |
|
|
|
|
|
|
|
|
return destPath; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const generateWorldData = async (p: Projection, size: number, isWidthDimension: boolean) => { |
|
|
const generateWorldData = async (p: Projection, size: number, isWidthDimension: boolean) => { |
|
|
|
|
|
await FileSystemServiceImpl.ensureDirectory('public/generated/resized'); |
|
|
|
|
|
const tempResizedFiles = await Promise.all( |
|
|
|
|
|
Object |
|
|
|
|
|
.values(MapType) |
|
|
|
|
|
.map(async (t: MapType) => resizeBaseData(t, p, size, isWidthDimension)) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
const mapTypeBuffers = await Promise.all(Object.values(MapType).map(async (t: MapType) => { |
|
|
const mapTypeBuffers = await Promise.all(Object.values(MapType).map(async (t: MapType) => { |
|
|
return { |
|
|
return { |
|
|
mapType: t, |
|
|
mapType: t, |
|
@@ -401,101 +112,73 @@ const generateWorldData = async (p: Projection, size: number, isWidthDimension: |
|
|
initialItems: [] as number[][], |
|
|
initialItems: [] as number[][], |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const tileService: TileService = new TileServiceImpl( |
|
|
|
|
|
data[MapType.BIOME].data, |
|
|
|
|
|
data[MapType.ELEVATION].data, |
|
|
|
|
|
data[MapType.WATER_MASK].data, |
|
|
|
|
|
data[MapType.BATHYMETRY].data |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
let i = 0; |
|
|
let i = 0; |
|
|
for (let y = 0; y < dy; y += 1) { |
|
|
for (let y = 0; y < dy; y += 1) { |
|
|
worldData.spriteWaterLayer[y] = []; |
|
|
worldData.spriteWaterLayer[y] = []; |
|
|
worldData.spriteBackgroundLayer[y] = []; |
|
|
worldData.spriteBackgroundLayer[y] = []; |
|
|
worldData.spriteForegroundLayer[y] = []; |
|
|
worldData.spriteForegroundLayer[y] = []; |
|
|
for (let x = 0; x < dx; x += 1) { |
|
|
for (let x = 0; x < dx; x += 1) { |
|
|
const isShallowWater = data[MapType.BATHYMETRY].data[i] >= SHALLOW_WATER_THRESHOLD; |
|
|
|
|
|
worldData.spriteWaterLayer[y][x] = isShallowWater ? WaterType.LIGHT : WaterType.DEFAULT; |
|
|
|
|
|
|
|
|
worldData.spriteWaterLayer[y][x] = tileService.determineWaterBackgroundTileType(x, y, dx); |
|
|
|
|
|
|
|
|
const landType = checkBiomeLandType(data[MapType.BIOME].data, x, y, dx); |
|
|
|
|
|
const tileType = determineBackgroundTileType(checkNeighboringLands(data[MapType.WATER_MASK].data, x, y, dx, dy)); |
|
|
|
|
|
worldData.spriteBackgroundLayer[y][x] = ((landType * 60) + tileType); |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 0; |
|
|
|
|
|
|
|
|
|
|
|
if (tileType === 1) { |
|
|
|
|
|
const isMountain = data[MapType.ELEVATION].data[i] >= 0x40 || isMountainBiome( |
|
|
|
|
|
data[MapType.BIOME].data, |
|
|
|
|
|
x, |
|
|
|
|
|
y, |
|
|
|
|
|
dx |
|
|
|
|
|
); |
|
|
|
|
|
if (isMountain) { |
|
|
|
|
|
switch (landType) { |
|
|
|
|
|
case LandType.DESERT: |
|
|
|
|
|
case LandType.MESA: |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 796; |
|
|
|
|
|
break; |
|
|
|
|
|
case LandType.ICE: |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 799; |
|
|
|
|
|
break; |
|
|
|
|
|
case LandType.FOREST: |
|
|
|
|
|
case LandType.PLAINS: |
|
|
|
|
|
default: |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 797; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const isHill = isHillBiome(data[MapType.BIOME].data, x, y, dx); |
|
|
|
|
|
if (isHill) { |
|
|
|
|
|
if (landType === LandType.ICE) { |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 903 |
|
|
|
|
|
} else { |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 901 |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const isWooded = isWoodedBiome(data[MapType.BIOME].data, x, y, dx); |
|
|
|
|
|
if (isWooded) { |
|
|
|
|
|
if (landType === LandType.ICE) { |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 906 |
|
|
|
|
|
} else { |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = 905 |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const tileType = tileService.determineBackgroundTileType(x, y, dx, dy); |
|
|
|
|
|
worldData.spriteBackgroundLayer[y][x] = tileType; |
|
|
|
|
|
worldData.spriteForegroundLayer[y][x] = tileService.determineForegroundTileType(tileType, x, y, dx); |
|
|
i += 4; |
|
|
i += 4; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
await Promise.all(tempResizedFiles.map(f => fs.unlink(f))); |
|
|
|
|
|
await fs.rmdir('public/generated/resized'); |
|
|
return worldData; |
|
|
return worldData; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const ensureDirectories = async () => { |
|
|
|
|
|
await FileSystemServiceImpl.ensureDirectory('public/generated'); |
|
|
|
|
|
await FileSystemServiceImpl.ensureDirectory('public/generated/base'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const determineDimension = (width: string, height: string) => { |
|
|
|
|
|
if (width && Number.isFinite(Number(width))) { |
|
|
|
|
|
return { |
|
|
|
|
|
size: Number(width), |
|
|
|
|
|
isWidthDimension: true, |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (height && Number.isFinite(Number(height))) { |
|
|
|
|
|
return { |
|
|
|
|
|
size: Number(height), |
|
|
|
|
|
isWidthDimension: false, |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
throw new Error('Unspecified width or height'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const generateHandler: NextApiHandler = async (req, res) => { |
|
|
const generateHandler: NextApiHandler = async (req, res) => { |
|
|
const { |
|
|
const { |
|
|
projection = Projection.EQUIRECTANGULAR, |
|
|
projection = Projection.EQUIRECTANGULAR, |
|
|
width, |
|
|
width, |
|
|
height, |
|
|
height, |
|
|
|
|
|
bounds = '-180,90;180,-90', |
|
|
} = req.query |
|
|
} = req.query |
|
|
let isWidthDimension: boolean; |
|
|
|
|
|
let size: number; |
|
|
|
|
|
if (width && Number.isFinite(Number(width))) { |
|
|
|
|
|
size = Number(width); |
|
|
|
|
|
isWidthDimension = true; |
|
|
|
|
|
} else if (height && Number.isFinite(Number(height))) { |
|
|
|
|
|
size = Number(height); |
|
|
|
|
|
isWidthDimension = false; |
|
|
|
|
|
} else { |
|
|
|
|
|
throw new Error('Unspecified width or height'); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
const { size, isWidthDimension } = determineDimension( |
|
|
|
|
|
Array.isArray(width) ? width[0] : width, |
|
|
|
|
|
Array.isArray(height) ? height[0] : height, |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
await FileSystemServiceImpl.ensureDirectory('public/generated'); |
|
|
|
|
|
await FileSystemServiceImpl.ensureDirectory('public/generated/base'); |
|
|
|
|
|
await FileSystemServiceImpl.ensureDirectory('public/generated/resized'); |
|
|
|
|
|
await FileSystemServiceImpl.ensureDirectory('public/worlds'); |
|
|
|
|
|
|
|
|
await ensureDirectories(); |
|
|
const baseDataImageUrls = await Promise.all( |
|
|
const baseDataImageUrls = await Promise.all( |
|
|
Object |
|
|
Object |
|
|
.values(MapType) |
|
|
.values(MapType) |
|
|
.map(async (t: MapType) => generateProjectedBaseData(t, projection as Projection)) |
|
|
.map(async (t: MapType) => generateProjectedBaseData(t, projection as Projection)) |
|
|
); |
|
|
); |
|
|
await Promise.all( |
|
|
|
|
|
Object |
|
|
|
|
|
.values(MapType) |
|
|
|
|
|
.map(async (t: MapType) => resizeBaseData(t, projection as Projection, size, isWidthDimension)) |
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
const worldData = await generateWorldData(projection as Projection, size, isWidthDimension); |
|
|
const worldData = await generateWorldData(projection as Projection, size, isWidthDimension); |
|
|
|
|
|
|
|
|
res.json({ |
|
|
res.json({ |
|
|