@cogeotiff/core 6.1.1 → 7.2.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/build/__benchmark__/cog.read.benchmark.js +7 -4
  3. package/build/__test__/cog.image.test.js +26 -2
  4. package/build/cog.tiff.d.ts +2 -2
  5. package/build/cog.tiff.d.ts.map +1 -1
  6. package/build/cog.tiff.image.d.ts +16 -7
  7. package/build/cog.tiff.image.d.ts.map +1 -1
  8. package/build/cog.tiff.image.js +56 -33
  9. package/build/cog.tiff.js +19 -54
  10. package/build/const/tiff.mime.d.ts +5 -1
  11. package/build/const/tiff.mime.d.ts.map +1 -1
  12. package/build/const/tiff.mime.js +9 -1
  13. package/build/read/tag/__test__/tag.test.js +1 -1
  14. package/build/read/tag/tiff.tag.base.d.ts.map +1 -1
  15. package/build/read/tag/tiff.tag.base.js +1 -1
  16. package/build/read/tag/tiff.tag.lazy.d.ts +1 -2
  17. package/build/read/tag/tiff.tag.lazy.d.ts.map +1 -1
  18. package/build/read/tag/tiff.tag.lazy.js +3 -3
  19. package/build/read/tag/tiff.tag.offset.d.ts +2 -3
  20. package/build/read/tag/tiff.tag.offset.d.ts.map +1 -1
  21. package/build/read/tag/tiff.tag.offset.js +5 -5
  22. package/build/read/tiff.gdal.d.ts +6 -19
  23. package/build/read/tiff.gdal.d.ts.map +1 -1
  24. package/build/read/tiff.gdal.js +32 -53
  25. package/package.json +5 -6
  26. package/src/__benchmark__/cog.read.benchmark.ts +6 -3
  27. package/src/__test__/cog.image.test.ts +29 -1
  28. package/src/cog.tiff.image.ts +55 -33
  29. package/src/cog.tiff.ts +17 -68
  30. package/src/const/tiff.mime.ts +8 -0
  31. package/src/read/tag/__test__/tag.test.ts +1 -1
  32. package/src/read/tag/tiff.tag.base.ts +0 -1
  33. package/src/read/tag/tiff.tag.lazy.ts +2 -3
  34. package/src/read/tag/tiff.tag.offset.ts +4 -5
  35. package/src/read/tiff.gdal.ts +31 -58
  36. package/build/util/util.enum.d.ts +0 -6
  37. package/build/util/util.enum.d.ts.map +0 -1
  38. package/build/util/util.enum.js +0 -17
  39. package/src/util/util.enum.ts +0 -16
@@ -1,4 +1,3 @@
1
- import { LogType } from '@chunkd/core';
2
1
  import { CogTiff } from './cog.tiff.js';
3
2
  import { TiffCompression, TiffMimeType } from './const/tiff.mime.js';
4
3
  import { TiffTag, TiffTagGeo } from './const/tiff.tag.id.js';
