From 800cbcaef4a182898616c12a71e7c2c110c45742 Mon Sep 17 00:00:00 2001 From: Andrew Jacombs Date: Wed, 19 Jul 2023 12:06:33 +1200 Subject: [PATCH] feat: add --from-file option to cogify create command --- packages/cogify/src/cogify/cli/cli.cog.ts | 17 ++++++++++++---- packages/cogify/src/cogify/parsers.ts | 24 +++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/cogify/src/cogify/cli/cli.cog.ts b/packages/cogify/src/cogify/cli/cli.cog.ts index 6ae421b8a..4dc7d39dc 100644 --- a/packages/cogify/src/cogify/cli/cli.cog.ts +++ b/packages/cogify/src/cogify/cli/cli.cog.ts @@ -3,7 +3,7 @@ import { LogType, fsa } from '@basemaps/shared'; import { CliId, CliInfo } from '@basemaps/shared/build/cli/info.js'; import { CogTiff, TiffTag } from '@cogeotiff/core'; import { Metrics } from '@linzjs/metrics'; -import { command, flag, restPositionals } from 'cmd-ts'; +import { command, flag, option, optional, restPositionals } from 'cmd-ts'; import { mkdir, rm } from 'fs/promises'; import { tmpdir } from 'os'; import { StacAsset, StacCollection } from 'stac-ts'; @@ -13,7 +13,7 @@ import { HashTransform } from '../../hash.stream.js'; import { getLogger, logArguments } from '../../log.js'; import { gdalBuildCog, gdalBuildVrt, gdalBuildVrtWarp } from '../gdal.command.js'; import { GdalRunner } from '../gdal.runner.js'; -import { Url } from '../parsers.js'; +import { Url, UrlArrayJsonFile } from '../parsers.js'; import { CogifyCreationOptions, CogifyStacItem, getCutline, getSources } from '../stac.js'; // FIXME: HACK @cogeotiff/core to include the Lerc tiff tag @@ -61,15 +61,24 @@ export const BasemapsCogifyCreateCommand = command({ description: 'Create a COG from a covering configuration', args: { ...logArguments, - path: restPositionals({ type: Url, displayName: 'path', description: 'Path to item json' }), + path: restPositionals({ type: Url, displayName: 'path', description: 'Path to covering configuration' }), force: flag({ long: 'force', description: 'Overwrite existing tiff files' }), + fromFile: option({ + type: optional(UrlArrayJsonFile), + long: 'from-file', + description: + 'Path to JSON file containing array of paths to covering configurations. ' + + 'File must be an array of objects with key "path" and value of a path to a covering configuration.', + }), }, async handler(args) { const metrics = new Metrics(); const logger = getLogger(this, args); - const toCreate = await Promise.all(args.path.map(async (p) => loadItem(p, logger))); + const paths = args.fromFile != null ? args.path.concat(args.fromFile) : args.path; + + const toCreate = await Promise.all(paths.map(async (p) => loadItem(p, logger))); // // Filter out any missing items, also excluding items which already have COGs created const filtered = toCreate.filter((f) => { if (f == null) return false; diff --git a/packages/cogify/src/cogify/parsers.ts b/packages/cogify/src/cogify/parsers.ts index 07f3f3010..865143e10 100644 --- a/packages/cogify/src/cogify/parsers.ts +++ b/packages/cogify/src/cogify/parsers.ts @@ -1,3 +1,4 @@ +import { fsa } from '@basemaps/shared'; import { Type } from 'cmd-ts'; import { pathToFileURL } from 'node:url'; @@ -14,3 +15,26 @@ export const Url: Type = { } }, }; + +/** + * Parse a JSON file containing an array of URLs. + * + * JSON file must contain an outer array, inside of which is a series of objects + * with key "path", the value of which will be parsed as a URL. If the looks + * like a file path, it will instead be converted using `pathToFileURL`. + **/ +export const UrlArrayJsonFile: Type = { + async from(str) { + const raw: { path: string }[] = await fsa.readJson(str); + if (!Array.isArray(raw)) throw new Error('JSON does not contain an outer array'); + const urls = raw.map((f) => { + if (!('path' in f)) throw new Error('Missing key "path"'); + try { + return new URL(f.path); + } catch (e) { + return pathToFileURL(f.path); + } + }); + return urls; + }, +};