|
|
@@ -1,10 +1,57 @@ |
|
|
|
import yargs from 'yargs'; |
|
|
|
import { hideBin } from 'yargs/helpers'; |
|
|
|
import { project } from '@theoryofnekomata/orbis-core'; |
|
|
|
import { project, Bounds } from '@theoryofnekomata/orbis-core'; |
|
|
|
import { writeFile } from 'fs/promises'; |
|
|
|
import { basename, dirname, resolve } from 'path'; |
|
|
|
|
|
|
|
type ProjectArgs = { input: string, projection: string, output?: string }; |
|
|
|
type ProjectArgs = { |
|
|
|
input: string, |
|
|
|
projection: string, |
|
|
|
output?: string, |
|
|
|
bounds: Bounds, |
|
|
|
width?: number, |
|
|
|
height?: number, |
|
|
|
padding: [number, number], |
|
|
|
}; |
|
|
|
|
|
|
|
const coerceNumber = (n?: string) => { |
|
|
|
if (typeof n === 'undefined') { |
|
|
|
return undefined; |
|
|
|
} |
|
|
|
const tryWidth = Number(n); |
|
|
|
if (Number.isFinite(tryWidth)) { |
|
|
|
return tryWidth; |
|
|
|
} |
|
|
|
return undefined; |
|
|
|
}; |
|
|
|
|
|
|
|
const coercePadding = (n?: string) => { |
|
|
|
if (typeof n !== 'string') { |
|
|
|
return [0, 0]; |
|
|
|
} |
|
|
|
const [paddingXString = '0', paddingYString = paddingXString] = n.split(';'); |
|
|
|
return [ |
|
|
|
Number(paddingXString), |
|
|
|
Number(paddingYString), |
|
|
|
]; |
|
|
|
}; |
|
|
|
|
|
|
|
const coerceBounds = (n?: string): Bounds => { |
|
|
|
if (typeof n !== 'string') { |
|
|
|
return [ |
|
|
|
[-180, 90], |
|
|
|
[180, -90], |
|
|
|
] as Bounds; |
|
|
|
} |
|
|
|
|
|
|
|
return n |
|
|
|
.split(';') |
|
|
|
.map((p) => ( |
|
|
|
p |
|
|
|
.split(',') |
|
|
|
.map((c: string) => Number(c)) |
|
|
|
)) as Bounds; |
|
|
|
}; |
|
|
|
|
|
|
|
const main = async (argv: string | readonly string[]) => { |
|
|
|
await yargs |
|
|
@@ -19,9 +66,6 @@ const main = async (argv: string | readonly string[]) => { |
|
|
|
.option('output', { |
|
|
|
alias: 'o', |
|
|
|
}) |
|
|
|
.option('width', { |
|
|
|
alias: 'w', |
|
|
|
}) |
|
|
|
.coerce('output', (output) => { |
|
|
|
if (!output) { |
|
|
|
return null; |
|
|
@@ -31,17 +75,46 @@ const main = async (argv: string | readonly string[]) => { |
|
|
|
} |
|
|
|
return output; |
|
|
|
}) |
|
|
|
.option('width', { |
|
|
|
alias: 'w', |
|
|
|
}) |
|
|
|
.coerce('width', coerceNumber) |
|
|
|
.option('height', { |
|
|
|
alias: 'h', |
|
|
|
}) |
|
|
|
.coerce('height', coerceNumber) |
|
|
|
.option('padding', { |
|
|
|
alias: 'p', |
|
|
|
default: [0, 0], |
|
|
|
}) |
|
|
|
.coerce('padding', coercePadding) |
|
|
|
.option('bounds', { |
|
|
|
alias: 'b', |
|
|
|
default: '-180,90;180,-90', |
|
|
|
}), |
|
|
|
default: [[-180, 90], [180, -90]], |
|
|
|
}) |
|
|
|
.coerce('bounds', coerceBounds), |
|
|
|
handler: async (projectArgvRaw) => { |
|
|
|
const projectArgv = projectArgvRaw as unknown as ProjectArgs; |
|
|
|
const outputPng = await project(projectArgv.input, [projectArgv.projection]); |
|
|
|
const outputPng = await project(projectArgv.input, [projectArgv.projection], { |
|
|
|
bounds: projectArgv.bounds, |
|
|
|
wrapAround: false, |
|
|
|
outputSize: { |
|
|
|
width: projectArgv.width, |
|
|
|
height: projectArgv.height, |
|
|
|
}, |
|
|
|
outputPadding: { |
|
|
|
x: projectArgv.padding[0], |
|
|
|
y: projectArgv.padding[1], |
|
|
|
}, |
|
|
|
}); |
|
|
|
if (!outputPng) { |
|
|
|
process.stdout.write('No output created.\n'); |
|
|
|
return; |
|
|
|
} |
|
|
|
const outputFilename = projectArgv.output ?? `${basename(projectArgv.input, '.png')}.out.png`; |
|
|
|
const outputPath = resolve( |
|
|
|
dirname(projectArgv.input), |
|
|
|
outputFilename.endsWith('.png') ? outputFilename : `${outputFilename}.png` |
|
|
|
outputFilename.endsWith('.png') ? outputFilename : `${outputFilename}.png`, |
|
|
|
); |
|
|
|
await writeFile(outputPath, outputPng); |
|
|
|
process.stdout.write(`Created output file: ${outputPath}\n`); |
|
|
|