@cogeotiff/core 6.0.2 → 7.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.
@@ -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
 
@@ -270,8 +269,8 @@ export class CogTiffImage {
270
269
  */
271
270
  get size(): Size {
272
271
  return {
273
- width: this.value<number>(TiffTag.ImageWidth)!,
274
- height: this.value<number>(TiffTag.ImageHeight)!,
272
+ width: this.value<number>(TiffTag.ImageWidth) as number,
273
+ height: this.value<number>(TiffTag.ImageHeight) as number,
275
274
  };
276
275
  }
277
276
 
@@ -294,8 +293,8 @@ export class CogTiffImage {
294
293
  */
295
294
  get tileSize(): CogTiffImageTileSize {
296
295
  return {
297
- width: this.value<number>(TiffTag.TileWidth)!,
298
- height: this.value<number>(TiffTag.TileHeight)!,
296
+ width: this.value<number>(TiffTag.TileWidth) as number,
297
+ height: this.value<number>(TiffTag.TileHeight) as number,
299
298
  };
300
299
  }
301
300
 
@@ -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,22 @@ 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
+ protected async getTileSize(index: number): Promise<{ offset: number; imageSize: number }> {
449
447
  // GDAL optimizes tiles by storing the size of the tile in
450
448
  // the few bytes leading up to the tile
451
449
  if (this.tif.options.tileLeaderByteSize) {
452
- const offset = await this.getTileOffset(index, l);
450
+ const offset = await this.getTileOffset(index);
453
451
  // Sparse COG no data found
454
452
  if (offset === 0) return { offset: 0, imageSize: 0 };
455
453
 
456
454
  const leaderBytes = this.tif.options.tileLeaderByteSize;
457
455
  // This fetch will generally load in the bytes needed for the image too
458
456
  // provided the image size is less than the size of a chunk
459
- await this.tif.source.loadBytes(offset - leaderBytes, leaderBytes, l);
457
+ await this.tif.source.loadBytes(offset - leaderBytes, leaderBytes);
460
458
  return { offset, imageSize: this.tif.source.getUint(offset - leaderBytes, leaderBytes) };
461
459
  }
462
460
 
@@ -464,7 +462,7 @@ export class CogTiffImage {
464
462
  if (byteCounts == null) {
465
463
  throw new Error('No tile byte counts found');
466
464
  }
467
- const [offset, imageSize] = await Promise.all([this.getTileOffset(index, l), byteCounts.getValueAt(index, l)]);
465
+ const [offset, imageSize] = await Promise.all([this.getTileOffset(index), byteCounts.getValueAt(index)]);
468
466
  return { offset, imageSize };
469
467
  }
470
468
  }
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;
@@ -79,14 +79,14 @@ export class CogTiff {
79
79
  const ghostSize = nextOffsetIfd - this.cursor.currentOffset;
80
80
  // GDAL now stores metadata between the IFD inside a ghost storage area
81
81
  if (ghostSize > 0 && ghostSize < 16 * 1024) {
82
- logger?.debug(
83
- { offset: toHexString(this.cursor.currentOffset), length: toHexString(ghostSize) },
84
- 'GhostOptions',
85
- );
82
+ // logger?.debug(
83
+ // { offset: toHexString(this.cursor.currentOffset), length: toHexString(ghostSize) },
84
+ // 'GhostOptions',
85
+ // );
86
86
  // this.options.process(this.source, view.currentOffset, ghostSize);
87
87
  }
88
88
 
89
- return this.processIfd(nextOffsetIfd, logger);
89
+ return this.processIfd(nextOffsetIfd);
90
90
  }
91
91
 
92
92
  getImage(z: number): CogTiffImage {
@@ -135,38 +135,20 @@ export class CogTiff {
135
135
  return image.getTile(x, y);
136
136
  }
137
137
 
138
- private async processIfd(offset: number, logger?: LogType): Promise<void> {
139
- logger?.trace({ offset: toHexString(offset) }, 'NextImageOffset');
138
+ private async processIfd(offset: number): Promise<void> {
139
+ if (!this.source.hasBytes(offset, 4096)) await this.source.loadBytes(offset, 4096);
140
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);
141
+ const { image, nextOffset } = await this.readIfd(offset);
146
142
  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
143
 
161
- if (nextOffset) await this.processIfd(nextOffset, logger);
144
+ if (nextOffset) await this.processIfd(nextOffset);
162
145
  }
163
146
 
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);
147
+ private async readIfd(offset: number): Promise<{ nextOffset: number; image: CogTiffImage }> {
148
+ if (!this.source.hasBytes(offset, 1024)) await this.source.loadBytes(offset, this.source.chunkSize);
166
149
  const view = this.cursor.seekTo(offset);
167
150
  const tagCount = view.offset();
168
151
  const byteStart = offset + this.ifdConfig.offset;
169
- const logger = log?.child({ imageId: this.images.length });
170
152
  const tags: Map<TiffTag, CogTiffTagBase> = new Map();
171
153
 
172
154
  let pos = byteStart;
@@ -174,33 +156,7 @@ export class CogTiff {
174
156
  const tag = CogTiffTag.create(this, pos);
175
157
  pos += tag.size;
176
158
 
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
-
159
+ if (tag.name == null) throw new Error('Unknown IFD Tag: ' + toHexString(tag.id));
204
160
  tags.set(tag.id, tag);
205
161
  }
206
162
 
@@ -2,6 +2,7 @@
2
2
  * MimeType conversion for common tif image types
3
3
  */
4
4
  export enum TiffMimeType {
5
+ NONE = 'application/octet-stream',
5
6
  JPEG = 'image/jpeg',
6
7
  JP2 = 'image/jp2',
7
8
  WEBP = 'image/webp',
@@ -10,6 +11,7 @@ export enum TiffMimeType {
10
11
  }
11
12
 
12
13
  export const TiffCompression: { [key: number]: TiffMimeType } = {
14
+ 1: TiffMimeType.NONE,
13
15
  5: TiffMimeType.LZW,
14
16
  6: TiffMimeType.JPEG,
15
17
  7: TiffMimeType.JPEG,
@@ -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 () => {
@@ -34,7 +34,7 @@ export abstract class CogTiffTagBase<T = unknown> {
34
34
  this.name = TiffTag[this.id];
35
35
 
36
36
  this.dataType = this.tiff.source.getUint16(offset + 2);
37
- this.dataCount = this.tiff.source.getUint(offset + 4, this.tiff.ifdConfig.offset);
37
+ this.dataCount = this.tiff.source.getUint(offset + 4, this.tiff.ifdConfig.pointer);
38
38
  this.dataTypeSize = getTiffTagSize(this.dataType);
39
39
  this.dataLength = this.dataTypeSize * this.dataCount;
40
40
  }
@@ -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
  }
@@ -10,6 +10,6 @@ export class CogTifTagStatic<T> extends CogTiffTagBase<T> {
10
10
  this._value = this.readValue();
11
11
  this._isRead = true;
12
12
  }
13
- return this._value!;
13
+ return this._value as T;
14
14
  }
15
15
  }