@@ -56,27 +55,27 @@ export class CogTiffImage {
56
55
  *
57
56
  * @param loadGeoTags Whether to load the GeoKeyDirectory and unpack it
58
57
  */
59
- async init(loadGeoTags = false, l: LogType = null as any): Promise<void> {
58
+ async init(loadGeoTags = false): Promise<void> {
60
59
  const requiredTags = [
61
- this.fetch(TiffTag.Compression, l),
62
- this.fetch(TiffTag.ImageHeight, l),
63
- this.fetch(TiffTag.ImageWidth, l),
64
- this.fetch(TiffTag.ModelPixelScale, l),
65
- this.fetch(TiffTag.ModelTiePoint, l),
66
- this.fetch(TiffTag.ModelTransformation, l),
67
- this.fetch(TiffTag.TileHeight, l),
68
- this.fetch(TiffTag.TileWidth, l),
60
+ this.fetch(TiffTag.Compression),
61
+ this.fetch(TiffTag.ImageHeight),
62
+ this.fetch(TiffTag.ImageWidth),
63
+ this.fetch(TiffTag.ModelPixelScale),
64
+ this.fetch(TiffTag.ModelTiePoint),
65
+ this.fetch(TiffTag.ModelTransformation),
66
+ this.fetch(TiffTag.TileHeight),
67
+ this.fetch(TiffTag.TileWidth),
69
68
  ];
70
69
 
71
70
  if (loadGeoTags) {
72
- requiredTags.push(this.fetch(TiffTag.GeoKeyDirectory, l));
73
- requiredTags.push(this.fetch(TiffTag.GeoAsciiParams, l));
74
- requiredTags.push(this.fetch(TiffTag.GeoDoubleParams, l));
71
+ requiredTags.push(this.fetch(TiffTag.GeoKeyDirectory));
72
+ requiredTags.push(this.fetch(TiffTag.GeoAsciiParams));
73
+ requiredTags.push(this.fetch(TiffTag.GeoDoubleParams));
75
74
  }
76
75
 
77
76
  await Promise.all(requiredTags);
78
77
  if (loadGeoTags) {
79
- await this.loadGeoTiffTags(l);
78
+ await this.loadGeoTiffTags();
80
79
  }
81
80
  }
82
81
 
@@ -92,7 +91,7 @@ export class CogTiffImage {
92
91
  /**
93
92
  * Load and unpack the GeoKeyDirectory
94
93
  */
95
- async loadGeoTiffTags(l: LogType): Promise<void> {
94
+ async loadGeoTiffTags(): Promise<void> {
96
95
  // Already loaded
97
96
  if (this.tagsGeoLoaded) return;
98
97
  const sourceTag = this.tags.get(TiffTag.GeoKeyDirectory);
@@ -103,9 +102,9 @@ export class CogTiffImage {
103
102
  if (!sourceTag.isReady && sourceTag instanceof CogTiffTagLazy) {
104
103
  // Load all the required keys
105
104
  await Promise.all([
106
- this.fetch(TiffTag.GeoKeyDirectory, l),
107
- this.fetch(TiffTag.GeoAsciiParams, l),
108
- this.fetch(TiffTag.GeoDoubleParams, l),
105
+ this.fetch(TiffTag.GeoKeyDirectory),
106
+ this.fetch(TiffTag.GeoAsciiParams),
107
+ this.fetch(TiffTag.GeoDoubleParams),
109
108
  ]);
110
109
  }
111
110
  this.tagsGeoLoaded = true;
@@ -145,10 +144,10 @@ export class CogTiffImage {
145
144
  * Load a tag, if it is not currently loaded, fetch the required data for the tag.
146
145
  * @param tag tag to fetch
147
146
  */
148
- public async fetch<T>(tag: TiffTag, l: LogType): Promise<T | null> {
147
+ public async fetch<T>(tag: TiffTag): Promise<T | null> {
149
148
  const sourceTag = this.tags.get(tag);
150
149
  if (sourceTag == null) return null;
151
- if (CogTiffTag.isLazy(sourceTag)) return sourceTag.fetch(l) as any;
150
+ if (CogTiffTag.isLazy(sourceTag)) return sourceTag.fetch() as any;
152
151
  return sourceTag.value as T;
153
152
  }
154
153
 
@@ -342,14 +341,14 @@ export class CogTiffImage {
342
341
  * @param index tile index
343
342
  * @returns file offset of the specified tile
344
343
  */
345
- protected async getTileOffset(index: number, l?: LogType): Promise<number> {
344
+ protected async getTileOffset(index: number): Promise<number> {
346
345
  const tileOffset = this.tileOffset;
347
346
  if (index < 0 || index > tileOffset.dataCount) {
348
347
  throw new Error(`Tile offset: ${index} out of range: 0 -> ${tileOffset.dataCount}`);
349
348
  }
350
349
 
351
350
  // Fetch only the part of the offsets that are needed
352
- return tileOffset.getValueAt(index, l);
351
+ return tileOffset.getValueAt(index);
353
352
  }
354
353
 
355
354
  // Clamp the bounds of the output image to the size of the image, as sometimes the edge tiles are not full tiles
@@ -398,13 +397,12 @@ export class CogTiffImage {
398
397
  private async getBytes(
399
398
  offset: number,
400
399
  byteCount: number,
401
- l?: LogType,
402
400
  ): Promise<{ mimeType: TiffMimeType; bytes: Uint8Array } | null> {
403
401
  const mimeType = this.compression;
404
402
  if (mimeType == null) throw new Error('Unsupported compression: ' + this.value(TiffTag.Compression));
405
403
  if (byteCount === 0) return null;
406
404
 
407
- await this.tif.source.loadBytes(offset, byteCount, l);
405
+ await this.tif.source.loadBytes(offset, byteCount);
408
406
  const bytes = this.tif.source.bytes(offset, byteCount);
409
407
 
410
408
  if (this.compression === TiffMimeType.JPEG) {
@@ -421,7 +419,7 @@ export class CogTiffImage {
421
419
  * @param x Tile x offset
422
420
  * @param y Tile y offset
423
421
  */
424
- async getTile(x: number, y: number, l?: LogType): Promise<{ mimeType: TiffMimeType; bytes: Uint8Array } | null> {
422
+ async getTile(x: number, y: number): Promise<{ mimeType: TiffMimeType; bytes: Uint8Array } | null> {
425
423
  const mimeType = this.compression;
426
424
  const size = this.size;
427
425
  const tiles = this.tileSize;
@@ -441,22 +439,46 @@ export class CogTiffImage {
441
439
  const totalTiles = nxTiles * nyTiles;
442
440
  if (idx >= totalTiles) throw new Error(`Tile index is outside of tile range: ${idx} >= ${totalTiles}`);
443
441
 
444
- const { offset, imageSize } = await this.getTileSize(idx, l);
445
- return this.getBytes(offset, imageSize, l);
442
+ const { offset, imageSize } = await this.getTileSize(idx);
443
+ return this.getBytes(offset, imageSize);
446
444
  }
447
445
 
448
- protected async getTileSize(index: number, l?: LogType): Promise<{ offset: number; imageSize: number }> {
446
+ /**
447
+ * Does this tile exist in the tiff and does it actually have a value
448
+ *
449
+ * Sparse tiffs can have a lot of empty tiles, this checks to see if the tile actually has data.
450
+ *
451
+ * @param x Tile x offset
452
+ * @param y Tile y offset
453
+ * @returns if the tile exists and has data
454
+ */
455
+ async hasTile(x: number, y: number): Promise<boolean> {
456
+ const tiles = this.tileSize;
457
+ const size = this.size;
458
+
459
+ if (tiles == null) throw new Error('Tiff is not tiled');
460
+
461
+ // TODO support GhostOptionTileOrder
462
+ const nyTiles = Math.ceil(size.height / tiles.height);
463
+ const nxTiles = Math.ceil(size.width / tiles.width);
464
+ if (x >= nxTiles || y >= nyTiles) return false;
465
+ const idx = y * nxTiles + x;
466
+ const ret = await this.getTileSize(idx);
467
+ return ret.offset > 0;
468
+ }
469
+
470
+ protected async getTileSize(index: number): Promise<{ offset: number; imageSize: number }> {
449
471
  // GDAL optimizes tiles by storing the size of the tile in
450
472
  // the few bytes leading up to the tile
451
- if (this.tif.options.tileLeaderByteSize) {
452
- const offset = await this.getTileOffset(index, l);
473
+ const leaderBytes = this.tif.options.tileLeaderByteSize;
474
+ if (leaderBytes) {
475
+ const offset = await this.getTileOffset(index);
453
476
  // Sparse COG no data found
454
477
  if (offset === 0) return { offset: 0, imageSize: 0 };
455
478
 
456
- const leaderBytes = this.tif.options.tileLeaderByteSize;
457
479
  // This fetch will generally load in the bytes needed for the image too
458
480
  // provided the image size is less than the size of a chunk
459
- await this.tif.source.loadBytes(offset - leaderBytes, leaderBytes, l);
481
+ await this.tif.source.loadBytes(offset - leaderBytes, leaderBytes);
460
482
  return { offset, imageSize: this.tif.source.getUint(offset - leaderBytes, leaderBytes) };
461
483
  }
462
484
 
@@ -464,7 +486,7 @@ export class CogTiffImage {
464
486
  if (byteCounts == null) {
465
487
  throw new Error('No tile byte counts found');
466
488
  }
467
- const [offset, imageSize] = await Promise.all([this.getTileOffset(index, l), byteCounts.getValueAt(index, l)]);
489
+ const [offset, imageSize] = await Promise.all([this.getTileOffset(index), byteCounts.getValueAt(index)]);
468
490
  return { offset, imageSize };
469
491
  }
470
492
  }
package/src/cog.tiff.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ChunkSource, LogType } from '@chunkd/core';
1
+ import { ChunkSource } from '@chunkd/core';
2
2
  import { CogTiffImage } from './cog.tiff.image.js';
3
3
  import { TiffEndian } from './const/tiff.endian.js';
4
4
  import { TiffTag } from './const/tiff.tag.id.js';
@@ -38,24 +38,24 @@ export class CogTiff {
38
38
  *
39
39
  * @param loadGeoKeys Whether to also initialize the GeoKeyDirectory
40
40
  */
41
- init(loadGeoKeys = false, logger?: LogType): Promise<CogTiff> {
41
+ init(loadGeoKeys = false): Promise<CogTiff> {
42
42
  if (this._initPromise) return this._initPromise;
43
- this._initPromise = this.doInit(loadGeoKeys, logger);
43
+ this._initPromise = this.doInit(loadGeoKeys);
44
44
  return this._initPromise;
45
45
  }
46
46
 
47
- private async doInit(loadGeoKeys = false, logger?: LogType): Promise<CogTiff> {
47
+ private async doInit(loadGeoKeys = false): Promise<CogTiff> {
48
48
  if (this.isInitialized) return this;
49
49
  // Load the first few KB in, more loads will run as more data is required
50
- await this.source.loadBytes(0, this.source.chunkSize, logger);
51
- await this.fetchIfd(logger);
52
- await Promise.all(this.images.map((c) => c.init(loadGeoKeys, logger)));
50
+ await this.source.loadBytes(0, this.source.chunkSize);
51
+ await this.fetchIfd();
52
+ await Promise.all(this.images.map((c) => c.init(loadGeoKeys)));
53
53
 
54
54
  this.isInitialized = true;
55
55
  return this;
56
56
  }
57
57
 
58
- private async fetchIfd(logger?: LogType): Promise<void> {
58
+ private async fetchIfd(): Promise<void> {
59
59
  const view = this.cursor.seekTo(0);
60
60
  const endian = view.uint16();
61
61
  this.source.isLittleEndian = endian === TiffEndian.Little;
@@ -78,15 +78,9 @@ export class CogTiff {
78
78
 
79
79
  const ghostSize = nextOffsetIfd - this.cursor.currentOffset;
80
80
  // GDAL now stores metadata between the IFD inside a ghost storage area
81
- if (ghostSize > 0 && ghostSize < 16 * 1024) {
82
- logger?.debug(
83
- { offset: toHexString(this.cursor.currentOffset), length: toHexString(ghostSize) },
84
- 'GhostOptions',
85
- );
86
- // this.options.process(this.source, view.currentOffset, ghostSize);
87
- }
81
+ if (ghostSize > 0 && ghostSize < 16 * 1024) this.options.process(view.bytes(ghostSize));
88
82
 
89
- return this.processIfd(nextOffsetIfd, logger);
83
+ return this.processIfd(nextOffsetIfd);
90
84
  }
91
85
 
92
86
  getImage(z: number): CogTiffImage {
@@ -135,38 +129,19 @@ export class CogTiff {
135
129
  return image.getTile(x, y);
136
130
  }
137
131
 
138
- private async processIfd(offset: number, logger?: LogType): Promise<void> {
139
- logger?.trace({ offset: toHexString(offset) }, 'NextImageOffset');
140
-
141
- if (!this.source.hasBytes(offset, 4096)) {
142
- await this.source.loadBytes(offset, 4096, logger);
143
- }
144
-
145
- const { image, nextOffset } = await this.readIfd(offset, logger);
132
+ private async processIfd(offset: number): Promise<void> {
133
+ const { image, nextOffset } = await this.readIfd(offset);
146
134
  this.images.push(image);
147
- const size = image.size;
148
- if (image.isTiled()) {
149
- const tile = image.tileSize;
150
- logger?.debug(
151
- {
152
- ...size,
153
- tileWidth: tile.width,
154
- tileHeight: tile.height,
155
- tileCount: Math.ceil(size.width / tile.width),
156
- },
157
- 'GotImage',
158
- );
159
- }
160
135
 
161
- if (nextOffset) await this.processIfd(nextOffset, logger);
136
+ if (nextOffset) await this.processIfd(nextOffset);
162
137
  }
163
138
 
164
- private async readIfd(offset: number, log?: LogType): Promise<{ nextOffset: number; image: CogTiffImage }> {
165
- if (!this.source.hasBytes(offset, 1024)) await this.source.loadBytes(offset, this.source.chunkSize, log);
139
+ private async readIfd(offset: number): Promise<{ nextOffset: number; image: CogTiffImage }> {
140
+ if (!this.source.hasBytes(offset, 4096)) await this.source.loadBytes(offset, 4096);
141
+
166
142
  const view = this.cursor.seekTo(offset);
167
143
  const tagCount = view.offset();
168
144
  const byteStart = offset + this.ifdConfig.offset;
169
- const logger = log?.child({ imageId: this.images.length });
170
145
  const tags: Map<TiffTag, CogTiffTagBase> = new Map();
171
146
 
172
147
  let pos = byteStart;
@@ -174,33 +149,7 @@ export class CogTiff {
174
149
  const tag = CogTiffTag.create(this, pos);
175
150
  pos += tag.size;
176
151
 
177
- if (tag.name == null) {
178
- logger?.error({ code: toHexString(tag.id) }, `IFDUnknown`);
179
- continue;
180
- }
181
-
182
- if (!tag.isReady) {
183
- logger?.trace(
184
- {
185
- offset: toHexString(pos - offset),
186
- code: toHexString(tag.id),
187
- tagName: tag.name,
188
- ptr: toHexString(tag.valuePointer),
189
- },
190
- 'PartialReadIFD',
191
- );
192
- } else {
193
- logger?.trace(
194
- {
195
- offset: toHexString(pos - offset),
196
- code: toHexString(tag.id),
197
- tagName: tag.name,
198
- value: Array.isArray(tag.value) ? `[${tag.value.length}]` : tag.value,
199
- },
200
- 'ReadIFD',
201
- );
202
- }
203
-
152
+ if (tag.name == null) throw new Error('Unknown IFD Tag: ' + toHexString(tag.id));
204
153
  tags.set(tag.id, tag);
205
154
  }
206
155
 
@@ -5,9 +5,13 @@ export enum TiffMimeType {
5
5
  NONE = 'application/octet-stream',
6
6
  JPEG = 'image/jpeg',
7
7
  JP2 = 'image/jp2',
8
+ JPEGXL = 'image/jpegxl',
8
9
  WEBP = 'image/webp',
10
+ ZSTD = 'application/zstd',
9
11
  LZW = 'application/lzw',
10
12
  DEFLATE = 'application/deflate',
13
+ LZERC = 'application/lerc',
14
+ LZMA = 'application/x-lzma',
11
15
  }
12
16
 
13
17
  export const TiffCompression: { [key: number]: TiffMimeType } = {
@@ -16,6 +20,10 @@ export const TiffCompression: { [key: number]: TiffMimeType } = {
16
20
  6: TiffMimeType.JPEG,
17
21
  7: TiffMimeType.JPEG,
18
22
  8: TiffMimeType.DEFLATE,
23
+ 34887: TiffMimeType.LZERC,
24
+ 34925: TiffMimeType.LZMA,
19
25
  34712: TiffMimeType.JP2,
26
+ 50000: TiffMimeType.ZSTD,
20
27
  50001: TiffMimeType.WEBP,
28
+ 50002: TiffMimeType.JPEGXL,
21
29
  };
@@ -10,7 +10,7 @@ o.spec('TiffTag', () => {
10
10
  const cogSourceFile = new TestFileChunkSource(path.join(dirName, '../../../../../data/rgba8_tiled.tiff'));
11
11
 
12
12
  o.beforeEach(() => {
13
- cogSourceFile.chunks.clear();
13
+ (cogSourceFile.chunks as Map<unknown, unknown>).clear();
14
14
  });
15
15
 
16
16
  o('should load lazy tags', async () => {
@@ -32,7 +32,6 @@ export abstract class CogTiffTagBase<T = unknown> {
32
32
 
33
33
  this.id = id;
34
34
  this.name = TiffTag[this.id];
35
-
36
35
  this.dataType = this.tiff.source.getUint16(offset + 2);
37
36
  this.dataCount = this.tiff.source.getUint(offset + 4, this.tiff.ifdConfig.pointer);
38
37
  this.dataTypeSize = getTiffTagSize(this.dataType);
@@ -1,4 +1,3 @@
1
- import { LogType } from '@chunkd/core';
2
1
  import { CogTiffTagBase } from './tiff.tag.base.js';
3
2
 
4
3
  export class CogTiffTagLazy<T> extends CogTiffTagBase<T> {
@@ -8,9 +7,9 @@ export class CogTiffTagLazy<T> extends CogTiffTagBase<T> {
8
7
  return this.value != null;
9
8
  }
10
9
 
11
- async fetch(l?: LogType): Promise<T> {
10
+ async fetch(): Promise<T> {
12
11
  if (this.tiff.source.hasBytes(this.valuePointer, this.dataLength) === false) {
13
- await this.tiff.source.loadBytes(this.valuePointer, this.dataLength, l);
12
+ await this.tiff.source.loadBytes(this.valuePointer, this.dataLength);
14
13
  }
15
14
  this.value = this.readValue();
16
15
  return this.value;
@@ -1,4 +1,3 @@
1
- import { LogType } from '@chunkd/core';
2
1
  import { CogTiff } from '../../cog.tiff.js';
3
2
  import { getTiffTagValueReader } from '../tiff.value.reader.js';
4
3
  import { CogTiffTagBase } from './tiff.tag.base.js';
@@ -27,8 +26,8 @@ export class CogTiffTagOffset extends CogTiffTagBase<number[]> {
27
26
  }
28
27
 
29
28
  /** Load the entire index into memory */
30
- async load(l: LogType): Promise<void> {
31
- await this.tiff.source.loadBytes(this.valuePointer, this.dataLength, l);
29
+ async load(): Promise<void> {
30
+ await this.tiff.source.loadBytes(this.valuePointer, this.dataLength);
32
31
  this.readValue();
33
32
  }
34
33
 
@@ -47,7 +46,7 @@ export class CogTiffTagOffset extends CogTiffTagBase<number[]> {
47
46
  * Read a specific value from the offset array
48
47
  * @param index index to read at
49
48
  */
50
- async getValueAt(index: number, l?: LogType): Promise<number> {
49
+ async getValueAt(index: number): Promise<number> {
51
50
  if (this.loadedValues) return this.loadedValues[index];
52
51
 
53
52
  const dataSize = this.dataTypeSize;
@@ -55,7 +54,7 @@ export class CogTiffTagOffset extends CogTiffTagBase<number[]> {
55
54
  const convert = getTiffTagValueReader(this.dataType);
56
55
 
57
56
  if (!this.tiff.source.hasBytes(valueOffset, dataSize)) {
58
- await this.tiff.source.loadBytes(valueOffset, dataSize, l);
57
+ await this.tiff.source.loadBytes(valueOffset, dataSize);
59
58
  }
60
59
  return convert(this.tiff.source, valueOffset) as number;
61
60
  }
@@ -1,12 +1,5 @@
1
- import { ByteSize, ChunkSource } from '@chunkd/core';
2
- import { getReverseEnumValue } from '../util/util.enum.js';
3
-
4
- // GDAL_STRUCTURAL_METADATA_SIZE: '000140 bytes',
5
- // LAYOUT: 'IFDS_BEFORE_DATA',
6
- // BLOCK_ORDER: 'ROW_MAJOR',
7
- // BLOCK_LEADER: 'SIZE_AS_UINT4',
8
- // BLOCK_TRAILER: 'LAST_4_BYTES_REPEATED',
9
- // KNOWN_INCOMPATIBLE_EDITION: 'NO'
1
+ import { ByteSize } from '@chunkd/core';
2
+ import { CogTiff } from '../cog.tiff.js';
10
3
 
11
4
  export enum GhostOption {
12
5
  GDAL_STRUCTURAL_METADATA_SIZE = 'GDAL_STRUCTURAL_METADATA_SIZE',
@@ -25,16 +18,14 @@ export enum GhostOptionTileOrder {
25
18
  export enum GhostOptionTileLeader {
26
19
  uint32 = 'SIZE_AS_UINT4',
27
20
  }
28
- export const GhostOptionTileLeaderSize: { [key: string]: ByteSize } = {
29
- uint32: ByteSize.UInt32,
30
- };
31
21
 
32
22
  /**
33
23
  * GDAL has made a ghost set of options for Tif files
34
24
  * this class represents the optimizations that can be used
35
25
  */
36
26
  export class CogTifGhostOptions {
37
- options: Map<GhostOption, string> = new Map();
27
+ options: Map<string, string> = new Map();
28
+ source: CogTiff;
38
29
 
39
30
  /**
40
31
  * Has GDAL optimized this tif
@@ -51,61 +42,43 @@ export class CogTifGhostOptions {
51
42
  return this.options.get(GhostOption.KNOWN_INCOMPATIBLE_EDITION) === 'YES';
52
43
  }
53
44
 
54
- private set(key: GhostOption, val: string): void {
55
- this.options.set(key, val);
56
- }
57
-
58
45
  /**
59
46
  * Load the ghost options from a source
60
- * @param source Source to load from
61
- * @param offset Byte offset to start reading
62
- * @param length max number of bytes to read
47
+ * @param bytes the ghost header bytes
63
48
  */
64
- process(source: ChunkSource, offset: number, length: number): void {
65
- const chars: string[] = [];
66
- for (let i = offset; i < offset + length; i++) {
67
- const char = source.getUint8(i);
68
- if (char === 0) continue;
69
- chars.push(String.fromCharCode(char));
49
+ process(bytes: Uint8Array): void {
50
+ let key = '';
51
+ let value = '';
52
+ let setValue = false;
53
+ for (let i = 0; i < bytes.length; i++) {
54
+ const charCode = bytes[i];
55
+ if (charCode === 0) break;
56
+
57
+ const char = String.fromCharCode(charCode);
58
+ if (char === '\n') {
59
+ this.options.set(key.trim(), value.trim());
60
+ key = '';
61
+ value = '';
62
+ setValue = false;
63
+ } else if (char === '=') {
64
+ setValue = true;
65
+ } else {
66
+ if (setValue) value += char;
67
+ else key += char;
68
+ }
70
69
  }
71
-
72
- const keyValPairs = chars
73
- .join('')
74
- .trim()
75
- .split('\n')
76
- .map((c) => c.split('='));
77
-
78
- for (const [key, value] of keyValPairs) {
79
- this.options.set(GhostOption[key as GhostOption], value);
80
- }
81
- }
82
-
83
- private _getReverse<T>(e: Record<string, any>, key: GhostOption): T | null {
84
- const opt = this.options.get(key);
85
- if (opt == null) return null;
86
- return getReverseEnumValue<T>(e, opt);
87
- }
88
-
89
- /**
90
- * The ordering of tiles inside the tif
91
- */
92
- get tileOrder(): GhostOptionTileOrder | null {
93
- return this._getReverse(GhostOptionTileOrder, GhostOption.BLOCK_ORDER);
94
- }
95
-
96
- /**
97
- * Are tiles stored with the file size immediately before the tile
98
- */
99
- get tileLeader(): GhostOptionTileLeader | null {
100
- return this._getReverse(GhostOptionTileLeader, GhostOption.BLOCK_LEADER);
101
70
  }
102
71
 
103
72
  /**
104
73
  * If the tile leader is set, how many bytes are allocated to the tile size
105
74
  */
106
75
  get tileLeaderByteSize(): ByteSize | null {
107
- if (this.tileLeader == null) return null;
108
- return GhostOptionTileLeaderSize[this.tileLeader];
76
+ switch (this.options.get(GhostOption.BLOCK_LEADER)) {
77
+ case GhostOptionTileLeader.uint32:
78
+ return ByteSize.UInt32;
79
+ default:
80
+ return null;
81
+ }
109
82
  }
110
83
 
111
84
  get isMaskInterleaved(): boolean {
@@ -1,6 +0,0 @@
1
- /**
2
- * Typescript enums do not allow for easy reverse lookup of keys
3
- * This hacks a reverse lookup key onto the enum
4
- */
5
- export declare function getReverseEnumValue<T>(e: Record<string, string>, value: string): T;
6
- //# sourceMappingURL=util.enum.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"util.enum.d.ts","sourceRoot":"","sources":["../../src/util/util.enum.ts"],"names":[],"mappings":"AACA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAUlF"}
@@ -1,17 +0,0 @@
1
- const REVERSE_ENUM_KEY = '__reverse';
2
- /**
3
- * Typescript enums do not allow for easy reverse lookup of keys
4
- * This hacks a reverse lookup key onto the enum
5
- */
6
- export function getReverseEnumValue(e, value) {
7
- if (e[REVERSE_ENUM_KEY] == null) {
8
- const reverse = {};
9
- for (const key of Object.keys(e)) {
10
- const val = e[key];
11
- reverse[val] = key;
12
- }
13
- Object.defineProperty(e, REVERSE_ENUM_KEY, { enumerable: false, value: reverse, writable: false });
14
- }
15
- return e[REVERSE_ENUM_KEY][value];
16
- }
17
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbC5lbnVtLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWwvdXRpbC5lbnVtLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE1BQU0sZ0JBQWdCLEdBQUcsV0FBVyxDQUFDO0FBQ3JDOzs7R0FHRztBQUNILE1BQU0sVUFBVSxtQkFBbUIsQ0FBSSxDQUF5QixFQUFFLEtBQWE7SUFDM0UsSUFBSSxDQUFDLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxJQUFJLEVBQUU7UUFDN0IsTUFBTSxPQUFPLEdBQXdCLEVBQUUsQ0FBQztRQUN4QyxLQUFLLE1BQU0sR0FBRyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDOUIsTUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ25CLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxHQUFHLENBQUM7U0FDdEI7UUFDRCxNQUFNLENBQUMsY0FBYyxDQUFDLENBQUMsRUFBRSxnQkFBZ0IsRUFBRSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztLQUN0RztJQUNELE9BQU8sQ0FBQyxDQUFDLGdCQUFnQixDQUFDLENBQUMsS0FBWSxDQUFRLENBQUM7QUFDcEQsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IFJFVkVSU0VfRU5VTV9LRVkgPSAnX19yZXZlcnNlJztcbi8qKlxuICogVHlwZXNjcmlwdCBlbnVtcyBkbyBub3QgYWxsb3cgZm9yIGVhc3kgcmV2ZXJzZSBsb29rdXAgb2Yga2V5c1xuICogVGhpcyBoYWNrcyBhIHJldmVyc2UgbG9va3VwIGtleSBvbnRvIHRoZSBlbnVtXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRSZXZlcnNlRW51bVZhbHVlPFQ+KGU6IFJlY29yZDxzdHJpbmcsIHN0cmluZz4sIHZhbHVlOiBzdHJpbmcpOiBUIHtcbiAgICBpZiAoZVtSRVZFUlNFX0VOVU1fS0VZXSA9PSBudWxsKSB7XG4gICAgICAgIGNvbnN0IHJldmVyc2U6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fTtcbiAgICAgICAgZm9yIChjb25zdCBrZXkgb2YgT2JqZWN0LmtleXMoZSkpIHtcbiAgICAgICAgICAgIGNvbnN0IHZhbCA9IGVba2V5XTtcbiAgICAgICAgICAgIHJldmVyc2VbdmFsXSA9IGtleTtcbiAgICAgICAgfVxuICAgICAgICBPYmplY3QuZGVmaW5lUHJvcGVydHkoZSwgUkVWRVJTRV9FTlVNX0tFWSwgeyBlbnVtZXJhYmxlOiBmYWxzZSwgdmFsdWU6IHJldmVyc2UsIHdyaXRhYmxlOiBmYWxzZSB9KTtcbiAgICB9XG4gICAgcmV0dXJuIGVbUkVWRVJTRV9FTlVNX0tFWV1bdmFsdWUgYXMgYW55XSBhcyBhbnk7XG59XG4iXX0=
@@ -1,16 +0,0 @@
1
- const REVERSE_ENUM_KEY = '__reverse';
2
- /**
3
- * Typescript enums do not allow for easy reverse lookup of keys
4
- * This hacks a reverse lookup key onto the enum
5
- */
6
- export function getReverseEnumValue<T>(e: Record<string, string>, value: string): T {
7
- if (e[REVERSE_ENUM_KEY] == null) {
8
- const reverse: Record<string, any> = {};
9
- for (const key of Object.keys(e)) {
10
- const val = e[key];
11
- reverse[val] = key;
12
- }
13
- Object.defineProperty(e, REVERSE_ENUM_KEY, { enumerable: false, value: reverse, writable: false });
14
- }
15
- return e[REVERSE_ENUM_KEY][value as any] as any;
16
- }