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