@cogeotiff/cli 7.2.1 → 8.0.1
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/CHANGELOG.md +4 -493
- package/README.md +26 -34
- package/bin/cogeotiff.js +1 -2
- package/build/action.util.d.ts +2 -8
- package/build/action.util.js +7 -18
- package/build/action.util.js.map +1 -0
- package/build/bin.d.ts +1 -0
- package/build/bin.js +13 -0
- package/build/bin.js.map +1 -0
- package/build/cli.table.d.ts +0 -1
- package/build/cli.table.js +9 -4
- package/build/cli.table.js.map +1 -0
- package/build/commands/dump.d.ts +27 -0
- package/build/commands/dump.js +132 -0
- package/build/commands/dump.js.map +1 -0
- package/build/commands/info.d.ts +19 -0
- package/build/commands/info.js +185 -0
- package/build/commands/info.js.map +1 -0
- package/build/common.d.ts +16 -0
- package/build/common.js +19 -0
- package/build/common.js.map +1 -0
- package/build/fs.d.ts +6 -0
- package/build/fs.js +17 -0
- package/build/fs.js.map +1 -0
- package/build/index.d.ts +57 -2
- package/build/index.js +11 -8
- package/build/index.js.map +1 -0
- package/build/log.d.ts +11 -0
- package/build/log.js +37 -0
- package/build/log.js.map +1 -0
- package/build/util.bytes.d.ts +0 -1
- package/build/util.bytes.js +1 -1
- package/build/util.bytes.js.map +1 -0
- package/build/util.tile.d.ts +2 -3
- package/build/util.tile.js +20 -18
- package/build/util.tile.js.map +1 -0
- package/package.json +37 -37
- package/src/action.util.ts +22 -37
- package/src/bin.ts +14 -0
- package/src/cli.table.ts +27 -27
- package/src/commands/dump.ts +156 -0
- package/src/commands/info.ts +199 -0
- package/src/common.ts +20 -0
- package/src/fs.ts +18 -0
- package/src/index.ts +10 -7
- package/src/log.ts +43 -0
- package/src/util.bytes.ts +6 -6
- package/src/util.tile.ts +33 -33
- package/tsconfig.json +8 -8
- package/build/action.dump.tile.d.ts +0 -24
- package/build/action.dump.tile.d.ts.map +0 -1
- package/build/action.dump.tile.js +0 -194
- package/build/action.info.d.ts +0 -10
- package/build/action.info.d.ts.map +0 -1
- package/build/action.info.js +0 -183
- package/build/action.tile.d.ts +0 -9
- package/build/action.tile.d.ts.map +0 -1
- package/build/action.tile.js +0 -64
- package/build/action.util.d.ts.map +0 -1
- package/build/cli.cog.info.d.ts +0 -9
- package/build/cli.cog.info.d.ts.map +0 -1
- package/build/cli.cog.info.js +0 -42
- package/build/cli.log.d.ts +0 -3
- package/build/cli.log.d.ts.map +0 -1
- package/build/cli.log.js +0 -4
- package/build/cli.table.d.ts.map +0 -1
- package/build/index.d.ts.map +0 -1
- package/build/util.bytes.d.ts.map +0 -1
- package/build/util.tile.d.ts.map +0 -1
- package/src/action.dump.tile.ts +0 -240
- package/src/action.info.ts +0 -205
- package/src/action.tile.ts +0 -76
- package/src/cli.cog.info.ts +0 -45
- package/src/cli.log.ts +0 -4
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { fsa } from '@chunkd/fs';
|
|
2
|
+
import { CogTiff, TiffTag, TiffTagGeo, TiffTagId, TiffTagValueType, TiffVersion, toHex } from '@cogeotiff/core';
|
|
3
|
+
import { CogTiffImage } from '@cogeotiff/core/src/cog.tiff.image.js';
|
|
4
|
+
import c from 'ansi-colors';
|
|
5
|
+
import { command, flag, option, optional, restPositionals } from 'cmd-ts';
|
|
6
|
+
import { ActionUtil, CliResultMap } from '../action.util.js';
|
|
7
|
+
import { CliTable } from '../cli.table.js';
|
|
8
|
+
import { DefaultArgs, Url } from '../common.js';
|
|
9
|
+
import { FetchLog } from '../fs.js';
|
|
10
|
+
import { ensureS3fs, setupLogger } from '../log.js';
|
|
11
|
+
import { toByteSizeString } from '../util.bytes.js';
|
|
12
|
+
|
|
13
|
+
function round(num: number): number {
|
|
14
|
+
const opt = 10 ** 4;
|
|
15
|
+
return Math.floor(num * opt) / opt;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const commandInfo = command({
|
|
19
|
+
name: 'info',
|
|
20
|
+
args: {
|
|
21
|
+
...DefaultArgs,
|
|
22
|
+
path: option({ short: 'f', long: 'file', type: optional(Url) }),
|
|
23
|
+
tags: flag({ short: 't', long: 'tags', description: 'Dump tiff Tags' }),
|
|
24
|
+
offsets: flag({ short: 'o', long: 'offsets', description: 'Load byte offsets too' }),
|
|
25
|
+
paths: restPositionals({ type: Url, description: 'Files to process' }),
|
|
26
|
+
},
|
|
27
|
+
async handler(args) {
|
|
28
|
+
const logger = setupLogger(args);
|
|
29
|
+
const paths = [...args.paths, args.path].filter((f) => f != null) as URL[];
|
|
30
|
+
|
|
31
|
+
for (const path of paths) {
|
|
32
|
+
if (path.protocol === 's3:') await ensureS3fs();
|
|
33
|
+
logger.debug('Tiff:load', { path: path?.href });
|
|
34
|
+
FetchLog.reset();
|
|
35
|
+
|
|
36
|
+
const source = fsa.source(path);
|
|
37
|
+
const tiff = await new CogTiff(source).init();
|
|
38
|
+
|
|
39
|
+
const header = [
|
|
40
|
+
{ key: 'Tiff type', value: `${TiffVersion[tiff.version]} (v${String(tiff.version)})` },
|
|
41
|
+
{
|
|
42
|
+
key: 'Bytes read',
|
|
43
|
+
value: `${toByteSizeString(FetchLog.bytesRead)} (${FetchLog.fetches.length} Chunk${
|
|
44
|
+
FetchLog.fetches.length === 1 ? '' : 's'
|
|
45
|
+
})`,
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const firstImage = tiff.images[0];
|
|
50
|
+
const isGeoLocated = firstImage.isGeoLocated;
|
|
51
|
+
const images = [
|
|
52
|
+
{ key: 'Compression', value: firstImage.compression },
|
|
53
|
+
isGeoLocated ? { key: 'Origin', value: firstImage.origin.map(round).join(', ') } : null,
|
|
54
|
+
isGeoLocated ? { key: 'Resolution', value: firstImage.resolution.map(round).join(', ') } : null,
|
|
55
|
+
isGeoLocated ? { key: 'BoundingBox', value: firstImage.bbox.map(round).join(', ') } : null,
|
|
56
|
+
firstImage.epsg ? { key: 'EPSG', value: `EPSG:${firstImage.epsg} (https://epsg.io/${firstImage.epsg})` } : null,
|
|
57
|
+
{ key: 'Images', value: '\n' + TiffImageInfoTable.print(tiff.images, '\t').join('\n') },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const ghostOptions = [...(tiff.options?.options.entries() ?? [])];
|
|
61
|
+
const gdalMetadata = parseGdalMetadata(firstImage);
|
|
62
|
+
const gdal = [
|
|
63
|
+
{
|
|
64
|
+
key: 'COG optimized',
|
|
65
|
+
value: String(tiff.options?.isCogOptimized),
|
|
66
|
+
enabled: tiff.options?.isCogOptimized === true,
|
|
67
|
+
},
|
|
68
|
+
{ key: 'COG broken', value: String(tiff.options?.isBroken), enabled: tiff.options?.isBroken === true },
|
|
69
|
+
{
|
|
70
|
+
key: 'Ghost Options',
|
|
71
|
+
value: '\n' + ghostOptions.map((c) => `\t\t${c[0]} = ${c[1]}`).join('\n'),
|
|
72
|
+
enabled: ghostOptions.length > 0,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: 'Metadata',
|
|
76
|
+
value: '\n' + gdalMetadata?.map((c) => `\t\t${c}`).join('\n'),
|
|
77
|
+
enabled: gdalMetadata != null,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const result: CliResultMap[] = [
|
|
82
|
+
{ keys: header },
|
|
83
|
+
{ title: 'Images', keys: images },
|
|
84
|
+
{ title: 'GDAL', keys: gdal, enabled: gdal.filter((g) => g.enabled == null || g.enabled).length > 0 },
|
|
85
|
+
];
|
|
86
|
+
if (args.tags) {
|
|
87
|
+
for (const img of tiff.images) {
|
|
88
|
+
const tiffTags = [...img.tags.values()];
|
|
89
|
+
// Load the first 25 values of offset arrays
|
|
90
|
+
if (args.offsets) {
|
|
91
|
+
for (const tag of tiffTags) {
|
|
92
|
+
if (tag.type !== 'offset') continue;
|
|
93
|
+
// for (let i = 0; i < Math.min(tag.count, 100); i++) tag.getValueAt(i);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
result.push({
|
|
98
|
+
title: `Image: ${img.id} - Tiff tags`,
|
|
99
|
+
keys: tiffTags.map(formatTag),
|
|
100
|
+
});
|
|
101
|
+
await img.loadGeoTiffTags();
|
|
102
|
+
if (img.tagsGeo.size > 0) {
|
|
103
|
+
const tiffTagsGeo = [...img.tagsGeo.entries()];
|
|
104
|
+
const keys = tiffTagsGeo.map(([key, value]) => formatGeoTag(key, value));
|
|
105
|
+
if (keys.length > 0) {
|
|
106
|
+
result.push({ title: `Image: ${img.id} - Geo Tiff tags`, keys });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const msg = ActionUtil.formatResult(`\n${c.bold('COG File Info')} - ${c.bold(path.href)}`, result);
|
|
113
|
+
console.log(msg.join('\n'));
|
|
114
|
+
|
|
115
|
+
await source.close?.();
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const TiffImageInfoTable = new CliTable<CogTiffImage>();
|
|
121
|
+
TiffImageInfoTable.add({ name: 'Id', width: 4, get: (_i, index) => String(index) });
|
|
122
|
+
TiffImageInfoTable.add({ name: 'Size', width: 20, get: (i) => `${i.size.width}x${i.size.height}` });
|
|
123
|
+
TiffImageInfoTable.add({
|
|
124
|
+
name: 'Tile Size',
|
|
125
|
+
width: 20,
|
|
126
|
+
get: (i) => `${i.tileSize.width}x${i.tileSize.height}`,
|
|
127
|
+
enabled: (i) => i.isTiled(),
|
|
128
|
+
});
|
|
129
|
+
TiffImageInfoTable.add({
|
|
130
|
+
name: 'Tile Count',
|
|
131
|
+
width: 20,
|
|
132
|
+
get: (i) => `${i.tileCount.x}x${i.tileCount.y} (${i.tileCount.x * i.tileCount.y})`,
|
|
133
|
+
enabled: (i) => i.isTiled(),
|
|
134
|
+
});
|
|
135
|
+
TiffImageInfoTable.add({
|
|
136
|
+
name: 'Strip Count',
|
|
137
|
+
width: 20,
|
|
138
|
+
get: (i) => `${i.tags.get(TiffTagId.StripOffsets)?.count}`,
|
|
139
|
+
enabled: (i) => !i.isTiled(),
|
|
140
|
+
});
|
|
141
|
+
TiffImageInfoTable.add({
|
|
142
|
+
name: 'Resolution',
|
|
143
|
+
width: 20,
|
|
144
|
+
get: (i) => `${round(i.resolution[0])}`,
|
|
145
|
+
enabled: (i) => i.isGeoLocated,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Show compression only if it varies between images
|
|
149
|
+
TiffImageInfoTable.add({
|
|
150
|
+
name: 'Compression',
|
|
151
|
+
width: 20,
|
|
152
|
+
get: (i) => i.compression,
|
|
153
|
+
enabled: (i) => {
|
|
154
|
+
const formats = new Set();
|
|
155
|
+
i.tiff.images.forEach((f) => formats.add(f.compression));
|
|
156
|
+
return formats.size > 1;
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Parse out the GDAL Metadata to be more friendly to read
|
|
162
|
+
*
|
|
163
|
+
* TODO using a XML Parser will make this even better
|
|
164
|
+
* @param img
|
|
165
|
+
*/
|
|
166
|
+
function parseGdalMetadata(img: CogTiffImage): string[] | null {
|
|
167
|
+
const metadata = img.value(TiffTagId.GdalMetadata);
|
|
168
|
+
if (typeof metadata !== 'string') return null;
|
|
169
|
+
if (!metadata.startsWith('<GDALMetadata>')) return null;
|
|
170
|
+
return metadata
|
|
171
|
+
.replace('<GDALMetadata>\n', '')
|
|
172
|
+
.replace('</GDALMetadata>\n', '')
|
|
173
|
+
.replace('\n\x00', '')
|
|
174
|
+
.split('\n')
|
|
175
|
+
.map((c) => c.trim());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function formatTag(tag: TiffTag): { key: string; value: string } {
|
|
179
|
+
const tagName = TiffTagId[tag.id];
|
|
180
|
+
const tagDebug = `(${TiffTagValueType[tag.dataType]}${tag.count > 1 ? ' x' + tag.count : ''}`;
|
|
181
|
+
const key = `${c.dim(toHex(tag.id)).padEnd(7, ' ')} ${String(tagName)} ${c.dim(tagDebug)})`.padEnd(50, ' ');
|
|
182
|
+
|
|
183
|
+
if (Array.isArray(tag.value)) return { key, value: JSON.stringify(tag.value.slice(0, 250)) };
|
|
184
|
+
|
|
185
|
+
let tagString = JSON.stringify(tag.value) ?? c.dim('null');
|
|
186
|
+
if (tagString.length > 256) tagString = tagString.slice(0, 250) + '...';
|
|
187
|
+
return { key, value: tagString };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function formatGeoTag(tagId: TiffTagGeo, value: string | number): { key: string; value: string } {
|
|
191
|
+
const tagName = TiffTagGeo[tagId];
|
|
192
|
+
const key = `${c.dim(toHex(tagId)).padEnd(7, ' ')} ${String(tagName).padEnd(30)}`;
|
|
193
|
+
|
|
194
|
+
if (Array.isArray(value)) return { key, value: JSON.stringify(value.slice(0, 250)) };
|
|
195
|
+
|
|
196
|
+
let tagString = JSON.stringify(value) ?? c.dim('null');
|
|
197
|
+
if (tagString.length > 256) tagString = tagString.slice(0, 250) + '...';
|
|
198
|
+
return { key, value: tagString };
|
|
199
|
+
}
|
package/src/common.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Type, flag } from 'cmd-ts';
|
|
2
|
+
import { pathToFileURL } from 'url';
|
|
3
|
+
|
|
4
|
+
export const verbose = flag({ long: 'verbose', description: 'Verbose logging', short: 'v' });
|
|
5
|
+
export const extraVerbose = flag({ long: 'extra-verbose', description: 'Extra verbose logging', short: 'V' });
|
|
6
|
+
|
|
7
|
+
export const DefaultArgs = {
|
|
8
|
+
verbose,
|
|
9
|
+
extraVerbose,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const Url: Type<string, URL> = {
|
|
13
|
+
async from(s: string): Promise<URL> {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(s);
|
|
16
|
+
} catch (e) {
|
|
17
|
+
return pathToFileURL(s);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
};
|
package/src/fs.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SourceCallback, SourceMiddleware, SourceRequest } from '@chunkd/source';
|
|
2
|
+
import { logger } from './log.js';
|
|
3
|
+
|
|
4
|
+
export const FetchLog: SourceMiddleware & { reset(): void; fetches: SourceRequest[]; bytesRead: number } = {
|
|
5
|
+
name: 'source:log',
|
|
6
|
+
fetch(req: SourceRequest, next: SourceCallback) {
|
|
7
|
+
this.fetches.push(req);
|
|
8
|
+
this.bytesRead += req.length ?? 0;
|
|
9
|
+
logger.info('Tiff:fetch', { href: req.source.url.href, offset: req.offset, length: req.length });
|
|
10
|
+
return next(req);
|
|
11
|
+
},
|
|
12
|
+
reset() {
|
|
13
|
+
this.fetches = [];
|
|
14
|
+
this.bytesRead = 0;
|
|
15
|
+
},
|
|
16
|
+
fetches: [],
|
|
17
|
+
bytesRead: 0,
|
|
18
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { subcommands } from 'cmd-ts';
|
|
2
|
+
import { commandInfo } from './commands/info.js';
|
|
3
|
+
import { commandDump } from './commands/dump.js';
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
export const cmd = subcommands({
|
|
6
|
+
name: 'cogeotiff',
|
|
7
|
+
description: 'COG utilities',
|
|
8
|
+
cmds: {
|
|
9
|
+
info: commandInfo,
|
|
10
|
+
dump: commandDump,
|
|
11
|
+
},
|
|
9
12
|
});
|
package/src/log.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { FsHttp, fsa } from '@chunkd/fs';
|
|
2
|
+
import { SourceCache, SourceChunk } from '@chunkd/middleware';
|
|
3
|
+
import { log } from '@linzjs/tracing';
|
|
4
|
+
import { FetchLog } from './fs.js';
|
|
5
|
+
|
|
6
|
+
// Cache the last 10MB of chunks for reuse
|
|
7
|
+
export const sourceCache = new SourceCache({ size: 10 * 1024 * 1024 });
|
|
8
|
+
export const sourceChunk = new SourceChunk({ size: 32 * 1024 });
|
|
9
|
+
|
|
10
|
+
export function setupLogger(cfg: { verbose?: boolean; extraVerbose?: boolean }): typeof log {
|
|
11
|
+
if (cfg.verbose) {
|
|
12
|
+
log.level = 'debug';
|
|
13
|
+
} else if (cfg.extraVerbose) {
|
|
14
|
+
log.level = 'trace';
|
|
15
|
+
} else {
|
|
16
|
+
log.level = 'warn';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fsa.register('http://', new FsHttp());
|
|
20
|
+
fsa.register('https://', new FsHttp());
|
|
21
|
+
|
|
22
|
+
// Order of these are really important
|
|
23
|
+
// Chunk all requests into 32KB chunks
|
|
24
|
+
fsa.middleware.push(sourceChunk);
|
|
25
|
+
// Cache the last 10MB of chunks for reuse
|
|
26
|
+
fsa.middleware.push(sourceCache);
|
|
27
|
+
|
|
28
|
+
fsa.middleware.push(FetchLog);
|
|
29
|
+
|
|
30
|
+
return log;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const logger = log;
|
|
34
|
+
|
|
35
|
+
/** S3 client adds approx 300ms to the cli startup time, so only register it if needed */
|
|
36
|
+
export async function ensureS3fs(): Promise<void> {
|
|
37
|
+
if (fsa.systems.find((f) => f.prefix.startsWith('s3'))) return;
|
|
38
|
+
|
|
39
|
+
const S3Client = await import('@aws-sdk/client-s3');
|
|
40
|
+
const FsAwsS3 = await import('@chunkd/fs-aws');
|
|
41
|
+
|
|
42
|
+
fsa.register('s3://', new FsAwsS3.FsAwsS3(new S3Client.S3Client({})));
|
|
43
|
+
}
|
package/src/util.bytes.ts
CHANGED
|
@@ -8,11 +8,11 @@ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
|
8
8
|
* @param bytes byte count to convert
|
|
9
9
|
*/
|
|
10
10
|
export function toByteSizeString(bytes: number): string {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
if (bytes === 1) return '1 Byte';
|
|
12
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
13
|
+
const output = bytes / Math.pow(1024, i);
|
|
14
|
+
const byteSize = sizes[i];
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
if (i === 1) return `${Math.round(output)} ${byteSize}`;
|
|
17
|
+
return `${Math.floor(output * 100) / 100} ${byteSize}`;
|
|
18
18
|
}
|
package/src/util.tile.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { CogTiff, TiffMimeType } from '@cogeotiff/core';
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type pino from 'pino';
|
|
2
|
+
import { log } from '@linzjs/tracing';
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
5
4
|
|
|
6
|
-
const FileExtension:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
const FileExtension: Record<string, string> = {
|
|
6
|
+
[TiffMimeType.Jpeg]: 'jpeg',
|
|
7
|
+
[TiffMimeType.Jp2]: 'jp2',
|
|
8
|
+
[TiffMimeType.Webp]: 'webp',
|
|
9
|
+
[TiffMimeType.Lzw]: 'lzw',
|
|
10
|
+
[TiffMimeType.Deflate]: 'deflate',
|
|
11
|
+
[TiffMimeType.None]: 'bin',
|
|
12
|
+
[TiffMimeType.JpegXl]: 'jpeg',
|
|
13
|
+
[TiffMimeType.Zstd]: 'zstd',
|
|
14
|
+
[TiffMimeType.Lerc]: 'lerc',
|
|
15
|
+
[TiffMimeType.Lzma]: 'lzma',
|
|
12
16
|
};
|
|
13
17
|
|
|
14
18
|
/**
|
|
@@ -22,31 +26,27 @@ const FileExtension: { [key: string]: string } = {
|
|
|
22
26
|
* @returns tile name eg `001_002_12.png`
|
|
23
27
|
*/
|
|
24
28
|
export function getTileName(mimeType: string, index: number, x: number, y: number): string {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (fileExt == null) {
|
|
30
|
-
throw new Error(`Unable to process tile type:${mimeType}`);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return `${xS}_${yS}_${index}.${fileExt}`;
|
|
29
|
+
const xS = `${x}`.padStart(3, '0');
|
|
30
|
+
const yS = `${y}`.padStart(3, '0');
|
|
31
|
+
const fileExt = FileExtension[mimeType] ?? 'unknown';
|
|
32
|
+
return `${xS}_${yS}_${index}.${fileExt}`;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
export async function writeTile(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
): Promise<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
tiff: CogTiff,
|
|
37
|
+
x: number,
|
|
38
|
+
y: number,
|
|
39
|
+
index: number,
|
|
40
|
+
outputPath: URL,
|
|
41
|
+
logger: typeof log,
|
|
42
|
+
): Promise<string | null> {
|
|
43
|
+
const tile = await tiff.images[index].getTile(x, y);
|
|
44
|
+
if (tile == null) {
|
|
45
|
+
logger.debug('Tile:Empty', { source: tiff.source.url.href, index, x, y });
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const fileName = getTileName(tile.mimeType, index, x, y);
|
|
49
|
+
await fs.writeFile(new URL(fileName, outputPath), Buffer.from(tile.bytes));
|
|
50
|
+
logger.debug('Tile:Write', { source: tiff.source.url.href, index, x, y, fileName, bytes: tile.bytes.byteLength });
|
|
51
|
+
return fileName;
|
|
52
52
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"rootDir": "./src",
|
|
5
|
+
"outDir": "./build",
|
|
6
|
+
"lib": ["es2018", "dom"]
|
|
7
|
+
},
|
|
8
|
+
"include": ["src"],
|
|
9
|
+
"references": [{ "path": "../core" }]
|
|
10
10
|
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { CogTiff } from '@cogeotiff/core';
|
|
2
|
-
import { CommandLineAction } from '@rushstack/ts-command-line';
|
|
3
|
-
export interface GeoJsonPolygon {
|
|
4
|
-
type: 'Feature';
|
|
5
|
-
geometry: {
|
|
6
|
-
type: 'Polygon';
|
|
7
|
-
coordinates: [[[number, number], [number, number], [number, number], [number, number], [number, number]]];
|
|
8
|
-
};
|
|
9
|
-
properties: Record<string, unknown>;
|
|
10
|
-
}
|
|
11
|
-
export declare class ActionDumpTile extends CommandLineAction {
|
|
12
|
-
private file;
|
|
13
|
-
private imageIndex;
|
|
14
|
-
private output;
|
|
15
|
-
private outputCount;
|
|
16
|
-
private logger;
|
|
17
|
-
constructor();
|
|
18
|
-
dumpBounds(tif: CogTiff, output: string, index: number): Promise<void>;
|
|
19
|
-
dumpIndex(tif: CogTiff, output: string, index: number): Promise<void>;
|
|
20
|
-
dumpTiles(tif: CogTiff, output: string, index: number): Promise<void>;
|
|
21
|
-
onExecute(): Promise<void>;
|
|
22
|
-
protected onDefineParameters(): void;
|
|
23
|
-
}
|
|
24
|
-
//# sourceMappingURL=action.dump.tile.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"action.dump.tile.d.ts","sourceRoot":"","sources":["../src/action.dump.tile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAA2D,MAAM,4BAA4B,CAAC;AAoBxH,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE;QACN,IAAI,EAAE,SAAS,CAAC;QAChB,WAAW,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;KAC7G,CAAC;IACF,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAwBD,qBAAa,cAAe,SAAQ,iBAAiB;IACjD,OAAO,CAAC,IAAI,CAA2C;IACvD,OAAO,CAAC,UAAU,CAA4C;IAC9D,OAAO,CAAC,MAAM,CAA2C;IACzD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,MAAM,CAAc;;IAYtB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCtE,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8BrE,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBrE,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAuDhC,SAAS,CAAC,kBAAkB,IAAI,IAAI;CAyBvC"}
|