@allmaps/cli
Advanced tools
Comparing version 1.0.0-beta.14 to 1.0.0-beta.15
import { Command } from 'commander'; | ||
import { parseJsonInput, print } from '../../lib/io.js'; | ||
import { parseJsonInput, printString } from '../../lib/io.js'; | ||
import { parseAnnotationsValidateMaps } from '../../lib/parse.js'; | ||
import { createSvgString, resourceMaskToSvgPolygon } from '../../lib/svg.js'; | ||
import { svgGeometriesToSvgString, mapToResourceMaskSvgPolygon } from '@allmaps/stdlib'; | ||
export default function svg() { | ||
@@ -13,6 +13,6 @@ return new Command('svg') | ||
const maps = parseAnnotationsValidateMaps(jsonValues); | ||
const polygons = maps.map(resourceMaskToSvgPolygon); | ||
const svg = createSvgString(polygons); | ||
print(svg); | ||
const polygons = maps.map(mapToResourceMaskSvgPolygon); | ||
const svg = svgGeometriesToSvgString(polygons); | ||
printString(svg); | ||
}); | ||
} |
import { Command } from 'commander'; | ||
import position from './transform/position.js'; | ||
import svg from './transform/svg.js'; | ||
import geojson from './transform/geojson.js'; | ||
import resourceMask from './transform/resource-mask.js'; | ||
import coordinates from './transform/coordinates.js'; | ||
export default function transform() { | ||
return new Command('transform') | ||
.summary('transform SVG to GeoJSON and vice versa') | ||
.description('Transforms SVGs with resource coordinates to GeoJSON and vice versa, using a Georeference Annotation') | ||
.addCommand(position()) | ||
.summary('transform resource coordinates to geospatial coordinates and vice versa') | ||
.description('Transforms resource coordinates to geospatial coordinates and vice versa') | ||
.addCommand(svg()) | ||
.addCommand(geojson()) | ||
.addCommand(resourceMask()); | ||
.addCommand(resourceMask()) | ||
.addCommand(coordinates()); | ||
} |
import { Command } from 'commander'; | ||
import { GCPTransformer } from '@allmaps/transform'; | ||
import { parseJsonInput, print } from '../../lib/io.js'; | ||
import { GcpTransformer } from '@allmaps/transform'; | ||
import { parseJsonInput, printString } from '../../lib/io.js'; | ||
import { parseGcps, parseMap, parseTransformOptions, parseTransformationType } from '../../lib/parse.js'; | ||
import { addAnnotationOptions, addTransformOptions } from '../../lib/options.js'; | ||
import { createSvgString, transformGeoJsonToSvg } from '../../lib/svg.js'; | ||
import { isGeoJSONGeometry } from '../../lib/geojson.js'; | ||
import { isGeojsonGeometry, svgGeometriesToSvgString } from '@allmaps/stdlib'; | ||
export default function geojson() { | ||
@@ -20,9 +19,12 @@ let command = new Command('geojson') | ||
const transformOptions = parseTransformOptions(options); | ||
const transformer = new GCPTransformer(gcps, transformationType); | ||
const transformer = new GcpTransformer(gcps, transformationType); | ||
if (options.inverse) { | ||
throw new Error('Inverse transformation not supported for this command'); | ||
} | ||
const geoJsonGeometries = await parseJsonInput(files); | ||
const svgElements = []; | ||
const svgGeometries = []; | ||
for (const geoJsonGeometry of geoJsonGeometries) { | ||
if (isGeoJSONGeometry(geoJsonGeometry)) { | ||
const svgElement = transformGeoJsonToSvg(transformer, geoJsonGeometry, transformOptions); | ||
svgElements.push(svgElement); | ||
if (isGeojsonGeometry(geoJsonGeometry)) { | ||
const svgGeometry = transformer.transformGeojsonToSvg(geoJsonGeometry, transformOptions); | ||
svgGeometries.push(svgGeometry); | ||
} | ||
@@ -33,5 +35,5 @@ else { | ||
} | ||
const svg = createSvgString(svgElements); | ||
print(svg); | ||
const svg = svgGeometriesToSvgString(svgGeometries); | ||
printString(svg); | ||
}); | ||
} |
import { Command } from 'commander'; | ||
import { GCPTransformer } from '@allmaps/transform'; | ||
import { readInput, print, readFromStdinLine } from '../../lib/io.js'; | ||
import { parseCoordinatesArrayArray, parseGcps, parseMap, parseTransformationType } from '../../lib/parse.js'; | ||
import { GcpTransformer } from '@allmaps/transform'; | ||
import { readInput, printString, readFromStdinLine } from '../../lib/io.js'; | ||
import { parseMap, parseGcps, parseCoordinatesArrayArray, parseTransformationType } from '../../lib/parse.js'; | ||
import { addAnnotationOptions } from '../../lib/options.js'; | ||
@@ -21,3 +21,3 @@ export default function position() { | ||
const transformationType = parseTransformationType(options, map); | ||
const transformer = new GCPTransformer(gcps, transformationType); | ||
const transformer = new GcpTransformer(gcps, transformationType); | ||
const positionStrings = await readInput(files); | ||
@@ -30,7 +30,7 @@ if (positionStrings.length) { | ||
else { | ||
print('Enter X Y values separated by space, and press Return.'); | ||
printString('Enter X Y values separated by space, and press Return.'); | ||
let positionString = await readFromStdinLine(); | ||
while (positionString) { | ||
processPositionString(positionString, transformer, options); | ||
print(''); | ||
printString(''); | ||
positionString = await readFromStdinLine(); | ||
@@ -46,6 +46,6 @@ } | ||
positionArray.forEach((position) => outputPositions.push(options.inverse | ||
? transformer.toResource(position) | ||
: transformer.toGeo(position))); | ||
? transformer.transformToResource(position) | ||
: transformer.transformToGeo(position))); | ||
// Print transformed positions | ||
outputPositions.forEach((outputPosition) => print(outputPosition[0] + ' ' + outputPosition[1])); | ||
outputPositions.forEach((outputPosition) => printString(outputPosition[0] + ' ' + outputPosition[1])); | ||
} |
import { Command } from 'commander'; | ||
import { GCPTransformer } from '@allmaps/transform'; | ||
import { GcpTransformer } from '@allmaps/transform'; | ||
import { parseJsonInput, printJson } from '../../lib/io.js'; | ||
import { parseAnnotationsValidateMaps, parseTransformOptions } from '../../lib/parse.js'; | ||
import { addTransformOptions } from '../../lib/options.js'; | ||
import { featuresToFeatureCollection, geometryToFeature } from '@allmaps/stdlib'; | ||
export default function resourceMask() { | ||
@@ -16,27 +17,17 @@ let command = new Command('resource-mask') | ||
const maps = parseAnnotationsValidateMaps(jsonValues); | ||
const transformOptions = parseTransformOptions(options); | ||
if (options.inverse) { | ||
throw new Error('Inverse transformation not supported for this command'); | ||
} | ||
const features = []; | ||
const transformOptions = parseTransformOptions(options); | ||
for (const map of maps) { | ||
if (map.gcps.length >= 3) { | ||
const transformer = new GCPTransformer(map.gcps, map.transformation?.type); | ||
const polygon = transformer.toGeoJSONPolygon(map.resourceMask, transformOptions); | ||
features.push({ | ||
type: 'Feature', | ||
properties: { | ||
imageId: map.resource.id | ||
}, | ||
geometry: polygon | ||
}); | ||
} | ||
else { | ||
// TODO: this can be removed because an error will be given by the transformer. | ||
console.error('Encountered Georeference Annotation with less than 3 points'); | ||
} | ||
const transformer = new GcpTransformer(map.gcps, map.transformation?.type); | ||
const polygon = transformer.transformForwardAsGeojson([map.resourceMask], transformOptions); | ||
features.push(geometryToFeature(polygon, { | ||
imageId: map.resource.id | ||
})); | ||
} | ||
const featureCollection = { | ||
type: 'FeatureCollection', | ||
features | ||
}; | ||
const featureCollection = featuresToFeatureCollection(features); | ||
printJson(featureCollection); | ||
}); | ||
} |
import { Command } from 'commander'; | ||
import { GCPTransformer } from '@allmaps/transform'; | ||
import { GcpTransformer } from '@allmaps/transform'; | ||
import { readInput, printJson } from '../../lib/io.js'; | ||
import { parseGcps, parseMap, parseTransformOptions, parseTransformationType } from '../../lib/parse.js'; | ||
import { addAnnotationOptions, addTransformOptions } from '../../lib/options.js'; | ||
import { geomEach } from '../../lib/svg.js'; | ||
import { transformSvgToGeoJson, createFeatureCollection } from '../../lib/geojson.js'; | ||
import { stringToSvgGeometriesGenerator, geometriesToFeatureCollection } from '@allmaps/stdlib'; | ||
export default function svg() { | ||
@@ -20,14 +19,17 @@ let command = new Command('svg') | ||
const transformOptions = parseTransformOptions(options); | ||
const transformer = new GCPTransformer(gcps, transformationType); | ||
const transformer = new GcpTransformer(gcps, transformationType); | ||
if (options.inverse) { | ||
throw new Error('Inverse transformation not supported for this command'); | ||
} | ||
const svgs = await readInput(files); | ||
const geoJsonGeometries = []; | ||
const geojsonGeometries = []; | ||
for (const svg of svgs) { | ||
for (const geometry of geomEach(svg)) { | ||
const geoJsonGeometry = transformSvgToGeoJson(transformer, geometry, transformOptions); | ||
geoJsonGeometries.push(geoJsonGeometry); | ||
for (const svgGeometry of stringToSvgGeometriesGenerator(svg)) { | ||
const geojsonGeometry = transformer.transformSvgToGeojson(svgGeometry, transformOptions); | ||
geojsonGeometries.push(geojsonGeometry); | ||
} | ||
} | ||
const featureCollection = createFeatureCollection(geoJsonGeometries); | ||
const featureCollection = geometriesToFeatureCollection(geojsonGeometries); | ||
printJson(featureCollection); | ||
}); | ||
} |
#!/usr/bin/env node | ||
import { Command, CommanderError } from 'commander'; | ||
import { fromZodError } from 'zod-validation-error'; | ||
import annotation from './commands/annotation.js'; | ||
import id from './commands/id.js'; | ||
import iiif from './commands/iiif.js'; | ||
import annotation from './commands/annotation.js'; | ||
import transform from './commands/transform.js'; | ||
@@ -11,4 +12,4 @@ const fixedWidth = process.env.NODE_ENV === 'test'; | ||
.exitOverride() | ||
// .showHelpAfterError() | ||
.addCommand(annotation()) | ||
.addCommand(id()) | ||
.addCommand(transform()) | ||
@@ -15,0 +16,0 @@ .addCommand(iiif()) |
/// <reference types="node" /> | ||
import { Readable } from 'stream'; | ||
import type { GCP } from '@allmaps/transform'; | ||
export declare function readInput(files: string[]): Promise<string[]>; | ||
export declare function parseJsonInput(files?: string[]): Promise<unknown[]>; | ||
export declare function parseJsonFromStream(stream: Readable): Promise<unknown[]>; | ||
export declare function parseJsonFromFile(file: string): unknown; | ||
export declare function parseCoordinateArrayArrayFromFile(file: string): number[][]; | ||
export declare function parseGcpsFromFile(file: string): GCP[]; | ||
export declare function readFromFiles(files: string[]): string[]; | ||
export declare function parseJsonFromFiles(files: string[]): Promise<unknown[]>; | ||
export declare function readFromFile(file: string): string; | ||
export declare function readFromStdin(): Promise<string>; | ||
export declare function readFromStdinLine(): Promise<string>; | ||
export declare function parseJsonInput(files?: string[]): Promise<unknown[]>; | ||
export declare function parseJsonFromFiles(files: string[]): Promise<unknown[]>; | ||
export declare function parseJsonFromFile(file: string): unknown; | ||
export declare function parseJsonFromStdin(): Promise<unknown[]>; | ||
export declare function parseJsonFromStream(stream: Readable): Promise<unknown[]>; | ||
export declare function printJson(json: unknown): void; | ||
export declare function print(str: string): void; | ||
export declare function printString(str: string): void; |
import { createReadStream, readFileSync } from 'fs'; | ||
import { Readable } from 'stream'; | ||
import StreamValues from 'stream-json/streamers/StreamValues.js'; | ||
import { parseCoordinatesArrayArray } from './parse.js'; | ||
async function* concatStreams(readables) { | ||
for (const readable of readables) { | ||
for await (const chunk of readable) { | ||
yield chunk; | ||
} | ||
} | ||
} | ||
// Read Input: files (or file) and stdin. For reading general files (svg, strings, ...). | ||
export async function readInput(files) { | ||
@@ -23,40 +16,2 @@ if (process.stdin.isTTY) { | ||
} | ||
export async function parseJsonInput(files) { | ||
let jsonValues = []; | ||
// TODO: does this function need the isTTY check? | ||
if (files && Array.isArray(files) && files.length) { | ||
jsonValues = await parseJsonFromFiles(files); | ||
} | ||
else if (!process.stdin.isTTY) { | ||
jsonValues = await parseJsonFromStdin(); | ||
} | ||
if (jsonValues.length > 0) { | ||
return jsonValues; | ||
} | ||
else { | ||
throw new Error('No input files supplied, and nothing to read from the standard input'); | ||
} | ||
} | ||
export function parseJsonFromStream(stream) { | ||
return new Promise((resolve, reject) => { | ||
const jsonValues = []; | ||
stream.on('error', (err) => reject(err)); | ||
const pipeline = stream.pipe(StreamValues.withParser()); | ||
pipeline.on('data', (data) => jsonValues.push(data.value)); | ||
pipeline.on('end', () => resolve(jsonValues)); | ||
pipeline.on('error', (err) => reject(err)); | ||
}); | ||
} | ||
export function parseJsonFromFile(file) { | ||
return JSON.parse(readFileSync(file, { encoding: 'utf8', flag: 'r' })); | ||
} | ||
export function parseCoordinateArrayArrayFromFile(file) { | ||
return parseCoordinatesArrayArray(readFileSync(file, { encoding: 'utf8', flag: 'r' })); | ||
} | ||
export function parseGcpsFromFile(file) { | ||
return parseCoordinateArrayArrayFromFile(file).map((coordinateArray) => ({ | ||
resource: [coordinateArray[0], coordinateArray[1]], | ||
geo: [coordinateArray[2], coordinateArray[3]] | ||
})); | ||
} | ||
export function readFromFiles(files) { | ||
@@ -66,11 +21,6 @@ if (!files || !files.length) { | ||
} | ||
return files.map((file) => readFileSync(file, { encoding: 'utf8', flag: 'r' })); | ||
return files.map((file) => readFromFile(file)); | ||
} | ||
export async function parseJsonFromFiles(files) { | ||
if (!files || !files.length) { | ||
return []; | ||
} | ||
const iterable = await concatStreams(files.map((file) => createReadStream(file))); | ||
const mergedStream = Readable.from(iterable); | ||
return await parseJsonFromStream(mergedStream); | ||
export function readFromFile(file) { | ||
return readFileSync(file, { encoding: 'utf8', flag: 'r' }); | ||
} | ||
@@ -119,2 +69,31 @@ export function readFromStdin() { | ||
} | ||
// Parse JSON Input: files (or file) and stdin. For reading JSON files. | ||
export async function parseJsonInput(files) { | ||
let jsonValues = []; | ||
// TODO: does this function need the isTTY check? | ||
if (files && Array.isArray(files) && files.length) { | ||
jsonValues = await parseJsonFromFiles(files); | ||
} | ||
else if (!process.stdin.isTTY) { | ||
jsonValues = await parseJsonFromStdin(); | ||
} | ||
if (jsonValues.length > 0) { | ||
return jsonValues; | ||
} | ||
else { | ||
throw new Error('No input files supplied, and nothing to read from the standard input'); | ||
} | ||
} | ||
// TODO: Not clear why this uses streams, and readFromFiles (which is equally called in streams) doesn't. | ||
export async function parseJsonFromFiles(files) { | ||
if (!files || !files.length) { | ||
return []; | ||
} | ||
const iterable = await concatStreams(files.map((file) => createReadStream(file))); | ||
const mergedStream = Readable.from(iterable); | ||
return await parseJsonFromStream(mergedStream); | ||
} | ||
export function parseJsonFromFile(file) { | ||
return JSON.parse(readFileSync(file, { encoding: 'utf8', flag: 'r' })); | ||
} | ||
export async function parseJsonFromStdin() { | ||
@@ -125,2 +104,22 @@ process.stdin.resume(); | ||
} | ||
// Parse JSON from stream | ||
export function parseJsonFromStream(stream) { | ||
return new Promise((resolve, reject) => { | ||
const jsonValues = []; | ||
stream.on('error', (err) => reject(err)); | ||
const pipeline = stream.pipe(StreamValues.withParser()); | ||
pipeline.on('data', (data) => jsonValues.push(data.value)); | ||
pipeline.on('end', () => resolve(jsonValues)); | ||
pipeline.on('error', (err) => reject(err)); | ||
}); | ||
} | ||
// Stream helpers | ||
async function* concatStreams(readables) { | ||
for (const readable of readables) { | ||
for await (const chunk of readable) { | ||
yield chunk; | ||
} | ||
} | ||
} | ||
export function printJson(json) { | ||
@@ -136,4 +135,4 @@ console.log(JSON.stringify(json, null, 2)); | ||
} | ||
export function print(str) { | ||
export function printString(str) { | ||
console.log(str); | ||
} |
const DEFAULT_MAX_OFFSET_RATIO = 0; | ||
const DEFAULT_MAX_DEPTH = 6; | ||
export function addAnnotationOptions(command) { | ||
// Note: annotation is not required since transformer can be built using only gcps, which could be specified individually. This is especially useful when transforming positions, outside of the allmaps annotation context. An error message is still displayed when neither an annotation or gcps are specified | ||
// Note: annotation is not required since transformer can be built using only GCPs, which could be specified individually. | ||
// This is especially useful when transforming coordinates, outside of the Allmaps context. | ||
// An error message is still displayed when neither an annotation or GCPs are specified | ||
return command | ||
.option('-a, --annotation <filename>', 'Filename of Georeference Annotation') | ||
.option('-a, --annotation <filename>', 'Filename of Georeference Annotation') | ||
.option('-i, --inverse', 'Compute backward ("inverse") transformation.') | ||
.option('-g, --gcps <filename>', 'Filename of GCPs. This overwrites the GCPs in the annotation argument if such is also used.') | ||
.option('-t, --transformationType <transformationType>', 'Transformation Type. One of "helmert", "polynomial", "thinPlateSpline", "projective". This overwrites the transformation type in the annotation argument if such is also used.', 'polynomial') | ||
.option('-o, --transformationOrder <transformationOrder>', 'Order of polynomial transformation. One of "1", "2" or "3".', '1'); | ||
.option('-i, --inverse', 'Computes the inverse/backward transformation') | ||
.option('-g, --gcps <filename>', 'Filename of GCP file. These GCPs take precedence over the GCPs from the Georeference Annotation') | ||
.option('-t, --transformation-type <type>', 'Transformation type. One of "helmert", "polynomial", "thinPlateSpline", "projective". ' + | ||
'This takes precedence over the transformation type from the Georeference Annotation', 'polynomial') | ||
.option('-o, --polynomial-order <order>', 'Order of polynomial transformation. Either 1, 2 or 3.', '1'); | ||
} | ||
export function addTransformOptions(command) { | ||
return command | ||
.option('-p, --max-offset-ratio <number>', 'Maximum offset ratio between original and transformed midpoints', `${DEFAULT_MAX_OFFSET_RATIO}`) | ||
.option('-d, --max-depth <number>', 'Maximum recursion depth', `${DEFAULT_MAX_DEPTH}`); | ||
.option('-p, --max-offset-ratio <number>', | ||
// TODO: needs better description | ||
'Maximum offset ratio between original and transformed midpoints', `${DEFAULT_MAX_OFFSET_RATIO}`) | ||
.option('-d, --max-depth <number>', 'Maximum recursion depth', `${DEFAULT_MAX_DEPTH}`) | ||
.option('--destination-is-geographic', 'Destination is geographic') | ||
.option('--source-is-geographic', 'Source is geographic'); | ||
} |
import type { Map } from '@allmaps/annotation'; | ||
import type { GCP, OptionalTransformOptions, TransformationType } from '@allmaps/transform'; | ||
import type { PartialTransformOptions, TransformationType } from '@allmaps/transform'; | ||
import type { Gcp } from '@allmaps/types'; | ||
export declare function parseMap(options: { | ||
@@ -9,3 +10,6 @@ annotation: string; | ||
annotation: string; | ||
}, map: Map): GCP[]; | ||
}, map: Map): Gcp[]; | ||
export declare function parseGcpsFromFile(file: string): Gcp[]; | ||
export declare function parseCoordinateArrayArrayFromFile(file: string): number[][]; | ||
export declare function parseCoordinatesArrayArray(coordinatesString: string): number[][]; | ||
export declare function parseTransformationType(options: { | ||
@@ -18,3 +22,2 @@ annotation: string; | ||
export declare function parseAnnotationsValidateMaps(jsonValues: unknown[]): Map[]; | ||
export declare function parseCoordinatesArrayArray(coordinatesString: string): number[][]; | ||
export declare function parseTransformOptions(options: unknown): OptionalTransformOptions; | ||
export declare function parseTransformOptions(options: unknown): PartialTransformOptions; |
import { parseAnnotation, validateMap } from '@allmaps/annotation'; | ||
import { parseGcpsFromFile, parseJsonFromFile } from './io.js'; | ||
import { readFromFile, parseJsonFromFile } from './io.js'; | ||
export function parseMap(options) { | ||
@@ -24,6 +24,25 @@ let map; | ||
else { | ||
throw new Error('No GCPs supplied. Specify an annotation or GCPs.'); | ||
throw new Error('No GCPs supplied. Supply a Georeference Annotation or a file containing GCPs.'); | ||
} | ||
return gcps; | ||
} | ||
export function parseGcpsFromFile(file) { | ||
// TODO: also allow file to contain GCPs in the Georeference Annotation GCP format | ||
return parseCoordinateArrayArrayFromFile(file).map((coordinateArray) => ({ | ||
resource: [coordinateArray[0], coordinateArray[1]], | ||
geo: [coordinateArray[2], coordinateArray[3]] | ||
})); | ||
} | ||
export function parseCoordinateArrayArrayFromFile(file) { | ||
return parseCoordinatesArrayArray(readFromFile(file)); | ||
} | ||
export function parseCoordinatesArrayArray(coordinatesString) { | ||
// String from mutliline file where each line contains multiple coordinates separated by whitespace | ||
return coordinatesString | ||
.trim() | ||
.split('\n') | ||
.map((coordinatesLineString) => coordinatesLineString | ||
.split(/\s+/) | ||
.map((coordinateString) => Number(coordinateString.trim()))); | ||
} | ||
export function parseTransformationType(options, map) { | ||
@@ -69,11 +88,2 @@ let transformationType; | ||
} | ||
export function parseCoordinatesArrayArray(coordinatesString) { | ||
// String from mutli line file where each line contains multiple coordinates separated by space (or multiple spaces or tabs) | ||
return coordinatesString | ||
.trim() | ||
.split('\n') | ||
.map((coordinatesLineString) => coordinatesLineString | ||
.split(/[ \t]+/) | ||
.map((coordinateString) => Number(coordinateString.trim()))); | ||
} | ||
export function parseTransformOptions(options) { | ||
@@ -88,4 +98,13 @@ const transformOptions = {}; | ||
} | ||
if ('destinationIsGeographic' in options && | ||
options.destinationIsGeographic) { | ||
transformOptions.destinationIsGeographic = | ||
options.destinationIsGeographic; | ||
} | ||
if ('sourceIsGeographic' in options && options.sourceIsGeographic) { | ||
transformOptions.sourceIsGeographic = | ||
options.sourceIsGeographic; | ||
} | ||
} | ||
return transformOptions; | ||
} |
{ | ||
"name": "@allmaps/cli", | ||
"version": "1.0.0-beta.14", | ||
"version": "1.0.0-beta.15", | ||
"author": { | ||
@@ -54,8 +54,9 @@ "name": "Bert Spaan", | ||
"dependencies": { | ||
"@allmaps/annotation": "^1.0.0-beta.16", | ||
"@allmaps/iiif-parser": "^1.0.0-beta.25", | ||
"@allmaps/transform": "^1.0.0-beta.15", | ||
"@allmaps/annotation": "^1.0.0-beta.17", | ||
"@allmaps/id": "^1.0.0-beta.17", | ||
"@allmaps/iiif-parser": "^1.0.0-beta.26", | ||
"@allmaps/stdlib": "^1.0.0-beta.13", | ||
"@allmaps/transform": "^1.0.0-beta.16", | ||
"commander": "^10.0.0", | ||
"stream-json": "^1.7.4", | ||
"svg-parser": "^2.0.4", | ||
"zod-validation-error": "^1.5.0" | ||
@@ -66,3 +67,2 @@ }, | ||
"@types/stream-json": "^1.7.2", | ||
"@types/svg-parser": "^2.0.3", | ||
"@typescript-eslint/eslint-plugin": "^5.45.0", | ||
@@ -78,3 +78,3 @@ "@typescript-eslint/parser": "^5.45.0", | ||
}, | ||
"gitHead": "ef3a0afc0cd0ca16fa6f79f2a18b88c9c29d4949" | ||
"gitHead": "7ce2bc3fe2133055b11e3034fcf0a386e903492e" | ||
} |
128
README.md
# @allmaps/cli | ||
This module enables you to use the allmaps functionallity in your terminal! | ||
Command-line interface for [Allmaps](https://allmaps.org/). | ||
## Installation | ||
Use [pnpm](https://pnpm.io/) or [npm](https://www.npmjs.com/) to install this CLI tool globally in your system: | ||
Use npm to install Allmaps CLI globally: | ||
```sh | ||
pnpm add -g @allmaps/cli | ||
npm install -g @allmaps/cli | ||
``` | ||
or | ||
```sh | ||
nnpm install -g @allmaps/cli | ||
``` | ||
## Usage | ||
### General Usage and Help | ||
Run Allmaps CLI in your terminal using: | ||
@@ -39,5 +31,5 @@ | ||
### Input and Output | ||
### Input and output | ||
All CLI commands accept one or more files as input. You can supply these files in two ways: | ||
Most CLI commands accept one or more files as input. You can supply these files in two ways: | ||
@@ -67,3 +59,3 @@ - Supplied at the end of the command using their full or relative paths. In the CLI's help output, this is shown as `[files...]`. | ||
Parse input files and output them in the format used internally by Allmaps: | ||
Parse input files and output them in parsed Georeference Annotations (the format used internally by Allmaps): | ||
@@ -90,12 +82,15 @@ ```sh | ||
#### Transform Position | ||
#### Transform coordinates | ||
Transform positions from input files forward (or backward) using a transformation built from the GCPs and transformation type specified in a Georeference Annotation or separately. | ||
Transform coordinates from input files forward (or backward) using a transformation built from the GCPs and transformation type specified in a Georeference Annotation. It's also possible to supply the GCPs and transformation type separately. | ||
Position-files are expected to contain one position on each line, formatted as pairs of coordinates in decimal form separated by spaces. E.g. `X_origin Y_origin` | ||
Input files with coordinates are expected to contain one coordinate on each line, formatted as pairs of coordinates in decimal form separated by spaces: | ||
E.g. `X_origin Y_origin`. | ||
GCP-files are similar: `X_origin Y_origin X_destination Y_destination` | ||
For this specific command, if no input-files are supplied in the a promt will show up in stdin, enabling you to enter positions one by one in the same format as above and read the transformed result. | ||
For this specific command, if no input files are supplied in the a prompt will show up in stdin, enabling you to enter coordinates one by one in the same format as above and read the transformed result. | ||
This command (and position format) was inspired by gdaltransform. | ||
This command was inspired by [gdaltransform](https://gdal.org/programs/gdaltransform.html). | ||
@@ -108,6 +103,5 @@ **Examples:** | ||
For example, with | ||
For example, with a file `/path/to/coordinates.txt` that contains two coordinates: | ||
``` | ||
// positionFile.txt | ||
100 100 | ||
@@ -117,11 +111,11 @@ 200 200 | ||
You can use the command as follows | ||
You can use the command as follows: | ||
```sh | ||
allmaps transform position -a path/to/myAnnotation.json path/to/positionsFile.txt | ||
allmaps transform coordinates -a /path/to/annotation.json /path/to/coordinates.txt | ||
``` | ||
Returns, e.g. | ||
This will output: | ||
```sh | ||
``` | ||
4.35748950266836 52.00802521697614 | ||
@@ -134,11 +128,11 @@ 4.357492297361325 52.008035790231254 | ||
```sh | ||
cat path/to/positionFile.txt | allmaps transform position -a path/to/myAnnotation.json > path/to/outputPositionsFile.txt | ||
cat /path/to/coordinates.txt | allmaps transform coordinates -a /path/to/annotation.json \ | ||
> /path/to/transformed-coordinates.txt | ||
``` | ||
Or transform using a specific set of GCPs and specified transformation type, instead of reading those from an annotation | ||
Or transform using a specific set of GCPs and specified transformation type, instead of reading those from a Georeference Annotation: | ||
With | ||
With a file `/path/to/gcps.txt` that contains four GCPs: | ||
```sh | ||
// gcpFile.txt | ||
``` | ||
3899 6412 9.9301538 53.5814021 | ||
@@ -148,9 +142,9 @@ 6584 819 25.4101689 71.0981125 | ||
1409 5436 -3.2014645 55.959946 | ||
1765 1737 -18.1014216 64.3331759``` | ||
1765 1737 -18.1014216 64.3331759 | ||
``` | ||
This is done via | ||
This is done with the following command: | ||
```sh | ||
allmaps transform position -g path/to/gcpFile.json -t thinPlateSpline path/to/positionsFile.txt | ||
allmaps transform coordinates -g /path/to/gcps.txt -t thinPlateSpline /path/to/coordinates.txt | ||
``` | ||
@@ -160,3 +154,3 @@ | ||
Transform SVG to GeoJSON using a transformation built from the GCPs and transformation type specified in a Georeference Annotation or separately. | ||
Transform SVG forward to GeoJSON Geometry using a transformation built from the GCPs and transformation type specified in a Georeference Annotation. You can also supply the GCPs and transformation type separately. | ||
@@ -170,13 +164,12 @@ **Examples:** | ||
```sh | ||
allmaps transform svg -a path/to/myAnnotation.json path/to/mySVG.svg | ||
allmaps transform svg -a /path/to/annotation.json /path/to/svg.svg | ||
``` | ||
```sh | ||
allmaps transform svg -g path/to/gcpFile.json -t thinPlateSpline path/to/mySVG.svg | ||
allmaps transform svg -g /path/to/gcps.txt -t thinPlateSpline /path/to/svg.svg | ||
``` | ||
#### Transform GeoJSON | ||
Transform GeoJSON to SVG using a transformation built from the GCPs and transformation type specified in a Georeference Annotation or separately. | ||
Transform GeoJSON Geometry backwards to SVG using a transformation built from the GCPs and transformation type specified in a Georeference Annotation or separately. | ||
@@ -190,7 +183,7 @@ **Examples:** | ||
```sh | ||
allmaps transform geojson -a path/to/myAnnotation.json path/to/myGeoJSON.geosjon | ||
allmaps transform geojson -a /path/to/annotation.json path/to/myGeoJSON.geosjon | ||
``` | ||
```sh | ||
allmaps transform geojson -g path/to/gcpFile.json -t thinPlateSpline path/to/myGeoJSON.geosjon | ||
allmaps transform geojson -g path/to/gcps.txt -t thinPlateSpline /path/to/myGeoJSON.geosjon | ||
``` | ||
@@ -200,3 +193,3 @@ | ||
Transform SVG resource masks of input Georeference Annotations to GeoJSON using a transformation built from the GCPs and transformation type specified in a Georeference Annotation itself. | ||
Transform SVG resource masks of input Georeference Annotations forward to GeoJSO Polygon using a transformation built from the GCPs and transformation type specified in a Georeference Annotation itself. | ||
@@ -215,17 +208,19 @@ This is a faster alternative for 'transform svg' where the resource mask from the Georeference Annotation specified in the arguments is also the input SVG. | ||
All the commands above (except `resource-mask`) accept the following options for specifying the transformations: | ||
All the commands above accept the following options for specifying the transformations: | ||
| Option | Description | Default | | ||
| :------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | | ||
| `-i, --inverse` | Compute backward ("inverse") transformation | | | ||
| `-g, --gcps <filename>` | Filename of GCPs. This overwrites the GCPs in the annotation argument if such is also used. | | | ||
| `-t, --transformationType <transformationType>` | Transformation Type. One of `helmert`, `polynomial`, `thinPlateSpline`, `projective`. This overwrites the transformation type in the annotation argument if such is also used. | `polynomial` | | ||
| `-o, --transformationOrder <transformationOrder>` | Order of polynomial transformation. One of `1`, `2` or `3`. | `1` | | ||
| Option | Description | Default | | ||
| :----------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------- | | ||
| `-i, --inverse` | Compute backward ("inverse") transformation | | | ||
| `-g, --gcps <filename>` | Filename of GCPs. This overwrites the GCPs in the annotation argument if such is also used. | | | ||
| `-t, --transformation-type <transformationType>` | Transformation type. One of `helmert`, `polynomial`, `thinPlateSpline`, `projective`. This overwrites the transformation type in the annotation argument if such is also used. | `polynomial` | | ||
| `-o, --polynomial-order <transformationOrder>` | Order of polynomial transformation. Either 1, 2 or 3.' | `1` | | ||
All the commands above (except `position`) accept the following options for transforming lines or polygons: | ||
All the commands above (except `position`) accept the following options for transforming lines or polygons in a more granular way (see [@allmaps/transform](../../apps/transform/) for more details): | ||
| Option | Description | Default | | ||
| :-------------------------------- | :-------------------------------------------------------------- | :------ | | ||
| `-p, --max-offset-ratio <number>` | Maximum offset ratio between original and transformed midpoints | `0` | | ||
| `-d, --max-depth <number>` | Maximum recursion depth | `6` | | ||
| Option | Description | Default | | ||
| :-------------------------------- | :----------------------------------------------------------------------- | :------------------------------------------------------ | | ||
| `-p, --max-offset-ratio <number>` | Maximum offset ratio between original and transformed midpoints | `0` | | ||
| `-d, --max-depth <number>` | Maximum recursion depth | `6` | | ||
| `--source-is-geographic` | Use geographic distances and midpoints for lon-lat source positions | `false` (`true` for `geojson` command) | | ||
| `--destination-is-geographic` | Use geographic distances and midpoints for lon-lat destination positions | `false` (`true` for `svg` and `resource-mask` commands) | | ||
@@ -239,2 +234,3 @@ ### Parse and generate IIIF resources | ||
``` | ||
Parse IIIF resources and output them in the format used internally by Allmaps: | ||
@@ -254,5 +250,27 @@ | ||
### Generate Allmaps IDs | ||
Allmaps CLI can generate Allmaps IDs for input strings. | ||
Show help: | ||
```sh | ||
allmaps id --help | ||
``` | ||
Generate the Allmaps ID for a IIIF Manifest URL: | ||
```sh | ||
allmaps id https://digital.zlb.de/viewer/api/v1/records/34231682/manifest/ | ||
``` | ||
Using the same URL, but using standard input: | ||
```sh | ||
echo https://digital.zlb.de/viewer/api/v1/records/34231682/manifest/ | allmaps id | ||
``` | ||
## Examples | ||
### Turn masks of georeferenced maps into GeoJSON | ||
### Turn resource masks of georeferenced maps into GeoJSON | ||
@@ -281,3 +299,3 @@ Manifest URL: | ||
Georef Annotations: | ||
Georeference Annotations: | ||
@@ -284,0 +302,0 @@ - https://annotations.allmaps.org/?url=https://collections.leventhalmap.org/search/commonwealth:4t64k3596/manifest |
66319
11
62
1352
315
8
+ Added@allmaps/id@^1.0.0-beta.17
+ Added@allmaps/id@1.0.0-beta.30(transitive)
- Removedsvg-parser@^2.0.4