@basemaps/cli-raster 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/build/bin.d.ts +1 -0
- package/build/bin.js +16 -0
- package/build/bin.js.map +1 -0
- package/build/cogify/__test__/covering.test.d.ts +1 -0
- package/build/cogify/__test__/covering.test.js +64 -0
- package/build/cogify/__test__/covering.test.js.map +1 -0
- package/build/cogify/__test__/extract.test.d.ts +1 -0
- package/build/cogify/__test__/extract.test.js +34 -0
- package/build/cogify/__test__/extract.test.js.map +1 -0
- package/build/cogify/cli/__test__/cli.cover.test.d.ts +1 -0
- package/build/cogify/cli/__test__/cli.cover.test.js +48 -0
- package/build/cogify/cli/__test__/cli.cover.test.js.map +1 -0
- package/build/cogify/cli/__test__/cli.topo.test.d.ts +1 -0
- package/build/cogify/cli/__test__/cli.topo.test.js +53 -0
- package/build/cogify/cli/__test__/cli.topo.test.js.map +1 -0
- package/build/cogify/cli/cli.cog.d.ts +55 -0
- package/build/cogify/cli/cli.cog.js +353 -0
- package/build/cogify/cli/cli.cog.js.map +1 -0
- package/build/cogify/cli/cli.cover.d.ts +30 -0
- package/build/cogify/cli/cli.cover.js +157 -0
- package/build/cogify/cli/cli.cover.js.map +1 -0
- package/build/cogify/cli/cli.topo.d.ts +50 -0
- package/build/cogify/cli/cli.topo.js +169 -0
- package/build/cogify/cli/cli.topo.js.map +1 -0
- package/build/cogify/cli/cli.validate.d.ts +15 -0
- package/build/cogify/cli/cli.validate.js +30 -0
- package/build/cogify/cli/cli.validate.js.map +1 -0
- package/build/cogify/cli.d.ts +121 -0
- package/build/cogify/cli.js +15 -0
- package/build/cogify/cli.js.map +1 -0
- package/build/cogify/covering/covering.d.ts +28 -0
- package/build/cogify/covering/covering.js +126 -0
- package/build/cogify/covering/covering.js.map +1 -0
- package/build/cogify/covering/cutline.d.ts +29 -0
- package/build/cogify/covering/cutline.js +109 -0
- package/build/cogify/covering/cutline.js.map +1 -0
- package/build/cogify/covering/tile.cover.d.ts +38 -0
- package/build/cogify/covering/tile.cover.js +198 -0
- package/build/cogify/covering/tile.cover.js.map +1 -0
- package/build/cogify/gdal/gdal.command.d.ts +27 -0
- package/build/cogify/gdal/gdal.command.js +175 -0
- package/build/cogify/gdal/gdal.command.js.map +1 -0
- package/build/cogify/gdal/gdal.runner.d.ts +42 -0
- package/build/cogify/gdal/gdal.runner.js +194 -0
- package/build/cogify/gdal/gdal.runner.js.map +1 -0
- package/build/cogify/stac.d.ts +138 -0
- package/build/cogify/stac.js +25 -0
- package/build/cogify/stac.js.map +1 -0
- package/build/cogify/topo/extract.d.ts +82 -0
- package/build/cogify/topo/extract.js +195 -0
- package/build/cogify/topo/extract.js.map +1 -0
- package/build/cogify/topo/slug.d.ts +10 -0
- package/build/cogify/topo/slug.js +18 -0
- package/build/cogify/topo/slug.js.map +1 -0
- package/build/cogify/topo/stac.creation.d.ts +47 -0
- package/build/cogify/topo/stac.creation.js +171 -0
- package/build/cogify/topo/stac.creation.js.map +1 -0
- package/build/download.d.ts +57 -0
- package/build/download.js +144 -0
- package/build/download.js.map +1 -0
- package/build/hash.stream.d.ts +20 -0
- package/build/hash.stream.js +62 -0
- package/build/hash.stream.js.map +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +2 -0
- package/build/index.js.map +1 -0
- package/build/preset.d.ts +15 -0
- package/build/preset.js +66 -0
- package/build/preset.js.map +1 -0
- package/dist/index.cjs +85880 -0
- package/package.json +61 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { loadTiffsFromPaths } from '@basemaps/config-loader/build/json/tiff.config.js';
|
|
2
|
+
import { Bounds } from '@basemaps/geo';
|
|
3
|
+
import { fsa } from '@basemaps/shared';
|
|
4
|
+
import { getLogger, isArgo, logArguments, Url, UrlFolder } from '@basemaps/shared';
|
|
5
|
+
import { CliInfo } from '@basemaps/shared/build/cli/info.js';
|
|
6
|
+
import { boolean, command, flag, option, optional, restPositionals, string } from 'cmd-ts';
|
|
7
|
+
import pLimit from 'p-limit';
|
|
8
|
+
import { brokenTiffs, extractLatestTiffItemsByMapCode, extractTiffItemsByEpsg } from '../topo/extract.js';
|
|
9
|
+
import { mapEpsgToSlug } from '../topo/slug.js';
|
|
10
|
+
import { createStacCollection, createStacItems, writeStacFiles } from '../topo/stac.creation.js';
|
|
11
|
+
const Q = pLimit(10);
|
|
12
|
+
const MapSeriesTitle = {
|
|
13
|
+
topo50: 'Raster Topographic Maps 50k',
|
|
14
|
+
topo250: 'Raster Topographic Maps 250k',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Parses a source path directory topographic maps tiffs and writes out a directory structure
|
|
18
|
+
* of StacItem and StacCollection files to the target path.
|
|
19
|
+
*
|
|
20
|
+
* @param source: Location of the source files
|
|
21
|
+
* @example s3://linz-topographic-upload/topographic/TopoReleaseArchive/NZTopo50_GeoTif_Gridless/
|
|
22
|
+
*
|
|
23
|
+
* @param target: Location of the target path
|
|
24
|
+
*/
|
|
25
|
+
export const TopoStacCreationCommand = command({
|
|
26
|
+
name: 'cogify-topo-stac',
|
|
27
|
+
version: CliInfo.version,
|
|
28
|
+
description: 'List input topographic map files, create StacItems, and generate tiles for grouping.',
|
|
29
|
+
args: {
|
|
30
|
+
...logArguments,
|
|
31
|
+
title: option({
|
|
32
|
+
type: optional(string),
|
|
33
|
+
long: 'title',
|
|
34
|
+
description: 'Imported imagery title, default is created from map series',
|
|
35
|
+
}),
|
|
36
|
+
target: option({
|
|
37
|
+
type: UrlFolder,
|
|
38
|
+
long: 'target',
|
|
39
|
+
description: 'Target location for the output files',
|
|
40
|
+
}),
|
|
41
|
+
mapSeries: option({
|
|
42
|
+
type: string,
|
|
43
|
+
long: 'map-series',
|
|
44
|
+
description: 'Map series name, topo50, or topo250',
|
|
45
|
+
}),
|
|
46
|
+
latestOnly: flag({
|
|
47
|
+
type: boolean,
|
|
48
|
+
defaultValue: () => false,
|
|
49
|
+
long: 'latest-only',
|
|
50
|
+
description: 'Only process the latest version of each map sheet',
|
|
51
|
+
defaultValueIsSerializable: true,
|
|
52
|
+
}),
|
|
53
|
+
output: option({
|
|
54
|
+
type: optional(Url),
|
|
55
|
+
description: 'Output informational assets to specific location',
|
|
56
|
+
long: 'output',
|
|
57
|
+
}),
|
|
58
|
+
paths: restPositionals({
|
|
59
|
+
type: UrlFolder,
|
|
60
|
+
description: 'Location of the source files',
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
async handler(args) {
|
|
64
|
+
const logger = getLogger(this, args, 'cli-raster');
|
|
65
|
+
const startTime = performance.now();
|
|
66
|
+
logger.info('TopoCogify:Start');
|
|
67
|
+
const title = args.title ?? MapSeriesTitle[args.mapSeries];
|
|
68
|
+
if (title == null) {
|
|
69
|
+
throw new Error('--title must be defined if map series is not one of :' + Object.keys(MapSeriesTitle).join(', '));
|
|
70
|
+
}
|
|
71
|
+
const ctx = {
|
|
72
|
+
latestOnly: args.latestOnly,
|
|
73
|
+
paths: args.paths,
|
|
74
|
+
target: args.target,
|
|
75
|
+
title,
|
|
76
|
+
mapSeries: args.mapSeries,
|
|
77
|
+
output: args.output,
|
|
78
|
+
logger,
|
|
79
|
+
};
|
|
80
|
+
const { epsgDirectoryPaths, stacItemPaths } = await loadTiffsToCreateStacs(ctx);
|
|
81
|
+
if (epsgDirectoryPaths.length === 0 || stacItemPaths.length === 0)
|
|
82
|
+
throw new Error('No Stac items created');
|
|
83
|
+
// write stac items into an JSON array
|
|
84
|
+
if (args.output || isArgo()) {
|
|
85
|
+
const targetUrl = args.output ?? fsa.toUrl('/tmp/topo-stac-creation/');
|
|
86
|
+
// for create-config: we need to tell create-config to create a bundled config for each epsg folder (latest only).
|
|
87
|
+
// workflow: will loop 'targets.json' and create a node for each path where each node's job is to create a bundled config.
|
|
88
|
+
await fsa.write(new URL('targets.json', targetUrl), JSON.stringify(epsgDirectoryPaths, null, 2));
|
|
89
|
+
// tiles.json makes the tiff files
|
|
90
|
+
await fsa.write(new URL('tiles.json', targetUrl), JSON.stringify(stacItemPaths.map((m) => ({ path: m })), null, 2));
|
|
91
|
+
await fsa.write(new URL('broken-tiffs.json', targetUrl), JSON.stringify(brokenTiffs, null, 2));
|
|
92
|
+
}
|
|
93
|
+
logger.info({ duration: performance.now() - startTime }, 'TopoCogify:Done');
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* @param source: Source directory URL from which to load tiff files
|
|
98
|
+
*
|
|
99
|
+
* @param target: Destination directory URL into which to save the STAC collection and item JSON files
|
|
100
|
+
*
|
|
101
|
+
* @param title: The title of the collection
|
|
102
|
+
* @example "New Zealand Topo50 Map Series (Gridless)"
|
|
103
|
+
*
|
|
104
|
+
* @returns an array of StacItem objects
|
|
105
|
+
*/
|
|
106
|
+
async function loadTiffsToCreateStacs(ctx) {
|
|
107
|
+
const logger = ctx.logger;
|
|
108
|
+
const source = ctx.paths;
|
|
109
|
+
const target = ctx.target;
|
|
110
|
+
logger?.info({ source }, 'LoadTiffs:Start');
|
|
111
|
+
// extract all file paths from the source directory and convert them into URLs
|
|
112
|
+
const fileUrls = [];
|
|
113
|
+
for (const sourcePath of ctx.paths) {
|
|
114
|
+
for await (const path of fsa.list(sourcePath)) {
|
|
115
|
+
fileUrls.push(path);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// process all of the URLs into Tiffs
|
|
119
|
+
const tiffs = await loadTiffsFromPaths(fileUrls, Q);
|
|
120
|
+
if (tiffs.length === 0)
|
|
121
|
+
throw new Error('No TIFF files found in locations : ' + source.map((m) => m.href).join(', '));
|
|
122
|
+
logger.info({ count: tiffs.length, hrefs: source.map((m) => m.href) }, 'LoadTiffs:End');
|
|
123
|
+
logger.info('ExtractTiffs:Start');
|
|
124
|
+
const allTiffItems = extractTiffItemsByEpsg(tiffs, logger);
|
|
125
|
+
logger.info({ foundEpsgs: [...allTiffItems.keys()] }, 'ExtractTiffs:End');
|
|
126
|
+
const epsgDirectoryPaths = [];
|
|
127
|
+
const stacItemPaths = [];
|
|
128
|
+
// create and write stac items and collections
|
|
129
|
+
const scale = ctx.mapSeries;
|
|
130
|
+
// TODO: resolution is defined from the GSD over the map scale,
|
|
131
|
+
// and can be extracted in the future if we want to process higher resolution maps
|
|
132
|
+
const resolution = 'gridless_600dpi';
|
|
133
|
+
for (const [epsg, tiffItems] of allTiffItems.entries()) {
|
|
134
|
+
logger?.info({ epsg }, 'CreateStacFiles:Start');
|
|
135
|
+
// identify latest tiff items
|
|
136
|
+
const latestTiffItems = extractLatestTiffItemsByMapCode(tiffItems);
|
|
137
|
+
// create stac items
|
|
138
|
+
const stacItems = createStacItems(scale, resolution, tiffItems, latestTiffItems, logger);
|
|
139
|
+
// convert epsg to slug
|
|
140
|
+
const epsgSlug = mapEpsgToSlug(epsg.code);
|
|
141
|
+
if (epsgSlug == null)
|
|
142
|
+
throw new Error(`Failed to map epsg code '${epsg.code}' to a slug`);
|
|
143
|
+
const linzSlug = `${scale}-${epsgSlug}`;
|
|
144
|
+
// extract bounds
|
|
145
|
+
const allBounds = tiffItems.map((item) => item.bounds);
|
|
146
|
+
const latestBounds = Array.from(latestTiffItems.values()).map((item) => item.bounds);
|
|
147
|
+
// create stac collections
|
|
148
|
+
const title = ctx.title;
|
|
149
|
+
const collection = createStacCollection(title, linzSlug, epsg, Bounds.union(allBounds), stacItems.all, logger);
|
|
150
|
+
const latestCollection = createStacCollection(title, linzSlug, epsg, Bounds.union(latestBounds), stacItems.latest, logger);
|
|
151
|
+
logger?.info({ epsg }, 'CreateStacFiles:End');
|
|
152
|
+
// Write all stac items and collections
|
|
153
|
+
if (!ctx.latestOnly) {
|
|
154
|
+
const allTargetURL = new URL(`${scale}/${resolution}/${epsg.code}/`, target);
|
|
155
|
+
logger?.info({ epsg, target: allTargetURL.href }, 'WriteStacFiles:Start');
|
|
156
|
+
const allPaths = await writeStacFiles(allTargetURL, stacItems.all, collection, logger);
|
|
157
|
+
stacItemPaths.push(...allPaths.items);
|
|
158
|
+
}
|
|
159
|
+
// Write latest stac items and collections
|
|
160
|
+
const latestTargetURL = new URL(`${scale}_latest/${resolution}/${epsg.code}/`, target);
|
|
161
|
+
epsgDirectoryPaths.push({ epsg: epsg.code.toString(), url: latestTargetURL });
|
|
162
|
+
logger?.info({ epsg, target: latestTargetURL.href }, 'WriteStacFiles:Start');
|
|
163
|
+
const latestPaths = await writeStacFiles(latestTargetURL, stacItems.latest, latestCollection, logger);
|
|
164
|
+
stacItemPaths.push(...latestPaths.items);
|
|
165
|
+
logger?.info({ epsg }, 'WriteStacFiles:End');
|
|
166
|
+
}
|
|
167
|
+
return { epsgDirectoryPaths, stacItemPaths };
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=cli.topo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.topo.js","sourceRoot":"","sources":["../../../src/cogify/cli/cli.topo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const BasemapsCogifyValidateCommand: Partial<import("cmd-ts/dist/cjs/argparser.js").Register> & {
|
|
2
|
+
parse(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
|
|
3
|
+
concurrency: number;
|
|
4
|
+
tiffs: URL[];
|
|
5
|
+
verbose: boolean;
|
|
6
|
+
extraVerbose: boolean;
|
|
7
|
+
}>>;
|
|
8
|
+
} & import("cmd-ts/dist/cjs/helpdoc.js").PrintHelp & import("cmd-ts/dist/cjs/helpdoc.js").ProvidesHelp & import("cmd-ts/dist/cjs/helpdoc.js").Named & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Versioned> & import("cmd-ts/dist/cjs/argparser.js").Register & import("cmd-ts/dist/cjs/runner.js").Handling<{
|
|
9
|
+
concurrency: number;
|
|
10
|
+
tiffs: URL[];
|
|
11
|
+
verbose: boolean;
|
|
12
|
+
extraVerbose: boolean;
|
|
13
|
+
}, Promise<void>> & {
|
|
14
|
+
run(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<Promise<void>>>;
|
|
15
|
+
} & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Versioned & import("cmd-ts/dist/cjs/helpdoc.js").Descriptive & import("cmd-ts/dist/cjs/helpdoc.js").Aliased>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getLogger, logArguments, Url } from '@basemaps/shared';
|
|
2
|
+
import { CliInfo } from '@basemaps/shared/build/cli/info.js';
|
|
3
|
+
import { command, number, option, restPositionals } from 'cmd-ts';
|
|
4
|
+
import pLimit from 'p-limit';
|
|
5
|
+
import { validateOutputTiff } from './cli.cog.js';
|
|
6
|
+
export const BasemapsCogifyValidateCommand = command({
|
|
7
|
+
name: 'cogify-validate',
|
|
8
|
+
version: CliInfo.version,
|
|
9
|
+
description: 'Validate a COG is created in a way basemaps likes',
|
|
10
|
+
args: {
|
|
11
|
+
...logArguments,
|
|
12
|
+
concurrency: option({
|
|
13
|
+
type: number,
|
|
14
|
+
long: 'concurrency',
|
|
15
|
+
description: 'How many COGs to initialise at once',
|
|
16
|
+
defaultValue: () => 25,
|
|
17
|
+
defaultValueIsSerializable: true,
|
|
18
|
+
}),
|
|
19
|
+
tiffs: restPositionals({ type: Url, displayName: 'paths', description: 'COG to validate' }),
|
|
20
|
+
},
|
|
21
|
+
async handler(args) {
|
|
22
|
+
const logger = getLogger(this, args, 'cli-raster');
|
|
23
|
+
const q = pLimit(args.concurrency);
|
|
24
|
+
const promises = args.tiffs.map((tiff) => {
|
|
25
|
+
return q(() => validateOutputTiff(tiff, undefined, logger));
|
|
26
|
+
});
|
|
27
|
+
await Promise.allSettled(promises);
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=cli.validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.validate.js","sourceRoot":"","sources":["../../../src/cogify/cli/cli.validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAClE,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,CAAC,MAAM,6BAA6B,GAAG,OAAO,CAAC;IACnD,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,OAAO,CAAC,OAAO;IACxB,WAAW,EAAE,mDAAmD;IAChE,IAAI,EAAE;QACJ,GAAG,YAAY;QACf,WAAW,EAAE,MAAM,CAAC;YAClB,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,WAAW,EAAE,qCAAqC;YAClD,YAAY,EAAE,GAAG,EAAE,CAAC,EAAE;YACtB,0BAA0B,EAAE,IAAI;SACjC,CAAC;QACF,KAAK,EAAE,eAAe,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;KAC5F;IAED,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAEnC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACvC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export declare const CogifyCli: Partial<import("cmd-ts/dist/cjs/argparser.js").Register> & {
|
|
2
|
+
parse(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
|
|
3
|
+
command: "cover";
|
|
4
|
+
args: {
|
|
5
|
+
target: URL;
|
|
6
|
+
cutline: URL | undefined;
|
|
7
|
+
cutlineBlend: number;
|
|
8
|
+
paths: URL[];
|
|
9
|
+
preset: string;
|
|
10
|
+
tileMatrix: string;
|
|
11
|
+
baseZoomOffset: number | undefined;
|
|
12
|
+
requireStacCollection: boolean;
|
|
13
|
+
background: import("@basemaps/config").Rgba | undefined;
|
|
14
|
+
verbose: boolean;
|
|
15
|
+
extraVerbose: boolean;
|
|
16
|
+
};
|
|
17
|
+
} | {
|
|
18
|
+
command: "create";
|
|
19
|
+
args: {
|
|
20
|
+
path: URL[];
|
|
21
|
+
force: boolean;
|
|
22
|
+
concurrency: number;
|
|
23
|
+
docker: boolean;
|
|
24
|
+
fromFile: URL[] | undefined;
|
|
25
|
+
verbose: boolean;
|
|
26
|
+
extraVerbose: boolean;
|
|
27
|
+
};
|
|
28
|
+
} | {
|
|
29
|
+
command: "validate";
|
|
30
|
+
args: {
|
|
31
|
+
concurrency: number;
|
|
32
|
+
tiffs: URL[];
|
|
33
|
+
verbose: boolean;
|
|
34
|
+
extraVerbose: boolean;
|
|
35
|
+
};
|
|
36
|
+
} | {
|
|
37
|
+
command: "topo";
|
|
38
|
+
args: {
|
|
39
|
+
title: string | undefined;
|
|
40
|
+
target: URL;
|
|
41
|
+
mapSeries: string;
|
|
42
|
+
latestOnly: boolean;
|
|
43
|
+
output: URL | undefined;
|
|
44
|
+
paths: URL[];
|
|
45
|
+
verbose: boolean;
|
|
46
|
+
extraVerbose: boolean;
|
|
47
|
+
};
|
|
48
|
+
}>>;
|
|
49
|
+
} & import("cmd-ts/dist/cjs/helpdoc.js").Named & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Descriptive & import("cmd-ts/dist/cjs/helpdoc.js").Versioned> & import("cmd-ts/dist/cjs/helpdoc.js").PrintHelp & Partial<import("cmd-ts/dist/cjs/helpdoc.js").Versioned> & import("cmd-ts/dist/cjs/argparser.js").Register & import("cmd-ts/dist/cjs/runner.js").Handling<{
|
|
50
|
+
command: "cover";
|
|
51
|
+
args: {
|
|
52
|
+
target: URL;
|
|
53
|
+
cutline: URL | undefined;
|
|
54
|
+
cutlineBlend: number;
|
|
55
|
+
paths: URL[];
|
|
56
|
+
preset: string;
|
|
57
|
+
tileMatrix: string;
|
|
58
|
+
baseZoomOffset: number | undefined;
|
|
59
|
+
requireStacCollection: boolean;
|
|
60
|
+
background: import("@basemaps/config").Rgba | undefined;
|
|
61
|
+
verbose: boolean;
|
|
62
|
+
extraVerbose: boolean;
|
|
63
|
+
};
|
|
64
|
+
} | {
|
|
65
|
+
command: "create";
|
|
66
|
+
args: {
|
|
67
|
+
path: URL[];
|
|
68
|
+
force: boolean;
|
|
69
|
+
concurrency: number;
|
|
70
|
+
docker: boolean;
|
|
71
|
+
fromFile: URL[] | undefined;
|
|
72
|
+
verbose: boolean;
|
|
73
|
+
extraVerbose: boolean;
|
|
74
|
+
};
|
|
75
|
+
} | {
|
|
76
|
+
command: "validate";
|
|
77
|
+
args: {
|
|
78
|
+
concurrency: number;
|
|
79
|
+
tiffs: URL[];
|
|
80
|
+
verbose: boolean;
|
|
81
|
+
extraVerbose: boolean;
|
|
82
|
+
};
|
|
83
|
+
} | {
|
|
84
|
+
command: "topo";
|
|
85
|
+
args: {
|
|
86
|
+
title: string | undefined;
|
|
87
|
+
target: URL;
|
|
88
|
+
mapSeries: string;
|
|
89
|
+
latestOnly: boolean;
|
|
90
|
+
output: URL | undefined;
|
|
91
|
+
paths: URL[];
|
|
92
|
+
verbose: boolean;
|
|
93
|
+
extraVerbose: boolean;
|
|
94
|
+
};
|
|
95
|
+
}, {
|
|
96
|
+
command: "cover";
|
|
97
|
+
value: Promise<void>;
|
|
98
|
+
} | {
|
|
99
|
+
command: "create";
|
|
100
|
+
value: Promise<void>;
|
|
101
|
+
} | {
|
|
102
|
+
command: "validate";
|
|
103
|
+
value: Promise<void>;
|
|
104
|
+
} | {
|
|
105
|
+
command: "topo";
|
|
106
|
+
value: Promise<void>;
|
|
107
|
+
}> & {
|
|
108
|
+
run(context: import("cmd-ts/dist/cjs/argparser.js").ParseContext): Promise<import("cmd-ts/dist/cjs/argparser.js").ParsingResult<{
|
|
109
|
+
command: "cover";
|
|
110
|
+
value: Promise<void>;
|
|
111
|
+
} | {
|
|
112
|
+
command: "create";
|
|
113
|
+
value: Promise<void>;
|
|
114
|
+
} | {
|
|
115
|
+
command: "validate";
|
|
116
|
+
value: Promise<void>;
|
|
117
|
+
} | {
|
|
118
|
+
command: "topo";
|
|
119
|
+
value: Promise<void>;
|
|
120
|
+
}>>;
|
|
121
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { subcommands } from 'cmd-ts';
|
|
2
|
+
import { BasemapsCogifyCreateCommand } from './cli/cli.cog.js';
|
|
3
|
+
import { BasemapsCogifyCoverCommand } from './cli/cli.cover.js';
|
|
4
|
+
import { TopoStacCreationCommand } from './cli/cli.topo.js';
|
|
5
|
+
import { BasemapsCogifyValidateCommand } from './cli/cli.validate.js';
|
|
6
|
+
export const CogifyCli = subcommands({
|
|
7
|
+
name: 'cogify',
|
|
8
|
+
cmds: {
|
|
9
|
+
cover: BasemapsCogifyCoverCommand,
|
|
10
|
+
create: BasemapsCogifyCreateCommand,
|
|
11
|
+
validate: BasemapsCogifyValidateCommand,
|
|
12
|
+
topo: TopoStacCreationCommand,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cogify/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,6BAA6B,EAAE,MAAM,uBAAuB,CAAC;AAEtE,MAAM,CAAC,MAAM,SAAS,GAAG,WAAW,CAAC;IACnC,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE;QACJ,KAAK,EAAE,0BAA0B;QACjC,MAAM,EAAE,2BAA2B;QACnC,QAAQ,EAAE,6BAA6B;QACvC,IAAI,EAAE,uBAAuB;KAC9B;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Tile, TileMatrixSet } from '@basemaps/geo';
|
|
2
|
+
import { LogType } from '@basemaps/shared';
|
|
3
|
+
import { MultiPolygon } from '@linzjs/geojson';
|
|
4
|
+
import { Metrics } from '@linzjs/metrics';
|
|
5
|
+
import { CutlineOptimizer } from '../covering/cutline.js';
|
|
6
|
+
export declare function addChildren(tile: Tile, todo?: Tile[]): Tile[];
|
|
7
|
+
export declare function addSurrounding(tile: Tile, tileMatrix: TileMatrixSet, todo?: Tile[], seen?: Set<string>): Tile[];
|
|
8
|
+
export interface CoveringContext {
|
|
9
|
+
/** Tile matrix to cover with */
|
|
10
|
+
tileMatrix: TileMatrixSet;
|
|
11
|
+
/** MultiPolygon to cover */
|
|
12
|
+
source: MultiPolygon;
|
|
13
|
+
/** Zoom to start covering at */
|
|
14
|
+
targetZoom: number;
|
|
15
|
+
/** Zoom level the imagery is optimized */
|
|
16
|
+
baseZoom: number;
|
|
17
|
+
/** Min amount of a tile to be covered before using a smaller tiler @default 0.25 (25%) */
|
|
18
|
+
minCoveragePercent?: number;
|
|
19
|
+
/** maximal difference between output tiles and input tiles, @default +2 */
|
|
20
|
+
maxZoomDifference?: number;
|
|
21
|
+
/** Cutline to apply */
|
|
22
|
+
cutline: CutlineOptimizer;
|
|
23
|
+
/** Optional metrics provider to track how long actions take */
|
|
24
|
+
metrics?: Metrics;
|
|
25
|
+
/** Optional logger to trace covering creation */
|
|
26
|
+
logger?: LogType;
|
|
27
|
+
}
|
|
28
|
+
export declare function createCovering(ctx: CoveringContext): Tile[];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Simplify, TileId } from '@basemaps/geo';
|
|
2
|
+
import { Area, intersection } from '@linzjs/geojson';
|
|
3
|
+
/**
|
|
4
|
+
* Tile offsets for surrounding tiles
|
|
5
|
+
* In the order North, West, South, East
|
|
6
|
+
*/
|
|
7
|
+
const SurroundingTiles = [
|
|
8
|
+
{ x: 0, y: -1 }, // North
|
|
9
|
+
{ x: 1, y: 0 }, // West
|
|
10
|
+
{ x: 0, y: 1 }, // South
|
|
11
|
+
{ x: -1, y: 0 }, // East
|
|
12
|
+
];
|
|
13
|
+
export function addChildren(tile, todo = []) {
|
|
14
|
+
const childZ = tile.z + 1;
|
|
15
|
+
const xOffset = tile.x * 2;
|
|
16
|
+
const yOffset = tile.y * 2;
|
|
17
|
+
todo.push({ z: childZ, x: xOffset, y: yOffset }); //top left
|
|
18
|
+
todo.push({ z: childZ, x: xOffset + 1, y: yOffset }); // top right
|
|
19
|
+
todo.push({ z: childZ, x: xOffset, y: yOffset + 1 }); // bottom left
|
|
20
|
+
todo.push({ z: childZ, x: xOffset + 1, y: yOffset + 1 }); // bottom right
|
|
21
|
+
return todo;
|
|
22
|
+
}
|
|
23
|
+
export function addSurrounding(tile, tileMatrix, todo = [], seen) {
|
|
24
|
+
// Zoom should not have surrounding tiles
|
|
25
|
+
if (tile.z === 0)
|
|
26
|
+
return [];
|
|
27
|
+
for (const sr of SurroundingTiles) {
|
|
28
|
+
// TODO this should loop around if it goes outside the bounds of the tile matrix eg crossing the antimeridian
|
|
29
|
+
const nextTile = { z: tile.z, x: sr.x + tile.x, y: sr.y + tile.y };
|
|
30
|
+
const tileZoom = tileMatrix.zooms[tile.z];
|
|
31
|
+
if (nextTile.x >= tileZoom.matrixWidth)
|
|
32
|
+
nextTile.x = nextTile.x - tileZoom.matrixWidth;
|
|
33
|
+
if (nextTile.x < 0)
|
|
34
|
+
nextTile.x = nextTile.x + tileZoom.matrixWidth;
|
|
35
|
+
if (nextTile.y >= tileZoom.matrixHeight)
|
|
36
|
+
nextTile.y = nextTile.y - tileZoom.matrixHeight;
|
|
37
|
+
if (nextTile.y < 0)
|
|
38
|
+
nextTile.y = nextTile.y + tileZoom.matrixHeight;
|
|
39
|
+
if (seen) {
|
|
40
|
+
const tileId = TileId.fromTile(nextTile);
|
|
41
|
+
if (seen?.has(tileId))
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
todo.push(nextTile);
|
|
45
|
+
}
|
|
46
|
+
return todo;
|
|
47
|
+
}
|
|
48
|
+
export function createCovering(ctx) {
|
|
49
|
+
const minCoveragePercent = ctx.minCoveragePercent ?? 0.25;
|
|
50
|
+
const maxZoomDifference = ctx.maxZoomDifference ?? 2;
|
|
51
|
+
const outputTiles = [];
|
|
52
|
+
const tileMatrix = ctx.tileMatrix;
|
|
53
|
+
// Reduce the complexity of the cutline applied polygon to same as one pixel at the covering resolution
|
|
54
|
+
// this is a approximation and can cause some false positive/negative tiles.
|
|
55
|
+
// but signficiantly improves performance of finding tile covers
|
|
56
|
+
ctx.metrics?.start('cutline:simplify');
|
|
57
|
+
const onePixelAtOverviewResolution = ctx.tileMatrix.pixelScale(ctx.targetZoom);
|
|
58
|
+
const onePixelAtBaseResolution = ctx.tileMatrix.pixelScale(ctx.baseZoom);
|
|
59
|
+
const cutBounds = Simplify.multiPolygon(ctx.cutline.cut(ctx.source), // Cut the source to the cutline
|
|
60
|
+
onePixelAtOverviewResolution, onePixelAtBaseResolution);
|
|
61
|
+
const cutlineDuration = ctx.metrics?.end('cutline:simplify');
|
|
62
|
+
// No imagery was left after the cutline
|
|
63
|
+
if (cutBounds == null)
|
|
64
|
+
throw new Error('Cutline excludes all imagery');
|
|
65
|
+
ctx.logger?.debug({ duration: cutlineDuration }, 'Cutline:Simplified');
|
|
66
|
+
// Tiles that have been seen before
|
|
67
|
+
const visited = new Set();
|
|
68
|
+
const todo = [];
|
|
69
|
+
// Find the tile that intersects with the top left point of each polygon and use that as the starting point
|
|
70
|
+
for (const poly of cutBounds) {
|
|
71
|
+
const topLeftPoint = poly[0][0];
|
|
72
|
+
const targetPx = tileMatrix.sourceToPixels(topLeftPoint[0], topLeftPoint[1], ctx.targetZoom);
|
|
73
|
+
const tile = tileMatrix.pixelsToTile(targetPx.x, targetPx.y, ctx.targetZoom);
|
|
74
|
+
const tileId = TileId.fromTile(tile);
|
|
75
|
+
if (visited.has(tileId))
|
|
76
|
+
continue;
|
|
77
|
+
todo.push(tile);
|
|
78
|
+
visited.add(tileId);
|
|
79
|
+
}
|
|
80
|
+
visited.clear();
|
|
81
|
+
const tilesByZoom = [];
|
|
82
|
+
let zoomDifference = 0;
|
|
83
|
+
while (todo.length > 0) {
|
|
84
|
+
const tile = todo.shift();
|
|
85
|
+
if (tile == null)
|
|
86
|
+
continue;
|
|
87
|
+
const tileId = TileId.fromTile(tile);
|
|
88
|
+
if (visited.has(tileId))
|
|
89
|
+
continue;
|
|
90
|
+
visited.add(tileId);
|
|
91
|
+
const tileSource = tileMatrix.tileToSourceBounds(tile);
|
|
92
|
+
const tileArea = tileSource.width * tileSource.height;
|
|
93
|
+
const tilePolygon = tileSource.toPolygon();
|
|
94
|
+
const tileIntersection = intersection(cutBounds, tilePolygon);
|
|
95
|
+
if (tileIntersection.length === 0)
|
|
96
|
+
continue;
|
|
97
|
+
// Determine how big of a intersection there is
|
|
98
|
+
const area = Area.multiPolygon(tileIntersection);
|
|
99
|
+
const areaPercent = area / tileArea;
|
|
100
|
+
// Is this tile a different zoom level to our target
|
|
101
|
+
const zDiff = tile.z - ctx.targetZoom;
|
|
102
|
+
// Count the number of pixels in the output tiff that would be used
|
|
103
|
+
const tileScale = tileSource.width / ctx.tileMatrix.tileSize;
|
|
104
|
+
const pixelCount = (tileArea * areaPercent) / tileScale;
|
|
105
|
+
if (areaPercent < minCoveragePercent && zDiff < maxZoomDifference) {
|
|
106
|
+
// Not enough coverage was found with this tile, use a more zoomed in tile and try again
|
|
107
|
+
addChildren(tile, todo);
|
|
108
|
+
}
|
|
109
|
+
else if (pixelCount > 1) {
|
|
110
|
+
zoomDifference = Math.max(zDiff, zoomDifference);
|
|
111
|
+
outputTiles.push(tile);
|
|
112
|
+
tilesByZoom[tile.z] = (tilesByZoom[tile.z] ?? 0) + 1;
|
|
113
|
+
ctx.logger?.debug({ tileId, areaPercent: Number((areaPercent * 100).toFixed(2)) }, 'Cover:Tile');
|
|
114
|
+
if (outputTiles.length % 25 === 0) {
|
|
115
|
+
ctx.logger?.info({ tiles: outputTiles.length, tilesByZoom }, 'Cover:Progress');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
ctx.logger?.debug({ pixelCount, tileId }, 'Cover:SkipTile');
|
|
120
|
+
}
|
|
121
|
+
if (zDiff === 0)
|
|
122
|
+
addSurrounding(tile, ctx.tileMatrix, todo, visited);
|
|
123
|
+
}
|
|
124
|
+
return outputTiles;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=covering.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"covering.js","sourceRoot":"","sources":["../../../src/cogify/covering/covering.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EpsgCode, Tile, TileMatrixSet } from '@basemaps/geo';
|
|
2
|
+
import { FeatureCollectionWithCrs, MultiPolygon } from '@linzjs/geojson';
|
|
3
|
+
import { CogifyLinkCutline } from '../stac.js';
|
|
4
|
+
export declare function loadCutline(path: URL): Promise<{
|
|
5
|
+
polygon: MultiPolygon;
|
|
6
|
+
projection: EpsgCode;
|
|
7
|
+
}>;
|
|
8
|
+
export declare class CutlineOptimizer {
|
|
9
|
+
/** Pad tiles by 200m when optimizing cutlines so blending will not be affected */
|
|
10
|
+
static TilePixelPadding: number;
|
|
11
|
+
path: URL | null;
|
|
12
|
+
cutline: MultiPolygon | null;
|
|
13
|
+
blend: number;
|
|
14
|
+
tileMatrix: TileMatrixSet;
|
|
15
|
+
constructor(path: URL | null, cutline: MultiPolygon | null, blend: number, tileMatrix: TileMatrixSet);
|
|
16
|
+
/** Cut a polygon to the cutline */
|
|
17
|
+
cut(poly: MultiPolygon): MultiPolygon;
|
|
18
|
+
/** Optimize a cutline for a tile, if it doesn't intersect then ignore the cutline */
|
|
19
|
+
optimize(tile: Tile): FeatureCollectionWithCrs | null;
|
|
20
|
+
/**
|
|
21
|
+
* Convert cutline to geojson FeatureCollection
|
|
22
|
+
*
|
|
23
|
+
* @returns GeoJSON FeatureCollection if cutline exists, null otherwise
|
|
24
|
+
*/
|
|
25
|
+
toGeoJson(): FeatureCollectionWithCrs | null;
|
|
26
|
+
static load(path: URL | undefined, blend: number, tileMatrix: TileMatrixSet): Promise<CutlineOptimizer>;
|
|
27
|
+
/** Create a cutline optimizer from a STAC cutline link */
|
|
28
|
+
static loadFromLink(l: CogifyLinkCutline | null, tileMatrix: TileMatrixSet): Promise<CutlineOptimizer>;
|
|
29
|
+
}
|