@gisatcz/deckgl-geolib 2.4.0 → 2.5.0-dev.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/README.md +1 -0
- package/dist/cjs/index.js +1514 -545
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/index.min.js +2 -2
- package/dist/cjs/index.min.js.map +1 -1
- package/dist/esm/index.js +1514 -545
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.min.js +2 -2
- package/dist/esm/index.min.js.map +1 -1
- package/dist/esm/types/core/CogTiles.d.ts +4 -2
- package/dist/esm/types/core/GeoImage.d.ts +2 -0
- package/dist/esm/types/core/lib/BitmapGenerator.d.ts +21 -1
- package/dist/esm/types/core/lib/KernelGenerator.d.ts +12 -0
- package/dist/esm/types/core/lib/ReliefCompositor.d.ts +28 -0
- package/dist/esm/types/core/lib/TerrainGenerator.d.ts +6 -1
- package/dist/esm/types/core/types.d.ts +16 -0
- package/package.json +2 -2
package/dist/cjs/index.js
CHANGED
|
@@ -447,30 +447,31 @@ function findTagsByName(xml, tagName, options) {
|
|
|
447
447
|
return tags;
|
|
448
448
|
}
|
|
449
449
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
450
|
+
/** @import {TypedArray} from './geotiff.js' */
|
|
451
|
+
const fieldTypes = {
|
|
452
|
+
BYTE: /** @type {1} */ (0x0001),
|
|
453
|
+
ASCII: /** @type {2} */ (0x0002),
|
|
454
|
+
SHORT: /** @type {3} */ (0x0003),
|
|
455
|
+
LONG: /** @type {4} */ (0x0004),
|
|
456
|
+
RATIONAL: /** @type {5} */ (0x0005),
|
|
457
|
+
SBYTE: /** @type {6} */ (0x0006),
|
|
458
|
+
UNDEFINED: /** @type {7} */ (0x0007),
|
|
459
|
+
SSHORT: /** @type {8} */ (0x0008),
|
|
460
|
+
SLONG: /** @type {9} */ (0x0009),
|
|
461
|
+
SRATIONAL: /** @type {10} */ (0x000a),
|
|
462
|
+
FLOAT: /** @type {11} */ (0x000b),
|
|
463
|
+
DOUBLE: /** @type {12} */ (0x000c),
|
|
463
464
|
// IFD offset, suggested by https://owl.phy.queensu.ca/~phil/exiftool/standards.html
|
|
464
|
-
IFD: 0x000d,
|
|
465
|
+
IFD: /** @type {13} */ (0x000d),
|
|
465
466
|
// introduced by BigTIFF
|
|
466
|
-
LONG8: 0x0010,
|
|
467
|
-
SLONG8: 0x0011,
|
|
468
|
-
IFD8: 0x0012,
|
|
469
|
-
}
|
|
467
|
+
LONG8: /** @type {16} */ (0x0010),
|
|
468
|
+
SLONG8: /** @type {17} */ (0x0011),
|
|
469
|
+
IFD8: /** @type {18} */ (0x0012),
|
|
470
|
+
};
|
|
470
471
|
/** @typedef {keyof fieldTypes} FieldTypeName */
|
|
471
472
|
/** @typedef {fieldTypes[keyof typeof fieldTypes]} FieldType */
|
|
472
473
|
/** @typedef {Record<FieldTypeName, number>} FieldTypeSizes */
|
|
473
|
-
const fieldTypeSizes =
|
|
474
|
+
const fieldTypeSizes = /** @type {const} */ ({
|
|
474
475
|
[fieldTypes.BYTE]: 1,
|
|
475
476
|
[fieldTypes.ASCII]: 1,
|
|
476
477
|
[fieldTypes.SBYTE]: 1,
|
|
@@ -502,219 +503,196 @@ function getFieldTypeSize(fieldType) {
|
|
|
502
503
|
}
|
|
503
504
|
return size;
|
|
504
505
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
},
|
|
517
|
-
{ tag:
|
|
518
|
-
{ tag:
|
|
519
|
-
{ tag:
|
|
520
|
-
{ tag:
|
|
521
|
-
{ tag:
|
|
522
|
-
{ tag:
|
|
523
|
-
{ tag:
|
|
524
|
-
{ tag:
|
|
525
|
-
{ tag:
|
|
526
|
-
{ tag:
|
|
527
|
-
{ tag:
|
|
528
|
-
{ tag:
|
|
529
|
-
{ tag:
|
|
530
|
-
{ tag:
|
|
531
|
-
{ tag:
|
|
532
|
-
{ tag:
|
|
533
|
-
{ tag:
|
|
534
|
-
{ tag:
|
|
535
|
-
{ tag:
|
|
536
|
-
{ tag:
|
|
537
|
-
{ tag:
|
|
538
|
-
{ tag:
|
|
539
|
-
{ tag:
|
|
540
|
-
{ tag:
|
|
541
|
-
{ tag:
|
|
542
|
-
{ tag:
|
|
543
|
-
{
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
},
|
|
549
|
-
{ tag:
|
|
550
|
-
{ tag:
|
|
551
|
-
{ tag:
|
|
552
|
-
{ tag:
|
|
553
|
-
{ tag:
|
|
554
|
-
{ tag:
|
|
555
|
-
{ tag:
|
|
556
|
-
{ tag:
|
|
557
|
-
{ tag:
|
|
558
|
-
{ tag:
|
|
559
|
-
{ tag:
|
|
560
|
-
{
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
},
|
|
566
|
-
{ tag:
|
|
567
|
-
{ tag:
|
|
568
|
-
{ tag:
|
|
569
|
-
{ tag:
|
|
570
|
-
{ tag:
|
|
571
|
-
{ tag:
|
|
572
|
-
{ tag:
|
|
573
|
-
{ tag:
|
|
574
|
-
{ tag:
|
|
575
|
-
{ tag:
|
|
576
|
-
{ tag:
|
|
577
|
-
{ tag:
|
|
578
|
-
{
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
},
|
|
585
|
-
{ tag:
|
|
586
|
-
{ tag:
|
|
587
|
-
{ tag:
|
|
588
|
-
{ tag:
|
|
589
|
-
{ tag:
|
|
590
|
-
{ tag:
|
|
591
|
-
{ tag:
|
|
592
|
-
{
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
},
|
|
604
|
-
{ tag:
|
|
605
|
-
{ tag:
|
|
606
|
-
{ tag:
|
|
607
|
-
{
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
},
|
|
613
|
-
{ tag:
|
|
614
|
-
{ tag:
|
|
615
|
-
{
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
},
|
|
621
|
-
{ tag:
|
|
622
|
-
|
|
623
|
-
{ tag:
|
|
624
|
-
{ tag:
|
|
625
|
-
{ tag:
|
|
626
|
-
{ tag:
|
|
627
|
-
{ tag:
|
|
628
|
-
{ tag:
|
|
629
|
-
{ tag:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
{
|
|
681
|
-
tag: 34264,
|
|
682
|
-
name: 'ModelTransformation',
|
|
683
|
-
type: fieldTypes.DOUBLE,
|
|
684
|
-
isArray: true,
|
|
685
|
-
eager: true,
|
|
686
|
-
},
|
|
687
|
-
{
|
|
688
|
-
tag: 34735,
|
|
689
|
-
name: 'GeoKeyDirectory',
|
|
690
|
-
type: fieldTypes.SHORT,
|
|
691
|
-
isArray: true,
|
|
692
|
-
eager: true,
|
|
693
|
-
},
|
|
694
|
-
{
|
|
695
|
-
tag: 34736,
|
|
696
|
-
name: 'GeoDoubleParams',
|
|
697
|
-
type: fieldTypes.DOUBLE,
|
|
698
|
-
isArray: true,
|
|
699
|
-
eager: true,
|
|
700
|
-
},
|
|
701
|
-
{ tag: 34737, name: 'GeoAsciiParams', type: fieldTypes.ASCII, eager: true },
|
|
702
|
-
// LERC
|
|
703
|
-
{ tag: 50674, name: 'LercParameters', eager: true },
|
|
704
|
-
];
|
|
506
|
+
/**
|
|
507
|
+
* @typedef {Object} TagDictionaryEntry
|
|
508
|
+
* @property {number} tag
|
|
509
|
+
* @property {string} [name]
|
|
510
|
+
* @property {number} [type]
|
|
511
|
+
* @property {boolean} [isArray]
|
|
512
|
+
* @property {boolean} [eager]
|
|
513
|
+
*/
|
|
514
|
+
const tagDictionary = /** @type {const} */ ({
|
|
515
|
+
NewSubfileType: { tag: 254, type: fieldTypes.LONG, eager: true },
|
|
516
|
+
SubfileType: { tag: 255, type: fieldTypes.SHORT, eager: true },
|
|
517
|
+
ImageWidth: { tag: 256, type: fieldTypes.SHORT, eager: true },
|
|
518
|
+
ImageLength: { tag: 257, type: fieldTypes.SHORT, eager: true },
|
|
519
|
+
BitsPerSample: { tag: 258, type: fieldTypes.SHORT, isArray: true, eager: true },
|
|
520
|
+
Compression: { tag: 259, type: fieldTypes.SHORT, eager: true },
|
|
521
|
+
PhotometricInterpretation: { tag: 262, type: fieldTypes.SHORT, eager: true },
|
|
522
|
+
Threshholding: { tag: 263, type: fieldTypes.SHORT },
|
|
523
|
+
CellWidth: { tag: 264, type: fieldTypes.SHORT },
|
|
524
|
+
CellLength: { tag: 265, type: fieldTypes.SHORT },
|
|
525
|
+
FillOrder: { tag: 266, type: fieldTypes.SHORT },
|
|
526
|
+
DocumentName: { tag: 269, type: fieldTypes.ASCII },
|
|
527
|
+
ImageDescription: { tag: 270, type: fieldTypes.ASCII },
|
|
528
|
+
Make: { tag: 271, type: fieldTypes.ASCII },
|
|
529
|
+
Model: { tag: 272, type: fieldTypes.ASCII },
|
|
530
|
+
StripOffsets: { tag: 273, type: fieldTypes.SHORT, isArray: true },
|
|
531
|
+
Orientation: { tag: 274, type: fieldTypes.SHORT },
|
|
532
|
+
SamplesPerPixel: { tag: 277, type: fieldTypes.SHORT, eager: true },
|
|
533
|
+
RowsPerStrip: { tag: 278, type: fieldTypes.SHORT, eager: true },
|
|
534
|
+
StripByteCounts: { tag: 279, type: fieldTypes.LONG, isArray: true },
|
|
535
|
+
MinSampleValue: { tag: 280, type: fieldTypes.SHORT, isArray: true },
|
|
536
|
+
MaxSampleValue: { tag: 281, type: fieldTypes.SHORT, isArray: true },
|
|
537
|
+
XResolution: { tag: 282, type: fieldTypes.RATIONAL },
|
|
538
|
+
YResolution: { tag: 283, type: fieldTypes.RATIONAL },
|
|
539
|
+
PlanarConfiguration: { tag: 284, type: fieldTypes.SHORT, eager: true },
|
|
540
|
+
PageName: { tag: 285, type: fieldTypes.ASCII },
|
|
541
|
+
XPosition: { tag: 286, type: fieldTypes.RATIONAL },
|
|
542
|
+
YPosition: { tag: 287, type: fieldTypes.RATIONAL },
|
|
543
|
+
FreeOffsets: { tag: 288, type: fieldTypes.LONG },
|
|
544
|
+
FreeByteCounts: { tag: 289, type: fieldTypes.LONG },
|
|
545
|
+
GrayResponseUnit: { tag: 290, type: fieldTypes.SHORT },
|
|
546
|
+
GrayResponseCurve: { tag: 291, type: fieldTypes.SHORT, isArray: true },
|
|
547
|
+
T4Options: { tag: 292, type: fieldTypes.LONG },
|
|
548
|
+
T6Options: { tag: 293, type: fieldTypes.LONG },
|
|
549
|
+
ResolutionUnit: { tag: 296, type: fieldTypes.SHORT },
|
|
550
|
+
PageNumber: { tag: 297, type: fieldTypes.SHORT, isArray: true },
|
|
551
|
+
TransferFunction: { tag: 301, type: fieldTypes.SHORT, isArray: true },
|
|
552
|
+
Software: { tag: 305, type: fieldTypes.ASCII },
|
|
553
|
+
DateTime: { tag: 306, type: fieldTypes.ASCII },
|
|
554
|
+
Artist: { tag: 315, type: fieldTypes.ASCII },
|
|
555
|
+
HostComputer: { tag: 316, type: fieldTypes.ASCII },
|
|
556
|
+
Predictor: { tag: 317, type: fieldTypes.SHORT },
|
|
557
|
+
WhitePoint: { tag: 318, type: fieldTypes.RATIONAL, isArray: true },
|
|
558
|
+
PrimaryChromaticities: { tag: 319, type: fieldTypes.RATIONAL, isArray: true },
|
|
559
|
+
ColorMap: { tag: 320, type: fieldTypes.SHORT, isArray: true },
|
|
560
|
+
HalftoneHints: { tag: 321, type: fieldTypes.SHORT, isArray: true },
|
|
561
|
+
TileWidth: { tag: 322, type: fieldTypes.SHORT, eager: true },
|
|
562
|
+
TileLength: { tag: 323, type: fieldTypes.SHORT, eager: true },
|
|
563
|
+
TileOffsets: { tag: 324, type: fieldTypes.LONG, isArray: true },
|
|
564
|
+
TileByteCounts: { tag: 325, type: fieldTypes.SHORT, isArray: true },
|
|
565
|
+
InkSet: { tag: 332, type: fieldTypes.SHORT },
|
|
566
|
+
InkNames: { tag: 333, type: fieldTypes.ASCII },
|
|
567
|
+
NumberOfInks: { tag: 334, type: fieldTypes.SHORT },
|
|
568
|
+
DotRange: { tag: 336, type: fieldTypes.BYTE, isArray: true },
|
|
569
|
+
TargetPrinter: { tag: 337, type: fieldTypes.ASCII },
|
|
570
|
+
ExtraSamples: { tag: 338, type: fieldTypes.BYTE, isArray: true, eager: true },
|
|
571
|
+
SampleFormat: { tag: 339, type: fieldTypes.SHORT, isArray: true, eager: true },
|
|
572
|
+
SMinSampleValue: { tag: 340, isArray: true },
|
|
573
|
+
SMaxSampleValue: { tag: 341, isArray: true },
|
|
574
|
+
TransferRange: { tag: 342, type: fieldTypes.SHORT, isArray: true },
|
|
575
|
+
JPEGProc: { tag: 512, type: fieldTypes.SHORT },
|
|
576
|
+
JPEGInterchangeFormat: { tag: 513, type: fieldTypes.LONG },
|
|
577
|
+
JPEGInterchangeFormatLngth: { tag: 514, type: fieldTypes.LONG },
|
|
578
|
+
JPEGRestartInterval: { tag: 515, type: fieldTypes.SHORT },
|
|
579
|
+
JPEGLosslessPredictors: { tag: 517, type: fieldTypes.SHORT, isArray: true },
|
|
580
|
+
JPEGPointTransforms: { tag: 518, type: fieldTypes.SHORT, isArray: true },
|
|
581
|
+
JPEGQTables: { tag: 519, type: fieldTypes.LONG, isArray: true },
|
|
582
|
+
JPEGDCTables: { tag: 520, type: fieldTypes.LONG, isArray: true },
|
|
583
|
+
JPEGACTables: { tag: 521, type: fieldTypes.LONG, isArray: true },
|
|
584
|
+
YCbCrCoefficients: { tag: 529, type: fieldTypes.RATIONAL, isArray: true },
|
|
585
|
+
YCbCrSubSampling: { tag: 530, type: fieldTypes.SHORT, isArray: true },
|
|
586
|
+
YCbCrPositioning: { tag: 531, type: fieldTypes.SHORT },
|
|
587
|
+
ReferenceBlackWhite: { tag: 532, type: fieldTypes.LONG, isArray: true },
|
|
588
|
+
Copyright: { tag: 33432, type: fieldTypes.ASCII },
|
|
589
|
+
BadFaxLines: { tag: 326 },
|
|
590
|
+
CleanFaxData: { tag: 327 },
|
|
591
|
+
ClipPath: { tag: 343 },
|
|
592
|
+
ConsecutiveBadFaxLines: { tag: 328 },
|
|
593
|
+
Decode: { tag: 433 },
|
|
594
|
+
DefaultImageColor: { tag: 434 },
|
|
595
|
+
Indexed: { tag: 346 },
|
|
596
|
+
JPEGTables: { tag: 347, isArray: true, eager: true },
|
|
597
|
+
StripRowCounts: { tag: 559, isArray: true },
|
|
598
|
+
SubIFDs: { tag: 330, isArray: true },
|
|
599
|
+
XClipPathUnits: { tag: 344 },
|
|
600
|
+
YClipPathUnits: { tag: 345 },
|
|
601
|
+
ApertureValue: { tag: 37378 },
|
|
602
|
+
ColorSpace: { tag: 40961 },
|
|
603
|
+
DateTimeDigitized: { tag: 36868 },
|
|
604
|
+
DateTimeOriginal: { tag: 36867 },
|
|
605
|
+
ExifIFD: { tag: 34665, name: 'Exif IFD', type: fieldTypes.LONG },
|
|
606
|
+
ExifVersion: { tag: 36864 },
|
|
607
|
+
ExposureTime: { tag: 33434 },
|
|
608
|
+
FileSource: { tag: 41728 },
|
|
609
|
+
Flash: { tag: 37385 },
|
|
610
|
+
FlashpixVersion: { tag: 40960 },
|
|
611
|
+
FNumber: { tag: 33437 },
|
|
612
|
+
ImageUniqueID: { tag: 42016 },
|
|
613
|
+
LightSource: { tag: 37384 },
|
|
614
|
+
MakerNote: { tag: 37500 },
|
|
615
|
+
ShutterSpeedValue: { tag: 37377 },
|
|
616
|
+
UserComment: { tag: 37510 },
|
|
617
|
+
IPTC: { tag: 33723 },
|
|
618
|
+
CZ_LSMINFO: { tag: 34412 },
|
|
619
|
+
ICCProfile: { tag: 34675, name: 'ICC Profile' },
|
|
620
|
+
XMP: { tag: 700 },
|
|
621
|
+
GDAL_METADATA: { tag: 42112 },
|
|
622
|
+
GDAL_NODATA: { tag: 42113, type: fieldTypes.ASCII, eager: true },
|
|
623
|
+
Photoshop: { tag: 34377 },
|
|
624
|
+
ModelPixelScale: { tag: 33550, type: fieldTypes.DOUBLE, isArray: true, eager: true },
|
|
625
|
+
ModelTiepoint: { tag: 33922, type: fieldTypes.DOUBLE, isArray: true, eager: true },
|
|
626
|
+
ModelTransformation: { tag: 34264, type: fieldTypes.DOUBLE, isArray: true, eager: true },
|
|
627
|
+
GeoKeyDirectory: { tag: 34735, type: fieldTypes.SHORT, isArray: true, eager: true },
|
|
628
|
+
GeoDoubleParams: { tag: 34736, type: fieldTypes.DOUBLE, isArray: true, eager: true },
|
|
629
|
+
GeoAsciiParams: { tag: 34737, type: fieldTypes.ASCII, eager: true },
|
|
630
|
+
LercParameters: { tag: 50674, eager: true },
|
|
631
|
+
});
|
|
632
|
+
/** @typedef {keyof typeof tagDictionary} TagName */
|
|
633
|
+
/** @typedef {typeof tagDictionary[keyof typeof tagDictionary]['tag']} Tag */
|
|
634
|
+
/**
|
|
635
|
+
* @typedef {Extract<keyof typeof tagDictionary,
|
|
636
|
+
* { [K in keyof typeof tagDictionary]: (typeof tagDictionary)[K] extends { eager: true }
|
|
637
|
+
* ? K : never }[keyof typeof tagDictionary]>} EagerTagName
|
|
638
|
+
*/
|
|
639
|
+
/**
|
|
640
|
+
* @typedef {Extract<Tag, { [K in keyof typeof tagDictionary]: (typeof tagDictionary)[K] extends { eager: true }
|
|
641
|
+
* ? (typeof tagDictionary)[K]['tag'] : never }[keyof typeof tagDictionary]>} EagerTag
|
|
642
|
+
*/
|
|
643
|
+
/** @typedef {Extract<typeof tagDictionary[keyof typeof tagDictionary], {type: any}>['type']} TagType */
|
|
644
|
+
/**
|
|
645
|
+
* @template {number} T
|
|
646
|
+
* @typedef {T extends 2 ? string : number} GeoTiffPrimitive
|
|
647
|
+
*/
|
|
648
|
+
/**
|
|
649
|
+
* @template {TagName} T
|
|
650
|
+
* @typedef {typeof tagDictionary[T]} TagDef
|
|
651
|
+
*/
|
|
652
|
+
/**
|
|
653
|
+
* @typedef {{
|
|
654
|
+
* 1: number;
|
|
655
|
+
* 2: string;
|
|
656
|
+
* 3: number;
|
|
657
|
+
* 4: number;
|
|
658
|
+
* 5: number;
|
|
659
|
+
* 6: number;
|
|
660
|
+
* 7: ArrayBuffer;
|
|
661
|
+
* 8: number;
|
|
662
|
+
* 9: number;
|
|
663
|
+
* 10: number;
|
|
664
|
+
* 11: number;
|
|
665
|
+
* 12: number;
|
|
666
|
+
* 16: number;
|
|
667
|
+
* 17: number;
|
|
668
|
+
* 18: number;
|
|
669
|
+
* }} FieldTypeMap
|
|
670
|
+
*/
|
|
671
|
+
/**
|
|
672
|
+
* @template {TagName} T
|
|
673
|
+
* @typedef {TagDef<T> extends { isArray: true }
|
|
674
|
+
* ? (TagDef<T> extends { type: typeof fieldTypes.DOUBLE } ? number[] :
|
|
675
|
+
* TagDef<T> extends { type: typeof fieldTypes.ASCII } ? string[] :
|
|
676
|
+
* TagDef<T> extends { type: typeof fieldTypes.BYTE | typeof fieldTypes.SBYTE | typeof fieldTypes.UNDEFINED }
|
|
677
|
+
* ? Uint8Array | Int8Array :
|
|
678
|
+
* (number[] | TypedArray))
|
|
679
|
+
* : (TagDef<T> extends { type: keyof FieldTypeMap } ? FieldTypeMap[TagDef<T>['type']] : any)} TagValue
|
|
680
|
+
*/
|
|
705
681
|
/**
|
|
706
682
|
* Maps tag names to their numeric values
|
|
683
|
+
* @type {Record<string, number>}
|
|
707
684
|
*/
|
|
708
685
|
const tags = {};
|
|
709
686
|
/**
|
|
710
687
|
* Maps tag numbers to their definitions
|
|
688
|
+
* @type {Record<number, { tag: number, name: string, type: string|number|undefined, isArray: boolean, eager: boolean }>}
|
|
711
689
|
*/
|
|
712
690
|
const tagDefinitions = {};
|
|
713
691
|
/**
|
|
714
692
|
* Registers a new field tag
|
|
715
693
|
* @param {number} tag the numeric tiff tag
|
|
716
694
|
* @param {string} name the name of the tag that will be reported in the IFD
|
|
717
|
-
* @param {
|
|
695
|
+
* @param {keyof fieldTypes|number|undefined} type the tags data type
|
|
718
696
|
* @param {Boolean} isArray whether the tag is an array
|
|
719
697
|
* @param {boolean} [eager=false] whether to eagerly fetch deferred fields.
|
|
720
698
|
* When false (default), tags are loaded lazily on-demand.
|
|
@@ -724,8 +702,9 @@ function registerTag(tag, name, type, isArray = false, eager = false) {
|
|
|
724
702
|
tags[name] = tag;
|
|
725
703
|
tagDefinitions[tag] = { tag, name, type: typeof type === 'string' ? fieldTypes[type] : type, isArray, eager };
|
|
726
704
|
}
|
|
727
|
-
for (const
|
|
728
|
-
|
|
705
|
+
for (const [key, value] of Object.entries(tagDictionary)) {
|
|
706
|
+
const entry = /** @type {TagDictionaryEntry} */ (value);
|
|
707
|
+
registerTag(entry.tag, entry.name || key, entry.type, entry.isArray, entry.eager);
|
|
729
708
|
}
|
|
730
709
|
/**
|
|
731
710
|
* @param {number|string} tagIdentifier The field tag ID or name
|
|
@@ -755,7 +734,7 @@ const LercAddCompression = {
|
|
|
755
734
|
Deflate: 1,
|
|
756
735
|
Zstandard: 2,
|
|
757
736
|
};
|
|
758
|
-
const geoKeyNames =
|
|
737
|
+
const geoKeyNames = /** @type {const} */ ({
|
|
759
738
|
1024: 'GTModelTypeGeoKey',
|
|
760
739
|
1025: 'GTRasterTypeGeoKey',
|
|
761
740
|
1026: 'GTCitationGeoKey',
|
|
@@ -804,15 +783,7 @@ const geoKeyNames = Object.freeze({
|
|
|
804
783
|
4098: 'VerticalDatumGeoKey',
|
|
805
784
|
4099: 'VerticalUnitsGeoKey',
|
|
806
785
|
});
|
|
807
|
-
|
|
808
|
-
/**
|
|
809
|
-
* @type {Record<GeoKeyName, number>}
|
|
810
|
-
*/
|
|
811
|
-
const geoKeys = /** @type {Record<GeoKeyName, number>} */ ({});
|
|
812
|
-
for (const key in geoKeyNames) {
|
|
813
|
-
if (geoKeyNames.hasOwnProperty(key)) {
|
|
814
|
-
geoKeys[geoKeyNames[key]] = parseInt(key, 10);
|
|
815
|
-
}
|
|
786
|
+
for (const [key, name] of Object.entries(geoKeyNames)) {
|
|
816
787
|
}
|
|
817
788
|
|
|
818
789
|
function fromWhiteIsZero(raster, max) {
|
|
@@ -912,39 +883,40 @@ function fromCIELab(cieLabRaster) {
|
|
|
912
883
|
return rgbRaster;
|
|
913
884
|
}
|
|
914
885
|
|
|
915
|
-
|
|
886
|
+
/** @import BaseDecoder, {BaseDecoderParameters} from "./basedecoder.js" */
|
|
916
887
|
/**
|
|
917
|
-
* @typedef {Object}
|
|
918
|
-
* @property {
|
|
919
|
-
* @property {
|
|
920
|
-
* @property {
|
|
921
|
-
* @property {number} bitsPerSample
|
|
922
|
-
* @property {number} predictor
|
|
888
|
+
* @typedef {Object} RegistryEntry
|
|
889
|
+
* @property {function():Promise<typeof BaseDecoder>} importFn
|
|
890
|
+
* @property {function(import("../imagefiledirectory.js").ImageFileDirectory):Promise<BaseDecoderParameters>} decoderParameterFn
|
|
891
|
+
* @property {boolean} preferWorker
|
|
923
892
|
*/
|
|
893
|
+
/** @type {Map<number | undefined, RegistryEntry>} */
|
|
894
|
+
const registry = new Map();
|
|
924
895
|
/**
|
|
925
896
|
* Default decoder parameter retrieval function
|
|
926
|
-
* @param {import("../imagefiledirectory").ImageFileDirectory} fileDirectory
|
|
927
|
-
* @returns {Promise<
|
|
897
|
+
* @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory
|
|
898
|
+
* @returns {Promise<BaseDecoderParameters>}
|
|
928
899
|
*/
|
|
929
900
|
async function defaultDecoderParameterFn(fileDirectory) {
|
|
930
901
|
const isTiled = !fileDirectory.hasTag('StripOffsets');
|
|
931
|
-
return {
|
|
932
|
-
tileWidth: isTiled
|
|
933
|
-
|
|
902
|
+
return /** @type {BaseDecoderParameters} */ ({
|
|
903
|
+
tileWidth: isTiled
|
|
904
|
+
? await fileDirectory.loadValue('TileWidth')
|
|
905
|
+
: await fileDirectory.loadValue('ImageWidth'),
|
|
906
|
+
tileHeight: isTiled
|
|
907
|
+
? await fileDirectory.loadValue('TileLength')
|
|
908
|
+
: (await fileDirectory.loadValue('RowsPerStrip')
|
|
909
|
+
|| await fileDirectory.loadValue('ImageLength')),
|
|
934
910
|
planarConfiguration: await fileDirectory.loadValue('PlanarConfiguration'),
|
|
935
911
|
bitsPerSample: await fileDirectory.loadValue('BitsPerSample'),
|
|
936
912
|
predictor: await fileDirectory.loadValue('Predictor') || 1,
|
|
937
|
-
};
|
|
913
|
+
});
|
|
938
914
|
}
|
|
939
|
-
/**
|
|
940
|
-
* Either a number or undefined.
|
|
941
|
-
* @typedef {(number|undefined)} NumberOrUndefined
|
|
942
|
-
*/
|
|
943
915
|
/**
|
|
944
916
|
* Register a decoder for a specific compression method or a range of compressions
|
|
945
|
-
* @param {(
|
|
946
|
-
* @param {function():Promise} importFn the function to import the decoder
|
|
947
|
-
* @param {function(import("../imagefiledirectory").ImageFileDirectory):Promise} decoderParameterFn
|
|
917
|
+
* @param {(number|undefined|(number|undefined)[])} cases ids of the compression methods to register for
|
|
918
|
+
* @param {function():Promise<typeof BaseDecoder>} importFn the function to import the decoder
|
|
919
|
+
* @param {function(import("../imagefiledirectory.js").ImageFileDirectory):Promise<BaseDecoderParameters>} decoderParameterFn
|
|
948
920
|
* @param {boolean} preferWorker_ Whether to prefer running the decoder in a worker
|
|
949
921
|
*/
|
|
950
922
|
function addDecoder(cases, importFn, decoderParameterFn = defaultDecoderParameterFn, preferWorker_ = true) {
|
|
@@ -957,27 +929,27 @@ function addDecoder(cases, importFn, decoderParameterFn = defaultDecoderParamete
|
|
|
957
929
|
}
|
|
958
930
|
/**
|
|
959
931
|
* Get the required decoder parameters for a specific compression method
|
|
960
|
-
* @param {
|
|
932
|
+
* @param {number|undefined} compression
|
|
961
933
|
* @param {import('../imagefiledirectory.js').ImageFileDirectory} fileDirectory
|
|
962
934
|
*/
|
|
963
935
|
async function getDecoderParameters(compression, fileDirectory) {
|
|
964
936
|
if (!registry.has(compression)) {
|
|
965
937
|
throw new Error(`Unknown compression method identifier: ${compression}`);
|
|
966
938
|
}
|
|
967
|
-
const { decoderParameterFn } = registry.get(compression);
|
|
939
|
+
const { decoderParameterFn } = /** @type {RegistryEntry} */ (registry.get(compression));
|
|
968
940
|
return decoderParameterFn(fileDirectory);
|
|
969
941
|
}
|
|
970
942
|
/**
|
|
971
943
|
* Get a decoder for a specific compression and parameters
|
|
972
944
|
* @param {number} compression the compression method identifier
|
|
973
|
-
* @param {
|
|
945
|
+
* @param {BaseDecoderParameters} decoderParameters the parameters for the decoder
|
|
974
946
|
* @returns {Promise<import('./basedecoder.js').default>}
|
|
975
947
|
*/
|
|
976
948
|
async function getDecoder(compression, decoderParameters) {
|
|
977
949
|
if (!registry.has(compression)) {
|
|
978
950
|
throw new Error(`Unknown compression method identifier: ${compression}`);
|
|
979
951
|
}
|
|
980
|
-
const { importFn } = registry.get(compression);
|
|
952
|
+
const { importFn } = /** @type {RegistryEntry} */ (registry.get(compression));
|
|
981
953
|
const Decoder = await importFn();
|
|
982
954
|
return new Decoder(decoderParameters);
|
|
983
955
|
}
|
|
@@ -1004,6 +976,9 @@ const defaultDecoderDefinitions = [
|
|
|
1004
976
|
{
|
|
1005
977
|
cases: 7,
|
|
1006
978
|
importFn: () => Promise.resolve().then(function () { return jpeg; }).then((m) => m.default),
|
|
979
|
+
/**
|
|
980
|
+
* @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory
|
|
981
|
+
*/
|
|
1007
982
|
decoderParameterFn: async (fileDirectory) => {
|
|
1008
983
|
return {
|
|
1009
984
|
...await defaultDecoderParameterFn(fileDirectory),
|
|
@@ -1030,6 +1005,9 @@ const defaultDecoderDefinitions = [
|
|
|
1030
1005
|
return m;
|
|
1031
1006
|
})
|
|
1032
1007
|
.then((m) => m.default),
|
|
1008
|
+
/**
|
|
1009
|
+
* @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory
|
|
1010
|
+
*/
|
|
1033
1011
|
decoderParameterFn: async (fileDirectory) => {
|
|
1034
1012
|
return {
|
|
1035
1013
|
...await defaultDecoderParameterFn(fileDirectory),
|
|
@@ -1051,10 +1029,13 @@ const defaultDecoderDefinitions = [
|
|
|
1051
1029
|
{
|
|
1052
1030
|
cases: 50001,
|
|
1053
1031
|
importFn: () => Promise.resolve().then(function () { return webimage; }).then((m) => m.default),
|
|
1032
|
+
/**
|
|
1033
|
+
* @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory
|
|
1034
|
+
*/
|
|
1054
1035
|
decoderParameterFn: async (fileDirectory) => {
|
|
1055
1036
|
return {
|
|
1056
1037
|
...await defaultDecoderParameterFn(fileDirectory),
|
|
1057
|
-
samplesPerPixel: await fileDirectory.loadValue('SamplesPerPixel') || 4,
|
|
1038
|
+
samplesPerPixel: Number(await fileDirectory.loadValue('SamplesPerPixel')) || 4,
|
|
1058
1039
|
};
|
|
1059
1040
|
},
|
|
1060
1041
|
preferWorker: false,
|
|
@@ -1069,17 +1050,23 @@ for (const decoderDefinition of defaultDecoderDefinitions) {
|
|
|
1069
1050
|
/**
|
|
1070
1051
|
* @module resample
|
|
1071
1052
|
*/
|
|
1053
|
+
/**
|
|
1054
|
+
* @param {import("./geotiff.js").TypedArray} array
|
|
1055
|
+
* @param {number} width
|
|
1056
|
+
* @param {number} height
|
|
1057
|
+
* @param {number} [samplesPerPixel=1]
|
|
1058
|
+
*/
|
|
1072
1059
|
function copyNewSize(array, width, height, samplesPerPixel = 1) {
|
|
1073
1060
|
return new (Object.getPrototypeOf(array).constructor)(width * height * samplesPerPixel);
|
|
1074
1061
|
}
|
|
1075
1062
|
/**
|
|
1076
1063
|
* Resample the input arrays using nearest neighbor value selection.
|
|
1077
|
-
* @param {import("./geotiff").TypedArray[]} valueArrays The input arrays to resample
|
|
1064
|
+
* @param {import("./geotiff.js").TypedArray[]} valueArrays The input arrays to resample
|
|
1078
1065
|
* @param {number} inWidth The width of the input rasters
|
|
1079
1066
|
* @param {number} inHeight The height of the input rasters
|
|
1080
1067
|
* @param {number} outWidth The desired width of the output rasters
|
|
1081
1068
|
* @param {number} outHeight The desired height of the output rasters
|
|
1082
|
-
* @returns {import("./geotiff").TypedArray[]} The resampled rasters
|
|
1069
|
+
* @returns {import("./geotiff.js").TypedArray[]} The resampled rasters
|
|
1083
1070
|
*/
|
|
1084
1071
|
function resampleNearest(valueArrays, inWidth, inHeight, outWidth, outHeight) {
|
|
1085
1072
|
const relX = inWidth / outWidth;
|
|
@@ -1099,17 +1086,22 @@ function resampleNearest(valueArrays, inWidth, inHeight, outWidth, outHeight) {
|
|
|
1099
1086
|
}
|
|
1100
1087
|
// simple linear interpolation, code from:
|
|
1101
1088
|
// https://en.wikipedia.org/wiki/Linear_interpolation#Programming_language_support
|
|
1089
|
+
/**
|
|
1090
|
+
* @param {number} v0
|
|
1091
|
+
* @param {number} v1
|
|
1092
|
+
* @param {number} t
|
|
1093
|
+
*/
|
|
1102
1094
|
function lerp(v0, v1, t) {
|
|
1103
1095
|
return ((1 - t) * v0) + (t * v1);
|
|
1104
1096
|
}
|
|
1105
1097
|
/**
|
|
1106
1098
|
* Resample the input arrays using bilinear interpolation.
|
|
1107
|
-
* @param {import("./geotiff").TypedArray[]} valueArrays The input arrays to resample
|
|
1099
|
+
* @param {import("./geotiff.js").TypedArray[]} valueArrays The input arrays to resample
|
|
1108
1100
|
* @param {number} inWidth The width of the input rasters
|
|
1109
1101
|
* @param {number} inHeight The height of the input rasters
|
|
1110
1102
|
* @param {number} outWidth The desired width of the output rasters
|
|
1111
1103
|
* @param {number} outHeight The desired height of the output rasters
|
|
1112
|
-
* @returns {import("./geotiff").TypedArray[]} The resampled rasters
|
|
1104
|
+
* @returns {import("./geotiff.js").TypedArray[]} The resampled rasters
|
|
1113
1105
|
*/
|
|
1114
1106
|
function resampleBilinear(valueArrays, inWidth, inHeight, outWidth, outHeight) {
|
|
1115
1107
|
const relX = inWidth / outWidth;
|
|
@@ -1138,13 +1130,13 @@ function resampleBilinear(valueArrays, inWidth, inHeight, outWidth, outHeight) {
|
|
|
1138
1130
|
}
|
|
1139
1131
|
/**
|
|
1140
1132
|
* Resample the input arrays using the selected resampling method.
|
|
1141
|
-
* @param {import("./geotiff").TypedArray[]} valueArrays The input arrays to resample
|
|
1133
|
+
* @param {import("./geotiff.js").TypedArray[]} valueArrays The input arrays to resample
|
|
1142
1134
|
* @param {number} inWidth The width of the input rasters
|
|
1143
1135
|
* @param {number} inHeight The height of the input rasters
|
|
1144
1136
|
* @param {number} outWidth The desired width of the output rasters
|
|
1145
1137
|
* @param {number} outHeight The desired height of the output rasters
|
|
1146
1138
|
* @param {string} [method = 'nearest'] The desired resampling method
|
|
1147
|
-
* @returns {import("./geotiff").TypedArray[]} The resampled rasters
|
|
1139
|
+
* @returns {import("./geotiff.js").TypedArray[]} The resampled rasters
|
|
1148
1140
|
*/
|
|
1149
1141
|
function resample(valueArrays, inWidth, inHeight, outWidth, outHeight, method = 'nearest') {
|
|
1150
1142
|
switch (method.toLowerCase()) {
|
|
@@ -1159,14 +1151,14 @@ function resample(valueArrays, inWidth, inHeight, outWidth, outHeight, method =
|
|
|
1159
1151
|
}
|
|
1160
1152
|
/**
|
|
1161
1153
|
* Resample the pixel interleaved input array using nearest neighbor value selection.
|
|
1162
|
-
* @param {import("./geotiff").TypedArray} valueArray The input array to resample
|
|
1154
|
+
* @param {import("./geotiff.js").TypedArray} valueArray The input array to resample
|
|
1163
1155
|
* @param {number} inWidth The width of the input rasters
|
|
1164
1156
|
* @param {number} inHeight The height of the input rasters
|
|
1165
1157
|
* @param {number} outWidth The desired width of the output rasters
|
|
1166
1158
|
* @param {number} outHeight The desired height of the output rasters
|
|
1167
1159
|
* @param {number} samples The number of samples per pixel for pixel
|
|
1168
1160
|
* interleaved data
|
|
1169
|
-
* @returns {import("./geotiff").TypedArray} The resampled raster
|
|
1161
|
+
* @returns {import("./geotiff.js").TypedArray} The resampled raster
|
|
1170
1162
|
*/
|
|
1171
1163
|
function resampleNearestInterleaved(valueArray, inWidth, inHeight, outWidth, outHeight, samples) {
|
|
1172
1164
|
const relX = inWidth / outWidth;
|
|
@@ -1186,14 +1178,14 @@ function resampleNearestInterleaved(valueArray, inWidth, inHeight, outWidth, out
|
|
|
1186
1178
|
}
|
|
1187
1179
|
/**
|
|
1188
1180
|
* Resample the pixel interleaved input array using bilinear interpolation.
|
|
1189
|
-
* @param {import("./geotiff").TypedArray} valueArray The input array to resample
|
|
1181
|
+
* @param {import("./geotiff.js").TypedArray} valueArray The input array to resample
|
|
1190
1182
|
* @param {number} inWidth The width of the input rasters
|
|
1191
1183
|
* @param {number} inHeight The height of the input rasters
|
|
1192
1184
|
* @param {number} outWidth The desired width of the output rasters
|
|
1193
1185
|
* @param {number} outHeight The desired height of the output rasters
|
|
1194
1186
|
* @param {number} samples The number of samples per pixel for pixel
|
|
1195
1187
|
* interleaved data
|
|
1196
|
-
* @returns {import("./geotiff").TypedArray} The resampled raster
|
|
1188
|
+
* @returns {import("./geotiff.js").TypedArray} The resampled raster
|
|
1197
1189
|
*/
|
|
1198
1190
|
function resampleBilinearInterleaved(valueArray, inWidth, inHeight, outWidth, outHeight, samples) {
|
|
1199
1191
|
const relX = inWidth / outWidth;
|
|
@@ -1222,7 +1214,7 @@ function resampleBilinearInterleaved(valueArray, inWidth, inHeight, outWidth, ou
|
|
|
1222
1214
|
}
|
|
1223
1215
|
/**
|
|
1224
1216
|
* Resample the pixel interleaved input array using the selected resampling method.
|
|
1225
|
-
* @param {import("./geotiff").TypedArray} valueArray The input array to resample
|
|
1217
|
+
* @param {import("./geotiff.js").TypedArray} valueArray The input array to resample
|
|
1226
1218
|
* @param {number} inWidth The width of the input rasters
|
|
1227
1219
|
* @param {number} inHeight The height of the input rasters
|
|
1228
1220
|
* @param {number} outWidth The desired width of the output rasters
|
|
@@ -1230,7 +1222,7 @@ function resampleBilinearInterleaved(valueArray, inWidth, inHeight, outWidth, ou
|
|
|
1230
1222
|
* @param {number} samples The number of samples per pixel for pixel
|
|
1231
1223
|
* interleaved data
|
|
1232
1224
|
* @param {string} [method = 'nearest'] The desired resampling method
|
|
1233
|
-
* @returns {import("./geotiff").TypedArray} The resampled rasters
|
|
1225
|
+
* @returns {import("./geotiff.js").TypedArray} The resampled rasters
|
|
1234
1226
|
*/
|
|
1235
1227
|
function resampleInterleaved(valueArray, inWidth, inHeight, outWidth, outHeight, samples, method = 'nearest') {
|
|
1236
1228
|
switch (method.toLowerCase()) {
|
|
@@ -1245,10 +1237,16 @@ function resampleInterleaved(valueArray, inWidth, inHeight, outWidth, outHeight,
|
|
|
1245
1237
|
}
|
|
1246
1238
|
|
|
1247
1239
|
/** @module geotiffimage */
|
|
1248
|
-
/** @import {DecoderWorker, TypedArray} from "./geotiff" */
|
|
1249
|
-
/** @import {ReadRasterResult} from "./geotiff" */
|
|
1250
|
-
/** @import {ReadRastersOptions} from "./geotiff" */
|
|
1251
|
-
/** @import {ReadRGBOptions} from "./geotiff" */
|
|
1240
|
+
/** @import {DecoderWorker, TypedArray} from "./geotiff.js" */
|
|
1241
|
+
/** @import {ReadRasterResult} from "./geotiff.js" */
|
|
1242
|
+
/** @import {ReadRastersOptions} from "./geotiff.js" */
|
|
1243
|
+
/** @import {ReadRGBOptions} from "./geotiff.js" */
|
|
1244
|
+
/**
|
|
1245
|
+
* @param {Array<number>|TypedArray} array
|
|
1246
|
+
* @param {number} start
|
|
1247
|
+
* @param {number} end
|
|
1248
|
+
* @returns {number}
|
|
1249
|
+
*/
|
|
1252
1250
|
function sum(array, start, end) {
|
|
1253
1251
|
let s = 0;
|
|
1254
1252
|
for (let i = start; i < end; ++i) {
|
|
@@ -1256,42 +1254,64 @@ function sum(array, start, end) {
|
|
|
1256
1254
|
}
|
|
1257
1255
|
return s;
|
|
1258
1256
|
}
|
|
1259
|
-
|
|
1257
|
+
/**
|
|
1258
|
+
* @param {1|2|3} format
|
|
1259
|
+
* @param {number} bitsPerSample
|
|
1260
|
+
* @param {number|ArrayBufferLike} sizeOrData
|
|
1261
|
+
* @returns {TypedArray}
|
|
1262
|
+
*/
|
|
1263
|
+
function arrayForType(format, bitsPerSample, sizeOrData) {
|
|
1264
|
+
let TypedArrayConstructor;
|
|
1260
1265
|
switch (format) {
|
|
1261
1266
|
case 1: // unsigned integer data
|
|
1262
1267
|
if (bitsPerSample <= 8) {
|
|
1263
|
-
|
|
1268
|
+
TypedArrayConstructor = Uint8Array;
|
|
1264
1269
|
}
|
|
1265
1270
|
else if (bitsPerSample <= 16) {
|
|
1266
|
-
|
|
1271
|
+
TypedArrayConstructor = Uint16Array;
|
|
1267
1272
|
}
|
|
1268
1273
|
else if (bitsPerSample <= 32) {
|
|
1269
|
-
|
|
1274
|
+
TypedArrayConstructor = Uint32Array;
|
|
1270
1275
|
}
|
|
1271
1276
|
break;
|
|
1272
1277
|
case 2: // twos complement signed integer data
|
|
1273
1278
|
if (bitsPerSample === 8) {
|
|
1274
|
-
|
|
1279
|
+
TypedArrayConstructor = Int8Array;
|
|
1275
1280
|
}
|
|
1276
1281
|
else if (bitsPerSample === 16) {
|
|
1277
|
-
|
|
1282
|
+
TypedArrayConstructor = Int16Array;
|
|
1278
1283
|
}
|
|
1279
1284
|
else if (bitsPerSample === 32) {
|
|
1280
|
-
|
|
1285
|
+
TypedArrayConstructor = Int32Array;
|
|
1281
1286
|
}
|
|
1282
1287
|
break;
|
|
1283
1288
|
case 3: // floating point data
|
|
1284
1289
|
switch (bitsPerSample) {
|
|
1285
1290
|
case 16:
|
|
1286
1291
|
case 32:
|
|
1287
|
-
|
|
1292
|
+
TypedArrayConstructor = Float32Array;
|
|
1293
|
+
break;
|
|
1288
1294
|
case 64:
|
|
1289
|
-
|
|
1295
|
+
TypedArrayConstructor = Float64Array;
|
|
1296
|
+
break;
|
|
1290
1297
|
}
|
|
1291
1298
|
break;
|
|
1292
1299
|
}
|
|
1300
|
+
if (TypedArrayConstructor) {
|
|
1301
|
+
if (typeof sizeOrData === 'number') {
|
|
1302
|
+
return new TypedArrayConstructor(sizeOrData);
|
|
1303
|
+
}
|
|
1304
|
+
else if (sizeOrData instanceof ArrayBuffer) {
|
|
1305
|
+
return new TypedArrayConstructor(sizeOrData);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1293
1308
|
throw Error('Unsupported data format/bitsPerSample');
|
|
1294
1309
|
}
|
|
1310
|
+
/**
|
|
1311
|
+
* @param {1|2|3} format
|
|
1312
|
+
* @param {number} bitsPerSample
|
|
1313
|
+
* @returns {boolean}
|
|
1314
|
+
*/
|
|
1295
1315
|
function needsNormalization(format, bitsPerSample) {
|
|
1296
1316
|
if ((format === 1 || format === 2) && bitsPerSample <= 32 && bitsPerSample % 8 === 0) {
|
|
1297
1317
|
return false;
|
|
@@ -1301,6 +1321,16 @@ function needsNormalization(format, bitsPerSample) {
|
|
|
1301
1321
|
}
|
|
1302
1322
|
return true;
|
|
1303
1323
|
}
|
|
1324
|
+
/**
|
|
1325
|
+
* @param {ArrayBufferLike} inBuffer
|
|
1326
|
+
* @param {1|2|3} format
|
|
1327
|
+
* @param {1|2} planarConfiguration
|
|
1328
|
+
* @param {number} samplesPerPixel
|
|
1329
|
+
* @param {number} bitsPerSample
|
|
1330
|
+
* @param {number} tileWidth
|
|
1331
|
+
* @param {number} tileHeight
|
|
1332
|
+
* @returns {ArrayBufferLike}
|
|
1333
|
+
*/
|
|
1304
1334
|
function normalizeArray(inBuffer, format, planarConfiguration, samplesPerPixel, bitsPerSample, tileWidth, tileHeight) {
|
|
1305
1335
|
// const inByteArray = new Uint8Array(inBuffer);
|
|
1306
1336
|
const view = new DataView(inBuffer);
|
|
@@ -1374,7 +1404,7 @@ function normalizeArray(inBuffer, format, planarConfiguration, samplesPerPixel,
|
|
|
1374
1404
|
class GeoTIFFImage {
|
|
1375
1405
|
/**
|
|
1376
1406
|
* @constructor
|
|
1377
|
-
* @param {import("./imagefiledirectory").ImageFileDirectory} fileDirectory The parsed file directory
|
|
1407
|
+
* @param {import("./imagefiledirectory.js").ImageFileDirectory} fileDirectory The parsed file directory
|
|
1378
1408
|
* @param {Boolean} littleEndian Whether the file is encoded in little or big endian
|
|
1379
1409
|
* @param {Boolean} cache Whether or not decoded tiles shall be cached
|
|
1380
1410
|
* @param {import('./source/basesource.js').BaseSource} source The datasource to read from
|
|
@@ -1382,7 +1412,8 @@ class GeoTIFFImage {
|
|
|
1382
1412
|
constructor(fileDirectory, littleEndian, cache, source) {
|
|
1383
1413
|
this.fileDirectory = fileDirectory;
|
|
1384
1414
|
this.littleEndian = littleEndian;
|
|
1385
|
-
|
|
1415
|
+
/** @type {Array<Promise<ArrayBufferLike>>|null} */
|
|
1416
|
+
this.tiles = cache ? [] : null;
|
|
1386
1417
|
this.isTiled = !fileDirectory.hasTag('StripOffsets');
|
|
1387
1418
|
const planarConfiguration = fileDirectory.getValue('PlanarConfiguration') ?? 1;
|
|
1388
1419
|
if (planarConfiguration !== 1 && planarConfiguration !== 2) {
|
|
@@ -1394,7 +1425,7 @@ class GeoTIFFImage {
|
|
|
1394
1425
|
}
|
|
1395
1426
|
/**
|
|
1396
1427
|
* Returns the associated parsed file directory.
|
|
1397
|
-
* @returns {import("./imagefiledirectory").ImageFileDirectory} the parsed file directory
|
|
1428
|
+
* @returns {import("./imagefiledirectory.js").ImageFileDirectory} the parsed file directory
|
|
1398
1429
|
*/
|
|
1399
1430
|
getFileDirectory() {
|
|
1400
1431
|
return this.fileDirectory;
|
|
@@ -1411,46 +1442,50 @@ class GeoTIFFImage {
|
|
|
1411
1442
|
* @returns {Number} the width of the image
|
|
1412
1443
|
*/
|
|
1413
1444
|
getWidth() {
|
|
1414
|
-
return this.fileDirectory.getValue('ImageWidth');
|
|
1445
|
+
return this.fileDirectory.getValue('ImageWidth') || 0;
|
|
1415
1446
|
}
|
|
1416
1447
|
/**
|
|
1417
1448
|
* Returns the height of the image.
|
|
1418
1449
|
* @returns {Number} the height of the image
|
|
1419
1450
|
*/
|
|
1420
1451
|
getHeight() {
|
|
1421
|
-
return this.fileDirectory.getValue('ImageLength');
|
|
1452
|
+
return this.fileDirectory.getValue('ImageLength') || 0;
|
|
1422
1453
|
}
|
|
1423
1454
|
/**
|
|
1424
1455
|
* Returns the number of samples per pixel.
|
|
1425
|
-
* @returns {
|
|
1456
|
+
* @returns {number} the number of samples per pixel
|
|
1426
1457
|
*/
|
|
1427
1458
|
getSamplesPerPixel() {
|
|
1428
|
-
return this.fileDirectory.
|
|
1429
|
-
? this.fileDirectory.getValue('SamplesPerPixel') : 1;
|
|
1459
|
+
return this.fileDirectory.getValue('SamplesPerPixel') ?? 1;
|
|
1430
1460
|
}
|
|
1431
1461
|
/**
|
|
1432
1462
|
* Returns the width of each tile.
|
|
1433
|
-
* @returns {
|
|
1463
|
+
* @returns {number} the width of each tile
|
|
1434
1464
|
*/
|
|
1435
1465
|
getTileWidth() {
|
|
1436
|
-
return this.isTiled ? this.fileDirectory.getValue('TileWidth') : this.getWidth();
|
|
1466
|
+
return this.isTiled ? (this.fileDirectory.getValue('TileWidth') || 0) : this.getWidth();
|
|
1437
1467
|
}
|
|
1438
1468
|
/**
|
|
1439
1469
|
* Returns the height of each tile.
|
|
1440
|
-
* @returns {
|
|
1470
|
+
* @returns {number} the height of each tile
|
|
1441
1471
|
*/
|
|
1442
1472
|
getTileHeight() {
|
|
1443
1473
|
if (this.isTiled) {
|
|
1444
|
-
return this.fileDirectory.getValue('TileLength');
|
|
1474
|
+
return this.fileDirectory.getValue('TileLength') || 0;
|
|
1445
1475
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1476
|
+
const rowsPerStrip = this.fileDirectory.hasTag('RowsPerStrip') && this.fileDirectory.getValue('RowsPerStrip');
|
|
1477
|
+
if (rowsPerStrip) {
|
|
1478
|
+
return Math.min(rowsPerStrip, this.getHeight());
|
|
1448
1479
|
}
|
|
1449
1480
|
return this.getHeight();
|
|
1450
1481
|
}
|
|
1451
1482
|
getBlockWidth() {
|
|
1452
1483
|
return this.getTileWidth();
|
|
1453
1484
|
}
|
|
1485
|
+
/**
|
|
1486
|
+
* @param {number} y
|
|
1487
|
+
* @returns {number}
|
|
1488
|
+
*/
|
|
1454
1489
|
getBlockHeight(y) {
|
|
1455
1490
|
if (this.isTiled || (y + 1) * this.getTileHeight() <= this.getHeight()) {
|
|
1456
1491
|
return this.getTileHeight();
|
|
@@ -1467,22 +1502,32 @@ class GeoTIFFImage {
|
|
|
1467
1502
|
getBytesPerPixel() {
|
|
1468
1503
|
let bytes = 0;
|
|
1469
1504
|
// this is a short list, so we assume this is already loaded
|
|
1470
|
-
|
|
1505
|
+
const bitsPerSample = this.fileDirectory.getValue('BitsPerSample') || [];
|
|
1506
|
+
for (let i = 0; i < bitsPerSample.length; ++i) {
|
|
1471
1507
|
bytes += this.getSampleByteSize(i);
|
|
1472
1508
|
}
|
|
1473
1509
|
return bytes;
|
|
1474
1510
|
}
|
|
1511
|
+
/**
|
|
1512
|
+
* @param {number} i
|
|
1513
|
+
* @returns {number}
|
|
1514
|
+
*/
|
|
1475
1515
|
getSampleByteSize(i) {
|
|
1476
|
-
const bitsPerSample = this.fileDirectory.getValue('BitsPerSample');
|
|
1516
|
+
const bitsPerSample = this.fileDirectory.getValue('BitsPerSample') || [];
|
|
1477
1517
|
if (i >= bitsPerSample.length) {
|
|
1478
1518
|
throw new RangeError(`Sample index ${i} is out of range.`);
|
|
1479
1519
|
}
|
|
1480
1520
|
return Math.ceil(bitsPerSample[i] / 8);
|
|
1481
1521
|
}
|
|
1522
|
+
/**
|
|
1523
|
+
* @param {number} sampleIndex
|
|
1524
|
+
* @returns {(this: DataView, byteOffset: number, littleEndian: boolean) => number}
|
|
1525
|
+
*/
|
|
1482
1526
|
getReaderForSample(sampleIndex) {
|
|
1483
|
-
const
|
|
1484
|
-
|
|
1485
|
-
|
|
1527
|
+
const sampleFormat = this.fileDirectory.getValue('SampleFormat');
|
|
1528
|
+
const format = sampleFormat
|
|
1529
|
+
? sampleFormat[sampleIndex] : 1;
|
|
1530
|
+
const bitsPerSample = (this.fileDirectory.getValue('BitsPerSample') || [])[sampleIndex];
|
|
1486
1531
|
switch (format) {
|
|
1487
1532
|
case 1: // unsigned integer data
|
|
1488
1533
|
if (bitsPerSample <= 8) {
|
|
@@ -1522,26 +1567,32 @@ class GeoTIFFImage {
|
|
|
1522
1567
|
throw Error('Unsupported data format/bitsPerSample');
|
|
1523
1568
|
}
|
|
1524
1569
|
getSampleFormat(sampleIndex = 0) {
|
|
1525
|
-
|
|
1526
|
-
|
|
1570
|
+
const sampleFormat = this.fileDirectory.getValue('SampleFormat');
|
|
1571
|
+
return sampleFormat ? sampleFormat[sampleIndex] : 1;
|
|
1527
1572
|
}
|
|
1528
1573
|
getBitsPerSample(sampleIndex = 0) {
|
|
1529
|
-
|
|
1574
|
+
const bitsPerSample = this.fileDirectory.getValue('BitsPerSample');
|
|
1575
|
+
return bitsPerSample ? bitsPerSample[sampleIndex] : 0;
|
|
1530
1576
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1577
|
+
/**
|
|
1578
|
+
* @param {number} sampleIndex
|
|
1579
|
+
* @param {number|ArrayBufferLike} sizeOrData
|
|
1580
|
+
* @returns {TypedArray}
|
|
1581
|
+
*/
|
|
1582
|
+
getArrayForSample(sampleIndex, sizeOrData) {
|
|
1583
|
+
const format = /** @type {1|2|3} */ (this.getSampleFormat(sampleIndex));
|
|
1533
1584
|
const bitsPerSample = this.getBitsPerSample(sampleIndex);
|
|
1534
|
-
return arrayForType(format, bitsPerSample,
|
|
1585
|
+
return arrayForType(format, bitsPerSample, sizeOrData);
|
|
1535
1586
|
}
|
|
1536
1587
|
/**
|
|
1537
1588
|
* Returns the decoded strip or tile.
|
|
1538
1589
|
* @param {Number} x the strip or tile x-offset
|
|
1539
1590
|
* @param {Number} y the tile y-offset (0 for stripped images)
|
|
1540
1591
|
* @param {Number} sample the sample to get for separated samples
|
|
1541
|
-
* @param {DecoderWorker|import("./geotiff").BaseDecoder} poolOrDecoder the decoder or decoder pool
|
|
1592
|
+
* @param {DecoderWorker|import("./geotiff.js").BaseDecoder} poolOrDecoder the decoder or decoder pool
|
|
1542
1593
|
* @param {AbortSignal} [signal] An AbortSignal that may be signalled if the request is
|
|
1543
1594
|
* to be aborted
|
|
1544
|
-
* @returns {Promise.<{x: number, y: number, sample: number, data:
|
|
1595
|
+
* @returns {Promise.<{x: number, y: number, sample: number, data: ArrayBufferLike}>} the decoded strip or tile
|
|
1545
1596
|
*/
|
|
1546
1597
|
async getTileOrStrip(x, y, sample, poolOrDecoder, signal) {
|
|
1547
1598
|
const numTilesPerRow = Math.ceil(this.getWidth() / this.getTileWidth());
|
|
@@ -1560,12 +1611,12 @@ class GeoTIFFImage {
|
|
|
1560
1611
|
let offset;
|
|
1561
1612
|
let byteCount;
|
|
1562
1613
|
if (this.isTiled) {
|
|
1563
|
-
offset = await this.fileDirectory.loadValueIndexed('TileOffsets', index);
|
|
1564
|
-
byteCount = await this.fileDirectory.loadValueIndexed('TileByteCounts', index);
|
|
1614
|
+
offset = Number(await this.fileDirectory.loadValueIndexed('TileOffsets', index));
|
|
1615
|
+
byteCount = Number(await this.fileDirectory.loadValueIndexed('TileByteCounts', index));
|
|
1565
1616
|
}
|
|
1566
1617
|
else {
|
|
1567
|
-
offset = await this.fileDirectory.loadValueIndexed('StripOffsets', index);
|
|
1568
|
-
byteCount = await this.fileDirectory.loadValueIndexed('StripByteCounts', index);
|
|
1618
|
+
offset = Number(await this.fileDirectory.loadValueIndexed('StripOffsets', index));
|
|
1619
|
+
byteCount = Number(await this.fileDirectory.loadValueIndexed('StripByteCounts', index));
|
|
1569
1620
|
}
|
|
1570
1621
|
if (byteCount === 0) {
|
|
1571
1622
|
const nPixels = this.getBlockHeight(y) * this.getTileWidth();
|
|
@@ -1581,7 +1632,7 @@ class GeoTIFFImage {
|
|
|
1581
1632
|
// resolve each request by potentially applying array normalization
|
|
1582
1633
|
request = (async () => {
|
|
1583
1634
|
let data = await poolOrDecoder.decode(slice);
|
|
1584
|
-
const sampleFormat = this.getSampleFormat();
|
|
1635
|
+
const sampleFormat = /** @type {1|2|3} */ (this.getSampleFormat());
|
|
1585
1636
|
const bitsPerSample = this.getBitsPerSample();
|
|
1586
1637
|
if (needsNormalization(sampleFormat, bitsPerSample)) {
|
|
1587
1638
|
data = normalizeArray(data, sampleFormat, this.planarConfiguration, this.getSamplesPerPixel(), bitsPerSample, this.getTileWidth(), this.getBlockHeight(y));
|
|
@@ -1603,11 +1654,11 @@ class GeoTIFFImage {
|
|
|
1603
1654
|
/**
|
|
1604
1655
|
* Internal read function.
|
|
1605
1656
|
* @private
|
|
1606
|
-
* @param {Array} imageWindow The image window in pixel coordinates
|
|
1607
|
-
* @param {Array} samples The selected samples (0-based indices)
|
|
1657
|
+
* @param {Array<number>} imageWindow The image window in pixel coordinates
|
|
1658
|
+
* @param {Array<number>} samples The selected samples (0-based indices)
|
|
1608
1659
|
* @param {TypedArray|TypedArray[]} valueArrays The array(s) to write into
|
|
1609
1660
|
* @param {boolean|undefined} interleave Whether or not to write in an interleaved manner
|
|
1610
|
-
* @param {DecoderWorker|import("./geotiff").BaseDecoder} poolOrDecoder the decoder or decoder pool
|
|
1661
|
+
* @param {DecoderWorker|import("./geotiff.js").BaseDecoder} poolOrDecoder the decoder or decoder pool
|
|
1611
1662
|
* @param {number} [width] the width of window to be read into
|
|
1612
1663
|
* @param {number} [height] the height of window to be read into
|
|
1613
1664
|
* @param {string} [resampleMethod] the resampling method to be used when interpolating
|
|
@@ -1626,11 +1677,17 @@ class GeoTIFFImage {
|
|
|
1626
1677
|
const maxYTile = Math.min(Math.ceil(imageWindow[3] / tileHeight), Math.ceil(imageHeight / tileHeight));
|
|
1627
1678
|
const windowWidth = imageWindow[2] - imageWindow[0];
|
|
1628
1679
|
let bytesPerPixel = this.getBytesPerPixel();
|
|
1680
|
+
/** @type {Array<number>} */
|
|
1629
1681
|
const srcSampleOffsets = [];
|
|
1682
|
+
/** @type {Array<(this: DataView, byteOffset: number, littleEndian: boolean) => number>} */
|
|
1630
1683
|
const sampleReaders = [];
|
|
1631
1684
|
for (let i = 0; i < samples.length; ++i) {
|
|
1632
1685
|
if (this.planarConfiguration === 1) {
|
|
1633
|
-
|
|
1686
|
+
const bitsPerSample = await this.fileDirectory.loadValue('BitsPerSample');
|
|
1687
|
+
if (typeof bitsPerSample !== 'object') {
|
|
1688
|
+
throw new Error('Expected BitsPerSample to be an array or typed array.');
|
|
1689
|
+
}
|
|
1690
|
+
srcSampleOffsets.push(sum(bitsPerSample, 0, samples[i]) / 8);
|
|
1634
1691
|
}
|
|
1635
1692
|
else {
|
|
1636
1693
|
srcSampleOffsets.push(0);
|
|
@@ -1679,7 +1736,7 @@ class GeoTIFFImage {
|
|
|
1679
1736
|
}
|
|
1680
1737
|
else {
|
|
1681
1738
|
windowCoordinate = ((y + firstLine - imageWindow[1]) * windowWidth) + x + firstCol - imageWindow[0];
|
|
1682
|
-
valueArrays[si][windowCoordinate] = value;
|
|
1739
|
+
/** @type {TypedArray} */ (valueArrays[si])[windowCoordinate] = value;
|
|
1683
1740
|
}
|
|
1684
1741
|
}
|
|
1685
1742
|
}
|
|
@@ -1715,12 +1772,12 @@ class GeoTIFFImage {
|
|
|
1715
1772
|
/**
|
|
1716
1773
|
* @overload
|
|
1717
1774
|
* @param {ReadRastersOptions & {interleave: true}} options optional parameters
|
|
1718
|
-
* @returns {Promise<import("./geotiff").TypedArrayWithDimensions>} the decoded arrays as a promise
|
|
1775
|
+
* @returns {Promise<import("./geotiff.js").TypedArrayWithDimensions>} the decoded arrays as a promise
|
|
1719
1776
|
*/
|
|
1720
1777
|
/**
|
|
1721
1778
|
* @overload
|
|
1722
1779
|
* @param {ReadRastersOptions & {interleave: false}} options optional parameters
|
|
1723
|
-
* @returns {Promise<import("./geotiff").TypedArrayArrayWithDimensions>} the decoded arrays as a promise
|
|
1780
|
+
* @returns {Promise<import("./geotiff.js").TypedArrayArrayWithDimensions>} the decoded arrays as a promise
|
|
1724
1781
|
*/
|
|
1725
1782
|
/**
|
|
1726
1783
|
* @overload
|
|
@@ -1730,7 +1787,7 @@ class GeoTIFFImage {
|
|
|
1730
1787
|
/**
|
|
1731
1788
|
* @overload
|
|
1732
1789
|
* @param {ReadRastersOptions} [options={}] optional parameters
|
|
1733
|
-
* @returns {Promise<import("./geotiff").TypedArrayArrayWithDimensions>} the decoded arrays as a promise
|
|
1790
|
+
* @returns {Promise<import("./geotiff.js").TypedArrayArrayWithDimensions>} the decoded arrays as a promise
|
|
1734
1791
|
*/
|
|
1735
1792
|
/**
|
|
1736
1793
|
* Reads raster data from the image. This function reads all selected samples
|
|
@@ -1768,9 +1825,16 @@ class GeoTIFFImage {
|
|
|
1768
1825
|
/** @type {TypedArray|TypedArray[]} */
|
|
1769
1826
|
let valueArrays;
|
|
1770
1827
|
if (interleave) {
|
|
1771
|
-
const
|
|
1772
|
-
|
|
1773
|
-
const
|
|
1828
|
+
const { fileDirectory } = this;
|
|
1829
|
+
const sampleFormat = fileDirectory.getValue('SampleFormat');
|
|
1830
|
+
const format = sampleFormat
|
|
1831
|
+
? Math.max.apply(null, Array.from(sampleFormat)) : 1;
|
|
1832
|
+
if (format !== 1 && format !== 2 && format !== 3) {
|
|
1833
|
+
throw new Error('Unsupported sample format for interleaved data. Must be 1, 2, or 3.');
|
|
1834
|
+
}
|
|
1835
|
+
const bitsPerSample_ = fileDirectory.getValue('BitsPerSample');
|
|
1836
|
+
const bitsPerSample = bitsPerSample_
|
|
1837
|
+
? Math.max.apply(null, Array.from(bitsPerSample_)) : 8;
|
|
1774
1838
|
valueArrays = arrayForType(format, bitsPerSample, numPixels * samples.length);
|
|
1775
1839
|
if (fillValue) {
|
|
1776
1840
|
if (Array.isArray(fillValue)) {
|
|
@@ -1803,12 +1867,12 @@ class GeoTIFFImage {
|
|
|
1803
1867
|
/**
|
|
1804
1868
|
* @overload
|
|
1805
1869
|
* @param {ReadRGBOptions & {interleave: true}} options optional parameters
|
|
1806
|
-
* @returns {Promise<import("./geotiff").TypedArrayWithDimensions>} the RGB array as a Promise
|
|
1870
|
+
* @returns {Promise<import("./geotiff.js").TypedArrayWithDimensions>} the RGB array as a Promise
|
|
1807
1871
|
*/
|
|
1808
1872
|
/**
|
|
1809
1873
|
* @overload
|
|
1810
1874
|
* @param {ReadRGBOptions & {interleave: false}} options optional parameters
|
|
1811
|
-
* @returns {Promise<import("./geotiff").TypedArrayArrayWithDimensions>} the RGB array as a Promise
|
|
1875
|
+
* @returns {Promise<import("./geotiff.js").TypedArrayArrayWithDimensions>} the RGB array as a Promise
|
|
1812
1876
|
*/
|
|
1813
1877
|
/**
|
|
1814
1878
|
* @overload
|
|
@@ -1818,7 +1882,7 @@ class GeoTIFFImage {
|
|
|
1818
1882
|
/**
|
|
1819
1883
|
* @overload
|
|
1820
1884
|
* @param {ReadRGBOptions} [options={}] optional parameters
|
|
1821
|
-
* @returns {Promise<import("./geotiff").TypedArrayArrayWithDimensions>} the RGB array as a Promise
|
|
1885
|
+
* @returns {Promise<import("./geotiff.js").TypedArrayArrayWithDimensions>} the RGB array as a Promise
|
|
1822
1886
|
*/
|
|
1823
1887
|
/**
|
|
1824
1888
|
* Reads raster data from the image as RGB.
|
|
@@ -1841,9 +1905,11 @@ class GeoTIFFImage {
|
|
|
1841
1905
|
const pi = this.fileDirectory.getValue('PhotometricInterpretation');
|
|
1842
1906
|
if (pi === photometricInterpretations.RGB) {
|
|
1843
1907
|
let s = [0, 1, 2];
|
|
1844
|
-
|
|
1908
|
+
const extraSamples = this.fileDirectory.getValue('ExtraSamples');
|
|
1909
|
+
if (extraSamples && extraSamples[0] !== ExtraSamplesValues.Unspecified && enableAlpha) {
|
|
1845
1910
|
s = [];
|
|
1846
|
-
|
|
1911
|
+
const bitsPerSample = this.fileDirectory.getValue('BitsPerSample') || [];
|
|
1912
|
+
for (let i = 0; i < bitsPerSample.length; i += 1) {
|
|
1847
1913
|
s.push(i);
|
|
1848
1914
|
}
|
|
1849
1915
|
}
|
|
@@ -1898,7 +1964,7 @@ class GeoTIFFImage {
|
|
|
1898
1964
|
data = fromBlackIsZero(raster, max);
|
|
1899
1965
|
break;
|
|
1900
1966
|
case photometricInterpretations.Palette:
|
|
1901
|
-
data = fromPalette(raster, await fileDirectory.loadValue('ColorMap'));
|
|
1967
|
+
data = fromPalette(raster, /** @type {Uint16Array} */ (await fileDirectory.loadValue('ColorMap')));
|
|
1902
1968
|
break;
|
|
1903
1969
|
case photometricInterpretations.CMYK:
|
|
1904
1970
|
data = fromCMYK(raster);
|
|
@@ -1925,20 +1991,23 @@ class GeoTIFFImage {
|
|
|
1925
1991
|
}
|
|
1926
1992
|
data = [red, green, blue];
|
|
1927
1993
|
}
|
|
1928
|
-
const dataWithDimensions = /** @type {import("./geotiff").ReadRasterResult} */ (data);
|
|
1994
|
+
const dataWithDimensions = /** @type {import("./geotiff.js").ReadRasterResult} */ (data);
|
|
1929
1995
|
dataWithDimensions.width = raster.width;
|
|
1930
1996
|
dataWithDimensions.height = raster.height;
|
|
1931
1997
|
return dataWithDimensions;
|
|
1932
1998
|
}
|
|
1933
1999
|
/**
|
|
1934
2000
|
* Returns an array of tiepoints.
|
|
1935
|
-
* @returns {Promise<
|
|
2001
|
+
* @returns {Promise<Array<{i: number, j: number, k: number, x: number, y: number, z: number}>>} the tiepoints
|
|
1936
2002
|
*/
|
|
1937
2003
|
async getTiePoints() {
|
|
1938
2004
|
if (!this.fileDirectory.hasTag('ModelTiepoint')) {
|
|
1939
2005
|
return [];
|
|
1940
2006
|
}
|
|
1941
2007
|
const modelTiePoint = await this.fileDirectory.loadValue('ModelTiepoint');
|
|
2008
|
+
if (typeof modelTiePoint !== 'object') {
|
|
2009
|
+
throw new Error('Expected ModelTiepoint to be an array or typed array.');
|
|
2010
|
+
}
|
|
1942
2011
|
const tiePoints = [];
|
|
1943
2012
|
for (let i = 0; i < modelTiePoint.length; i += 6) {
|
|
1944
2013
|
tiePoints.push({
|
|
@@ -1959,14 +2028,16 @@ class GeoTIFFImage {
|
|
|
1959
2028
|
* Otherwise only metadata specific to the provided sample will be returned.
|
|
1960
2029
|
*
|
|
1961
2030
|
* @param {number|null} [sample=null] The sample index.
|
|
1962
|
-
* @returns {Promise<
|
|
2031
|
+
* @returns {Promise<Record<string, unknown>|null>} The GDAL metadata items
|
|
1963
2032
|
*/
|
|
1964
2033
|
async getGDALMetadata(sample = null) {
|
|
2034
|
+
/** @type {Record<string, unknown>} */
|
|
1965
2035
|
const metadata = {};
|
|
1966
2036
|
if (!this.fileDirectory.hasTag('GDAL_METADATA')) {
|
|
1967
2037
|
return null;
|
|
1968
2038
|
}
|
|
1969
2039
|
const string = await this.fileDirectory.loadValue('GDAL_METADATA');
|
|
2040
|
+
/** @type {Array<{inner: unknown}>} */
|
|
1970
2041
|
let items = findTagsByName(string, 'Item');
|
|
1971
2042
|
if (sample === null) {
|
|
1972
2043
|
items = items.filter((item) => getAttribute(item, 'sample') === undefined);
|
|
@@ -1985,10 +2056,10 @@ class GeoTIFFImage {
|
|
|
1985
2056
|
* @returns {number|null}
|
|
1986
2057
|
*/
|
|
1987
2058
|
getGDALNoData() {
|
|
1988
|
-
|
|
2059
|
+
const string = this.fileDirectory.hasTag('GDAL_NODATA') && this.fileDirectory.getValue('GDAL_NODATA');
|
|
2060
|
+
if (!string) {
|
|
1989
2061
|
return null;
|
|
1990
2062
|
}
|
|
1991
|
-
const string = this.fileDirectory.getValue('GDAL_NODATA');
|
|
1992
2063
|
return Number(string.substring(0, string.length - 1));
|
|
1993
2064
|
}
|
|
1994
2065
|
/**
|
|
@@ -2077,9 +2148,9 @@ class GeoTIFFImage {
|
|
|
2077
2148
|
getBoundingBox(tilegrid = false) {
|
|
2078
2149
|
const height = this.getHeight();
|
|
2079
2150
|
const width = this.getWidth();
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
const [a, b,
|
|
2151
|
+
const modelTransformation = this.fileDirectory.getValue('ModelTransformation');
|
|
2152
|
+
if (modelTransformation && !tilegrid) {
|
|
2153
|
+
const [a, b, , d, e, f, , h] = modelTransformation;
|
|
2083
2154
|
const corners = [
|
|
2084
2155
|
[0, 0],
|
|
2085
2156
|
[0, height],
|
|
@@ -2117,12 +2188,20 @@ class GeoTIFFImage {
|
|
|
2117
2188
|
}
|
|
2118
2189
|
|
|
2119
2190
|
class DataView64 {
|
|
2191
|
+
/**
|
|
2192
|
+
* @param {ArrayBufferLike} arrayBuffer
|
|
2193
|
+
*/
|
|
2120
2194
|
constructor(arrayBuffer) {
|
|
2121
2195
|
this._dataView = new DataView(arrayBuffer);
|
|
2122
2196
|
}
|
|
2123
2197
|
get buffer() {
|
|
2124
2198
|
return this._dataView.buffer;
|
|
2125
2199
|
}
|
|
2200
|
+
/**
|
|
2201
|
+
* @param {number} offset
|
|
2202
|
+
* @param {boolean} littleEndian
|
|
2203
|
+
* @returns {number}
|
|
2204
|
+
*/
|
|
2126
2205
|
getUint64(offset, littleEndian) {
|
|
2127
2206
|
const left = this.getUint32(offset, littleEndian);
|
|
2128
2207
|
const right = this.getUint32(offset + 4, littleEndian);
|
|
@@ -2142,7 +2221,12 @@ class DataView64 {
|
|
|
2142
2221
|
}
|
|
2143
2222
|
return combined;
|
|
2144
2223
|
}
|
|
2145
|
-
|
|
2224
|
+
/**
|
|
2225
|
+
* Adapted from https://stackoverflow.com/a/55338384/8060591
|
|
2226
|
+
* @param {number} offset
|
|
2227
|
+
* @param {boolean} littleEndian
|
|
2228
|
+
* @returns {number}
|
|
2229
|
+
*/
|
|
2146
2230
|
getInt64(offset, littleEndian) {
|
|
2147
2231
|
let value = 0;
|
|
2148
2232
|
const isNegative = (this._dataView.getUint8(offset + (littleEndian ? 7 : 0)) & 0x80) > 0;
|
|
@@ -2167,36 +2251,85 @@ class DataView64 {
|
|
|
2167
2251
|
}
|
|
2168
2252
|
return value;
|
|
2169
2253
|
}
|
|
2254
|
+
/**
|
|
2255
|
+
* @param {number} offset
|
|
2256
|
+
* @returns {number}
|
|
2257
|
+
*/
|
|
2170
2258
|
getUint8(offset) {
|
|
2171
2259
|
return this._dataView.getUint8(offset);
|
|
2172
2260
|
}
|
|
2261
|
+
/**
|
|
2262
|
+
* @param {number} offset
|
|
2263
|
+
* @returns {number}
|
|
2264
|
+
*/
|
|
2173
2265
|
getInt8(offset) {
|
|
2174
2266
|
return this._dataView.getInt8(offset);
|
|
2175
2267
|
}
|
|
2268
|
+
/**
|
|
2269
|
+
* @param {number} offset
|
|
2270
|
+
* @param {boolean} littleEndian
|
|
2271
|
+
* @returns {number}
|
|
2272
|
+
*/
|
|
2176
2273
|
getUint16(offset, littleEndian) {
|
|
2177
2274
|
return this._dataView.getUint16(offset, littleEndian);
|
|
2178
2275
|
}
|
|
2276
|
+
/**
|
|
2277
|
+
* @param {number} offset
|
|
2278
|
+
* @param {boolean} littleEndian
|
|
2279
|
+
* @returns {number}
|
|
2280
|
+
*/
|
|
2179
2281
|
getInt16(offset, littleEndian) {
|
|
2180
2282
|
return this._dataView.getInt16(offset, littleEndian);
|
|
2181
2283
|
}
|
|
2284
|
+
/**
|
|
2285
|
+
* @param {number} offset
|
|
2286
|
+
* @param {boolean} littleEndian
|
|
2287
|
+
* @returns {number}
|
|
2288
|
+
*/
|
|
2182
2289
|
getUint32(offset, littleEndian) {
|
|
2183
2290
|
return this._dataView.getUint32(offset, littleEndian);
|
|
2184
2291
|
}
|
|
2292
|
+
/**
|
|
2293
|
+
* @param {number} offset
|
|
2294
|
+
* @param {boolean} littleEndian
|
|
2295
|
+
* @returns {number}
|
|
2296
|
+
*/
|
|
2185
2297
|
getInt32(offset, littleEndian) {
|
|
2186
2298
|
return this._dataView.getInt32(offset, littleEndian);
|
|
2187
2299
|
}
|
|
2300
|
+
/**
|
|
2301
|
+
* @param {number} offset
|
|
2302
|
+
* @param {boolean} littleEndian
|
|
2303
|
+
* @returns {number}
|
|
2304
|
+
*/
|
|
2188
2305
|
getFloat16(offset, littleEndian) {
|
|
2189
2306
|
return getFloat16(this._dataView, offset, littleEndian);
|
|
2190
2307
|
}
|
|
2308
|
+
/**
|
|
2309
|
+
* @param {number} offset
|
|
2310
|
+
* @param {boolean} littleEndian
|
|
2311
|
+
* @returns {number}
|
|
2312
|
+
*/
|
|
2191
2313
|
getFloat32(offset, littleEndian) {
|
|
2192
2314
|
return this._dataView.getFloat32(offset, littleEndian);
|
|
2193
2315
|
}
|
|
2316
|
+
/**
|
|
2317
|
+
* @param {number} offset
|
|
2318
|
+
* @param {boolean} littleEndian
|
|
2319
|
+
* @returns {number}
|
|
2320
|
+
*/
|
|
2194
2321
|
getFloat64(offset, littleEndian) {
|
|
2195
2322
|
return this._dataView.getFloat64(offset, littleEndian);
|
|
2196
2323
|
}
|
|
2197
2324
|
}
|
|
2198
2325
|
|
|
2199
2326
|
class DataSlice {
|
|
2327
|
+
/**
|
|
2328
|
+
* @param {ArrayBufferLike} arrayBuffer
|
|
2329
|
+
* @param {number} sliceOffset
|
|
2330
|
+
* @param {boolean} littleEndian
|
|
2331
|
+
* @param {boolean} bigTiff
|
|
2332
|
+
*/
|
|
2200
2333
|
constructor(arrayBuffer, sliceOffset, littleEndian, bigTiff) {
|
|
2201
2334
|
this._dataView = new DataView(arrayBuffer);
|
|
2202
2335
|
this._sliceOffset = sliceOffset;
|
|
@@ -2218,33 +2351,74 @@ class DataSlice {
|
|
|
2218
2351
|
get buffer() {
|
|
2219
2352
|
return this._dataView.buffer;
|
|
2220
2353
|
}
|
|
2354
|
+
/**
|
|
2355
|
+
* @param {number} offset
|
|
2356
|
+
* @param {number} length
|
|
2357
|
+
* @returns {boolean}
|
|
2358
|
+
*/
|
|
2221
2359
|
covers(offset, length) {
|
|
2222
2360
|
return this.sliceOffset <= offset && this.sliceTop >= offset + length;
|
|
2223
2361
|
}
|
|
2362
|
+
/**
|
|
2363
|
+
* @param {number} offset
|
|
2364
|
+
* @returns {number}
|
|
2365
|
+
*/
|
|
2224
2366
|
readUint8(offset) {
|
|
2225
2367
|
return this._dataView.getUint8(offset - this._sliceOffset);
|
|
2226
2368
|
}
|
|
2369
|
+
/**
|
|
2370
|
+
* @param {number} offset
|
|
2371
|
+
* @returns {number}
|
|
2372
|
+
*/
|
|
2227
2373
|
readInt8(offset) {
|
|
2228
2374
|
return this._dataView.getInt8(offset - this._sliceOffset);
|
|
2229
2375
|
}
|
|
2376
|
+
/**
|
|
2377
|
+
* @param {number} offset
|
|
2378
|
+
* @returns {number}
|
|
2379
|
+
*/
|
|
2230
2380
|
readUint16(offset) {
|
|
2231
2381
|
return this._dataView.getUint16(offset - this._sliceOffset, this._littleEndian);
|
|
2232
2382
|
}
|
|
2383
|
+
/**
|
|
2384
|
+
* @param {number} offset
|
|
2385
|
+
* @returns {number}
|
|
2386
|
+
*/
|
|
2233
2387
|
readInt16(offset) {
|
|
2234
2388
|
return this._dataView.getInt16(offset - this._sliceOffset, this._littleEndian);
|
|
2235
2389
|
}
|
|
2390
|
+
/**
|
|
2391
|
+
* @param {number} offset
|
|
2392
|
+
* @returns {number}
|
|
2393
|
+
*/
|
|
2236
2394
|
readUint32(offset) {
|
|
2237
2395
|
return this._dataView.getUint32(offset - this._sliceOffset, this._littleEndian);
|
|
2238
2396
|
}
|
|
2397
|
+
/**
|
|
2398
|
+
* @param {number} offset
|
|
2399
|
+
* @returns {number}
|
|
2400
|
+
*/
|
|
2239
2401
|
readInt32(offset) {
|
|
2240
2402
|
return this._dataView.getInt32(offset - this._sliceOffset, this._littleEndian);
|
|
2241
2403
|
}
|
|
2404
|
+
/**
|
|
2405
|
+
* @param {number} offset
|
|
2406
|
+
* @returns {number}
|
|
2407
|
+
*/
|
|
2242
2408
|
readFloat32(offset) {
|
|
2243
2409
|
return this._dataView.getFloat32(offset - this._sliceOffset, this._littleEndian);
|
|
2244
2410
|
}
|
|
2411
|
+
/**
|
|
2412
|
+
* @param {number} offset
|
|
2413
|
+
* @returns {number}
|
|
2414
|
+
*/
|
|
2245
2415
|
readFloat64(offset) {
|
|
2246
2416
|
return this._dataView.getFloat64(offset - this._sliceOffset, this._littleEndian);
|
|
2247
2417
|
}
|
|
2418
|
+
/**
|
|
2419
|
+
* @param {number} offset
|
|
2420
|
+
* @returns {number}
|
|
2421
|
+
*/
|
|
2248
2422
|
readUint64(offset) {
|
|
2249
2423
|
const left = this.readUint32(offset);
|
|
2250
2424
|
const right = this.readUint32(offset + 4);
|
|
@@ -2264,7 +2438,11 @@ class DataSlice {
|
|
|
2264
2438
|
}
|
|
2265
2439
|
return combined;
|
|
2266
2440
|
}
|
|
2267
|
-
|
|
2441
|
+
/**
|
|
2442
|
+
* Adapted from https://stackoverflow.com/a/55338384/8060591
|
|
2443
|
+
* @param {number} offset
|
|
2444
|
+
* @returns {number}
|
|
2445
|
+
*/
|
|
2268
2446
|
readInt64(offset) {
|
|
2269
2447
|
let value = 0;
|
|
2270
2448
|
const isNegative = (this._dataView.getUint8(offset + (this._littleEndian ? 7 : 0)) & 0x80)
|
|
@@ -2290,6 +2468,10 @@ class DataSlice {
|
|
|
2290
2468
|
}
|
|
2291
2469
|
return value;
|
|
2292
2470
|
}
|
|
2471
|
+
/**
|
|
2472
|
+
* @param {number} offset
|
|
2473
|
+
* @returns {number}
|
|
2474
|
+
*/
|
|
2293
2475
|
readOffset(offset) {
|
|
2294
2476
|
if (this._bigTiff) {
|
|
2295
2477
|
return this.readUint64(offset);
|
|
@@ -2299,13 +2481,17 @@ class DataSlice {
|
|
|
2299
2481
|
}
|
|
2300
2482
|
|
|
2301
2483
|
const CRLFCRLF = '\r\n\r\n';
|
|
2302
|
-
|
|
2484
|
+
/**
|
|
2303
2485
|
* Shim for 'Object.fromEntries'
|
|
2486
|
+
* @template T
|
|
2487
|
+
* @param {Array<[string, T]>} items
|
|
2488
|
+
* @return {Record<string, T>}
|
|
2304
2489
|
*/
|
|
2305
2490
|
function itemsToObject(items) {
|
|
2306
2491
|
if (typeof Object.fromEntries !== 'undefined') {
|
|
2307
2492
|
return Object.fromEntries(items);
|
|
2308
2493
|
}
|
|
2494
|
+
/** @type {Record<string, T>} */
|
|
2309
2495
|
const obj = {};
|
|
2310
2496
|
for (const [key, value] of items) {
|
|
2311
2497
|
obj[key.toLowerCase()] = value;
|
|
@@ -2314,14 +2500,15 @@ function itemsToObject(items) {
|
|
|
2314
2500
|
}
|
|
2315
2501
|
/**
|
|
2316
2502
|
* Parse HTTP headers from a given string.
|
|
2317
|
-
* @param {
|
|
2318
|
-
* @returns {
|
|
2503
|
+
* @param {string} text the text to parse the headers from
|
|
2504
|
+
* @returns {Record<string, string>} the parsed headers with lowercase keys
|
|
2319
2505
|
*/
|
|
2320
2506
|
function parseHeaders(text) {
|
|
2507
|
+
/** @type {Array<[string, string]>} */
|
|
2321
2508
|
const items = text
|
|
2322
2509
|
.split('\r\n')
|
|
2323
2510
|
.map((line) => {
|
|
2324
|
-
const kv = line.split(':').map((str) => str.trim());
|
|
2511
|
+
const kv = /** @type {[string, string]} */ (line.split(':').map((str) => str.trim()));
|
|
2325
2512
|
kv[0] = kv[0].toLowerCase();
|
|
2326
2513
|
return kv;
|
|
2327
2514
|
});
|
|
@@ -2329,8 +2516,8 @@ function parseHeaders(text) {
|
|
|
2329
2516
|
}
|
|
2330
2517
|
/**
|
|
2331
2518
|
* Parse a 'Content-Type' header value to the content-type and parameters
|
|
2332
|
-
* @param {string|
|
|
2333
|
-
* @returns {{type: string|null, params:
|
|
2519
|
+
* @param {string|undefined} rawContentType the raw string to parse from
|
|
2520
|
+
* @returns {{type: string|null, params: Record<string, string>}}
|
|
2334
2521
|
* the parsed content type with the fields: type and params
|
|
2335
2522
|
*/
|
|
2336
2523
|
function parseContentType(rawContentType) {
|
|
@@ -2338,12 +2525,12 @@ function parseContentType(rawContentType) {
|
|
|
2338
2525
|
return { type: null, params: {} };
|
|
2339
2526
|
}
|
|
2340
2527
|
const [type, ...rawParams] = rawContentType.split(';').map((s) => s.trim());
|
|
2341
|
-
const paramsItems = rawParams.map((param) => param.split('='));
|
|
2528
|
+
const paramsItems = /** @type {Array<[string, string]>} */ (rawParams.map((param) => param.split('=')));
|
|
2342
2529
|
return { type, params: itemsToObject(paramsItems) };
|
|
2343
2530
|
}
|
|
2344
2531
|
/**
|
|
2345
2532
|
* Parse a 'Content-Range' header value to its start, end, and total parts
|
|
2346
|
-
* @param {string|
|
|
2533
|
+
* @param {string|undefined} rawContentRange the raw string to parse from
|
|
2347
2534
|
* @returns {{start: number, end: number, total: number}} the parsed parts
|
|
2348
2535
|
*/
|
|
2349
2536
|
function parseContentRange(rawContentRange) {
|
|
@@ -2363,11 +2550,12 @@ function parseContentRange(rawContentRange) {
|
|
|
2363
2550
|
* - offset: the offset of the byterange within its originating file
|
|
2364
2551
|
* - length: the length of the byterange
|
|
2365
2552
|
* @param {ArrayBuffer} responseArrayBuffer the response to be parsed and split
|
|
2366
|
-
* @param {
|
|
2367
|
-
* @returns {
|
|
2553
|
+
* @param {string} boundary the boundary string used to split the sections
|
|
2554
|
+
* @returns {Array<{headers: Record<string, string>, data: ArrayBuffer, offset: number, length: number, fileSize: number}>}
|
|
2555
|
+
* the parsed byteranges
|
|
2368
2556
|
*/
|
|
2369
2557
|
function parseByteRanges(responseArrayBuffer, boundary) {
|
|
2370
|
-
let offset =
|
|
2558
|
+
let offset = -1;
|
|
2371
2559
|
const decoder = new TextDecoder('ascii');
|
|
2372
2560
|
const out = [];
|
|
2373
2561
|
const startBoundary = `--${boundary}`;
|
|
@@ -2380,7 +2568,7 @@ function parseByteRanges(responseArrayBuffer, boundary) {
|
|
|
2380
2568
|
offset = i;
|
|
2381
2569
|
}
|
|
2382
2570
|
}
|
|
2383
|
-
if (offset ===
|
|
2571
|
+
if (offset === -1) {
|
|
2384
2572
|
throw new Error('Could not find initial boundary');
|
|
2385
2573
|
}
|
|
2386
2574
|
while (offset < responseArrayBuffer.byteLength) {
|
|
@@ -2419,29 +2607,31 @@ function parseByteRanges(responseArrayBuffer, boundary) {
|
|
|
2419
2607
|
}
|
|
2420
2608
|
|
|
2421
2609
|
/**
|
|
2422
|
-
* @typedef Slice
|
|
2610
|
+
* @typedef {Object} Slice
|
|
2423
2611
|
* @property {number} offset
|
|
2424
2612
|
* @property {number} length
|
|
2425
2613
|
*/
|
|
2614
|
+
/** @typedef {Slice & {data: ArrayBufferLike}} SliceWithData */
|
|
2426
2615
|
class BaseSource {
|
|
2427
2616
|
/**
|
|
2428
2617
|
* @param {Array<Slice>} slices
|
|
2429
2618
|
* @param {AbortSignal} [signal]
|
|
2430
|
-
* @returns {Promise
|
|
2619
|
+
* @returns {Promise<ArrayBufferLike[]>}
|
|
2431
2620
|
*/
|
|
2432
2621
|
async fetch(slices, signal) {
|
|
2433
|
-
return Promise.all(slices.map((slice) => this.fetchSlice(slice, signal)));
|
|
2622
|
+
return Promise.all(slices.map(async (slice) => (await this.fetchSlice(slice, signal)).data));
|
|
2434
2623
|
}
|
|
2435
2624
|
/**
|
|
2436
2625
|
* @param {Slice} slice
|
|
2437
2626
|
* @param {AbortSignal} [_signal]
|
|
2438
|
-
* @returns {Promise
|
|
2627
|
+
* @returns {Promise<SliceWithData>}
|
|
2439
2628
|
*/
|
|
2440
2629
|
async fetchSlice(slice, _signal) {
|
|
2441
2630
|
throw new Error(`fetching of slice ${slice} not possible, not implemented`);
|
|
2442
2631
|
}
|
|
2443
2632
|
/**
|
|
2444
2633
|
* Returns the filesize if already determined and null otherwise
|
|
2634
|
+
* @returns {number|null}
|
|
2445
2635
|
*/
|
|
2446
2636
|
get fileSize() {
|
|
2447
2637
|
return null;
|
|
@@ -2735,18 +2925,14 @@ class QuickLRU extends Map {
|
|
|
2735
2925
|
}
|
|
2736
2926
|
}
|
|
2737
2927
|
|
|
2738
|
-
|
|
2928
|
+
/**
|
|
2739
2929
|
* Promisified wrapper around 'setTimeout' to allow 'await'
|
|
2930
|
+
* @param {number} [milliseconds]
|
|
2931
|
+
* @returns {Promise<void>}
|
|
2740
2932
|
*/
|
|
2741
2933
|
async function wait(milliseconds) {
|
|
2742
2934
|
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
2743
2935
|
}
|
|
2744
|
-
/**
|
|
2745
|
-
* @template T,U
|
|
2746
|
-
* @param {Iterable<T>} a
|
|
2747
|
-
* @param {Iterable<U>} b
|
|
2748
|
-
* @returns {Array<[T, U]>}
|
|
2749
|
-
*/
|
|
2750
2936
|
function zip(a, b) {
|
|
2751
2937
|
const A = Array.isArray(a) ? a : Array.from(a);
|
|
2752
2938
|
const B = Array.isArray(b) ? b : Array.from(b);
|
|
@@ -2754,14 +2940,15 @@ function zip(a, b) {
|
|
|
2754
2940
|
}
|
|
2755
2941
|
// Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
|
|
2756
2942
|
class AbortError extends Error {
|
|
2757
|
-
constructor(
|
|
2943
|
+
constructor(...args) {
|
|
2758
2944
|
// Pass remaining arguments (including vendor specific ones) to parent constructor
|
|
2759
|
-
super(
|
|
2945
|
+
super(...args);
|
|
2760
2946
|
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
2761
2947
|
if (Error.captureStackTrace) {
|
|
2762
2948
|
Error.captureStackTrace(this, AbortError);
|
|
2763
2949
|
}
|
|
2764
2950
|
this.name = 'AbortError';
|
|
2951
|
+
this.signal = undefined;
|
|
2765
2952
|
}
|
|
2766
2953
|
}
|
|
2767
2954
|
class CustomAggregateError extends Error {
|
|
@@ -2836,7 +3023,7 @@ class BlockedSource extends BaseSource {
|
|
|
2836
3023
|
return this.source.fileSize;
|
|
2837
3024
|
}
|
|
2838
3025
|
/**
|
|
2839
|
-
* @param {import("./basesource").Slice[]} slices
|
|
3026
|
+
* @param {import("./basesource.js").Slice[]} slices
|
|
2840
3027
|
* @param {AbortSignal} [signal]
|
|
2841
3028
|
* @return {Promise<ArrayBuffer[]>}
|
|
2842
3029
|
*/
|
|
@@ -2918,24 +3105,25 @@ class BlockedSource extends BaseSource {
|
|
|
2918
3105
|
if (this.blockIdsToFetch.size > 0) {
|
|
2919
3106
|
const groups = this.groupBlocks(this.blockIdsToFetch);
|
|
2920
3107
|
// start requesting slices of data
|
|
2921
|
-
const groupRequests = this.source.
|
|
3108
|
+
const groupRequests = groups.map(async (group) => ({ ...group, ...(await this.source.fetchSlice(group, signal)) }));
|
|
2922
3109
|
for (let groupIndex = 0; groupIndex < groups.length; ++groupIndex) {
|
|
2923
3110
|
const group = groups[groupIndex];
|
|
2924
3111
|
for (const blockId of group.blockIds) {
|
|
2925
3112
|
// make an async IIFE for each block
|
|
2926
3113
|
this.blockRequests.set(blockId, (async () => {
|
|
2927
3114
|
try {
|
|
2928
|
-
const response = (await groupRequests)[groupIndex];
|
|
3115
|
+
const response = (await Promise.all(groupRequests))[groupIndex];
|
|
2929
3116
|
const blockOffset = blockId * this.blockSize;
|
|
2930
3117
|
const o = blockOffset - response.offset;
|
|
2931
3118
|
const t = Math.min(o + this.blockSize, response.data.byteLength);
|
|
2932
3119
|
const data = response.data.slice(o, t);
|
|
2933
|
-
const block = new Block(blockOffset, data.byteLength,
|
|
3120
|
+
const block = new Block(blockOffset, data.byteLength,
|
|
3121
|
+
/** @type {ArrayBuffer} */ (data));
|
|
2934
3122
|
this.blockCache.set(blockId, block);
|
|
2935
3123
|
this.abortedBlockIds.delete(blockId);
|
|
2936
3124
|
}
|
|
2937
3125
|
catch (err) {
|
|
2938
|
-
if (err.name === 'AbortError') {
|
|
3126
|
+
if (err instanceof AbortError && err.name === 'AbortError') {
|
|
2939
3127
|
// store the signal here, we need it to determine later if an
|
|
2940
3128
|
// error was caused by this signal
|
|
2941
3129
|
err.signal = signal;
|
|
@@ -2957,7 +3145,7 @@ class BlockedSource extends BaseSource {
|
|
|
2957
3145
|
}
|
|
2958
3146
|
/**
|
|
2959
3147
|
*
|
|
2960
|
-
* @param {Set} blockIds
|
|
3148
|
+
* @param {Set<number>} blockIds
|
|
2961
3149
|
* @returns {BlockGroup[]}
|
|
2962
3150
|
*/
|
|
2963
3151
|
groupBlocks(blockIds) {
|
|
@@ -2965,6 +3153,7 @@ class BlockedSource extends BaseSource {
|
|
|
2965
3153
|
if (sortedBlockIds.length === 0) {
|
|
2966
3154
|
return [];
|
|
2967
3155
|
}
|
|
3156
|
+
/** @type {number[]} */
|
|
2968
3157
|
let current = [];
|
|
2969
3158
|
let lastBlockId = null;
|
|
2970
3159
|
const groups = [];
|
|
@@ -2983,7 +3172,7 @@ class BlockedSource extends BaseSource {
|
|
|
2983
3172
|
return groups;
|
|
2984
3173
|
}
|
|
2985
3174
|
/**
|
|
2986
|
-
* @param {import("./basesource").Slice[]} slices
|
|
3175
|
+
* @param {import("./basesource.js").Slice[]} slices
|
|
2987
3176
|
* @param {Map<number, Block>} blocks
|
|
2988
3177
|
* @returns {ArrayBuffer[]}
|
|
2989
3178
|
*/
|
|
@@ -3043,10 +3232,10 @@ class BaseResponse {
|
|
|
3043
3232
|
}
|
|
3044
3233
|
/**
|
|
3045
3234
|
* Returns the value of the specified header
|
|
3046
|
-
* @param {string}
|
|
3047
|
-
* @returns {string|
|
|
3235
|
+
* @param {string} _headerName the header name
|
|
3236
|
+
* @returns {string|undefined} the header value
|
|
3048
3237
|
*/
|
|
3049
|
-
getHeader(
|
|
3238
|
+
getHeader(_headerName) {
|
|
3050
3239
|
throw new Error('not implemented');
|
|
3051
3240
|
}
|
|
3052
3241
|
/**
|
|
@@ -3057,15 +3246,16 @@ class BaseResponse {
|
|
|
3057
3246
|
}
|
|
3058
3247
|
}
|
|
3059
3248
|
class BaseClient {
|
|
3249
|
+
/** @param {string} url */
|
|
3060
3250
|
constructor(url) {
|
|
3061
3251
|
this.url = url;
|
|
3062
3252
|
}
|
|
3063
3253
|
/**
|
|
3064
3254
|
* Send a request with the options
|
|
3065
|
-
* @param {RequestInit} [
|
|
3255
|
+
* @param {RequestInit} [_options={}]
|
|
3066
3256
|
* @returns {Promise<BaseResponse>}
|
|
3067
3257
|
*/
|
|
3068
|
-
async request(
|
|
3258
|
+
async request(_options) {
|
|
3069
3259
|
throw new Error('request is not implemented');
|
|
3070
3260
|
}
|
|
3071
3261
|
}
|
|
@@ -3084,10 +3274,10 @@ class FetchResponse extends BaseResponse {
|
|
|
3084
3274
|
}
|
|
3085
3275
|
/**
|
|
3086
3276
|
* @param {string} name
|
|
3087
|
-
* @returns {string|
|
|
3277
|
+
* @returns {string|undefined}
|
|
3088
3278
|
*/
|
|
3089
3279
|
getHeader(name) {
|
|
3090
|
-
return this.response.headers.get(name);
|
|
3280
|
+
return this.response.headers.get(name) || undefined;
|
|
3091
3281
|
}
|
|
3092
3282
|
async getData() {
|
|
3093
3283
|
const data = this.response.arrayBuffer
|
|
@@ -3098,6 +3288,10 @@ class FetchResponse extends BaseResponse {
|
|
|
3098
3288
|
}
|
|
3099
3289
|
}
|
|
3100
3290
|
class FetchClient extends BaseClient {
|
|
3291
|
+
/**
|
|
3292
|
+
* @param {string} url
|
|
3293
|
+
* @param {RequestCredentials} [credentials]
|
|
3294
|
+
*/
|
|
3101
3295
|
constructor(url, credentials) {
|
|
3102
3296
|
super(url);
|
|
3103
3297
|
this.credentials = credentials;
|
|
@@ -3128,14 +3322,23 @@ class XHRResponse extends BaseResponse {
|
|
|
3128
3322
|
get status() {
|
|
3129
3323
|
return this.xhr.status;
|
|
3130
3324
|
}
|
|
3325
|
+
/**
|
|
3326
|
+
* @param {string} name
|
|
3327
|
+
* @returns {string|undefined}
|
|
3328
|
+
*/
|
|
3131
3329
|
getHeader(name) {
|
|
3132
|
-
return this.xhr.getResponseHeader(name);
|
|
3330
|
+
return this.xhr.getResponseHeader(name) || undefined;
|
|
3133
3331
|
}
|
|
3134
3332
|
async getData() {
|
|
3135
3333
|
return this.data;
|
|
3136
3334
|
}
|
|
3137
3335
|
}
|
|
3138
3336
|
class XHRClient extends BaseClient {
|
|
3337
|
+
/**
|
|
3338
|
+
* @param {Object<string, string>} headers
|
|
3339
|
+
* @param {AbortSignal} [signal]
|
|
3340
|
+
* @returns {Promise<XHRResponse>}
|
|
3341
|
+
*/
|
|
3139
3342
|
constructRequest(headers, signal) {
|
|
3140
3343
|
return new Promise((resolve, reject) => {
|
|
3141
3344
|
const xhr = new XMLHttpRequest();
|
|
@@ -3160,7 +3363,7 @@ class XHRClient extends BaseClient {
|
|
|
3160
3363
|
}
|
|
3161
3364
|
});
|
|
3162
3365
|
}
|
|
3163
|
-
async request({ headers =
|
|
3366
|
+
async request({ headers = {}, signal = undefined } = {}) {
|
|
3164
3367
|
const response = await this.constructRequest(headers, signal);
|
|
3165
3368
|
return response;
|
|
3166
3369
|
}
|
|
@@ -3172,6 +3375,7 @@ class HttpResponse extends BaseResponse {
|
|
|
3172
3375
|
/**
|
|
3173
3376
|
* BaseResponse facade for node HTTP/HTTPS API Response
|
|
3174
3377
|
* @param {import('http').IncomingMessage} response
|
|
3378
|
+
* @param {Promise<ArrayBuffer>} dataPromise
|
|
3175
3379
|
*/
|
|
3176
3380
|
constructor(response, dataPromise) {
|
|
3177
3381
|
super();
|
|
@@ -3181,8 +3385,13 @@ class HttpResponse extends BaseResponse {
|
|
|
3181
3385
|
get status() {
|
|
3182
3386
|
return /** @type {number} */ (this.response.statusCode);
|
|
3183
3387
|
}
|
|
3388
|
+
/**
|
|
3389
|
+
* @param {string} name
|
|
3390
|
+
* @returns {string|undefined}
|
|
3391
|
+
*/
|
|
3184
3392
|
getHeader(name) {
|
|
3185
|
-
|
|
3393
|
+
const value = this.response.headers[name];
|
|
3394
|
+
return Array.isArray(value) ? value.join(', ') : value;
|
|
3186
3395
|
}
|
|
3187
3396
|
async getData() {
|
|
3188
3397
|
const data = await this.dataPromise;
|
|
@@ -3190,11 +3399,17 @@ class HttpResponse extends BaseResponse {
|
|
|
3190
3399
|
}
|
|
3191
3400
|
}
|
|
3192
3401
|
class HttpClient extends BaseClient {
|
|
3402
|
+
/** @param {string} url */
|
|
3193
3403
|
constructor(url) {
|
|
3194
3404
|
super(url);
|
|
3195
3405
|
this.parsedUrl = fs.parse(this.url);
|
|
3196
3406
|
this.httpApi = (this.parsedUrl.protocol === 'http:' ? fs : fs);
|
|
3197
3407
|
}
|
|
3408
|
+
/**
|
|
3409
|
+
* @param {Object<string, string>} headers
|
|
3410
|
+
* @param {AbortSignal} [signal]
|
|
3411
|
+
* @returns {Promise<HttpResponse>}
|
|
3412
|
+
*/
|
|
3198
3413
|
constructRequest(headers, signal) {
|
|
3199
3414
|
return new Promise((resolve, reject) => {
|
|
3200
3415
|
const request = this.httpApi.get({
|
|
@@ -3202,6 +3417,7 @@ class HttpClient extends BaseClient {
|
|
|
3202
3417
|
headers,
|
|
3203
3418
|
}, (response) => {
|
|
3204
3419
|
const dataPromise = new Promise((resolveData) => {
|
|
3420
|
+
/** @type {Uint8Array[]} */
|
|
3205
3421
|
const chunks = [];
|
|
3206
3422
|
// collect chunks
|
|
3207
3423
|
response.on('data', (chunk) => {
|
|
@@ -3225,7 +3441,7 @@ class HttpClient extends BaseClient {
|
|
|
3225
3441
|
}
|
|
3226
3442
|
});
|
|
3227
3443
|
}
|
|
3228
|
-
async request({ headers =
|
|
3444
|
+
async request({ headers = {}, signal = undefined } = {}) {
|
|
3229
3445
|
const response = await this.constructRequest(headers, signal);
|
|
3230
3446
|
return response;
|
|
3231
3447
|
}
|
|
@@ -3234,7 +3450,7 @@ class HttpClient extends BaseClient {
|
|
|
3234
3450
|
/** @import { RemoteSourceOptions, BlockedSourceOptions } from '../geotiff.js' */
|
|
3235
3451
|
class RemoteSource extends BaseSource {
|
|
3236
3452
|
/**
|
|
3237
|
-
* @param {import("../geotiff").BaseClient} client
|
|
3453
|
+
* @param {import("../geotiff.js").BaseClient} client
|
|
3238
3454
|
* @param {RemoteSourceOptions} options
|
|
3239
3455
|
*/
|
|
3240
3456
|
constructor(client, { headers, maxRanges = 0, allowFullFile } = {}) {
|
|
@@ -3248,24 +3464,29 @@ class RemoteSource extends BaseSource {
|
|
|
3248
3464
|
/**
|
|
3249
3465
|
* @param {import('./basesource.js').Slice[]} slices
|
|
3250
3466
|
* @param {AbortSignal} [signal]
|
|
3251
|
-
* @returns {Promise
|
|
3467
|
+
* @returns {Promise<ArrayBufferLike[]>}
|
|
3252
3468
|
*/
|
|
3253
3469
|
async fetch(slices, signal) {
|
|
3254
3470
|
// if we allow multi-ranges, split the incoming request into that many sub-requests
|
|
3255
3471
|
// and join them afterwards
|
|
3256
3472
|
if (this.maxRanges >= slices.length) {
|
|
3257
|
-
return this.fetchSlices(slices, signal);
|
|
3473
|
+
return this.fetchSlices(slices, signal).then((results) => results.map((r) => r.data));
|
|
3258
3474
|
}
|
|
3259
3475
|
else if (this.maxRanges > 0 && slices.length > 1) ;
|
|
3260
3476
|
// otherwise make a single request for each slice
|
|
3261
|
-
return Promise.all(slices.map((slice) => this.fetchSlice(slice, signal)));
|
|
3477
|
+
return Promise.all(slices.map(async (slice) => (await this.fetchSlice(slice, signal)).data));
|
|
3262
3478
|
}
|
|
3479
|
+
/**
|
|
3480
|
+
* @param {Array<import('./basesource.js').Slice>} slices
|
|
3481
|
+
* @param {AbortSignal} [signal]
|
|
3482
|
+
* @returns {Promise<Array<import('./basesource.js').SliceWithData>>}
|
|
3483
|
+
*/
|
|
3263
3484
|
async fetchSlices(slices, signal) {
|
|
3264
3485
|
const response = await this.client.request({
|
|
3265
3486
|
headers: {
|
|
3266
3487
|
...this.headers,
|
|
3267
3488
|
Range: `bytes=${slices
|
|
3268
|
-
.map(({ offset, length }) => `${offset}-${offset + length}`)
|
|
3489
|
+
.map(({ offset, length }) => `${offset}-${offset + length - 1}`)
|
|
3269
3490
|
.join(',')}`,
|
|
3270
3491
|
},
|
|
3271
3492
|
signal,
|
|
@@ -3283,10 +3504,11 @@ class RemoteSource extends BaseSource {
|
|
|
3283
3504
|
const data = await response.getData();
|
|
3284
3505
|
const { start, end, total } = parseContentRange(response.getHeader('content-range'));
|
|
3285
3506
|
this._fileSize = total || null;
|
|
3507
|
+
/** @type {import('./basesource.js').SliceWithData[]} */
|
|
3286
3508
|
const first = [{
|
|
3287
3509
|
data,
|
|
3288
3510
|
offset: start,
|
|
3289
|
-
length: end - start,
|
|
3511
|
+
length: end + 1 - start,
|
|
3290
3512
|
}];
|
|
3291
3513
|
if (slices.length > 1) {
|
|
3292
3514
|
// we requested more than one slice, but got only the first
|
|
@@ -3311,12 +3533,17 @@ class RemoteSource extends BaseSource {
|
|
|
3311
3533
|
}];
|
|
3312
3534
|
}
|
|
3313
3535
|
}
|
|
3536
|
+
/**
|
|
3537
|
+
* @param {import('./basesource.js').Slice} slice
|
|
3538
|
+
* @param {AbortSignal} [signal]
|
|
3539
|
+
* @returns {Promise<import('./basesource.js').SliceWithData>}
|
|
3540
|
+
*/
|
|
3314
3541
|
async fetchSlice(slice, signal) {
|
|
3315
3542
|
const { offset, length } = slice;
|
|
3316
3543
|
const response = await this.client.request({
|
|
3317
3544
|
headers: {
|
|
3318
3545
|
...this.headers,
|
|
3319
|
-
Range: `bytes=${offset}-${offset + length}`,
|
|
3546
|
+
Range: `bytes=${offset}-${offset + length - 1}`,
|
|
3320
3547
|
},
|
|
3321
3548
|
signal,
|
|
3322
3549
|
});
|
|
@@ -3357,7 +3584,7 @@ class RemoteSource extends BaseSource {
|
|
|
3357
3584
|
* @returns {BaseSource}
|
|
3358
3585
|
*/
|
|
3359
3586
|
function maybeWrapInBlockedSource(source, { blockSize, cacheSize }) {
|
|
3360
|
-
if (blockSize ===
|
|
3587
|
+
if (blockSize === undefined) {
|
|
3361
3588
|
return source;
|
|
3362
3589
|
}
|
|
3363
3590
|
return new BlockedSource(source, { blockSize, cacheSize });
|
|
@@ -3408,17 +3635,30 @@ function makeRemoteSource(url, { forceXHR = false, ...clientOptions } = {}) {
|
|
|
3408
3635
|
}
|
|
3409
3636
|
|
|
3410
3637
|
class ArrayBufferSource extends BaseSource {
|
|
3638
|
+
/**
|
|
3639
|
+
* @param {ArrayBuffer} arrayBuffer
|
|
3640
|
+
*/
|
|
3411
3641
|
constructor(arrayBuffer) {
|
|
3412
3642
|
super();
|
|
3413
3643
|
this.arrayBuffer = arrayBuffer;
|
|
3414
3644
|
}
|
|
3645
|
+
/**
|
|
3646
|
+
* @param {import('./basesource.js').Slice} slice
|
|
3647
|
+
* @param {AbortSignal} [signal]
|
|
3648
|
+
* @returns {Promise<import('./basesource.js').SliceWithData>}
|
|
3649
|
+
*/
|
|
3415
3650
|
fetchSlice(slice, signal) {
|
|
3416
3651
|
if (signal && signal.aborted) {
|
|
3417
3652
|
throw new AbortError('Request aborted');
|
|
3418
3653
|
}
|
|
3419
|
-
return
|
|
3654
|
+
return Promise.resolve({
|
|
3655
|
+
data: this.arrayBuffer.slice(slice.offset, slice.offset + slice.length),
|
|
3656
|
+
offset: slice.offset,
|
|
3657
|
+
length: slice.length,
|
|
3658
|
+
});
|
|
3420
3659
|
}
|
|
3421
3660
|
}
|
|
3661
|
+
/** @param {ArrayBuffer} arrayBuffer */
|
|
3422
3662
|
function makeBufferSource(arrayBuffer) {
|
|
3423
3663
|
return new ArrayBufferSource(arrayBuffer);
|
|
3424
3664
|
}
|
|
@@ -3516,6 +3756,17 @@ function getDataSliceReader(dataSlice, fieldType) {
|
|
|
3516
3756
|
* @param {true} isArray - Whether to always return an array (vs single value)
|
|
3517
3757
|
* @returns {import('./geotiff.js').TypedArray|Array<number>} The decoded value(s)
|
|
3518
3758
|
*/
|
|
3759
|
+
/**
|
|
3760
|
+
* @overload
|
|
3761
|
+
* @param {import('./geotiff.js').TypedArray|Array<number>|null} outValues - Optional pre-allocated output array
|
|
3762
|
+
* @param {Function} readMethod - DataView read method (e.g., getUint16)
|
|
3763
|
+
* @param {DataSlice} dataSlice - Source data slice
|
|
3764
|
+
* @param {number} fieldType - TIFF field type constant
|
|
3765
|
+
* @param {number} count - Number of values to read
|
|
3766
|
+
* @param {number} offset - Byte offset to start reading
|
|
3767
|
+
* @param {boolean} [isArray] - Whether to always return an array (vs single value)
|
|
3768
|
+
* @returns {import('./geotiff.js').TypedArray|Array<number>|string|number} The decoded value(s)
|
|
3769
|
+
*/
|
|
3519
3770
|
/**
|
|
3520
3771
|
* Reads field values from a DataSlice.
|
|
3521
3772
|
* @param {import('./geotiff.js').TypedArray|Array<number>|null} outValues - Optional pre-allocated output array
|
|
@@ -3640,9 +3891,11 @@ class DeferredArray {
|
|
|
3640
3891
|
class ImageFileDirectory {
|
|
3641
3892
|
/**
|
|
3642
3893
|
* Create an ImageFileDirectory.
|
|
3643
|
-
* @param {Map} actualizedFields the file directory,
|
|
3644
|
-
*
|
|
3645
|
-
* @param {Map}
|
|
3894
|
+
* @param {Map<string|number, number|string|Array<number|string>>} actualizedFields the file directory,
|
|
3895
|
+
* mapping tag names to values
|
|
3896
|
+
* @param {Map<string|number, Function>} deferredFields the deferred fields, mapping tag names to async functions
|
|
3897
|
+
* @param {Map<string|number, DeferredArray>} deferredArrays the deferred arrays, mapping tag names to
|
|
3898
|
+
* DeferredArray objects
|
|
3646
3899
|
* @param {number} nextIFDByteOffset the byte offset to the next IFD
|
|
3647
3900
|
*/
|
|
3648
3901
|
constructor(actualizedFields, deferredFields, deferredArrays, nextIFDByteOffset) {
|
|
@@ -3653,7 +3906,7 @@ class ImageFileDirectory {
|
|
|
3653
3906
|
this.nextIFDByteOffset = nextIFDByteOffset;
|
|
3654
3907
|
}
|
|
3655
3908
|
/**
|
|
3656
|
-
* @param {number
|
|
3909
|
+
* @param {import('./globals.js').TagName|number} tagIdentifier The field tag ID or name
|
|
3657
3910
|
* @returns {boolean} whether the field exists (actualized or deferred)
|
|
3658
3911
|
*/
|
|
3659
3912
|
hasTag(tagIdentifier) {
|
|
@@ -3662,8 +3915,11 @@ class ImageFileDirectory {
|
|
|
3662
3915
|
}
|
|
3663
3916
|
/**
|
|
3664
3917
|
* Synchronously retrieves the value for a given tag. If it is deferred, an error is thrown.
|
|
3665
|
-
* @
|
|
3666
|
-
* @
|
|
3918
|
+
* @template {import('./globals.js').EagerTagName | import('./globals.js').EagerTag} [T=any]
|
|
3919
|
+
* @param {T} tagIdentifier The field tag ID or name
|
|
3920
|
+
* @returns {T extends import('./globals.js').TagName ? (import('./globals.js').TagValue<T> | undefined) : any}
|
|
3921
|
+
* the field value,
|
|
3922
|
+
* or undefined if it does not exist
|
|
3667
3923
|
* @throws {Error} If the tag is deferred and requires asynchronous loading
|
|
3668
3924
|
*/
|
|
3669
3925
|
getValue(tagIdentifier) {
|
|
@@ -3674,25 +3930,27 @@ class ImageFileDirectory {
|
|
|
3674
3930
|
throw new Error(`Field '${tagName}' (${tag}) is deferred. Use loadValue() to load it asynchronously.`);
|
|
3675
3931
|
}
|
|
3676
3932
|
if (!this.actualizedFields.has(tag)) {
|
|
3677
|
-
return undefined;
|
|
3933
|
+
return /** @type {any} */ (undefined);
|
|
3678
3934
|
}
|
|
3679
|
-
return this.actualizedFields.get(tag);
|
|
3935
|
+
return /** @type {any} */ (this.actualizedFields.get(tag));
|
|
3680
3936
|
}
|
|
3681
3937
|
/**
|
|
3682
3938
|
* Retrieves the value for a given tag. If it is deferred, it will be loaded first.
|
|
3683
|
-
* @
|
|
3684
|
-
* @
|
|
3939
|
+
* @template {import('./globals.js').TagName} [T=any]
|
|
3940
|
+
* @param {T|number} tagIdentifier The field tag ID or name
|
|
3941
|
+
* @returns {Promise<T extends import('./globals.js').TagName ? (import('./globals.js').TagValue<T> | undefined) : any>}
|
|
3942
|
+
* the field value, or undefined if it does not exist
|
|
3685
3943
|
*/
|
|
3686
3944
|
async loadValue(tagIdentifier) {
|
|
3687
3945
|
const tag = resolveTag(tagIdentifier);
|
|
3688
3946
|
if (this.actualizedFields.has(tag)) {
|
|
3689
|
-
return this.actualizedFields.get(tag);
|
|
3947
|
+
return /** @type {any} */ (this.actualizedFields.get(tag));
|
|
3690
3948
|
}
|
|
3691
3949
|
if (this.deferredFieldsBeingResolved.has(tag)) {
|
|
3692
|
-
return this.deferredFieldsBeingResolved.get(tag);
|
|
3950
|
+
return /** @type {any} */ (this.deferredFieldsBeingResolved.get(tag));
|
|
3693
3951
|
}
|
|
3694
|
-
|
|
3695
|
-
|
|
3952
|
+
const loaderFn = this.deferredFields.get(tag);
|
|
3953
|
+
if (loaderFn) {
|
|
3696
3954
|
this.deferredFields.delete(tag);
|
|
3697
3955
|
// Set promise BEFORE starting async work to prevent race conditions
|
|
3698
3956
|
const valuePromise = (async () => {
|
|
@@ -3706,32 +3964,35 @@ class ImageFileDirectory {
|
|
|
3706
3964
|
}
|
|
3707
3965
|
})();
|
|
3708
3966
|
this.deferredFieldsBeingResolved.set(tag, valuePromise);
|
|
3709
|
-
return valuePromise;
|
|
3967
|
+
return /** @type {any} */ (valuePromise);
|
|
3710
3968
|
}
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
return deferredArray.loadAll();
|
|
3969
|
+
const deferredArray = this.deferredArrays.get(tag);
|
|
3970
|
+
if (deferredArray) {
|
|
3971
|
+
return /** @type {any} */ (deferredArray.loadAll());
|
|
3714
3972
|
}
|
|
3715
|
-
return undefined;
|
|
3973
|
+
return /** @type {any} */ (undefined);
|
|
3716
3974
|
}
|
|
3717
3975
|
/**
|
|
3718
3976
|
* Retrieves the value at a given index for a tag that is an array. If it is deferred, it will be loaded first.
|
|
3719
3977
|
* @param {number|string} tagIdentifier The field tag ID or name
|
|
3720
3978
|
* @param {number} index The index within the array
|
|
3721
|
-
* @returns the field value at the given index, or undefined if it does not exist
|
|
3979
|
+
* @returns {Promise<number|string|bigint|undefined>} the field value at the given index, or undefined if it does not exist
|
|
3722
3980
|
*/
|
|
3723
3981
|
async loadValueIndexed(tagIdentifier, index) {
|
|
3724
3982
|
const tag = resolveTag(tagIdentifier);
|
|
3725
3983
|
if (this.actualizedFields.has(tag)) {
|
|
3726
3984
|
const value = this.actualizedFields.get(tag);
|
|
3727
|
-
return value[index];
|
|
3985
|
+
return /** @type {any} */ (value)[index];
|
|
3728
3986
|
}
|
|
3729
3987
|
else if (this.deferredArrays.has(tag)) {
|
|
3730
|
-
const deferredArray = this.deferredArrays.get(tag);
|
|
3988
|
+
const deferredArray = /** @type {DeferredArray} */ (this.deferredArrays.get(tag));
|
|
3731
3989
|
return deferredArray.get(index);
|
|
3732
3990
|
}
|
|
3733
3991
|
else if (this.hasTag(tag)) {
|
|
3734
|
-
|
|
3992
|
+
const value = await this.loadValue(tag);
|
|
3993
|
+
if (value && typeof value !== 'number') {
|
|
3994
|
+
return value[index];
|
|
3995
|
+
}
|
|
3735
3996
|
}
|
|
3736
3997
|
return undefined;
|
|
3737
3998
|
}
|
|
@@ -3751,8 +4012,8 @@ class ImageFileDirectory {
|
|
|
3751
4012
|
/** @type {Partial<Record<import('./globals.js').GeoKeyName, *>>} */
|
|
3752
4013
|
const geoKeyDirectory = {};
|
|
3753
4014
|
for (let i = 4; i <= rawGeoKeyDirectory[3] * 4; i += 4) {
|
|
3754
|
-
const key = geoKeyNames[rawGeoKeyDirectory[i]];
|
|
3755
|
-
const location = rawGeoKeyDirectory[i + 1] || null;
|
|
4015
|
+
const key = ( /** @type {Record<number, import('./globals.js').GeoKeyName>} */(geoKeyNames))[rawGeoKeyDirectory[i]];
|
|
4016
|
+
const location = /** @type {import('./globals.js').EagerTag} */ (rawGeoKeyDirectory[i + 1]) || null;
|
|
3756
4017
|
const count = rawGeoKeyDirectory[i + 2];
|
|
3757
4018
|
const offset = rawGeoKeyDirectory[i + 3];
|
|
3758
4019
|
let value = null;
|
|
@@ -3779,9 +4040,10 @@ class ImageFileDirectory {
|
|
|
3779
4040
|
return geoKeyDirectory;
|
|
3780
4041
|
}
|
|
3781
4042
|
toObject() {
|
|
4043
|
+
/** @type {Record<string, unknown>} */
|
|
3782
4044
|
const obj = {};
|
|
3783
4045
|
for (const [tag, value] of this.actualizedFields.entries()) {
|
|
3784
|
-
const tagDefinition = tagDefinitions[tag];
|
|
4046
|
+
const tagDefinition = typeof tag === 'number' ? tagDefinitions[tag] : undefined;
|
|
3785
4047
|
const tagName = tagDefinition ? tagDefinition.name : `Tag${tag}`;
|
|
3786
4048
|
obj[tagName] = value;
|
|
3787
4049
|
}
|
|
@@ -3905,6 +4167,10 @@ class ImageFileDirectoryParser {
|
|
|
3905
4167
|
}
|
|
3906
4168
|
}
|
|
3907
4169
|
|
|
4170
|
+
/**
|
|
4171
|
+
* @param {Uint8Array|Uint16Array|Uint32Array} row
|
|
4172
|
+
* @param {number} stride
|
|
4173
|
+
*/
|
|
3908
4174
|
function decodeRowAcc(row, stride) {
|
|
3909
4175
|
let length = row.length - stride;
|
|
3910
4176
|
let offset = 0;
|
|
@@ -3916,6 +4182,11 @@ function decodeRowAcc(row, stride) {
|
|
|
3916
4182
|
length -= stride;
|
|
3917
4183
|
} while (length > 0);
|
|
3918
4184
|
}
|
|
4185
|
+
/**
|
|
4186
|
+
* @param {Uint8Array} row
|
|
4187
|
+
* @param {number} stride
|
|
4188
|
+
* @param {number} bytesPerSample
|
|
4189
|
+
*/
|
|
3919
4190
|
function decodeRowFloatingPoint(row, stride, bytesPerSample) {
|
|
3920
4191
|
let index = 0;
|
|
3921
4192
|
let count = row.length;
|
|
@@ -3934,6 +4205,15 @@ function decodeRowFloatingPoint(row, stride, bytesPerSample) {
|
|
|
3934
4205
|
}
|
|
3935
4206
|
}
|
|
3936
4207
|
}
|
|
4208
|
+
/**
|
|
4209
|
+
* @param {ArrayBufferLike} block
|
|
4210
|
+
* @param {number} predictor
|
|
4211
|
+
* @param {number} width
|
|
4212
|
+
* @param {number} height
|
|
4213
|
+
* @param {number[]} bitsPerSample
|
|
4214
|
+
* @param {number} planarConfiguration
|
|
4215
|
+
* @returns
|
|
4216
|
+
*/
|
|
3937
4217
|
function applyPredictor(block, predictor, width, height, bitsPerSample, planarConfiguration) {
|
|
3938
4218
|
if (!predictor || predictor === 1) {
|
|
3939
4219
|
return block;
|
|
@@ -3978,21 +4258,41 @@ function applyPredictor(block, predictor, width, height, bitsPerSample, planarCo
|
|
|
3978
4258
|
return block;
|
|
3979
4259
|
}
|
|
3980
4260
|
|
|
4261
|
+
/**
|
|
4262
|
+
* @typedef {Object} BaseDecoderParameters
|
|
4263
|
+
* @property {number} tileWidth
|
|
4264
|
+
* @property {number} tileHeight
|
|
4265
|
+
* @property {number} predictor
|
|
4266
|
+
* @property {number|number[]|import('../geotiff.js').TypedArray} bitsPerSample
|
|
4267
|
+
* @property {number} planarConfiguration
|
|
4268
|
+
* @property {number} [samplesPerPixel]
|
|
4269
|
+
*/
|
|
3981
4270
|
class BaseDecoder {
|
|
4271
|
+
/**
|
|
4272
|
+
* @param {BaseDecoderParameters} parameters
|
|
4273
|
+
*/
|
|
3982
4274
|
constructor(parameters) {
|
|
3983
4275
|
this.parameters = parameters;
|
|
3984
4276
|
}
|
|
3985
4277
|
/**
|
|
3986
4278
|
* @abstract
|
|
4279
|
+
* @param {ArrayBufferLike} _buffer
|
|
4280
|
+
* @returns {Promise<ArrayBufferLike>|ArrayBufferLike}
|
|
3987
4281
|
*/
|
|
3988
4282
|
decodeBlock(_buffer) {
|
|
3989
4283
|
throw new Error('decodeBlock not implemented');
|
|
3990
4284
|
}
|
|
4285
|
+
/**
|
|
4286
|
+
* @param {ArrayBufferLike} buffer
|
|
4287
|
+
* @returns {Promise<ArrayBufferLike>}
|
|
4288
|
+
*/
|
|
3991
4289
|
async decode(buffer) {
|
|
3992
4290
|
const decoded = await this.decodeBlock(buffer);
|
|
3993
4291
|
const { tileWidth, tileHeight, predictor, bitsPerSample, planarConfiguration, } = this.parameters;
|
|
3994
4292
|
if (predictor !== 1) {
|
|
3995
|
-
|
|
4293
|
+
const isBitsPerSampleArray = Array.isArray(bitsPerSample) || ArrayBuffer.isView(bitsPerSample);
|
|
4294
|
+
const adaptedBitsPerSample = isBitsPerSampleArray ? Array.from(bitsPerSample) : [bitsPerSample];
|
|
4295
|
+
return applyPredictor(decoded, predictor, tileWidth, tileHeight, adaptedBitsPerSample, planarConfiguration);
|
|
3996
4296
|
}
|
|
3997
4297
|
return decoded;
|
|
3998
4298
|
}
|
|
@@ -4018,6 +4318,43 @@ class BaseDecoder {
|
|
|
4018
4318
|
* `TypedArray[] & { height: number; width: number}`
|
|
4019
4319
|
* @typedef {TypedArray[] & Dimensions} TypedArrayArrayWithDimensions
|
|
4020
4320
|
*/
|
|
4321
|
+
/**
|
|
4322
|
+
* @typedef {Object} GeotiffWriterMetadata
|
|
4323
|
+
* @property {number | number[]} [ImageWidth]
|
|
4324
|
+
* @property {number | number[]} [ImageLength]
|
|
4325
|
+
* @property {number} [width]
|
|
4326
|
+
* @property {number} [height]
|
|
4327
|
+
* @property {number | number[]} [BitsPerSample]
|
|
4328
|
+
* @property {number | number[]} [Compression]
|
|
4329
|
+
* @property {number | number[]} [PlanarConfiguration]
|
|
4330
|
+
* @property {number | number[]} [ExtraSamples]
|
|
4331
|
+
* @property {number | number[]} [PhotometricInterpretation]
|
|
4332
|
+
* @property {number | number[]} [SamplesPerPixel]
|
|
4333
|
+
* @property {number | number[]} [StripByteCounts]
|
|
4334
|
+
* @property {number[]} [ModelPixelScale]
|
|
4335
|
+
* @property {number[]} [ModelTransformation]
|
|
4336
|
+
* @property {number[]} [ModelTiepoint]
|
|
4337
|
+
* @property {number[]} [GeoKeyDirectory]
|
|
4338
|
+
* @property {string} [GeoAsciiParams]
|
|
4339
|
+
* @property {number[]} [GeoDoubleParams]
|
|
4340
|
+
* @property {number | number[]} [Orientation]
|
|
4341
|
+
* @property {number | number[]} [ResolutionUnit]
|
|
4342
|
+
* @property {number | number[]} [XPosition]
|
|
4343
|
+
* @property {number | number[]} [YPosition]
|
|
4344
|
+
* @property {number | number[]} [RowsPerStrip]
|
|
4345
|
+
* @property {number[]} [SampleFormat]
|
|
4346
|
+
* @property {number | number[]} [TileWidth]
|
|
4347
|
+
* @property {number | number[]} [TileLength]
|
|
4348
|
+
* @property {number[]} [TileOffsets]
|
|
4349
|
+
* @property {number[]} [TileByteCounts]
|
|
4350
|
+
* @property {string} [GDAL_NODATA]
|
|
4351
|
+
* @property {number | number[]} [GeographicTypeGeoKey]
|
|
4352
|
+
* @property {number | number[]} [ProjectedCSTypeGeoKey]
|
|
4353
|
+
* @property {string} [GeogCitationGeoKey]
|
|
4354
|
+
* @property {string} [GTCitationGeoKey]
|
|
4355
|
+
* @property {number | number[]} [GTModelTypeGeoKey]
|
|
4356
|
+
* @property {number | number[]} [GTRasterTypeGeoKey]
|
|
4357
|
+
*/
|
|
4021
4358
|
/**
|
|
4022
4359
|
* The autogenerated docs are a little confusing here. The effective type is:
|
|
4023
4360
|
*
|
|
@@ -4029,7 +4366,7 @@ class BaseDecoder {
|
|
|
4029
4366
|
* Use the {@link Pool.bindParameters} method to get a decoder worker for
|
|
4030
4367
|
* a specific compression and its parameters.
|
|
4031
4368
|
*
|
|
4032
|
-
* @property {(buffer:
|
|
4369
|
+
* @property {(buffer: ArrayBufferLike) => Promise<ArrayBufferLike>} decode
|
|
4033
4370
|
* A function that takes a compressed buffer and returns a promise resolving to the decoded buffer.
|
|
4034
4371
|
*/
|
|
4035
4372
|
/**
|
|
@@ -4068,12 +4405,12 @@ class BaseDecoder {
|
|
|
4068
4405
|
*/
|
|
4069
4406
|
/**
|
|
4070
4407
|
* @typedef {Object} BlockedSourceOptions
|
|
4071
|
-
* @property {number
|
|
4408
|
+
* @property {number} [blockSize] Block size for a BlockedSource.
|
|
4072
4409
|
* @property {number} [cacheSize=100] The number of blocks to cache.
|
|
4073
4410
|
*/
|
|
4074
4411
|
/**
|
|
4075
4412
|
* @typedef {Object} RemoteSourceOptions
|
|
4076
|
-
* @property {
|
|
4413
|
+
* @property {Record<string, string>} [headers={}] Additional headers to add to each request
|
|
4077
4414
|
* @property {number} [maxRanges=0] Maximum number of ranges to request in a single HTTP request. 0 means no multi-range requests.
|
|
4078
4415
|
* @property {boolean} [allowFullFile=false] Whether to allow full file responses when requesting ranges
|
|
4079
4416
|
* @property {boolean} [forceXHR=false] When the Fetch API would be used, force using XMLHttpRequest instead.
|
|
@@ -4094,7 +4431,7 @@ class BaseDecoder {
|
|
|
4094
4431
|
* @returns {TypedArray|Array<number>|string}
|
|
4095
4432
|
*/
|
|
4096
4433
|
function getValues(dataSlice, fieldType, count, offset) {
|
|
4097
|
-
/** @type {TypedArray|Array
|
|
4434
|
+
/** @type {TypedArray|Array<number>|null} */
|
|
4098
4435
|
let values = null;
|
|
4099
4436
|
let readMethod = null;
|
|
4100
4437
|
const fieldTypeLength = getFieldTypeSize(fieldType);
|
|
@@ -4157,27 +4494,23 @@ function getValues(dataSlice, fieldType, count, offset) {
|
|
|
4157
4494
|
throw new RangeError(`Invalid field type: ${fieldType}`);
|
|
4158
4495
|
}
|
|
4159
4496
|
// normal fields
|
|
4160
|
-
|
|
4497
|
+
{
|
|
4161
4498
|
for (let i = 0; i < count; ++i) {
|
|
4162
4499
|
values[i] = readMethod.call(dataSlice, offset + (i * fieldTypeLength));
|
|
4163
4500
|
}
|
|
4164
4501
|
}
|
|
4165
|
-
|
|
4166
|
-
for (let i = 0; i < count; i += 2) {
|
|
4167
|
-
values[i] = readMethod.call(dataSlice, offset + (i * fieldTypeLength));
|
|
4168
|
-
values[i + 1] = readMethod.call(dataSlice, offset + ((i * fieldTypeLength) + 4));
|
|
4169
|
-
}
|
|
4170
|
-
}
|
|
4171
|
-
if (fieldType === fieldTypes.ASCII) {
|
|
4502
|
+
{
|
|
4172
4503
|
return new TextDecoder('utf-8').decode(/** @type {Uint8Array} */ (values));
|
|
4173
4504
|
}
|
|
4174
|
-
return values;
|
|
4175
4505
|
}
|
|
4176
4506
|
/**
|
|
4177
4507
|
* Error class for cases when an IFD index was requested, that does not exist
|
|
4178
4508
|
* in the file.
|
|
4179
4509
|
*/
|
|
4180
4510
|
class GeoTIFFImageIndexError extends Error {
|
|
4511
|
+
/**
|
|
4512
|
+
* @param {number} index
|
|
4513
|
+
*/
|
|
4181
4514
|
constructor(index) {
|
|
4182
4515
|
super(`No image at index ${index}`);
|
|
4183
4516
|
this.index = index;
|
|
@@ -4262,7 +4595,7 @@ class GeoTIFFBase {
|
|
|
4262
4595
|
const image = await this.getImage(i);
|
|
4263
4596
|
const subfileType = image.fileDirectory.getValue('SubfileType');
|
|
4264
4597
|
const newSubfileType = image.fileDirectory.getValue('NewSubfileType');
|
|
4265
|
-
if (i === 0 || subfileType === 2 || newSubfileType & 1) {
|
|
4598
|
+
if (i === 0 || subfileType === 2 || (newSubfileType || 0) & 1) {
|
|
4266
4599
|
allImages.push(image);
|
|
4267
4600
|
}
|
|
4268
4601
|
}
|
|
@@ -4322,9 +4655,16 @@ class GeoTIFF extends GeoTIFFBase {
|
|
|
4322
4655
|
this.bigTiff = bigTiff;
|
|
4323
4656
|
this.firstIFDOffset = firstIFDOffset;
|
|
4324
4657
|
this.cache = options.cache || false;
|
|
4658
|
+
/** @type {Array<Promise<import('./imagefiledirectory.js').ImageFileDirectory> | undefined>} */
|
|
4325
4659
|
this.ifdRequests = [];
|
|
4660
|
+
/** @type {Record<string, unknown>|null} */
|
|
4326
4661
|
this.ghostValues = null;
|
|
4327
4662
|
}
|
|
4663
|
+
/**
|
|
4664
|
+
* @param {number} offset
|
|
4665
|
+
* @param {number} [size]
|
|
4666
|
+
* @returns {Promise<DataSlice>}
|
|
4667
|
+
*/
|
|
4328
4668
|
async getSlice(offset, size) {
|
|
4329
4669
|
const fallbackSize = this.bigTiff ? 4048 : 1024;
|
|
4330
4670
|
return new DataSlice((await this.source.fetch([{
|
|
@@ -4332,6 +4672,10 @@ class GeoTIFF extends GeoTIFFBase {
|
|
|
4332
4672
|
length: typeof size !== 'undefined' ? size : fallbackSize,
|
|
4333
4673
|
}]))[0], offset, this.littleEndian, this.bigTiff);
|
|
4334
4674
|
}
|
|
4675
|
+
/**
|
|
4676
|
+
* @param {number} index
|
|
4677
|
+
* @return {Promise<import('./imagefiledirectory.js').ImageFileDirectory>}
|
|
4678
|
+
*/
|
|
4335
4679
|
async requestIFD(index) {
|
|
4336
4680
|
// see if we already have that IFD index requested.
|
|
4337
4681
|
if (this.ifdRequests[index]) {
|
|
@@ -4362,7 +4706,11 @@ class GeoTIFF extends GeoTIFFBase {
|
|
|
4362
4706
|
// if the previous IFD was loaded, we can finally fetch the one we are interested in.
|
|
4363
4707
|
// we need to wrap this in an IIFE, otherwise this.ifdRequests[index] would be delayed
|
|
4364
4708
|
this.ifdRequests[index] = (async () => {
|
|
4365
|
-
const
|
|
4709
|
+
const previousPromise = this.ifdRequests[index - 1];
|
|
4710
|
+
if (!previousPromise) {
|
|
4711
|
+
throw new Error('Previous IFD request missing');
|
|
4712
|
+
}
|
|
4713
|
+
const previousIfd = await previousPromise;
|
|
4366
4714
|
if (previousIfd.nextIFDByteOffset === 0) {
|
|
4367
4715
|
throw new GeoTIFFImageIndexError(index);
|
|
4368
4716
|
}
|
|
@@ -4407,11 +4755,11 @@ class GeoTIFF extends GeoTIFFBase {
|
|
|
4407
4755
|
/**
|
|
4408
4756
|
* Get the values of the COG ghost area as a parsed map.
|
|
4409
4757
|
* See https://gdal.org/drivers/raster/cog.html#header-ghost-area for reference
|
|
4410
|
-
* @returns {Promise<
|
|
4758
|
+
* @returns {Promise<Record<string, unknown>|null>} the parsed ghost area or null, if no such area was found
|
|
4411
4759
|
*/
|
|
4412
4760
|
async getGhostValues() {
|
|
4413
4761
|
const offset = this.bigTiff ? 16 : 8;
|
|
4414
|
-
if (this.ghostValues) {
|
|
4762
|
+
if (this.ghostValues !== null) {
|
|
4415
4763
|
return this.ghostValues;
|
|
4416
4764
|
}
|
|
4417
4765
|
const detectionString = 'GDAL_STRUCTURAL_METADATA_SIZE=';
|
|
@@ -4425,15 +4773,16 @@ class GeoTIFF extends GeoTIFFBase {
|
|
|
4425
4773
|
slice = await this.getSlice(offset, metadataSize);
|
|
4426
4774
|
}
|
|
4427
4775
|
const fullString = getValues(slice, fieldTypes.ASCII, metadataSize, offset);
|
|
4428
|
-
/** @type {
|
|
4429
|
-
|
|
4776
|
+
/** @type {Record<string, unknown>} */
|
|
4777
|
+
const ghostValues = {};
|
|
4430
4778
|
fullString
|
|
4431
4779
|
.split('\n')
|
|
4432
4780
|
.filter((line) => line.length > 0)
|
|
4433
4781
|
.map((line) => line.split('='))
|
|
4434
4782
|
.forEach(([key, value]) => {
|
|
4435
|
-
|
|
4783
|
+
ghostValues[key] = value;
|
|
4436
4784
|
});
|
|
4785
|
+
this.ghostValues = ghostValues;
|
|
4437
4786
|
}
|
|
4438
4787
|
return this.ghostValues;
|
|
4439
4788
|
}
|
|
@@ -4448,7 +4797,7 @@ class GeoTIFF extends GeoTIFFBase {
|
|
|
4448
4797
|
static async fromSource(source, options, signal) {
|
|
4449
4798
|
const headerData = (await source.fetch([{ offset: 0, length: 1024 }], signal))[0];
|
|
4450
4799
|
const dataView = new DataView64(headerData);
|
|
4451
|
-
const BOM = dataView.getUint16(0,
|
|
4800
|
+
const BOM = dataView.getUint16(0, false);
|
|
4452
4801
|
let littleEndian;
|
|
4453
4802
|
if (BOM === 0x4949) {
|
|
4454
4803
|
littleEndian = true;
|
|
@@ -4519,6 +4868,7 @@ async function fromArrayBuffer(arrayBuffer, signal) {
|
|
|
4519
4868
|
const DefaultGeoImageOptions = {
|
|
4520
4869
|
// --- Shared / Data ---
|
|
4521
4870
|
type: 'image',
|
|
4871
|
+
blockSize: 65536,
|
|
4522
4872
|
format: undefined,
|
|
4523
4873
|
useChannel: null,
|
|
4524
4874
|
useChannelIndex: null,
|
|
@@ -4545,9 +4895,23 @@ const DefaultGeoImageOptions = {
|
|
|
4545
4895
|
color: [255, 0, 255, 255],
|
|
4546
4896
|
colorScale: chroma.brewer.YlOrRd,
|
|
4547
4897
|
colorScaleValueRange: [0, 255],
|
|
4898
|
+
// colorScale: [
|
|
4899
|
+
// [75, 120, 90], // Brightened forest green
|
|
4900
|
+
// [100, 145, 100], // Soft meadow green
|
|
4901
|
+
// [130, 170, 110], // Bright moss
|
|
4902
|
+
// [185, 210, 145], // Sunny sage
|
|
4903
|
+
// [235, 235, 185], // Pale primrose (transitional)
|
|
4904
|
+
// [225, 195, 160], // Sand / light terracotta (matches slope)
|
|
4905
|
+
// [195, 160, 130], // Warm clay brown
|
|
4906
|
+
// [170, 155, 150], // Warm slate grey
|
|
4907
|
+
// [245, 245, 240], // Bright mist
|
|
4908
|
+
// [255, 255, 255], // Pure peak white
|
|
4909
|
+
// ],
|
|
4910
|
+
// colorScaleValueRange: [0, 6500],
|
|
4548
4911
|
colorsBasedOnValues: undefined,
|
|
4549
4912
|
colorClasses: undefined,
|
|
4550
4913
|
alpha: 100,
|
|
4914
|
+
maxGlazeAlpha: 128,
|
|
4551
4915
|
nullColor: [0, 0, 0, 0],
|
|
4552
4916
|
unidentifiedColor: [0, 0, 0, 0],
|
|
4553
4917
|
clippedColor: [0, 0, 0, 0],
|
|
@@ -4557,6 +4921,11 @@ const DefaultGeoImageOptions = {
|
|
|
4557
4921
|
hillshadeAzimuth: 315,
|
|
4558
4922
|
hillshadeAltitude: 45,
|
|
4559
4923
|
zFactor: 1,
|
|
4924
|
+
useSwissRelief: false,
|
|
4925
|
+
swissSlopeWeight: 0.5,
|
|
4926
|
+
useReliefGlaze: false,
|
|
4927
|
+
// --- Lighting control ---
|
|
4928
|
+
disableLighting: false,
|
|
4560
4929
|
};
|
|
4561
4930
|
|
|
4562
4931
|
class Martini {
|
|
@@ -5243,6 +5612,11 @@ function scale(num, inMin, inMax, outMin, outMax) {
|
|
|
5243
5612
|
}
|
|
5244
5613
|
|
|
5245
5614
|
class BitmapGenerator {
|
|
5615
|
+
/**
|
|
5616
|
+
* Cache for Swiss relief color LUTs to avoid regenerating on every tile.
|
|
5617
|
+
* Key: colorScale config + range, Value: pre-computed RGBA LUT
|
|
5618
|
+
*/
|
|
5619
|
+
static _swissColorLUTCache = new Map();
|
|
5246
5620
|
/**
|
|
5247
5621
|
* Main entry point: Generates an ImageBitmap from raw raster data.
|
|
5248
5622
|
*/
|
|
@@ -5269,7 +5643,31 @@ class BitmapGenerator {
|
|
|
5269
5643
|
// If planar support is added, this logic must be updated to handle both layouts correctly.
|
|
5270
5644
|
const numAvailableChannels = optionsLocal.numOfChannels ??
|
|
5271
5645
|
(rasters.length === 1 ? rasters[0].length / (width * height) : rasters.length);
|
|
5272
|
-
if (optionsLocal.
|
|
5646
|
+
if (optionsLocal.useReliefGlaze) {
|
|
5647
|
+
if (rasters.length >= 1) {
|
|
5648
|
+
// Relief glaze: pure black/white overlay with variable alpha
|
|
5649
|
+
imageData.data.set(this.getReliefGlazeRGBA(rasters, optionsLocal, size));
|
|
5650
|
+
}
|
|
5651
|
+
else {
|
|
5652
|
+
// Missing relief mask: fill with transparent
|
|
5653
|
+
const transparentData = new Uint8ClampedArray(size);
|
|
5654
|
+
transparentData.fill(0);
|
|
5655
|
+
imageData.data.set(transparentData);
|
|
5656
|
+
}
|
|
5657
|
+
}
|
|
5658
|
+
else if (optionsLocal.useSwissRelief) {
|
|
5659
|
+
if (rasters.length === 2) {
|
|
5660
|
+
// Normal Swiss relief rendering: hypsometric color × relief mask
|
|
5661
|
+
imageData.data.set(this.getColorValue(rasters, optionsLocal, size));
|
|
5662
|
+
}
|
|
5663
|
+
else { // Missing mask: fill with null color (fully transparent or a fallback)
|
|
5664
|
+
const defaultColorData = this.getDefaultColor(size, optionsLocal.nullColor);
|
|
5665
|
+
defaultColorData.forEach((value, index) => {
|
|
5666
|
+
imageData.data[index] = value;
|
|
5667
|
+
});
|
|
5668
|
+
}
|
|
5669
|
+
}
|
|
5670
|
+
else if (optionsLocal.useChannelIndex == null) {
|
|
5273
5671
|
if (isInterleaved) {
|
|
5274
5672
|
const ratio = rasters[0].length / (width * height);
|
|
5275
5673
|
if (ratio === 1) {
|
|
@@ -5337,9 +5735,68 @@ class BitmapGenerator {
|
|
|
5337
5735
|
const optAlpha = Math.floor((options.alpha ?? 100) * 2.55);
|
|
5338
5736
|
const rangeMin = options.colorScaleValueRange?.[0] ?? 0;
|
|
5339
5737
|
const rangeMax = options.colorScaleValueRange?.[1] ?? 255;
|
|
5340
|
-
const
|
|
5341
|
-
const
|
|
5342
|
-
|
|
5738
|
+
const isMultiRaster = Array.isArray(dataArray);
|
|
5739
|
+
const primaryBuffer = isMultiRaster ? dataArray[0] : dataArray;
|
|
5740
|
+
const isSwiss = options.useSwissRelief && isMultiRaster && dataArray.length >= 2;
|
|
5741
|
+
const is8Bit = primaryBuffer instanceof Uint8Array || primaryBuffer instanceof Uint8ClampedArray;
|
|
5742
|
+
const isFloatOrWide = !is8Bit && (primaryBuffer instanceof Float32Array || primaryBuffer instanceof Uint16Array || primaryBuffer instanceof Int16Array);
|
|
5743
|
+
// 1. SWISS MODE BRANCH
|
|
5744
|
+
if (isSwiss) {
|
|
5745
|
+
const reliefMask = dataArray[1];
|
|
5746
|
+
const rangeSpan = (rangeMax - rangeMin) || 1;
|
|
5747
|
+
// Only use LUT optimization for useHeatMap mode; other modes use calculateSingleColor per-pixel
|
|
5748
|
+
let lut = null;
|
|
5749
|
+
if (options.useHeatMap) {
|
|
5750
|
+
const LUT_SIZE = 1024;
|
|
5751
|
+
// Cache LUT: generate key from colorScale config + range + alpha
|
|
5752
|
+
const cacheKey = `${rangeMin}_${rangeMax}_${optAlpha}_${JSON.stringify(options.colorScale)}`;
|
|
5753
|
+
lut = this._swissColorLUTCache.get(cacheKey) || null;
|
|
5754
|
+
if (!lut) {
|
|
5755
|
+
// LUT not cached, generate it
|
|
5756
|
+
lut = new Uint8ClampedArray(LUT_SIZE * 4);
|
|
5757
|
+
for (let i = 0; i < LUT_SIZE; i++) {
|
|
5758
|
+
const domainVal = rangeMin + (i / (LUT_SIZE - 1)) * rangeSpan;
|
|
5759
|
+
const rgb = colorScale(domainVal).rgb();
|
|
5760
|
+
lut[i * 4] = rgb[0];
|
|
5761
|
+
lut[i * 4 + 1] = rgb[1];
|
|
5762
|
+
lut[i * 4 + 2] = rgb[2];
|
|
5763
|
+
lut[i * 4 + 3] = optAlpha;
|
|
5764
|
+
}
|
|
5765
|
+
this._swissColorLUTCache.set(cacheKey, lut);
|
|
5766
|
+
}
|
|
5767
|
+
}
|
|
5768
|
+
for (let i = 0, sampleIndex = (options.useChannelIndex ?? 0); i < arrayLength; i += 4, sampleIndex += samplesPerPixel) {
|
|
5769
|
+
const elevationVal = primaryBuffer[sampleIndex];
|
|
5770
|
+
// NaN-aware noData check for Swiss relief
|
|
5771
|
+
const isNoData = options.noDataValue !== undefined && (Number.isNaN(options.noDataValue)
|
|
5772
|
+
? Number.isNaN(elevationVal)
|
|
5773
|
+
: elevationVal === options.noDataValue);
|
|
5774
|
+
if (Number.isNaN(elevationVal) || isNoData) {
|
|
5775
|
+
colorsArray.set(options.nullColor, i);
|
|
5776
|
+
continue;
|
|
5777
|
+
}
|
|
5778
|
+
let baseColor;
|
|
5779
|
+
if (lut) {
|
|
5780
|
+
// LUT-optimized path for useHeatMap
|
|
5781
|
+
const t = (elevationVal - rangeMin) / rangeSpan;
|
|
5782
|
+
const lutIdx = Math.min(1023, Math.max(0, Math.floor(t * 1023))) * 4;
|
|
5783
|
+
baseColor = [lut[lutIdx], lut[lutIdx + 1], lut[lutIdx + 2], lut[lutIdx + 3]];
|
|
5784
|
+
}
|
|
5785
|
+
else {
|
|
5786
|
+
// Per-pixel calculation for useSingleColor, useColorClasses, useColorsBasedOnValues
|
|
5787
|
+
baseColor = this.calculateSingleColor(elevationVal, colorScale, options, optAlpha);
|
|
5788
|
+
}
|
|
5789
|
+
// Apply relief mask as multiplier (Ambient Fill approach)
|
|
5790
|
+
const maskVal = reliefMask[sampleIndex];
|
|
5791
|
+
const multiplier = 0.4 + 0.6 * (maskVal / 255);
|
|
5792
|
+
colorsArray[i] = Math.floor(baseColor[0] * multiplier);
|
|
5793
|
+
colorsArray[i + 1] = Math.floor(baseColor[1] * multiplier);
|
|
5794
|
+
colorsArray[i + 2] = Math.floor(baseColor[2] * multiplier);
|
|
5795
|
+
colorsArray[i + 3] = baseColor[3];
|
|
5796
|
+
}
|
|
5797
|
+
return colorsArray;
|
|
5798
|
+
}
|
|
5799
|
+
// 2. 8-BIT COMPREHENSIVE LUT
|
|
5343
5800
|
// Single-band 8-bit (grayscale or indexed): use LUT for fast mapping
|
|
5344
5801
|
if (is8Bit && !options.useDataForOpacity) {
|
|
5345
5802
|
const lut = new Uint8ClampedArray(256 * 4);
|
|
@@ -5353,7 +5810,7 @@ class BitmapGenerator {
|
|
|
5353
5810
|
}
|
|
5354
5811
|
}
|
|
5355
5812
|
for (let i = 0, sampleIndex = (options.useChannelIndex ?? 0); i < arrayLength; i += 4, sampleIndex += samplesPerPixel) {
|
|
5356
|
-
const lutIdx =
|
|
5813
|
+
const lutIdx = primaryBuffer[sampleIndex] * 4;
|
|
5357
5814
|
colorsArray[i] = lut[lutIdx];
|
|
5358
5815
|
colorsArray[i + 1] = lut[lutIdx + 1];
|
|
5359
5816
|
colorsArray[i + 2] = lut[lutIdx + 2];
|
|
@@ -5361,7 +5818,7 @@ class BitmapGenerator {
|
|
|
5361
5818
|
}
|
|
5362
5819
|
return colorsArray;
|
|
5363
5820
|
}
|
|
5364
|
-
//
|
|
5821
|
+
// 3. FLOAT / 16-BIT LUT (HEATMAP ONLY)
|
|
5365
5822
|
if (isFloatOrWide && options.useHeatMap && !options.useDataForOpacity) {
|
|
5366
5823
|
const LUT_SIZE = 1024;
|
|
5367
5824
|
const lut = new Uint8ClampedArray(LUT_SIZE * 4);
|
|
@@ -5381,7 +5838,7 @@ class BitmapGenerator {
|
|
|
5381
5838
|
}
|
|
5382
5839
|
}
|
|
5383
5840
|
for (let i = 0, sampleIndex = (options.useChannelIndex ?? 0); i < arrayLength; i += 4, sampleIndex += samplesPerPixel) {
|
|
5384
|
-
const val =
|
|
5841
|
+
const val = primaryBuffer[sampleIndex];
|
|
5385
5842
|
if (this.isInvalid(val, options)) {
|
|
5386
5843
|
colorsArray.set(this.getInvalidColor(val, options), i);
|
|
5387
5844
|
}
|
|
@@ -5396,10 +5853,10 @@ class BitmapGenerator {
|
|
|
5396
5853
|
}
|
|
5397
5854
|
return colorsArray;
|
|
5398
5855
|
}
|
|
5399
|
-
//
|
|
5856
|
+
// 4. FALLBACK LOOP (Categorical Float, Opacity, or Single Color)
|
|
5400
5857
|
let sampleIndex = options.useChannelIndex ?? 0;
|
|
5401
5858
|
for (let i = 0; i < arrayLength; i += 4) {
|
|
5402
|
-
const val =
|
|
5859
|
+
const val = primaryBuffer[sampleIndex];
|
|
5403
5860
|
let color;
|
|
5404
5861
|
if ((options.clipLow != null && val <= options.clipLow) || (options.clipHigh != null && val >= options.clipHigh)) {
|
|
5405
5862
|
color = options.clippedColor;
|
|
@@ -5415,6 +5872,50 @@ class BitmapGenerator {
|
|
|
5415
5872
|
}
|
|
5416
5873
|
return colorsArray;
|
|
5417
5874
|
}
|
|
5875
|
+
/**
|
|
5876
|
+
* Generate relief glaze RGBA output.
|
|
5877
|
+
* Maps relief mask (0-255) to pure black/white glaze with variable alpha.
|
|
5878
|
+
* - reliefValue < 128: Pure black (0,0,0) darkens shadows
|
|
5879
|
+
* - reliefValue > 128: Pure white (255,255,255) brightens highlights
|
|
5880
|
+
* - reliefValue == 128: Transparent (no effect)
|
|
5881
|
+
*
|
|
5882
|
+
* High-performance implementation using pre-computed alpha LUT to avoid 65k Math.pow calls.
|
|
5883
|
+
*
|
|
5884
|
+
* @param rasters Array of [relief mask raster] (single raster expected)
|
|
5885
|
+
* @param options GeoImageOptions (alpha used for opacity scaling)
|
|
5886
|
+
* @param arrayLength Total RGBA array length
|
|
5887
|
+
* @returns Uint8ClampedArray of RGBA values
|
|
5888
|
+
*/
|
|
5889
|
+
static getReliefGlazeRGBA(rasters, options, arrayLength) {
|
|
5890
|
+
const reliefMask = rasters[0];
|
|
5891
|
+
const opacityFactor = (options.maxGlazeAlpha ?? 128) / 255;
|
|
5892
|
+
// Pre-compute alpha lookup table (256 entries, one per relief value 0-255)
|
|
5893
|
+
const alphaLookup = new Uint8Array(256);
|
|
5894
|
+
for (let v = 0; v < 256; v++) {
|
|
5895
|
+
if (v === 0) {
|
|
5896
|
+
alphaLookup[v] = 0; // noData: fully transparent
|
|
5897
|
+
}
|
|
5898
|
+
else {
|
|
5899
|
+
const alphaDist = Math.abs(v - 128) / 128;
|
|
5900
|
+
const bias = v < 128 ? 0.6 : 0.8;
|
|
5901
|
+
alphaLookup[v] = Math.floor(Math.pow(alphaDist, bias) * 255 * opacityFactor);
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
const glazeArray = new Uint8ClampedArray(arrayLength);
|
|
5905
|
+
let maskIndex = 0;
|
|
5906
|
+
for (let i = 0; i < arrayLength; i += 4) {
|
|
5907
|
+
const reliefValue = reliefMask[maskIndex];
|
|
5908
|
+
// Pure black for shadows, pure white for highlights (no muddy grays)
|
|
5909
|
+
const glaze = reliefValue < 128 ? 0 : 255;
|
|
5910
|
+
const alpha = alphaLookup[reliefValue];
|
|
5911
|
+
glazeArray[i] = glaze; // R
|
|
5912
|
+
glazeArray[i + 1] = glaze; // G
|
|
5913
|
+
glazeArray[i + 2] = glaze; // B
|
|
5914
|
+
glazeArray[i + 3] = alpha; // A
|
|
5915
|
+
maskIndex++;
|
|
5916
|
+
}
|
|
5917
|
+
return glazeArray;
|
|
5918
|
+
}
|
|
5418
5919
|
static calculateSingleColor(val, colorScale, options, alpha) {
|
|
5419
5920
|
if (this.isInvalid(val, options)) {
|
|
5420
5921
|
return options.nullColor;
|
|
@@ -5495,6 +5996,21 @@ class BitmapGenerator {
|
|
|
5495
5996
|
* Output: Float32Array of 256×256 computed values.
|
|
5496
5997
|
*/
|
|
5497
5998
|
class KernelGenerator {
|
|
5999
|
+
/**
|
|
6000
|
+
* Compute terrain gradients (dzdx, dzdy) using Horn's method.
|
|
6001
|
+
* @param z1-z9 - 3×3 neighborhood elevation values (z5 is center)
|
|
6002
|
+
* @param cellSizeFactor - Pre-computed 1 / (8 * cellSize)
|
|
6003
|
+
* @param geographicConvention - If true, use north-minus-south for dzdy (hillshade). If false, use south-minus-north (slope).
|
|
6004
|
+
*/
|
|
6005
|
+
static computeGradients(z1, z2, z3, z4, /* z5 not needed */ z6, z7, z8, z9, cellSizeFactor, geographicConvention = true) {
|
|
6006
|
+
const dzdx = ((z3 + 2 * z6 + z9) - (z1 + 2 * z4 + z7)) * cellSizeFactor;
|
|
6007
|
+
// Geographic convention (hillshade): north minus south (top rows minus bottom rows)
|
|
6008
|
+
// Slope convention: south minus north (reversed)
|
|
6009
|
+
const dzdy = geographicConvention
|
|
6010
|
+
? ((z1 + 2 * z2 + z3) - (z7 + 2 * z8 + z9)) * cellSizeFactor
|
|
6011
|
+
: ((z7 + 2 * z8 + z9) - (z1 + 2 * z2 + z3)) * cellSizeFactor;
|
|
6012
|
+
return { dzdx, dzdy };
|
|
6013
|
+
}
|
|
5498
6014
|
/**
|
|
5499
6015
|
* Calculates slope (0–90 degrees) for each pixel using Horn's method.
|
|
5500
6016
|
*
|
|
@@ -5507,12 +6023,18 @@ class KernelGenerator {
|
|
|
5507
6023
|
const OUT = 256;
|
|
5508
6024
|
const IN = 258;
|
|
5509
6025
|
const out = new Float32Array(OUT * OUT);
|
|
6026
|
+
// Hoist division out of loop: multiplication is ~2-3x faster than division
|
|
6027
|
+
const cellSizeFactor = 1 / (8 * cellSize);
|
|
6028
|
+
// Cache constant for radians to degrees conversion
|
|
6029
|
+
const RAD_TO_DEG = 180 / Math.PI;
|
|
6030
|
+
const isNaNNoData = noDataValue !== undefined && Number.isNaN(noDataValue);
|
|
5510
6031
|
for (let r = 0; r < OUT; r++) {
|
|
5511
6032
|
for (let c = 0; c < OUT; c++) {
|
|
5512
6033
|
// 3×3 neighborhood in the 258×258 input, centered at (r+1, c+1)
|
|
5513
6034
|
const base = r * IN + c;
|
|
5514
6035
|
const z5 = src[base + IN + 1]; // center pixel
|
|
5515
|
-
|
|
6036
|
+
const isNoData = noDataValue !== undefined && (isNaNNoData ? Number.isNaN(z5) : z5 === noDataValue);
|
|
6037
|
+
if (isNoData) {
|
|
5516
6038
|
out[r * OUT + c] = NaN;
|
|
5517
6039
|
continue;
|
|
5518
6040
|
}
|
|
@@ -5524,10 +6046,9 @@ class KernelGenerator {
|
|
|
5524
6046
|
const z7 = src[base + 2 * IN]; // sw
|
|
5525
6047
|
const z8 = src[base + 2 * IN + 1]; // s
|
|
5526
6048
|
const z9 = src[base + 2 * IN + 2]; // se
|
|
5527
|
-
const dzdx
|
|
5528
|
-
const dzdy = ((z7 + 2 * z8 + z9) - (z1 + 2 * z2 + z3)) / (8 * cellSize);
|
|
6049
|
+
const { dzdx, dzdy } = this.computeGradients(z1, z2, z3, z4, z6, z7, z8, z9, cellSizeFactor, false);
|
|
5529
6050
|
const slopeRad = Math.atan(zFactor * Math.sqrt(dzdx * dzdx + dzdy * dzdy));
|
|
5530
|
-
out[r * OUT + c] = slopeRad *
|
|
6051
|
+
out[r * OUT + c] = slopeRad * RAD_TO_DEG;
|
|
5531
6052
|
}
|
|
5532
6053
|
}
|
|
5533
6054
|
return out;
|
|
@@ -5552,11 +6073,15 @@ class KernelGenerator {
|
|
|
5552
6073
|
if (azimuthMath >= 360)
|
|
5553
6074
|
azimuthMath -= 360;
|
|
5554
6075
|
const azimuthRad = azimuthMath * (Math.PI / 180);
|
|
6076
|
+
// Hoist division out of loop: multiplication is ~2-3x faster than division
|
|
6077
|
+
const cellSizeFactor = 1 / (8 * cellSize);
|
|
6078
|
+
const isNaNNoData = noDataValue !== undefined && Number.isNaN(noDataValue);
|
|
5555
6079
|
for (let r = 0; r < OUT; r++) {
|
|
5556
6080
|
for (let c = 0; c < OUT; c++) {
|
|
5557
6081
|
const base = r * IN + c;
|
|
5558
6082
|
const z5 = src[base + IN + 1]; // center pixel
|
|
5559
|
-
|
|
6083
|
+
const isNoData = noDataValue !== undefined && (isNaNNoData ? Number.isNaN(z5) : z5 === noDataValue);
|
|
6084
|
+
if (isNoData) {
|
|
5560
6085
|
out[r * OUT + c] = NaN;
|
|
5561
6086
|
continue;
|
|
5562
6087
|
}
|
|
@@ -5568,9 +6093,7 @@ class KernelGenerator {
|
|
|
5568
6093
|
const z7 = src[base + 2 * IN]; // sw
|
|
5569
6094
|
const z8 = src[base + 2 * IN + 1]; // s
|
|
5570
6095
|
const z9 = src[base + 2 * IN + 2]; // se
|
|
5571
|
-
const dzdx
|
|
5572
|
-
// dzdy: north minus south (geographic convention — top rows minus bottom rows in raster)
|
|
5573
|
-
const dzdy = ((z1 + 2 * z2 + z3) - (z7 + 2 * z8 + z9)) / (8 * cellSize);
|
|
6096
|
+
const { dzdx, dzdy } = this.computeGradients(z1, z2, z3, z4, z6, z7, z8, z9, cellSizeFactor, true);
|
|
5574
6097
|
const slopeRad = Math.atan(zFactor * Math.sqrt(dzdx * dzdx + dzdy * dzdy));
|
|
5575
6098
|
const aspectRad = Math.atan2(dzdy, -dzdx);
|
|
5576
6099
|
const hillshade = 255 * (Math.cos(zenithRad) * Math.cos(slopeRad) +
|
|
@@ -5580,6 +6103,139 @@ class KernelGenerator {
|
|
|
5580
6103
|
}
|
|
5581
6104
|
return out;
|
|
5582
6105
|
}
|
|
6106
|
+
/**
|
|
6107
|
+
* Calculates a weighted multi-directional hillshade (0–255).
|
|
6108
|
+
* Combines three light sources to reveal structure in shadows.
|
|
6109
|
+
*/
|
|
6110
|
+
static calculateMultiHillshade(src, cellSize, zFactor = 1, noDataValue) {
|
|
6111
|
+
const OUT = 256;
|
|
6112
|
+
const IN = 258;
|
|
6113
|
+
const out = new Float32Array(OUT * OUT);
|
|
6114
|
+
// Hoist division out of loop: multiplication is ~2-3x faster than division
|
|
6115
|
+
const cellSizeFactor = 1 / (8 * cellSize);
|
|
6116
|
+
const isNaNNoData = noDataValue !== undefined && Number.isNaN(noDataValue);
|
|
6117
|
+
// Setup 3 light sources: NW (Main), W (Fill), N (Fill)
|
|
6118
|
+
const lights = [
|
|
6119
|
+
{ az: 315, alt: 45, weight: 0.60 }, // Primary NW
|
|
6120
|
+
{ az: 225, alt: 35, weight: 0.25 }, // Secondary West/SW
|
|
6121
|
+
{ az: 0, alt: 35, weight: 0.15 } // Secondary North
|
|
6122
|
+
].map(l => {
|
|
6123
|
+
const zenithRad = (90 - l.alt) * (Math.PI / 180);
|
|
6124
|
+
let azMath = 360 - l.az + 90;
|
|
6125
|
+
if (azMath >= 360)
|
|
6126
|
+
azMath -= 360;
|
|
6127
|
+
return {
|
|
6128
|
+
zCos: Math.cos(zenithRad),
|
|
6129
|
+
zSin: Math.sin(zenithRad),
|
|
6130
|
+
aRad: azMath * (Math.PI / 180),
|
|
6131
|
+
w: l.weight
|
|
6132
|
+
};
|
|
6133
|
+
});
|
|
6134
|
+
for (let r = 0; r < OUT; r++) {
|
|
6135
|
+
for (let c = 0; c < OUT; c++) {
|
|
6136
|
+
const base = r * IN + c;
|
|
6137
|
+
const z5 = src[base + IN + 1];
|
|
6138
|
+
const isNoData = noDataValue !== undefined && (isNaNNoData ? Number.isNaN(z5) : z5 === noDataValue);
|
|
6139
|
+
if (isNoData) {
|
|
6140
|
+
out[r * OUT + c] = NaN;
|
|
6141
|
+
continue;
|
|
6142
|
+
}
|
|
6143
|
+
// Neighbors
|
|
6144
|
+
const z1 = src[base], z2 = src[base + 1], z3 = src[base + 2];
|
|
6145
|
+
const z4 = src[base + IN], z6 = src[base + IN + 2];
|
|
6146
|
+
const z7 = src[base + 2 * IN], z8 = src[base + 2 * IN + 1], z9 = src[base + 2 * IN + 2];
|
|
6147
|
+
const { dzdx, dzdy } = this.computeGradients(z1, z2, z3, z4, z6, z7, z8, z9, cellSizeFactor, true);
|
|
6148
|
+
const slopeRad = Math.atan(zFactor * Math.sqrt(dzdx * dzdx + dzdy * dzdy));
|
|
6149
|
+
const aspectRad = Math.atan2(dzdy, -dzdx);
|
|
6150
|
+
const cosSlope = Math.cos(slopeRad);
|
|
6151
|
+
const sinSlope = Math.sin(slopeRad);
|
|
6152
|
+
// Accumulate light from all three directions
|
|
6153
|
+
let multiHillshade = 0;
|
|
6154
|
+
for (const L of lights) {
|
|
6155
|
+
const intensity = L.zCos * cosSlope + L.zSin * sinSlope * Math.cos(L.aRad - aspectRad);
|
|
6156
|
+
multiHillshade += Math.max(0, intensity) * L.w;
|
|
6157
|
+
}
|
|
6158
|
+
out[r * OUT + c] = Math.min(255, multiHillshade * 255);
|
|
6159
|
+
}
|
|
6160
|
+
}
|
|
6161
|
+
return out;
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
|
|
6165
|
+
/**
|
|
6166
|
+
* Composes Swiss relief by combining slope and hillshade kernels via LUT.
|
|
6167
|
+
* Outputs a single 0-255 relief mask suitable for baking into hypsometry (terrain)
|
|
6168
|
+
* or creating transparent glaze overlays (bitmap).
|
|
6169
|
+
*/
|
|
6170
|
+
class ReliefCompositor {
|
|
6171
|
+
/**
|
|
6172
|
+
* Precompute and cache a 256x256 LUT for Swiss relief compositing.
|
|
6173
|
+
* LUT[hillshade][slope] = (hillshade * (1.0 - (slope * weight)))
|
|
6174
|
+
* All values normalized to [0,1].
|
|
6175
|
+
* Only computed on first use of Swiss relief mode.
|
|
6176
|
+
*/
|
|
6177
|
+
static _swissReliefLUT = null;
|
|
6178
|
+
static _lastWeight = null;
|
|
6179
|
+
static getSwissReliefLUT(weight = 0.5) {
|
|
6180
|
+
// Check if LUT exists AND if the weight matches the previous calculation
|
|
6181
|
+
if (this._swissReliefLUT && this._lastWeight === weight) {
|
|
6182
|
+
return this._swissReliefLUT;
|
|
6183
|
+
}
|
|
6184
|
+
const ambient = 0.010; // 1% minimum brightness to prevent pitch black northwest slopes
|
|
6185
|
+
const lut = new Float32Array(256 * 256); // 65536 values
|
|
6186
|
+
for (let h = 0; h < 256; h++) {
|
|
6187
|
+
const hillshade = h / 255;
|
|
6188
|
+
for (let s = 0; s < 256; s++) {
|
|
6189
|
+
const slope = s / 255;
|
|
6190
|
+
// 1. Calculate the 'Swiss Contrast'
|
|
6191
|
+
const contrast = 1.0 - (slope * weight);
|
|
6192
|
+
// Swiss Formula: (Hillshade) * (1.0 - (Slope * Weight))
|
|
6193
|
+
// This results in 0.0 to 1.0 multiplier
|
|
6194
|
+
lut[(h << 8) | s] = Math.max(ambient, hillshade * contrast);
|
|
6195
|
+
}
|
|
6196
|
+
}
|
|
6197
|
+
this._swissReliefLUT = lut;
|
|
6198
|
+
this._lastWeight = weight;
|
|
6199
|
+
return lut;
|
|
6200
|
+
}
|
|
6201
|
+
/**
|
|
6202
|
+
* Compute Swiss relief compositing: slope + hillshade → 0-255 relief mask.
|
|
6203
|
+
*
|
|
6204
|
+
* @param elevation - Padded elevation raster (258×258 for kernel input)
|
|
6205
|
+
* @param options - GeoImageOptions (must include zFactor, noDataValue, swissSlopeWeight)
|
|
6206
|
+
* @param cellSize - Grid cell size in meters
|
|
6207
|
+
* @param width - Output width (typically 256)
|
|
6208
|
+
* @param height - Output height (typically 256)
|
|
6209
|
+
* @returns Uint8ClampedArray of 0-255 relief values
|
|
6210
|
+
*/
|
|
6211
|
+
static composeSwissRelief(elevation, options, cellSize, width, height) {
|
|
6212
|
+
const weight = options.swissSlopeWeight ?? 0.5;
|
|
6213
|
+
// 1. Compute slope and hillshade kernels
|
|
6214
|
+
const rawSlope = KernelGenerator.calculateSlope(elevation, cellSize, options.zFactor ?? 1, options.noDataValue);
|
|
6215
|
+
const rawHillshade = KernelGenerator.calculateMultiHillshade(elevation, cellSize, options.zFactor ?? 1, options.noDataValue);
|
|
6216
|
+
// 2. Fetch pre-computed LUT
|
|
6217
|
+
const lut = this.getSwissReliefLUT(weight);
|
|
6218
|
+
// 3. Compose relief mask: quantize slope/hillshade, apply LUT
|
|
6219
|
+
// reliefMask = 0 is reserved as noData sentinel → fully transparent in glaze output
|
|
6220
|
+
const reliefMask = new Uint8ClampedArray(width * height);
|
|
6221
|
+
// Hoist division out of loop: multiplication is faster than division
|
|
6222
|
+
const SLOPE_SCALE = 255 / 90; // ~2.833...
|
|
6223
|
+
for (let i = 0; i < width * height; i++) {
|
|
6224
|
+
// noData pixels: slope is NaN (set by KernelGenerator when z5 === noDataValue)
|
|
6225
|
+
if (isNaN(rawSlope[i])) {
|
|
6226
|
+
reliefMask[i] = 0; // sentinel: transparent in glaze
|
|
6227
|
+
continue;
|
|
6228
|
+
}
|
|
6229
|
+
// Quantize Slope: Normalize 0-90° to 0-255 integer (avoid division in loop)
|
|
6230
|
+
const sIdx = Math.max(0, Math.min(255, (rawSlope[i] * SLOPE_SCALE) | 0));
|
|
6231
|
+
// Quantize Hillshade: Ensure 0-255 integer
|
|
6232
|
+
const hIdx = Math.max(0, Math.min(255, rawHillshade[i])) | 0;
|
|
6233
|
+
// LUT Lookup: Result is 0.0 - 1.0 (float)
|
|
6234
|
+
// Clamp to 1 to ensure output stays in 1-255 (0 is reserved for noData)
|
|
6235
|
+
reliefMask[i] = Math.max(1, (lut[(hIdx << 8) | sIdx] * 255) | 0);
|
|
6236
|
+
}
|
|
6237
|
+
return reliefMask;
|
|
6238
|
+
}
|
|
5583
6239
|
}
|
|
5584
6240
|
|
|
5585
6241
|
class TerrainGenerator {
|
|
@@ -5643,7 +6299,20 @@ class TerrainGenerator {
|
|
|
5643
6299
|
height: gridHeight,
|
|
5644
6300
|
};
|
|
5645
6301
|
// 3. Kernel path: compute slope or hillshade, store as rawDerived, generate texture
|
|
5646
|
-
if (isKernel &&
|
|
6302
|
+
if (isKernel && options.useSwissRelief) {
|
|
6303
|
+
const cellSize = input.cellSizeMeters ?? ((input.bounds[2] - input.bounds[0]) / 256);
|
|
6304
|
+
// Build a separate raster for kernel computation that preserves noData samples.
|
|
6305
|
+
const kernelTerrain = this.preserveNoDataForKernel(terrain, input.rasters[0], options.noDataValue);
|
|
6306
|
+
// Compose Swiss relief using ReliefCompositor
|
|
6307
|
+
const swissReliefResult = ReliefCompositor.composeSwissRelief(kernelTerrain, options, cellSize, 256, 256);
|
|
6308
|
+
tileResult.rawDerived = swissReliefResult;
|
|
6309
|
+
if (this.hasVisualizationOptions(options)) {
|
|
6310
|
+
const cropped = this.cropRaster(meshTerrain, gridWidth, gridHeight, 256, 256);
|
|
6311
|
+
const bitmapResult = await BitmapGenerator.generate({ width: 256, height: 256, rasters: [cropped, swissReliefResult] }, { ...options, type: 'image' });
|
|
6312
|
+
tileResult.texture = bitmapResult.map;
|
|
6313
|
+
}
|
|
6314
|
+
}
|
|
6315
|
+
else if (isKernel && (options.useSlope || options.useHillshade)) {
|
|
5647
6316
|
// Use pre-computed geographic cellSize (meters/pixel) from tile indices.
|
|
5648
6317
|
// Falls back to bounds-derived estimate if not provided.
|
|
5649
6318
|
const cellSize = input.cellSizeMeters ?? ((input.bounds[2] - input.bounds[0]) / 256);
|
|
@@ -5653,23 +6322,7 @@ class TerrainGenerator {
|
|
|
5653
6322
|
console.warn('[TerrainGenerator] useSlope and useHillshade are mutually exclusive; useSlope takes precedence.');
|
|
5654
6323
|
}
|
|
5655
6324
|
// Build a separate raster for kernel computation that preserves noData samples.
|
|
5656
|
-
const kernelTerrain =
|
|
5657
|
-
const sourceRaster = input.rasters[0];
|
|
5658
|
-
const noData = options.noDataValue;
|
|
5659
|
-
if (noData !== undefined &&
|
|
5660
|
-
noData !== null &&
|
|
5661
|
-
sourceRaster &&
|
|
5662
|
-
sourceRaster.length === terrain.length) {
|
|
5663
|
-
for (let i = 0; i < terrain.length; i++) {
|
|
5664
|
-
// If the source raster marks this sample as noData, keep it as noData for the kernel.
|
|
5665
|
-
// Otherwise, use the processed terrain elevation value.
|
|
5666
|
-
kernelTerrain[i] = sourceRaster[i] == noData ? noData : terrain[i];
|
|
5667
|
-
}
|
|
5668
|
-
}
|
|
5669
|
-
else {
|
|
5670
|
-
// Fallback: no usable noData metadata or mismatched lengths; mirror existing behavior.
|
|
5671
|
-
kernelTerrain.set(terrain);
|
|
5672
|
-
}
|
|
6325
|
+
const kernelTerrain = this.preserveNoDataForKernel(terrain, input.rasters[0], options.noDataValue);
|
|
5673
6326
|
let kernelOutput;
|
|
5674
6327
|
if (options.useSlope) {
|
|
5675
6328
|
kernelOutput = KernelGenerator.calculateSlope(kernelTerrain, cellSize, zFactor, options.noDataValue);
|
|
@@ -5691,7 +6344,6 @@ class TerrainGenerator {
|
|
|
5691
6344
|
}
|
|
5692
6345
|
return tileResult;
|
|
5693
6346
|
}
|
|
5694
|
-
/** Extracts rows 1–257, cols 1–257 from a 258×258 terrain array → 257×257 for mesh generation. */
|
|
5695
6347
|
static extractMeshRaster(terrain258) {
|
|
5696
6348
|
const MESH = 257;
|
|
5697
6349
|
const IN = 258;
|
|
@@ -5704,11 +6356,38 @@ class TerrainGenerator {
|
|
|
5704
6356
|
return out;
|
|
5705
6357
|
}
|
|
5706
6358
|
static hasVisualizationOptions(options) {
|
|
5707
|
-
return !!(options.
|
|
5708
|
-
options.
|
|
6359
|
+
return !!(options.useSingleColor ||
|
|
6360
|
+
options.useHeatMap ||
|
|
6361
|
+
options.useSwissRelief ||
|
|
5709
6362
|
options.useColorsBasedOnValues ||
|
|
5710
6363
|
options.useColorClasses);
|
|
5711
6364
|
}
|
|
6365
|
+
/**
|
|
6366
|
+
* Preserve noData values in a separate raster for kernel computation.
|
|
6367
|
+
* If the source raster marks a sample as noData, keep it as noData.
|
|
6368
|
+
* Otherwise, use the processed terrain elevation value.
|
|
6369
|
+
*/
|
|
6370
|
+
static preserveNoDataForKernel(terrain, sourceRaster, noDataValue) {
|
|
6371
|
+
const kernelTerrain = new Float32Array(terrain.length);
|
|
6372
|
+
if (noDataValue !== undefined &&
|
|
6373
|
+
noDataValue !== null &&
|
|
6374
|
+
sourceRaster &&
|
|
6375
|
+
sourceRaster.length === terrain.length) {
|
|
6376
|
+
const preserveNaNNoData = Number.isNaN(noDataValue);
|
|
6377
|
+
for (let i = 0; i < terrain.length; i++) {
|
|
6378
|
+
const sourceValue = sourceRaster[i];
|
|
6379
|
+
const isNoData = preserveNaNNoData
|
|
6380
|
+
? Number.isNaN(sourceValue)
|
|
6381
|
+
: sourceValue === noDataValue;
|
|
6382
|
+
kernelTerrain[i] = isNoData ? noDataValue : terrain[i];
|
|
6383
|
+
}
|
|
6384
|
+
}
|
|
6385
|
+
else {
|
|
6386
|
+
// Fallback: no usable noData metadata or mismatched lengths; mirror existing behavior.
|
|
6387
|
+
kernelTerrain.set(terrain);
|
|
6388
|
+
}
|
|
6389
|
+
return kernelTerrain;
|
|
6390
|
+
}
|
|
5712
6391
|
static cropRaster(src, srcWidth, _srcHeight, dstWidth, dstHeight) {
|
|
5713
6392
|
const out = new Float32Array(dstWidth * dstHeight);
|
|
5714
6393
|
for (let y = 0; y < dstHeight; y++) {
|
|
@@ -5908,36 +6587,57 @@ class CogTiles {
|
|
|
5908
6587
|
bounds = [0, 0, 0, 0];
|
|
5909
6588
|
geo = new GeoImage();
|
|
5910
6589
|
options;
|
|
6590
|
+
// Cache GeoTIFFImage Promises by index to prevent redundant HTTP requests from geotiff 3.0.4+ eager loading
|
|
6591
|
+
// Stores Promises (not resolved values) so concurrent requests share the same getImage() call
|
|
6592
|
+
imageCache = new Map();
|
|
6593
|
+
// Store initialization promise to prevent concurrent duplicate initializations
|
|
6594
|
+
initializePromise;
|
|
5911
6595
|
constructor(options) {
|
|
5912
6596
|
this.options = { ...CogTilesGeoImageOptionsDefaults, ...options };
|
|
5913
6597
|
}
|
|
5914
6598
|
async initializeCog(url) {
|
|
6599
|
+
// Return existing initialization promise if already in progress (prevents concurrent duplicates)
|
|
6600
|
+
if (this.initializePromise)
|
|
6601
|
+
return this.initializePromise;
|
|
5915
6602
|
if (this.cog)
|
|
5916
|
-
return;
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
6603
|
+
return;
|
|
6604
|
+
this.initializePromise = (async () => {
|
|
6605
|
+
try {
|
|
6606
|
+
// fromUrl's type declaration only exposes RemoteSourceOptions, but the implementation
|
|
6607
|
+
// also accepts BlockedSourceOptions (forwarded to makeFetchSource internally).
|
|
6608
|
+
// Explicitly enabling BlockedSource restores the block-level LRU cache that was
|
|
6609
|
+
// accidentally active in geotiff 3.0.3 (due to a null vs undefined bug there).
|
|
6610
|
+
// blockSize defaults to 65536 (64KB) and can be tuned via GeoImageOptions.
|
|
6611
|
+
const blockSize = this.options.blockSize ?? 65536;
|
|
6612
|
+
this.cog = await fromUrl(url, { blockSize });
|
|
6613
|
+
const imagePromise = this.cog.getImage();
|
|
6614
|
+
this.imageCache.set(0, imagePromise); // Cache base image (index 0) to avoid re-fetching during getTileFromImage
|
|
6615
|
+
const image = await imagePromise;
|
|
6616
|
+
const fileDirectory = image.fileDirectory;
|
|
6617
|
+
this.cogOrigin = image.getOrigin();
|
|
6618
|
+
this.options.noDataValue ??= await this.getNoDataValue(image);
|
|
6619
|
+
this.options.format ??= await this.getDataTypeFromTags(fileDirectory);
|
|
6620
|
+
this.options.numOfChannels = fileDirectory.getValue('SamplesPerPixel');
|
|
6621
|
+
this.options.planarConfig = fileDirectory.getValue('PlanarConfiguration');
|
|
6622
|
+
[this.cogZoomLookup, this.cogResolutionLookup] = await this.buildCogZoomResolutionLookup(this.cog);
|
|
6623
|
+
this.tileSize = image.getTileWidth();
|
|
6624
|
+
// 1. Validation: Ensure the image is tiled
|
|
6625
|
+
if (!this.tileSize || !image.getTileHeight()) {
|
|
6626
|
+
throw new Error('GeoTIFF Error: The provided image is not tiled. '
|
|
6627
|
+
+ 'Please use "rio cogeo create --web-optimized" to fix this.');
|
|
6628
|
+
}
|
|
6629
|
+
this.zoomRange = this.calculateZoomRange(this.tileSize, image.getResolution()[0], await this.cog.getImageCount());
|
|
6630
|
+
this.bounds = this.calculateBoundsAsLatLon(image.getBoundingBox());
|
|
6631
|
+
}
|
|
6632
|
+
catch (error) {
|
|
6633
|
+
// Reset initialization promise on error so retry can be attempted
|
|
6634
|
+
this.initializePromise = undefined;
|
|
6635
|
+
/* eslint-disable no-console */
|
|
6636
|
+
console.error(`[CogTiles] Failed to initialize COG from ${url}:`, error);
|
|
6637
|
+
throw error;
|
|
6638
|
+
}
|
|
6639
|
+
})();
|
|
6640
|
+
return this.initializePromise;
|
|
5941
6641
|
}
|
|
5942
6642
|
getZoomRange() {
|
|
5943
6643
|
return this.zoomRange;
|
|
@@ -6042,7 +6742,13 @@ class CogTiles {
|
|
|
6042
6742
|
}
|
|
6043
6743
|
async getTileFromImage(tileX, tileY, zoom, fetchSize) {
|
|
6044
6744
|
const imageIndex = this.getImageIndexForZoomLevel(zoom);
|
|
6045
|
-
|
|
6745
|
+
// Cache Promises to share in-flight requests across concurrent tiles at the same overview
|
|
6746
|
+
let imagePromise = this.imageCache.get(imageIndex);
|
|
6747
|
+
if (!imagePromise) {
|
|
6748
|
+
imagePromise = this.cog.getImage(imageIndex);
|
|
6749
|
+
this.imageCache.set(imageIndex, imagePromise);
|
|
6750
|
+
}
|
|
6751
|
+
const targetImage = await imagePromise;
|
|
6046
6752
|
// --- STEP 1: CALCULATE BOUNDS IN METERS ---
|
|
6047
6753
|
// 2. Get COG Metadata (image = COG)
|
|
6048
6754
|
const imageResolution = this.cogResolutionLookup[imageIndex];
|
|
@@ -6159,19 +6865,37 @@ class CogTiles {
|
|
|
6159
6865
|
async getTile(x, y, z, bounds, meshMaxError) {
|
|
6160
6866
|
let requiredSize = this.tileSize; // Default 256 for image/bitmap
|
|
6161
6867
|
if (this.options.type === 'terrain') {
|
|
6162
|
-
const isKernel = this.options.useSlope || this.options.useHillshade;
|
|
6868
|
+
const isKernel = this.options.useSlope || this.options.useHillshade || this.options.useSwissRelief;
|
|
6163
6869
|
requiredSize = this.tileSize + (isKernel ? 2 : 1); // 258 for kernel (3×3 border), 257 for normal stitching
|
|
6164
6870
|
}
|
|
6871
|
+
else if (this.options.type === 'image' && this.options.useReliefGlaze) {
|
|
6872
|
+
// Bitmap layer with relief glaze mode needs kernel padding for slope/hillshade computation
|
|
6873
|
+
requiredSize = this.tileSize + 2; // 258 for kernel
|
|
6874
|
+
}
|
|
6165
6875
|
const tileData = await this.getTileFromImage(x, y, z, requiredSize);
|
|
6166
6876
|
// Compute true ground cell size in meters from tile indices.
|
|
6167
6877
|
// Tile y in slippy-map convention → center latitude → Web Mercator distortion correction.
|
|
6168
6878
|
const latRad = Math.atan(Math.sinh(Math.PI * (1 - 2 * (y + 0.5) / Math.pow(2, z))));
|
|
6169
6879
|
const tileWidthMeters = (EARTH_CIRCUMFERENCE / Math.pow(2, z)) * Math.cos(latRad);
|
|
6170
6880
|
const cellSizeMeters = tileWidthMeters / this.tileSize;
|
|
6881
|
+
let rasters = [tileData[0]];
|
|
6882
|
+
let tileWidth = requiredSize;
|
|
6883
|
+
let tileHeight = requiredSize;
|
|
6884
|
+
// Relief glaze computation for bitmap layers
|
|
6885
|
+
// Note: For multi-band support (band selection via useChannelIndex), see issue #98
|
|
6886
|
+
if (this.options.type === 'image' && this.options.useReliefGlaze) {
|
|
6887
|
+
const elevation = tileData[0];
|
|
6888
|
+
// Pass full 258×258 padded elevation directly — KernelGenerator expects IN=258 and outputs 256×256
|
|
6889
|
+
const reliefMask = ReliefCompositor.composeSwissRelief(elevation, this.options, cellSizeMeters, this.tileSize, this.tileSize);
|
|
6890
|
+
// For glaze-only mode, pass ONLY the 256×256 relief mask
|
|
6891
|
+
rasters = [reliefMask];
|
|
6892
|
+
tileWidth = this.tileSize;
|
|
6893
|
+
tileHeight = this.tileSize;
|
|
6894
|
+
}
|
|
6171
6895
|
return this.geo.getMap({
|
|
6172
|
-
rasters
|
|
6173
|
-
width:
|
|
6174
|
-
height:
|
|
6896
|
+
rasters,
|
|
6897
|
+
width: tileWidth,
|
|
6898
|
+
height: tileHeight,
|
|
6175
6899
|
bounds,
|
|
6176
6900
|
cellSizeMeters,
|
|
6177
6901
|
}, this.options, meshMaxError);
|
|
@@ -6384,13 +7108,20 @@ class CogBitmapLayer extends core.CompositeLayer {
|
|
|
6384
7108
|
}
|
|
6385
7109
|
}
|
|
6386
7110
|
async getTiledBitmapData(tile) {
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
tileData.
|
|
7111
|
+
try {
|
|
7112
|
+
// TODO - pass signal to getTile
|
|
7113
|
+
// abort request if signal is aborted
|
|
7114
|
+
const tileData = await this.state.bitmapCogTiles.getTile(tile.index.x, tile.index.y, tile.index.z);
|
|
7115
|
+
if (tileData && !this.props.pickable) {
|
|
7116
|
+
tileData.raw = null;
|
|
7117
|
+
}
|
|
7118
|
+
return tileData;
|
|
7119
|
+
}
|
|
7120
|
+
catch (error) {
|
|
7121
|
+
// Log the error and rethrow so TileLayer can surface the failure via onTileError
|
|
7122
|
+
core.log.warn(`Failed to load bitmap tile at ${tile.index.z}/${tile.index.x}/${tile.index.y}:`, error)();
|
|
7123
|
+
throw error;
|
|
6392
7124
|
}
|
|
6393
|
-
return tileData;
|
|
6394
7125
|
}
|
|
6395
7126
|
renderSubLayers(props) {
|
|
6396
7127
|
const SubLayerClass = this.getSubLayerClass('image', layers.BitmapLayer);
|
|
@@ -6533,11 +7264,21 @@ class CogTerrainLayer extends core.CompositeLayer {
|
|
|
6533
7264
|
terrainUrl = '';
|
|
6534
7265
|
async initializeState(context) {
|
|
6535
7266
|
super.initializeState(context);
|
|
7267
|
+
const terrainCogTiles = this.props.cogTiles || new CogTiles(this.props.terrainOptions);
|
|
6536
7268
|
this.setState({
|
|
6537
|
-
terrainCogTiles
|
|
7269
|
+
terrainCogTiles,
|
|
6538
7270
|
initialized: false,
|
|
6539
7271
|
});
|
|
6540
|
-
|
|
7272
|
+
// Only initialize if not already done (e.g., provided cogTiles instance may be pre-initialized)
|
|
7273
|
+
if (!terrainCogTiles.cog) {
|
|
7274
|
+
await this.init();
|
|
7275
|
+
}
|
|
7276
|
+
else {
|
|
7277
|
+
// CogTiles already initialized; just extract zoom range and mark ready
|
|
7278
|
+
const zoomRange = terrainCogTiles.getZoomRange();
|
|
7279
|
+
const [minZoom, maxZoom] = zoomRange;
|
|
7280
|
+
this.setState({ initialized: true, minZoom, maxZoom });
|
|
7281
|
+
}
|
|
6541
7282
|
}
|
|
6542
7283
|
async init() {
|
|
6543
7284
|
await this.state.terrainCogTiles.initializeCog(this.props.elevationData);
|
|
@@ -6623,15 +7364,28 @@ class CogTerrainLayer extends core.CompositeLayer {
|
|
|
6623
7364
|
}
|
|
6624
7365
|
renderSubLayers(props) {
|
|
6625
7366
|
const SubLayerClass = this.getSubLayerClass('mesh', meshLayers.SimpleMeshLayer);
|
|
6626
|
-
const { color, wireframe,
|
|
7367
|
+
const { color, wireframe, terrainOptions } = this.props;
|
|
6627
7368
|
const { data } = props;
|
|
6628
7369
|
if (!data) {
|
|
6629
7370
|
return null;
|
|
6630
7371
|
}
|
|
6631
|
-
// const [mesh, texture] = data;
|
|
6632
7372
|
const [meshResult] = data;
|
|
6633
7373
|
const tileTexture = (!this.props.disableTexture && meshResult?.texture) ? meshResult.texture : null;
|
|
7374
|
+
const isSwiss = terrainOptions?.useSwissRelief;
|
|
7375
|
+
const disableLighting = terrainOptions?.disableLighting;
|
|
7376
|
+
const shouldDisableLighting = isSwiss || disableLighting;
|
|
7377
|
+
const lightingProps = shouldDisableLighting ? {
|
|
7378
|
+
material: {
|
|
7379
|
+
ambient: 1.0,
|
|
7380
|
+
diffuse: 0.0,
|
|
7381
|
+
shininess: 0.0,
|
|
7382
|
+
specularColor: [0, 0, 0]
|
|
7383
|
+
}
|
|
7384
|
+
} : {
|
|
7385
|
+
material: this.props.material
|
|
7386
|
+
};
|
|
6634
7387
|
return new SubLayerClass({ ...props, tileSize: props.tileSize }, {
|
|
7388
|
+
...lightingProps,
|
|
6635
7389
|
data: DUMMY_DATA,
|
|
6636
7390
|
mesh: meshResult?.map,
|
|
6637
7391
|
texture: tileTexture,
|
|
@@ -6641,7 +7395,6 @@ class CogTerrainLayer extends core.CompositeLayer {
|
|
|
6641
7395
|
// getPosition: (d) => [0, 0, 0],
|
|
6642
7396
|
getColor: tileTexture ? [255, 255, 255] : color,
|
|
6643
7397
|
wireframe,
|
|
6644
|
-
material,
|
|
6645
7398
|
});
|
|
6646
7399
|
}
|
|
6647
7400
|
// Update zRange of viewport
|
|
@@ -6688,13 +7441,14 @@ class CogTerrainLayer extends core.CompositeLayer {
|
|
|
6688
7441
|
updateTriggers: {
|
|
6689
7442
|
getTileData: {
|
|
6690
7443
|
elevationData: urlTemplateToUpdateTrigger(elevationData),
|
|
6691
|
-
// texture: urlTemplateToUpdateTrigger(texture),
|
|
6692
7444
|
meshMaxError,
|
|
6693
7445
|
elevationDecoder,
|
|
6694
|
-
// When cogTiles instance is swapped (e.g. mode switch), refetch tiles.
|
|
6695
|
-
// deck.gl keeps old tile content visible until new tiles are ready.
|
|
6696
7446
|
terrainCogTiles: this.state.terrainCogTiles,
|
|
6697
7447
|
},
|
|
7448
|
+
renderSubLayers: {
|
|
7449
|
+
disableTexture: this.props.disableTexture,
|
|
7450
|
+
terrainOptions: this.props.terrainOptions,
|
|
7451
|
+
},
|
|
6698
7452
|
},
|
|
6699
7453
|
onViewportLoad: this.onViewportLoad.bind(this),
|
|
6700
7454
|
zRange: this.state.zRange || null,
|
|
@@ -6716,6 +7470,7 @@ class CogTerrainLayer extends core.CompositeLayer {
|
|
|
6716
7470
|
}
|
|
6717
7471
|
|
|
6718
7472
|
class RawDecoder extends BaseDecoder {
|
|
7473
|
+
/** @param {ArrayBuffer} buffer */
|
|
6719
7474
|
decodeBlock(buffer) {
|
|
6720
7475
|
return buffer;
|
|
6721
7476
|
}
|
|
@@ -6730,6 +7485,12 @@ const MIN_BITS = 9;
|
|
|
6730
7485
|
const CLEAR_CODE = 256; // clear code
|
|
6731
7486
|
const EOI_CODE = 257; // end of information
|
|
6732
7487
|
const MAX_BYTELENGTH = 12;
|
|
7488
|
+
/**
|
|
7489
|
+
* @param {Uint8Array} array
|
|
7490
|
+
* @param {number} position
|
|
7491
|
+
* @param {number} length
|
|
7492
|
+
* @returns {number}
|
|
7493
|
+
*/
|
|
6733
7494
|
function getByte(array, position, length) {
|
|
6734
7495
|
const d = position % 8;
|
|
6735
7496
|
const a = Math.floor(position / 8);
|
|
@@ -6757,12 +7518,21 @@ function getByte(array, position, length) {
|
|
|
6757
7518
|
}
|
|
6758
7519
|
return chunks;
|
|
6759
7520
|
}
|
|
7521
|
+
/**
|
|
7522
|
+
* @template T
|
|
7523
|
+
* @param {Array<T>} dest
|
|
7524
|
+
* @param {Array<T>} source
|
|
7525
|
+
* @returns {Array<T>}
|
|
7526
|
+
*/
|
|
6760
7527
|
function appendReversed(dest, source) {
|
|
6761
7528
|
for (let i = source.length - 1; i >= 0; i--) {
|
|
6762
7529
|
dest.push(source[i]);
|
|
6763
7530
|
}
|
|
6764
7531
|
return dest;
|
|
6765
7532
|
}
|
|
7533
|
+
/**
|
|
7534
|
+
* @param {ArrayBuffer} input
|
|
7535
|
+
*/
|
|
6766
7536
|
function decompress(input) {
|
|
6767
7537
|
const dictionaryIndex = new Uint16Array(4093);
|
|
6768
7538
|
const dictionaryChar = new Uint8Array(4093);
|
|
@@ -6777,17 +7547,23 @@ function decompress(input) {
|
|
|
6777
7547
|
dictionaryLength = 258;
|
|
6778
7548
|
byteLength = MIN_BITS;
|
|
6779
7549
|
}
|
|
7550
|
+
/** @param {Uint8Array} array */
|
|
6780
7551
|
function getNext(array) {
|
|
6781
7552
|
const byte = getByte(array, position, byteLength);
|
|
6782
7553
|
position += byteLength;
|
|
6783
7554
|
return byte;
|
|
6784
7555
|
}
|
|
7556
|
+
/**
|
|
7557
|
+
* @param {number} i
|
|
7558
|
+
* @param {number} c
|
|
7559
|
+
*/
|
|
6785
7560
|
function addToDictionary(i, c) {
|
|
6786
7561
|
dictionaryChar[dictionaryLength] = c;
|
|
6787
7562
|
dictionaryIndex[dictionaryLength] = i;
|
|
6788
7563
|
dictionaryLength++;
|
|
6789
7564
|
return dictionaryLength - 1;
|
|
6790
7565
|
}
|
|
7566
|
+
/** @param {number} n */
|
|
6791
7567
|
function getDictionaryReversed(n) {
|
|
6792
7568
|
const rev = [];
|
|
6793
7569
|
for (let i = n; i !== 4096; i = dictionaryIndex[i]) {
|
|
@@ -6822,10 +7598,15 @@ function decompress(input) {
|
|
|
6822
7598
|
else if (code < dictionaryLength) {
|
|
6823
7599
|
const val = getDictionaryReversed(code);
|
|
6824
7600
|
appendReversed(result, val);
|
|
6825
|
-
|
|
7601
|
+
if (oldCode !== undefined) {
|
|
7602
|
+
addToDictionary(oldCode, val[val.length - 1]);
|
|
7603
|
+
}
|
|
6826
7604
|
oldCode = code;
|
|
6827
7605
|
}
|
|
6828
7606
|
else {
|
|
7607
|
+
if (oldCode === undefined) {
|
|
7608
|
+
throw new Error(`Invalid LZW code: ${code} with no previous code`);
|
|
7609
|
+
}
|
|
6829
7610
|
const oldVal = getDictionaryReversed(oldCode);
|
|
6830
7611
|
if (!oldVal) {
|
|
6831
7612
|
throw new Error(`Bogus entry. Not in dictionary, ${oldCode} / ${dictionaryLength}, position: ${position}`);
|
|
@@ -6848,6 +7629,7 @@ function decompress(input) {
|
|
|
6848
7629
|
return new Uint8Array(result);
|
|
6849
7630
|
}
|
|
6850
7631
|
class LZWDecoder extends BaseDecoder {
|
|
7632
|
+
/** @param {ArrayBuffer} buffer */
|
|
6851
7633
|
decodeBlock(buffer) {
|
|
6852
7634
|
return decompress(buffer).buffer;
|
|
6853
7635
|
}
|
|
@@ -6906,6 +7688,33 @@ const dctSqrt2 = 5793; // sqrt(2)
|
|
|
6906
7688
|
const dctSqrt1d2 = 2896; // sqrt(2) / 2
|
|
6907
7689
|
/** @typedef {(number|HuffmanNode)[]} HuffmanNode */
|
|
6908
7690
|
/** @typedef {{children: HuffmanNode, index: number}} Code */
|
|
7691
|
+
/**
|
|
7692
|
+
* @typedef {Object} JpegComponent
|
|
7693
|
+
* @property {number} h
|
|
7694
|
+
* @property {number} v
|
|
7695
|
+
* @property {number} [quantizationIdx]
|
|
7696
|
+
* @property {Int32Array} [quantizationTable]
|
|
7697
|
+
* @property {number} blocksPerLine
|
|
7698
|
+
* @property {number} blocksPerColumn
|
|
7699
|
+
* @property {Int32Array[][]} blocks
|
|
7700
|
+
* @property {HuffmanNode} [huffmanTableDC]
|
|
7701
|
+
* @property {HuffmanNode} [huffmanTableAC]
|
|
7702
|
+
* @property {number} [pred]
|
|
7703
|
+
*/
|
|
7704
|
+
/**
|
|
7705
|
+
* @typedef {Object} JpegFrame
|
|
7706
|
+
* @property {boolean} extended
|
|
7707
|
+
* @property {boolean} progressive
|
|
7708
|
+
* @property {number} precision
|
|
7709
|
+
* @property {number} scanLines
|
|
7710
|
+
* @property {number} samplesPerLine
|
|
7711
|
+
* @property {Object.<string, JpegComponent>} components
|
|
7712
|
+
* @property {number[]} componentsOrder
|
|
7713
|
+
* @property {number} maxH
|
|
7714
|
+
* @property {number} maxV
|
|
7715
|
+
* @property {number} mcusPerLine
|
|
7716
|
+
* @property {number} mcusPerColumn
|
|
7717
|
+
*/
|
|
6909
7718
|
/**
|
|
6910
7719
|
* @param {Uint8Array<ArrayBuffer>} codeLengths
|
|
6911
7720
|
* @param {Uint8Array<ArrayBuffer>} values
|
|
@@ -6955,8 +7764,25 @@ function buildHuffmanTable(codeLengths, values) {
|
|
|
6955
7764
|
}
|
|
6956
7765
|
return code[0].children;
|
|
6957
7766
|
}
|
|
7767
|
+
/**
|
|
7768
|
+
* @param {Uint8Array} data
|
|
7769
|
+
* @param {number} initialOffset
|
|
7770
|
+
* @param {JpegFrame} frame
|
|
7771
|
+
* @param {JpegComponent[]} components
|
|
7772
|
+
* @param {number} resetInterval
|
|
7773
|
+
* @param {number} spectralStart
|
|
7774
|
+
* @param {number} spectralEnd
|
|
7775
|
+
* @param {number} successivePrev
|
|
7776
|
+
* @param {number} successive
|
|
7777
|
+
*/
|
|
6958
7778
|
function decodeScan(data, initialOffset, frame, components, resetInterval, spectralStart, spectralEnd, successivePrev, successive) {
|
|
6959
7779
|
const { mcusPerLine, progressive } = frame;
|
|
7780
|
+
if (components.length > 1 && (mcusPerLine === undefined || frame.mcusPerColumn === undefined)) {
|
|
7781
|
+
throw new Error('Missing MCU dimensions');
|
|
7782
|
+
}
|
|
7783
|
+
if (components.length === 1 && (components[0].blocksPerLine === undefined || components[0].blocksPerColumn === undefined)) {
|
|
7784
|
+
throw new Error('Missing block dimensions');
|
|
7785
|
+
}
|
|
6960
7786
|
const startOffset = initialOffset;
|
|
6961
7787
|
let offset = initialOffset;
|
|
6962
7788
|
let bitsData = 0;
|
|
@@ -6977,20 +7803,26 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
6977
7803
|
bitsCount = 7;
|
|
6978
7804
|
return bitsData >>> 7;
|
|
6979
7805
|
}
|
|
7806
|
+
/** @param {HuffmanNode|undefined} tree */
|
|
6980
7807
|
function decodeHuffman(tree) {
|
|
7808
|
+
if (!tree) {
|
|
7809
|
+
throw new Error('Huffman table not found');
|
|
7810
|
+
}
|
|
6981
7811
|
let node = tree;
|
|
6982
7812
|
let bit;
|
|
6983
7813
|
while ((bit = readBit()) !== null) { // eslint-disable-line no-cond-assign
|
|
6984
|
-
|
|
6985
|
-
if (typeof
|
|
6986
|
-
return
|
|
7814
|
+
const next = node[bit];
|
|
7815
|
+
if (typeof next === 'number') {
|
|
7816
|
+
return next;
|
|
6987
7817
|
}
|
|
6988
|
-
if (typeof
|
|
7818
|
+
if (typeof next !== 'object') {
|
|
6989
7819
|
throw new Error('invalid huffman sequence');
|
|
6990
7820
|
}
|
|
7821
|
+
node = next;
|
|
6991
7822
|
}
|
|
6992
7823
|
return null;
|
|
6993
7824
|
}
|
|
7825
|
+
/** @param {number} initialLength */
|
|
6994
7826
|
function receive(initialLength) {
|
|
6995
7827
|
let length = initialLength;
|
|
6996
7828
|
let n = 0;
|
|
@@ -7004,6 +7836,7 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7004
7836
|
}
|
|
7005
7837
|
return n;
|
|
7006
7838
|
}
|
|
7839
|
+
/** @param {number} length */
|
|
7007
7840
|
function receiveAndExtend(length) {
|
|
7008
7841
|
const n = receive(length);
|
|
7009
7842
|
if (n === undefined) {
|
|
@@ -7014,9 +7847,22 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7014
7847
|
}
|
|
7015
7848
|
return n + (-1 << length) + 1;
|
|
7016
7849
|
}
|
|
7850
|
+
/**
|
|
7851
|
+
* @param {JpegComponent} component
|
|
7852
|
+
* @param {Int32Array} zz
|
|
7853
|
+
*/
|
|
7017
7854
|
function decodeBaseline(component, zz) {
|
|
7018
7855
|
const t = decodeHuffman(component.huffmanTableDC);
|
|
7856
|
+
if (t === null) {
|
|
7857
|
+
throw new Error('Huffman error');
|
|
7858
|
+
}
|
|
7019
7859
|
const diff = t === 0 ? 0 : receiveAndExtend(t);
|
|
7860
|
+
if (diff === undefined) {
|
|
7861
|
+
throw new Error('Unexpected end of stream');
|
|
7862
|
+
}
|
|
7863
|
+
if (component.pred === undefined) {
|
|
7864
|
+
component.pred = 0;
|
|
7865
|
+
}
|
|
7020
7866
|
component.pred += diff;
|
|
7021
7867
|
zz[0] = component.pred;
|
|
7022
7868
|
let k = 1;
|
|
@@ -7036,25 +7882,51 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7036
7882
|
else {
|
|
7037
7883
|
k += r;
|
|
7038
7884
|
const z = dctZigZag[k];
|
|
7039
|
-
|
|
7885
|
+
const val = receiveAndExtend(s);
|
|
7886
|
+
if (val === undefined) {
|
|
7887
|
+
throw new Error('Unexpected end of stream');
|
|
7888
|
+
}
|
|
7889
|
+
zz[z] = val;
|
|
7040
7890
|
k++;
|
|
7041
7891
|
}
|
|
7042
7892
|
}
|
|
7043
7893
|
}
|
|
7894
|
+
/**
|
|
7895
|
+
* @param {JpegComponent} component
|
|
7896
|
+
* @param {Int32Array} zz
|
|
7897
|
+
*/
|
|
7044
7898
|
function decodeDCFirst(component, zz) {
|
|
7045
7899
|
const t = decodeHuffman(component.huffmanTableDC);
|
|
7900
|
+
if (t === null) {
|
|
7901
|
+
throw new Error('Huffman error');
|
|
7902
|
+
}
|
|
7046
7903
|
const value = receiveAndExtend(t);
|
|
7047
7904
|
if (value === undefined) {
|
|
7048
7905
|
throw new Error('Unexpected end of data in DC coefficient decoding');
|
|
7049
7906
|
}
|
|
7050
7907
|
const diff = t === 0 ? 0 : (value << successive);
|
|
7908
|
+
if (component.pred === undefined) {
|
|
7909
|
+
component.pred = 0;
|
|
7910
|
+
}
|
|
7051
7911
|
component.pred += diff;
|
|
7052
7912
|
zz[0] = component.pred;
|
|
7053
7913
|
}
|
|
7054
|
-
|
|
7055
|
-
|
|
7914
|
+
/**
|
|
7915
|
+
* @param {JpegComponent} _
|
|
7916
|
+
* @param {Int32Array} zz
|
|
7917
|
+
*/
|
|
7918
|
+
function decodeDCSuccessive(_, zz) {
|
|
7919
|
+
const bit = readBit();
|
|
7920
|
+
if (bit === null) {
|
|
7921
|
+
throw new Error('Unexpected end of data in DC coefficient decoding');
|
|
7922
|
+
}
|
|
7923
|
+
zz[0] |= bit << successive;
|
|
7056
7924
|
}
|
|
7057
7925
|
let eobrun = 0;
|
|
7926
|
+
/**
|
|
7927
|
+
* @param {JpegComponent} component
|
|
7928
|
+
* @param {Int32Array} zz
|
|
7929
|
+
*/
|
|
7058
7930
|
function decodeACFirst(component, zz) {
|
|
7059
7931
|
if (eobrun > 0) {
|
|
7060
7932
|
eobrun--;
|
|
@@ -7093,7 +7965,12 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7093
7965
|
}
|
|
7094
7966
|
}
|
|
7095
7967
|
let successiveACState = 0;
|
|
7968
|
+
/** @type {number} */
|
|
7096
7969
|
let successiveACNextValue;
|
|
7970
|
+
/**
|
|
7971
|
+
* @param {JpegComponent} component
|
|
7972
|
+
* @param {Int32Array} zz
|
|
7973
|
+
*/
|
|
7097
7974
|
function decodeACSuccessive(component, zz) {
|
|
7098
7975
|
let k = spectralStart;
|
|
7099
7976
|
const e = spectralEnd;
|
|
@@ -7127,7 +8004,11 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7127
8004
|
if (s !== 1) {
|
|
7128
8005
|
throw new Error('invalid ACn encoding');
|
|
7129
8006
|
}
|
|
7130
|
-
|
|
8007
|
+
const nextVal = receiveAndExtend(s);
|
|
8008
|
+
if (nextVal === undefined) {
|
|
8009
|
+
throw new Error('Unexpected end of data in AC coefficient decoding');
|
|
8010
|
+
}
|
|
8011
|
+
successiveACNextValue = nextVal;
|
|
7131
8012
|
successiveACState = r ? 2 : 3;
|
|
7132
8013
|
}
|
|
7133
8014
|
continue; // eslint-disable-line no-continue
|
|
@@ -7135,7 +8016,11 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7135
8016
|
case 1: // skipping r zero items
|
|
7136
8017
|
case 2:
|
|
7137
8018
|
if (zz[z]) {
|
|
7138
|
-
|
|
8019
|
+
const bit = readBit();
|
|
8020
|
+
if (bit === null) {
|
|
8021
|
+
throw new Error('Unexpected end of data in AC coefficient decoding');
|
|
8022
|
+
}
|
|
8023
|
+
zz[z] += (bit << successive) * direction;
|
|
7139
8024
|
}
|
|
7140
8025
|
else {
|
|
7141
8026
|
r--;
|
|
@@ -7146,7 +8031,11 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7146
8031
|
break;
|
|
7147
8032
|
case 3: // set value for a zero item
|
|
7148
8033
|
if (zz[z]) {
|
|
7149
|
-
|
|
8034
|
+
const bit = readBit();
|
|
8035
|
+
if (bit === null) {
|
|
8036
|
+
throw new Error('Unexpected end of data in AC coefficient decoding');
|
|
8037
|
+
}
|
|
8038
|
+
zz[z] += (bit << successive) * direction;
|
|
7150
8039
|
}
|
|
7151
8040
|
else {
|
|
7152
8041
|
zz[z] = successiveACNextValue << successive;
|
|
@@ -7155,7 +8044,11 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7155
8044
|
break;
|
|
7156
8045
|
case 4: // eob
|
|
7157
8046
|
if (zz[z]) {
|
|
7158
|
-
|
|
8047
|
+
const bit = readBit();
|
|
8048
|
+
if (bit === null) {
|
|
8049
|
+
throw new Error('Unexpected end of data in AC coefficient decoding');
|
|
8050
|
+
}
|
|
8051
|
+
zz[z] += (bit << successive) * direction;
|
|
7159
8052
|
}
|
|
7160
8053
|
break;
|
|
7161
8054
|
}
|
|
@@ -7168,16 +8061,34 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7168
8061
|
}
|
|
7169
8062
|
}
|
|
7170
8063
|
}
|
|
8064
|
+
/**
|
|
8065
|
+
* @param {JpegComponent} component
|
|
8066
|
+
* @param {function} decodeFunction
|
|
8067
|
+
* @param {number} mcu
|
|
8068
|
+
* @param {number} row
|
|
8069
|
+
* @param {number} col
|
|
8070
|
+
*/
|
|
7171
8071
|
function decodeMcu(component, decodeFunction, mcu, row, col) {
|
|
7172
8072
|
const mcuRow = (mcu / mcusPerLine) | 0;
|
|
7173
8073
|
const mcuCol = mcu % mcusPerLine;
|
|
7174
8074
|
const blockRow = (mcuRow * component.v) + row;
|
|
7175
8075
|
const blockCol = (mcuCol * component.h) + col;
|
|
8076
|
+
if (!component.blocks) {
|
|
8077
|
+
throw new Error('Missing blocks');
|
|
8078
|
+
}
|
|
7176
8079
|
decodeFunction(component, component.blocks[blockRow][blockCol]);
|
|
7177
8080
|
}
|
|
8081
|
+
/**
|
|
8082
|
+
* @param {JpegComponent} component
|
|
8083
|
+
* @param {function} decodeFunction
|
|
8084
|
+
* @param {number} mcu
|
|
8085
|
+
*/
|
|
7178
8086
|
function decodeBlock(component, decodeFunction, mcu) {
|
|
7179
8087
|
const blockRow = (mcu / component.blocksPerLine) | 0;
|
|
7180
8088
|
const blockCol = mcu % component.blocksPerLine;
|
|
8089
|
+
if (!component.blocks) {
|
|
8090
|
+
throw new Error('Missing blocks');
|
|
8091
|
+
}
|
|
7181
8092
|
decodeFunction(component, component.blocks[blockRow][blockCol]);
|
|
7182
8093
|
}
|
|
7183
8094
|
const componentsLength = components.length;
|
|
@@ -7254,9 +8165,15 @@ function decodeScan(data, initialOffset, frame, components, resetInterval, spect
|
|
|
7254
8165
|
}
|
|
7255
8166
|
return offset - startOffset;
|
|
7256
8167
|
}
|
|
7257
|
-
|
|
8168
|
+
/**
|
|
8169
|
+
* @param {JpegComponent} component
|
|
8170
|
+
*/
|
|
8171
|
+
function buildComponentData(component) {
|
|
7258
8172
|
const lines = [];
|
|
7259
8173
|
const { blocksPerLine, blocksPerColumn } = component;
|
|
8174
|
+
if (!blocksPerLine || !blocksPerColumn || !component.blocks) {
|
|
8175
|
+
throw new Error('Missing component data');
|
|
8176
|
+
}
|
|
7260
8177
|
const samplesPerLine = blocksPerLine << 3;
|
|
7261
8178
|
const R = new Int32Array(64);
|
|
7262
8179
|
const r = new Uint8Array(64);
|
|
@@ -7265,8 +8182,16 @@ function buildComponentData(frame, component) {
|
|
|
7265
8182
|
// "Practical Fast 1-D DCT Algorithms with 11 Multiplications",
|
|
7266
8183
|
// IEEE Intl. Conf. on Acoustics, Speech & Signal Processing, 1989,
|
|
7267
8184
|
// 988-991.
|
|
8185
|
+
/**
|
|
8186
|
+
* @param {Int32Array} zz
|
|
8187
|
+
* @param {Uint8Array} dataOut
|
|
8188
|
+
* @param {Int32Array} dataIn
|
|
8189
|
+
*/
|
|
7268
8190
|
function quantizeAndInverse(zz, dataOut, dataIn) {
|
|
7269
8191
|
const qt = component.quantizationTable;
|
|
8192
|
+
if (!qt) {
|
|
8193
|
+
throw new Error('No quantization table found');
|
|
8194
|
+
}
|
|
7270
8195
|
let v0;
|
|
7271
8196
|
let v1;
|
|
7272
8197
|
let v2;
|
|
@@ -7445,14 +8370,21 @@ class JpegStreamReader {
|
|
|
7445
8370
|
constructor() {
|
|
7446
8371
|
this.jfif = null;
|
|
7447
8372
|
this.adobe = null;
|
|
8373
|
+
/** @type {number} */
|
|
8374
|
+
this.resetInterval = 0;
|
|
8375
|
+
/** @type {Int32Array[]} */
|
|
7448
8376
|
this.quantizationTables = [];
|
|
8377
|
+
/** @type {HuffmanNode[]} */
|
|
7449
8378
|
this.huffmanTablesAC = [];
|
|
8379
|
+
/** @type {HuffmanNode[]} */
|
|
7450
8380
|
this.huffmanTablesDC = [];
|
|
8381
|
+
/** @type {JpegFrame[]} */
|
|
7451
8382
|
this.frames = [];
|
|
7452
8383
|
}
|
|
7453
8384
|
resetFrames() {
|
|
7454
8385
|
this.frames = [];
|
|
7455
8386
|
}
|
|
8387
|
+
/** @param {Uint8Array} data */
|
|
7456
8388
|
parse(data) {
|
|
7457
8389
|
let offset = 0;
|
|
7458
8390
|
// const { length } = data;
|
|
@@ -7467,6 +8399,7 @@ class JpegStreamReader {
|
|
|
7467
8399
|
offset += array.length;
|
|
7468
8400
|
return array;
|
|
7469
8401
|
}
|
|
8402
|
+
/** @param {JpegFrame} frame */
|
|
7470
8403
|
function prepareComponents(frame) {
|
|
7471
8404
|
let maxH = 0;
|
|
7472
8405
|
let maxV = 0;
|
|
@@ -7593,15 +8526,21 @@ class JpegStreamReader {
|
|
|
7593
8526
|
case 0xFFC1: // SOF1 (Start of Frame, Extended DCT)
|
|
7594
8527
|
case 0xFFC2: { // SOF2 (Start of Frame, Progressive DCT)
|
|
7595
8528
|
readUint16(); // skip data length
|
|
8529
|
+
/** @type {JpegFrame} */
|
|
7596
8530
|
const frame = {
|
|
7597
8531
|
extended: (fileMarker === 0xFFC1),
|
|
7598
8532
|
progressive: (fileMarker === 0xFFC2),
|
|
7599
8533
|
precision: data[offset++],
|
|
7600
8534
|
scanLines: readUint16(),
|
|
7601
8535
|
samplesPerLine: readUint16(),
|
|
8536
|
+
/** @type {Object.<string, JpegComponent>} */
|
|
7602
8537
|
components: {},
|
|
7603
|
-
/** @type {
|
|
8538
|
+
/** @type {number[]} */
|
|
7604
8539
|
componentsOrder: [],
|
|
8540
|
+
maxH: 0,
|
|
8541
|
+
maxV: 0,
|
|
8542
|
+
mcusPerLine: 0,
|
|
8543
|
+
mcusPerColumn: 0,
|
|
7605
8544
|
};
|
|
7606
8545
|
const componentsCount = data[offset++];
|
|
7607
8546
|
let componentId;
|
|
@@ -7617,6 +8556,9 @@ class JpegStreamReader {
|
|
|
7617
8556
|
h,
|
|
7618
8557
|
v,
|
|
7619
8558
|
quantizationIdx: qId,
|
|
8559
|
+
blocksPerLine: 0,
|
|
8560
|
+
blocksPerColumn: 0,
|
|
8561
|
+
blocks: [],
|
|
7620
8562
|
};
|
|
7621
8563
|
offset += 3;
|
|
7622
8564
|
}
|
|
@@ -7701,11 +8643,17 @@ class JpegStreamReader {
|
|
|
7701
8643
|
for (let i = 0; i < this.frames.length; i++) {
|
|
7702
8644
|
const cp = this.frames[i].components;
|
|
7703
8645
|
for (const j of Object.keys(cp)) {
|
|
7704
|
-
|
|
7705
|
-
|
|
8646
|
+
const qIdx = cp[j].quantizationIdx;
|
|
8647
|
+
if (typeof qIdx === 'number') {
|
|
8648
|
+
cp[j].quantizationTable = this.quantizationTables[qIdx];
|
|
8649
|
+
delete cp[j].quantizationIdx;
|
|
8650
|
+
}
|
|
7706
8651
|
}
|
|
7707
8652
|
}
|
|
7708
8653
|
const frame = frames[0];
|
|
8654
|
+
if (!frame.maxH || !frame.maxV) {
|
|
8655
|
+
throw new Error('Invalid frame dimensions');
|
|
8656
|
+
}
|
|
7709
8657
|
const { components, componentsOrder } = frame;
|
|
7710
8658
|
const outComponents = [];
|
|
7711
8659
|
const width = frame.samplesPerLine;
|
|
@@ -7713,7 +8661,7 @@ class JpegStreamReader {
|
|
|
7713
8661
|
for (let i = 0; i < componentsOrder.length; i++) {
|
|
7714
8662
|
const component = components[componentsOrder[i]];
|
|
7715
8663
|
outComponents.push({
|
|
7716
|
-
lines: buildComponentData(
|
|
8664
|
+
lines: buildComponentData(component),
|
|
7717
8665
|
scaleX: component.h / frame.maxH,
|
|
7718
8666
|
scaleY: component.v / frame.maxV,
|
|
7719
8667
|
});
|
|
@@ -7733,6 +8681,9 @@ class JpegStreamReader {
|
|
|
7733
8681
|
}
|
|
7734
8682
|
}
|
|
7735
8683
|
class JpegDecoder extends BaseDecoder {
|
|
8684
|
+
/**
|
|
8685
|
+
* @param {import('./basedecoder.js').BaseDecoderParameters & { JPEGTables?: Uint8Array }} parameters
|
|
8686
|
+
*/
|
|
7736
8687
|
constructor(parameters) {
|
|
7737
8688
|
super(parameters);
|
|
7738
8689
|
this.reader = new JpegStreamReader();
|
|
@@ -7740,6 +8691,7 @@ class JpegDecoder extends BaseDecoder {
|
|
|
7740
8691
|
this.reader.parse(parameters.JPEGTables);
|
|
7741
8692
|
}
|
|
7742
8693
|
}
|
|
8694
|
+
/** @param {ArrayBuffer} buffer */
|
|
7743
8695
|
decodeBlock(buffer) {
|
|
7744
8696
|
this.reader.resetFrames();
|
|
7745
8697
|
this.reader.parse(new Uint8Array(buffer));
|
|
@@ -10988,6 +11940,7 @@ const { inflate} = inflate_1$1;
|
|
|
10988
11940
|
var inflate_1 = inflate;
|
|
10989
11941
|
|
|
10990
11942
|
class DeflateDecoder extends BaseDecoder {
|
|
11943
|
+
/** @param {ArrayBuffer} buffer */
|
|
10991
11944
|
decodeBlock(buffer) {
|
|
10992
11945
|
return inflate_1(new Uint8Array(buffer)).buffer;
|
|
10993
11946
|
}
|
|
@@ -10999,6 +11952,7 @@ var deflate = /*#__PURE__*/Object.freeze({
|
|
|
10999
11952
|
});
|
|
11000
11953
|
|
|
11001
11954
|
class PackbitsDecoder extends BaseDecoder {
|
|
11955
|
+
/** @param {ArrayBuffer} buffer */
|
|
11002
11956
|
decodeBlock(buffer) {
|
|
11003
11957
|
const dataView = new DataView(buffer);
|
|
11004
11958
|
const out = [];
|
|
@@ -13446,23 +14400,33 @@ let ZSTDDecoder$1 = class ZSTDDecoder {
|
|
|
13446
14400
|
// wasm:begin
|
|
13447
14401
|
const wasm$1 = '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AikACDcACCAHIDo3AAAgEUEZSA0BIARBGGohBANAIAIpABAhOiAEIAIpABg3AAggBCA6NwAAIAIpACAhOiAEIAIpACg3ABggBCA6NwAQIAJBIGohAiAEQSBqIgQgCkkNAAsMAQsCQAJAIAUoAswCIhEgBUHgAWogJUEHcUEMbGoiDygCACICaiIHIBxLDQAgCSAPKAIEIgogAmoiBmogG0sNACAGQSBqIBQgCWtNDQELIAUgDygCCDYCoAEgBSAPKQIANwOYASAJIBQgBUGYAWogBUHMAmogHCALIBcgIhAgIQYMAQsgAiAJaiEEIA8oAgghFSARKQAAITogCSARKQAINwAIIAkgOjcAAAJAIAJBEUkNACARKQAQITogCSARKQAYNwAYIAkgOjcAECACQRBrQRFIDQAgEUEQaiECIAlBIGohEgNAIAIpABAhOiASIAIpABg3AAggEiA6NwAAIAIpACAhOiASIAIpACg3ABggEiA6NwAQIAJBIGohAiASQSBqIhIgBEkNAAsLIAQgFWshAiAFIAc2AswCIAQgC2sgFUkEQCAVIAQgF2tLDQwgIiAiIAIgC2siD2oiByAKak8EQCAKRQ0CIAQgByAK/AoAAAwCC0EAIA9rIgIEQCAEIAcgAvwKAAALIAogD2ohCiAEIA9rIQQgCyECCyAVQRBPBEAgAikAACE6IAQgAikACDcACCAEIDo3AAAgCkERSA0BIAQgCmohByAEQRBqIQQDQCACKQAQITogBCACKQAYNwAIIAQgOjcAACACKQAgITogBCACKQAoNwAYIAQgOjcAECACQSBqIQIgBEEgaiIEIAdJDQALDAELAkAgFUEHTQRAIAQgAi0AADoAACAEIAItAAE6AAEgBCACLQACOgACIAQgAi0AAzoAAyAEIAIgFUECdCIHQeAaaigCAGoiAigAADYABCACIAdBgBtqKAIAayECDAELIAQgAikAADcAAAsgCkEJSQ0AIAQgCmohDyAEQQhqIgcgAkEIaiICa0EPTARAA0AgByACKQAANwAAIAJBCGohAiAHQQhqIgcgD0kNAAwCCwALIAIpAAAhOiAHIAIpAAg3AAggByA6NwAAIApBGUgNACAEQRhqIQQDQCACKQAQITogBCACKQAYNwAIIAQgOjcAACACKQAgITogBCACKQAoNwAYIAQgOjcAECACQSBqIQIgBEEgaiIEIA9JDQALCyAGQYh/SwRAIAYhAwwLCyAFQeABaiAlQQdxQQxsaiICIA02AgggAiAMNgIEIAIgEDYCAAsgBiAJaiEJICVBAWohJSAQIC1qIAxqIS0MAQsLIAUoArABIAUoArQBRw0HIAUoAqwBQSBHDQcgDiAoayEQA0ACQCAOIBBMBEBBACECA0AgAkEDRg0CIDIgAkECdCIDaiADICZqKAIANgIAIAJBAWohAgwACwALIAVB4AFqIBBBB3FBDGxqIQoCfwJAIAgoAoTsAUECRgRAIAUoAswCIg8gCigCACIEaiIHIAgoAoDsASICSwRAIAIgD0cEQCACIA9rIgIgFCAJa0sNCyAJIA8gAhAfIAogBCACayIENgIAIAIgCWohCQsgBSAWNgLMAiAIQQA2AoTsAQJAAkACQCAEQYCABEoNACAJIAooAgQiDSAEaiIGaiAbSw0AIAZBIGogFCAJa00NAQsgBSAKKAIINgJQIAUgCikCADcDSCAJIBQgBUHIAGogBUHMAmogGSALIBcgIhAgIQYMAQsgBCAWaiEHIAQgCWohDCAKKAIIIQogFikAACE6IAkgFikACDcACCAJIDo3AAACQCAEQRFJDQAgHikAACE6IAkgHikACDcAGCAJIDo3ABAgBEEQa0ERSA0AIAlBIGohAiAeIQQDQCAEKQAQITogAiAEKQAYNwAIIAIgOjcAACAEKQAgITogAiAEKQAoNwAYIAIgOjcAECAEQSBqIQQgAkEgaiICIAxJDQALCyAMIAprIQIgBSAHNgLMAiAMIAtrIApJBEAgCiAMIBdrSw0PICIgIiACIAtrIgdqIgQgDWpPBEAgDUUNAiAMIAQgDfwKAAAMAgtBACAHayICBEAgDCAEIAL8CgAACyAHIA1qIQ0gDCAHayEMIAshAgsgCkEQTwRAIAIpAAAhOiAMIAIpAAg3AAggDCA6NwAAIA1BEUgNASAMIA1qIQcgDEEQaiEEA0AgAikAECE6IAQgAikAGDcACCAEIDo3AAAgAikAICE6IAQgAikAKDcAGCAEIDo3ABAgAkEgaiECIARBIGoiBCAHSQ0ACwwBCwJAIApBB00EQCAMIAItAAA6AAAgDCACLQABOgABIAwgAi0AAjoAAiAMIAItAAM6AAMgDCACIApBAnQiBEHgGmooAgBqIgIoAAA2AAQgAiAEQYAbaigCAGshAgwBCyAMIAIpAAA3AAALIA1BCUkNACAMIA1qIQcgDEEIaiIEIAJBCGoiAmtBD0wEQANAIAQgAikAADcAACACQQhqIQIgBEEIaiIEIAdJDQAMAgsACyACKQAAITogBCACKQAINwAIIAQgOjcAACANQRlIDQAgDEEYaiEEA0AgAikAECE6IAQgAikAGDcACCAEIDo3AAAgAikAICE6IAQgAikAKDcAGCAEIDo3ABAgAkEgaiECIARBIGoiBCAHSQ0ACwsgBkGJf08EQCAGIQMMDgsgGSEcIAYgCWoMAwsgB0EgayECAkACQCAHIBxLDQAgCSAKKAIEIhIgBGoiDGogAksNACAMQSBqIBQgCWtNDQELIAUgCigCCDYCYCAFIAopAgA3A1ggCSAUIAIgBUHYAGogBUHMAmogHCALIBcgIhAhIQwMAgsgBCAJaiEGIAooAgghCiAPKQAAITogCSAPKQAINwAIIAkgOjcAAAJAIARBEUkNACAPKQAQITogCSAPKQAYNwAYIAkgOjcAECAEQRBrQRFIDQAgD0EQaiECIAlBIGohBANAIAIpABAhOiAEIAIpABg3AAggBCA6NwAAIAIpACAhOiAEIAIpACg3ABggBCA6NwAQIAJBIGohAiAEQSBqIgQgBkkNAAsLIAYgCmshAiAFIAc2AswCIAYgC2sgCkkEQCAKIAYgF2tLDQ0gIiAiIAIgC2siB2oiBCASak8EQCASRQ0DIAYgBCAS/AoAAAwDC0EAIAdrIgIEQCAGIAQgAvwKAAALIAcgEmohEiAGIAdrIQYgCyECCyAKQRBPBEAgAikAACE6IAYgAikACDcACCAGIDo3AAAgEkERSA0CIAYgEmohByAGQRBqIQQDQCACKQAQITogBCACKQAYNwAIIAQgOjcAACACKQAgITogBCACKQAoNwAYIAQgOjcAECACQSBqIQIgBEEgaiIEIAdJDQALDAILAkAgCkEHTQRAIAYgAi0AADoAACAGIAItAAE6AAEgBiACLQACOgACIAYgAi0AAzoAAyAGIAIgCkECdCIEQeAaaigCAGoiAigAADYABCACIARBgBtqKAIAayECDAELIAYgAikAADcAAAsgEkEJSQ0BIAYgEmohByAGQQhqIgQgAkEIaiICa0EPTARAA0AgBCACKQAANwAAIAJBCGohAiAE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';
|
|
13448
14402
|
|
|
14403
|
+
/**
|
|
14404
|
+
* @typedef {import('./basedecoder.js').BaseDecoderParameters & { LercParameters?: any }} LercDecoderParameters
|
|
14405
|
+
*/
|
|
13449
14406
|
const zstd$2 = new ZSTDDecoder$1();
|
|
13450
14407
|
class LercDecoder extends BaseDecoder {
|
|
14408
|
+
/**
|
|
14409
|
+
* @param {ArrayBufferLike} buffer
|
|
14410
|
+
* @returns {ArrayBufferLike}
|
|
14411
|
+
*/
|
|
13451
14412
|
decodeBlock(buffer) {
|
|
13452
|
-
const
|
|
14413
|
+
const params = /** @type {LercDecoderParameters} */ (this.parameters);
|
|
14414
|
+
const addCompression = params.LercParameters?.[LercParameters.AddCompression];
|
|
14415
|
+
/** @type {ArrayBufferLike} */
|
|
14416
|
+
let decoded = buffer;
|
|
13453
14417
|
switch (addCompression) {
|
|
13454
14418
|
case LercAddCompression.None:
|
|
13455
14419
|
break;
|
|
13456
14420
|
case LercAddCompression.Deflate:
|
|
13457
|
-
|
|
14421
|
+
decoded = inflate_1(new Uint8Array(decoded)).buffer;
|
|
13458
14422
|
break;
|
|
13459
14423
|
case LercAddCompression.Zstandard:
|
|
13460
|
-
|
|
14424
|
+
decoded = zstd$2.decode(new Uint8Array(decoded)).buffer;
|
|
13461
14425
|
break;
|
|
13462
14426
|
default:
|
|
13463
14427
|
throw new Error(`Unsupported LERC additional compression method identifier: ${addCompression}`);
|
|
13464
14428
|
}
|
|
13465
|
-
const lercResult = Lerc.decode(
|
|
14429
|
+
const lercResult = Lerc.decode(decoded, { returnPixelInterleavedDims: this.parameters.planarConfiguration === 1 });
|
|
13466
14430
|
const lercData = lercResult.pixels[0];
|
|
13467
14431
|
return lercData.buffer;
|
|
13468
14432
|
}
|
|
@@ -13638,8 +14602,9 @@ const wasm = 'AGFzbQEAAAABpgEVYAF/AGADf39/AX9gA39/fwBgAX8Bf2AFf39/f38Bf2ACf38AYA
|
|
|
13638
14602
|
|
|
13639
14603
|
const zstd = new ZSTDDecoder();
|
|
13640
14604
|
class ZstdDecoder extends BaseDecoder {
|
|
14605
|
+
/** @param {ArrayBuffer} buffer */
|
|
13641
14606
|
decodeBlock(buffer) {
|
|
13642
|
-
return zstd.decode(new Uint8Array(buffer)).buffer;
|
|
14607
|
+
return /** @type {ArrayBuffer} */ (zstd.decode(new Uint8Array(buffer)).buffer);
|
|
13643
14608
|
}
|
|
13644
14609
|
}
|
|
13645
14610
|
|
|
@@ -13656,6 +14621,9 @@ var zstd$1 = /*#__PURE__*/Object.freeze({
|
|
|
13656
14621
|
* formats like WebP when supported.
|
|
13657
14622
|
*/
|
|
13658
14623
|
class WebImageDecoder extends BaseDecoder {
|
|
14624
|
+
/**
|
|
14625
|
+
* @param {import('./basedecoder.js').BaseDecoderParameters} parameters
|
|
14626
|
+
*/
|
|
13659
14627
|
constructor(parameters) {
|
|
13660
14628
|
super(parameters);
|
|
13661
14629
|
if (typeof createImageBitmap === 'undefined') {
|
|
@@ -13665,6 +14633,7 @@ class WebImageDecoder extends BaseDecoder {
|
|
|
13665
14633
|
throw new Error('Cannot decode WebImage as neither `document` nor `OffscreenCanvas` is not available');
|
|
13666
14634
|
}
|
|
13667
14635
|
}
|
|
14636
|
+
/** @param {ArrayBuffer} buffer */
|
|
13668
14637
|
async decodeBlock(buffer) {
|
|
13669
14638
|
const blob = new Blob([buffer]);
|
|
13670
14639
|
const imageBitmap = await createImageBitmap(blob);
|