@basemaps/cli-raster 8.9.0 → 8.11.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.
Files changed (33) hide show
  1. package/build/cogify/cli/__test__/cli.cover.test.js +13 -0
  2. package/build/cogify/cli/__test__/cli.cover.test.js.map +1 -1
  3. package/build/cogify/cli/__test__/cli.topo.test.js +1 -0
  4. package/build/cogify/cli/__test__/cli.topo.test.js.map +1 -1
  5. package/build/cogify/cli/cli.charts.d.ts +0 -2
  6. package/build/cogify/cli/cli.charts.js +86 -44
  7. package/build/cogify/cli/cli.charts.js.map +1 -1
  8. package/build/cogify/cli/cli.cog.js +6 -5
  9. package/build/cogify/cli/cli.cog.js.map +1 -1
  10. package/build/cogify/cli/cli.cover.d.ts +4 -2
  11. package/build/cogify/cli/cli.cover.js +24 -3
  12. package/build/cogify/cli/cli.cover.js.map +1 -1
  13. package/build/cogify/cli/cli.topo.d.ts +6 -1
  14. package/build/cogify/cli/cli.topo.js +10 -2
  15. package/build/cogify/cli/cli.topo.js.map +1 -1
  16. package/build/cogify/cli.d.ts +6 -4
  17. package/build/cogify/covering/tile.cover.d.ts +6 -1
  18. package/build/cogify/covering/tile.cover.js +24 -8
  19. package/build/cogify/covering/tile.cover.js.map +1 -1
  20. package/build/cogify/gdal/gdal.command.d.ts +1 -1
  21. package/build/cogify/gdal/gdal.command.js +26 -10
  22. package/build/cogify/gdal/gdal.command.js.map +1 -1
  23. package/build/cogify/gdal/gdal.runner.js +1 -1
  24. package/build/cogify/gdal/gdal.runner.js.map +1 -1
  25. package/build/cogify/stac.d.ts +32 -18
  26. package/build/cogify/stac.js.map +1 -1
  27. package/build/cogify/topo/extract.js +4 -4
  28. package/build/cogify/topo/extract.js.map +1 -1
  29. package/build/preset.d.ts +67 -6
  30. package/build/preset.js +41 -0
  31. package/build/preset.js.map +1 -1
  32. package/dist/index.cjs +470 -124
  33. package/package.json +6 -6
package/dist/index.cjs CHANGED
@@ -4554,7 +4554,7 @@ var require_oneOf = __commonJS({
4554
4554
  Object.defineProperty(exports, "__esModule", { value: true });
4555
4555
  exports.oneOf = void 0;
4556
4556
  var util_1 = require("util");
4557
- function oneOf4(literals) {
4557
+ function oneOf3(literals) {
4558
4558
  const examples = literals.map((x) => (0, util_1.inspect)(x)).join(", ");
4559
4559
  return {
4560
4560
  async from(str) {
@@ -4567,7 +4567,7 @@ var require_oneOf = __commonJS({
4567
4567
  description: `One of ${examples}`
4568
4568
  };
4569
4569
  }
4570
- exports.oneOf = oneOf4;
4570
+ exports.oneOf = oneOf3;
4571
4571
  }
4572
4572
  });
4573
4573
 
@@ -75271,9 +75271,9 @@ var ulid2 = __toESM(require_index_umd(), 1);
75271
75271
  var CliInfo = {
75272
75272
  // Detect unlinked packages looks for this string since its a package name, slightly work around it
75273
75273
  package: "@basemaps/cli",
75274
- version: "v8.9.0",
75275
- hash: "55cd25b9f2c402fde88fcef9c43cec3f8eca9e60",
75276
- buildId: "17570995768-1"
75274
+ version: "v8.14.0",
75275
+ hash: "eb807c6d0f091b41e4d6d972b77c24f584cec230",
75276
+ buildId: "18453645562-1"
75277
75277
  };
75278
75278
  var CliDate = (/* @__PURE__ */ new Date()).toISOString();
75279
75279
  var CliId = ulid2.ulid();
@@ -79554,6 +79554,12 @@ var DefaultColorRampOutput = {
79554
79554
  // Taken from 0 of DefaultColorRamp
79555
79555
  background: { r: 172, g: 204, b: 226, alpha: 1 }
79556
79556
  };
79557
+ var DefaultBandExpandOutput = {
79558
+ title: "Gray",
79559
+ name: "expand",
79560
+ pipeline: [{ type: "color-ramp" }],
79561
+ background: { r: 255, g: 255, b: 255, alpha: 1 }
79562
+ };
79557
79563
 
79558
79564
  // ../config/build/config/tile.set.pipeline.js
79559
79565
  var ConfigImageFormatParser = z.union([
@@ -79563,15 +79569,39 @@ var ConfigImageFormatParser = z.union([
79563
79569
  z.literal("avif")
79564
79570
  ]);
79565
79571
  var ConfigResizeKernelParser = z.union([z.literal("nearest"), z.literal("lanczos3"), z.literal("lanczos2")]);
79566
- var ConfigTileSetPipelineParser = z.object({
79567
- /**
79568
- * type of pipeline function
79569
- *
79570
- * @example "terrain-rgb"
79571
- */
79572
- type: z.string()
79573
- // TODO allow custom arguments
79572
+ var ColorParser = z.object({
79573
+ r: z.number(),
79574
+ g: z.number(),
79575
+ b: z.number(),
79576
+ alpha: z.number()
79574
79577
  });
79578
+ var PipelineTerrainRgbParser = z.object({ type: z.literal("terrain-rgb") });
79579
+ var PipelineColorRampParser = z.object({ type: z.literal("color-ramp"), ramp: z.string().optional() });
79580
+ var PipelineNdviParser = z.object({
79581
+ type: z.literal("ndvi"),
79582
+ nir: z.number(),
79583
+ r: z.number(),
79584
+ alpha: z.number(),
79585
+ scale: z.object({
79586
+ nir: z.number(),
79587
+ r: z.number(),
79588
+ alpha: z.number()
79589
+ }).optional()
79590
+ });
79591
+ var PipelineExtractParser = z.object({
79592
+ type: z.literal("extract"),
79593
+ r: z.number(),
79594
+ g: z.number(),
79595
+ b: z.number(),
79596
+ alpha: z.number(),
79597
+ scale: ColorParser.optional()
79598
+ });
79599
+ var ConfigTileSetPipelineParser = z.discriminatedUnion("type", [
79600
+ PipelineExtractParser,
79601
+ PipelineTerrainRgbParser,
79602
+ PipelineColorRampParser,
79603
+ PipelineNdviParser
79604
+ ]);
79575
79605
  var ConfigRgbaParser = z.object({ r: z.number(), g: z.number(), b: z.number(), alpha: z.number() });
79576
79606
  var ConfigTileSetOutputParser = z.object({
79577
79607
  /**
@@ -79620,6 +79650,20 @@ var ConfigTileSetOutputParser = z.object({
79620
79650
  // ../config/build/memory/memory.config.js
79621
79651
  var import_ulid2 = __toESM(require_index_umd(), 1);
79622
79652
 
79653
+ // ../config/build/config/migration/imagery.js
79654
+ function migrateConfigImagery(img) {
79655
+ if (img.v === 2)
79656
+ return img;
79657
+ return {
79658
+ ...img,
79659
+ v: 2,
79660
+ title: img.title ?? img.name,
79661
+ bands: (img.bands ?? []).map((m) => {
79662
+ return { type: m };
79663
+ })
79664
+ };
79665
+ }
79666
+
79623
79667
  // ../config/build/name.convertor.js
79624
79668
  function fixChunkGsd(chunk) {
79625
79669
  if (!chunk.endsWith("m"))
@@ -79666,6 +79710,100 @@ function standardizeLayerName(name) {
79666
79710
  return output.join("-");
79667
79711
  }
79668
79712
 
79713
+ // ../config/build/memory/imagery.outputs.js
79714
+ function addDefaultOutputPipelines(ts, img) {
79715
+ if (ts.outputs != null)
79716
+ return ts.outputs;
79717
+ if (img.bands == null || img.bands.length === 0)
79718
+ return void 0;
79719
+ if (img.bands.length === 1) {
79720
+ if (img.bands[0].color === "gray")
79721
+ return [DefaultBandExpandOutput];
79722
+ return [DefaultTerrainRgbOutput, DefaultColorRampOutput];
79723
+ }
79724
+ const colors = { red: -1, green: -1, blue: -1, nir: -1, alpha: -1 };
79725
+ for (let i = 0; i < img.bands.length; i++) {
79726
+ const band = img.bands[i];
79727
+ if (band.color)
79728
+ colors[band.color] = i;
79729
+ }
79730
+ if (img.bands.length === 3 && colors.red >= 0 && colors.green >= 0 && colors.blue >= 0)
79731
+ return;
79732
+ if (img.bands.length === 4 && colors.red >= 0 && colors.green >= 0 && colors.blue >= 0 && colors.alpha >= 0)
79733
+ return;
79734
+ const pipelines = [];
79735
+ if (colors.red >= 0 && colors.green >= 0 && colors.blue >= 0) {
79736
+ const rgbExtract = {
79737
+ type: "extract",
79738
+ r: colors.red,
79739
+ g: colors.green,
79740
+ b: colors.blue,
79741
+ alpha: colors.alpha
79742
+ };
79743
+ const pipeline2 = { name: "rgb", title: "RGBA", pipeline: [rgbExtract] };
79744
+ rgbExtract.scale = getBandScales(img.bands, rgbExtract);
79745
+ pipelines.push(pipeline2);
79746
+ }
79747
+ if (colors.nir >= 0 && colors.red >= 0) {
79748
+ const ndviArgs = { type: "ndvi", r: colors.red, nir: colors.nir, alpha: colors.alpha };
79749
+ const scale = {
79750
+ r: getScale(img.bands[colors.red], "red"),
79751
+ nir: getScale(img.bands[colors.nir], "nir"),
79752
+ alpha: getAlphaScale(img.bands[colors.alpha])
79753
+ };
79754
+ if (scale.r && scale.nir && scale.alpha)
79755
+ ndviArgs.scale = scale;
79756
+ pipelines.push({
79757
+ name: "ndvi",
79758
+ title: "NDVI",
79759
+ pipeline: [ndviArgs]
79760
+ });
79761
+ }
79762
+ if (colors.red >= 0 && colors.green >= 0 && colors.nir >= 0) {
79763
+ const falseColorExtract = {
79764
+ type: "extract",
79765
+ r: colors.nir,
79766
+ g: colors.red,
79767
+ b: colors.green,
79768
+ alpha: colors.alpha
79769
+ };
79770
+ const pipeline2 = { name: "false-color", title: "FalseColor", pipeline: [falseColorExtract] };
79771
+ falseColorExtract.scale = getBandScales(img.bands, falseColorExtract);
79772
+ pipelines.push(pipeline2);
79773
+ }
79774
+ if (pipelines.length > 0)
79775
+ return pipelines;
79776
+ return void 0;
79777
+ }
79778
+ function getBandScales(bands, pipe) {
79779
+ const scale = {
79780
+ r: getScale(bands[pipe.r], "red"),
79781
+ g: getScale(bands[pipe.g], "green"),
79782
+ b: getScale(bands[pipe.b], "blue"),
79783
+ alpha: getAlphaScale(bands[pipe.alpha])
79784
+ };
79785
+ if (scale.r && scale.g && scale.b && scale.alpha)
79786
+ return scale;
79787
+ return void 0;
79788
+ }
79789
+ function getScale(band, color) {
79790
+ if (band.type === "uint8")
79791
+ return void 0;
79792
+ if (band.stats == null)
79793
+ return void 0;
79794
+ const stdDevs = color === "nir" ? 3 : 3;
79795
+ return Math.round(band.stats.mean + stdDevs * band.stats.stddev);
79796
+ }
79797
+ function getAlphaScale(band) {
79798
+ if (band == null)
79799
+ return void 0;
79800
+ if (band.type === "uint8")
79801
+ return void 0;
79802
+ if (band.stats == null)
79803
+ return void 0;
79804
+ return Math.round(band.stats.max);
79805
+ }
79806
+
79669
79807
  // ../config/build/memory/memory.config.js
79670
79808
  function isConfigImagery(i) {
79671
79809
  return ConfigId.getPrefix(i.id) === ConfigPrefix.Imagery;
@@ -79769,14 +79907,14 @@ var ConfigProviderMemory = class _ConfigProviderMemory extends BasemapsConfigPro
79769
79907
  }
79770
79908
  toJson() {
79771
79909
  const cfg = {
79910
+ v: 2,
79772
79911
  id: "",
79773
79912
  hash: "",
79774
79913
  assets: this.assets,
79775
79914
  imagery: [],
79776
79915
  style: [],
79777
79916
  provider: [],
79778
- tileSet: [],
79779
- duplicateImagery: this.duplicateImagery
79917
+ tileSet: []
79780
79918
  };
79781
79919
  for (const val2 of this.objects.values()) {
79782
79920
  const prefix = val2.id.slice(0, val2.id.indexOf("_"));
@@ -79833,6 +79971,8 @@ var ConfigProviderMemory = class _ConfigProviderMemory extends BasemapsConfigPro
79833
79971
  const tileSet = this.objects.get(this.TileSet.id(l.name));
79834
79972
  if (tileSet.outputs)
79835
79973
  continue;
79974
+ if (tileSet.category === "Charts")
79975
+ continue;
79836
79976
  const newLayer = { ...l, minZoom: 32 };
79837
79977
  delete newLayer.maxZoom;
79838
79978
  layerByName.set(newLayer.name, { ...layerByName.get(l.name), ...newLayer });
@@ -79867,9 +80007,9 @@ var ConfigProviderMemory = class _ConfigProviderMemory extends BasemapsConfigPro
79867
80007
  };
79868
80008
  removeUndefined(existing);
79869
80009
  this.put(existing);
79870
- if (i.bands?.length === 1) {
79871
- existing.outputs = [DefaultTerrainRgbOutput, DefaultColorRampOutput];
79872
- }
80010
+ const outputs = addDefaultOutputPipelines(existing, i);
80011
+ if (outputs != null)
80012
+ existing.outputs = outputs;
79873
80013
  }
79874
80014
  const existingImageryId = existing.layers[0][i.projection];
79875
80015
  if (existingImageryId) {
@@ -79895,9 +80035,9 @@ var ConfigProviderMemory = class _ConfigProviderMemory extends BasemapsConfigPro
79895
80035
  background: { r: 0, g: 0, b: 0, alpha: 0 },
79896
80036
  updatedAt: Date.now()
79897
80037
  };
79898
- if (i.bands?.length === 1) {
79899
- ts.outputs = [DefaultTerrainRgbOutput, DefaultColorRampOutput];
79900
- }
80038
+ const outputs = addDefaultOutputPipelines(ts, i);
80039
+ if (outputs != null)
80040
+ ts.outputs = outputs;
79901
80041
  return ts;
79902
80042
  }
79903
80043
  /** Load a bundled configuration creating virtual tilesets for all imagery */
@@ -79912,8 +80052,10 @@ var ConfigProviderMemory = class _ConfigProviderMemory extends BasemapsConfigPro
79912
80052
  mem.put(st);
79913
80053
  for (const pv of cfg.provider)
79914
80054
  mem.put(pv);
79915
- for (const img of cfg.imagery)
79916
- mem.put(img);
80055
+ for (const img of cfg.imagery) {
80056
+ const parsed = migrateConfigImagery(img);
80057
+ mem.put(parsed);
80058
+ }
79917
80059
  const updatedAt = (0, import_ulid2.decodeTime)(ConfigId.unprefix(ConfigPrefix.ConfigBundle, cfg.id));
79918
80060
  for (const obj of mem.objects.values())
79919
80061
  obj.updatedAt = updatedAt;
@@ -81314,6 +81456,13 @@ var Env = {
81314
81456
  /** Index to use for storing analytic data */
81315
81457
  ElasticIndexName: "ELASTIC_INDEX_NAME"
81316
81458
  },
81459
+ /** Load a environment variable throw a exception if the value is empty */
81460
+ getRequired(envName) {
81461
+ const value = Env.get(envName);
81462
+ if (value == null || value === "")
81463
+ throw new Error(`Missing env $${envName}`);
81464
+ return value;
81465
+ },
81317
81466
  /** Load a environment var defaulting to defaultOutput if it does not exist */
81318
81467
  get(envName) {
81319
81468
  return process.env[envName];
@@ -81925,11 +82074,11 @@ var Bounds = class _Bounds {
81925
82074
 
81926
82075
  // ../geo/build/epsg.js
81927
82076
  var EpsgCode;
81928
- (function(EpsgCode4) {
81929
- EpsgCode4[EpsgCode4["Google"] = 3857] = "Google";
81930
- EpsgCode4[EpsgCode4["Wgs84"] = 4326] = "Wgs84";
81931
- EpsgCode4[EpsgCode4["Nztm2000"] = 2193] = "Nztm2000";
81932
- EpsgCode4[EpsgCode4["Citm2000"] = 3793] = "Citm2000";
82077
+ (function(EpsgCode3) {
82078
+ EpsgCode3[EpsgCode3["Google"] = 3857] = "Google";
82079
+ EpsgCode3[EpsgCode3["Wgs84"] = 4326] = "Wgs84";
82080
+ EpsgCode3[EpsgCode3["Nztm2000"] = 2193] = "Nztm2000";
82081
+ EpsgCode3[EpsgCode3["Citm2000"] = 3793] = "Citm2000";
81933
82082
  })(EpsgCode || (EpsgCode = {}));
81934
82083
  var EpsgText = {
81935
82084
  google: EpsgCode.Google,
@@ -85274,7 +85423,7 @@ function getTiffTagSize(fieldType) {
85274
85423
  case TiffTagValueType.Ifd8:
85275
85424
  return 8;
85276
85425
  default:
85277
- throw new Error(`Invalid fieldType ${fieldType}`);
85426
+ throw new Error(`Invalid fieldType ${String(fieldType)}`);
85278
85427
  }
85279
85428
  }
85280
85429
 
@@ -85448,10 +85597,14 @@ async function getValueAt(tiff, tag, index) {
85448
85597
  const bytes = await tiff.source.fetch(tag.dataOffset + index * dataTypeSize, dataTypeSize);
85449
85598
  const view = new DataView(bytes);
85450
85599
  const value2 = readValue(tiff, void 0, view, 0, tag.dataType, 1);
85600
+ if (typeof value2 !== "number")
85601
+ throw new Error("Value is not a number");
85451
85602
  tag.value[index] = value2;
85452
85603
  return value2;
85453
85604
  }
85454
85605
  const value = readValue(tiff, void 0, tag.view, index * dataTypeSize, tag.dataType, 1);
85606
+ if (typeof value !== "number")
85607
+ throw new Error("Value is not a number");
85455
85608
  tag.value[index] = value;
85456
85609
  return value;
85457
85610
  }
@@ -86219,8 +86372,6 @@ var Tiff = class _Tiff {
86219
86372
  const endian = bytes.getUint16(offset, this.isLittleEndian);
86220
86373
  offset += 2;
86221
86374
  this.isLittleEndian = endian === TiffEndian.Little;
86222
- if (!this.isLittleEndian)
86223
- throw new Error("Only little endian is supported");
86224
86375
  this.version = bytes.getUint16(offset, this.isLittleEndian);
86225
86376
  offset += 2;
86226
86377
  let nextOffsetIfd;
@@ -86240,7 +86391,7 @@ var Tiff = class _Tiff {
86240
86391
  nextOffsetIfd = getUint(bytes, offset, this.ifdConfig.pointer, this.isLittleEndian);
86241
86392
  offset += this.ifdConfig.pointer;
86242
86393
  } else {
86243
- throw new Error(`Only tiff supported version:${this.version}`);
86394
+ throw new Error(`Only tiff supported version:${String(this.version)}`);
86244
86395
  }
86245
86396
  const ghostSize = nextOffsetIfd - offset;
86246
86397
  if (ghostSize > 0 && ghostSize < 16 * 1024) {
@@ -86254,7 +86405,7 @@ var Tiff = class _Tiff {
86254
86405
  lastView = new DataView(bytes2);
86255
86406
  lastView.sourceOffset = nextOffsetIfd;
86256
86407
  }
86257
- nextOffsetIfd = await this.readIfd(nextOffsetIfd, lastView);
86408
+ nextOffsetIfd = this.readIfd(nextOffsetIfd, lastView);
86258
86409
  }
86259
86410
  await Promise.all(this.images.map((i) => i.init()));
86260
86411
  this.isInitialized = true;
@@ -86266,7 +86417,7 @@ var Tiff = class _Tiff {
86266
86417
  * @param offset file offset to read the header from
86267
86418
  * @param view offset that contains the bytes for the header
86268
86419
  */
86269
- async readIfd(offset, view) {
86420
+ readIfd(offset, view) {
86270
86421
  const viewOffset = offset - view.sourceOffset;
86271
86422
  const tagCount = getUint(view, viewOffset, this.ifdConfig.offset, this.isLittleEndian);
86272
86423
  const tags = /* @__PURE__ */ new Map();
@@ -86720,17 +86871,59 @@ var lzw = {
86720
86871
  overviewResampling: "bilinear"
86721
86872
  }
86722
86873
  };
86874
+ var zstd_17 = {
86875
+ name: "zstd_17",
86876
+ options: {
86877
+ blockSize: 512,
86878
+ compression: "zstd",
86879
+ predictor: 2,
86880
+ level: 17,
86881
+ warpResampling: "bilinear",
86882
+ overviewResampling: "lanczos"
86883
+ }
86884
+ };
86723
86885
  var Presets = {
86724
86886
  [webP.name]: webP,
86725
86887
  [webP80.name]: webP80,
86726
86888
  [lerc1mm.name]: lerc1mm,
86727
86889
  [lerc10mm.name]: lerc10mm,
86728
- [lzw.name]: lzw
86890
+ [lzw.name]: lzw,
86891
+ [zstd_17.name]: zstd_17
86892
+ };
86893
+ var BandPresets = {
86894
+ /**
86895
+ * 4 band image, generally [uint8,uint8,uint8,uint8]
86896
+ * - Red,Green,Blue,Alpha
86897
+ */
86898
+ rgba: ["red", "green", "blue", "alpha"],
86899
+ /**
86900
+ * 4 Band image, generally [uint8/16,uint8/16,uint8/16,uint8/16]
86901
+ * - Red,Green,Blue,Near Infrared
86902
+ */
86903
+ rgbi: ["red", "green", "blue", "nir", "alpha"],
86904
+ rgbna: ["red", "green", "blue", "nir", "alpha"],
86905
+ /**
86906
+ * One band image, generally [float32]
86907
+ */
86908
+ elevation: ["undefined"],
86909
+ /** One band image, generally 0-255 greyscale */
86910
+ hillshade: ["gray"]
86911
+ };
86912
+ var allPresets = Object.keys(BandPresets);
86913
+ var AllowedPresets = {
86914
+ // Webp is only suitable for RGB(A) images
86915
+ [webP.name]: ["rgba"],
86916
+ [webP80.name]: ["rgba"],
86917
+ [lerc1mm.name]: allPresets,
86918
+ [lerc10mm.name]: allPresets,
86919
+ [lzw.name]: allPresets,
86920
+ [zstd_17.name]: allPresets
86729
86921
  };
86730
86922
 
86731
86923
  // src/cogify/gdal/gdal.command.ts
86732
86924
  var isPowerOfTwo = (x) => (x & x - 1) === 0;
86733
- var DefaultTrimPixelRight = 1.7;
86925
+ var PixelTrimTop = 1;
86926
+ var PixelTrimRight = 4;
86734
86927
  function gdalBuildVrt(targetVrt, source, addalpha) {
86735
86928
  if (source.length === 0)
86736
86929
  throw new Error("No source files given for :" + targetVrt.href);
@@ -86751,8 +86944,6 @@ function gdalBuildVrtWarp(targetVrt, sourceVrt, sourceProjection, cutline, opt)
86751
86944
  args: [
86752
86945
  ["-of", "vrt"],
86753
86946
  // Output as a VRT
86754
- // ['-co', 'compress=lzw'],
86755
- // ['-co', 'bigtiff=yes'],
86756
86947
  "-multi",
86757
86948
  // Mutithread IO
86758
86949
  ["-wo", "NUM_THREADS=ALL_CPUS"],
@@ -86769,6 +86960,24 @@ function gdalBuildVrtWarp(targetVrt, sourceVrt, sourceProjection, cutline, opt)
86769
86960
  ].filter((f) => f != null).flat().map(String)
86770
86961
  };
86771
86962
  }
86963
+ function getCompressionArgs(cfg) {
86964
+ if (cfg.compression === "lerc") {
86965
+ return [
86966
+ ["-co", `MAX_Z_ERROR=${cfg.maxZError}`],
86967
+ ["-co", `MAX_Z_ERROR_OVERVIEW=${cfg.maxZErrorOverview}`]
86968
+ ];
86969
+ }
86970
+ if (cfg.compression === "zstd") {
86971
+ return [cfg.predictor ? ["-co", `PREDICTOR=${cfg.predictor}`] : [], ["-co", `LEVEL=${cfg.level}`]];
86972
+ }
86973
+ if (cfg.compression === "webp" || cfg.compression === "jpeg") {
86974
+ return [["-co", `QUALITY=${cfg.quality}`]];
86975
+ }
86976
+ if (cfg.compression === "lzw") {
86977
+ return [cfg.predictor ? ["-co", `PREDICTOR=${cfg.predictor}`] : []];
86978
+ }
86979
+ throw new Error("Unknown compression preset:" + String(cfg["compression"]));
86980
+ }
86772
86981
  function gdalBuildCog(targetTiff, sourceVrt, opt) {
86773
86982
  const cfg = { ...Presets[opt.preset], ...opt };
86774
86983
  const tileMatrix = TileMatrixSets.find(cfg.tileMatrix);
@@ -86789,8 +86998,6 @@ function gdalBuildCog(targetTiff, sourceVrt, opt) {
86789
86998
  ["-of", "COG"],
86790
86999
  ["-co", "NUM_THREADS=ALL_CPUS"],
86791
87000
  // Use all CPUS
86792
- ["--config", "GDAL_NUM_THREADS", "all_cpus"],
86793
- // Also required to NUM_THREADS till gdal 3.7.x
86794
87001
  ["-co", "BIGTIFF=IF_NEEDED"],
86795
87002
  // BigTiff is somewhat slower and most (All?) of the COGS should be well below 4GB
86796
87003
  ["-co", "ADD_ALPHA=YES"],
@@ -86800,16 +87007,15 @@ function gdalBuildCog(targetTiff, sourceVrt, opt) {
86800
87007
  */
86801
87008
  ["-co", "OVERVIEWS=IGNORE_EXISTING"],
86802
87009
  ["-co", `BLOCKSIZE=${cfg.blockSize}`],
86803
- // ['-co', 'RESAMPLING=cubic'],
86804
87010
  ["-co", `WARP_RESAMPLING=${cfg.warpResampling}`],
86805
87011
  ["-co", `OVERVIEW_RESAMPLING=${cfg.overviewResampling}`],
86806
87012
  ["-co", `COMPRESS=${cfg.compression}`],
86807
- cfg.quality ? ["-co", `QUALITY=${cfg.quality}`] : void 0,
86808
- cfg.maxZError ? ["-co", `MAX_Z_ERROR=${cfg.maxZError}`] : void 0,
86809
- cfg.maxZErrorOverview ? ["-co", `MAX_Z_ERROR_OVERVIEW=${cfg.maxZErrorOverview}`] : void 0,
87013
+ ...getCompressionArgs(cfg),
87014
+ cfg.presetBands ? ["-colorinterp", cfg.presetBands.join(",")] : void 0,
86810
87015
  ["-co", "SPARSE_OK=YES"],
86811
87016
  ["-co", `TARGET_SRS=${tileMatrix.projection.toEpsgString()}`],
86812
87017
  ["-co", `EXTENT=${tileExtent.join(",")},`],
87018
+ ["-stats"],
86813
87019
  ["-tr", targetResolution, targetResolution],
86814
87020
  urlToString(sourceVrt),
86815
87021
  urlToString(targetTiff)
@@ -86854,7 +87060,7 @@ function gdalBuildTopoRasterCommands(targetTiff, sourceVrt, opt, width, height)
86854
87060
  // Force stats (re)computation
86855
87061
  ["-of", "COG"],
86856
87062
  // Output format
86857
- ["-srcwin", "0", "0", `${width - DefaultTrimPixelRight}`, `${height}`],
87063
+ ["-srcwin", 0, PixelTrimTop, width - PixelTrimRight, height - PixelTrimTop],
86858
87064
  ["-a_srs", `EPSG:${opt.sourceEpsg}`],
86859
87065
  // https://gdal.org/en/latest/drivers/raster/cog.html#creation-options
86860
87066
  ["-co", "BIGTIFF=NO"],
@@ -86886,7 +87092,9 @@ function gdalBuildChartsCommand(target, source, cutline, tileMatrix) {
86886
87092
  "-multi",
86887
87093
  ["-wo", "NUM_THREADS=ALL_CPUS"],
86888
87094
  ["-t_srs", tileMatrix.projection.toEpsgString()],
86889
- "-dstalpha",
87095
+ ["-b", "1", "-b", "2", "-b", "3"],
87096
+ // Drop dummy band 4 if it exists
87097
+ ["-dstalpha"],
86890
87098
  ["-cutline", urlToString(cutline)],
86891
87099
  ["-crop_to_cutline"],
86892
87100
  ["-co", "BIGTIFF=NO"],
@@ -86903,7 +87111,7 @@ var import_events = require("events");
86903
87111
  var import_path = require("path");
86904
87112
  function getDockerContainer() {
86905
87113
  const containerPath = process.env["GDAL_DOCKER_CONTAINER"] ?? "ghcr.io/osgeo/gdal";
86906
- const tag = process.env["GDAL_DOCKER_CONTAINER_TAG"] ?? "ubuntu-small-3.8.0";
87114
+ const tag = process.env["GDAL_DOCKER_CONTAINER_TAG"] ?? "ubuntu-small-3.11.3";
86907
87115
  return `${containerPath}:${tag}`;
86908
87116
  }
86909
87117
  function toDockerArgs(cmd) {
@@ -87095,13 +87303,6 @@ var ChartsCreationCommand = (0, import_cmd_ts2.command)({
87095
87303
  long: "target",
87096
87304
  description: "Target location for the output files"
87097
87305
  }),
87098
- tileMatrix: (0, import_cmd_ts2.option)({
87099
- type: (0, import_cmd_ts2.oneOf)([Nztm2000QuadTms.identifier, GoogleTms.identifier]),
87100
- long: "tile-matrix",
87101
- description: `Output TileMatrix to use. Either: ${Nztm2000QuadTms.identifier}, or ${GoogleTms.identifier}.`,
87102
- defaultValue: () => GoogleTms.identifier,
87103
- defaultValueIsSerializable: true
87104
- }),
87105
87306
  cutline: (0, import_cmd_ts2.option)({
87106
87307
  type: UrlFolder,
87107
87308
  long: "cutline",
@@ -87129,9 +87330,6 @@ var ChartsCreationCommand = (0, import_cmd_ts2.command)({
87129
87330
  const logger = getLogger(this, args, "cli-raster");
87130
87331
  logger.info("Charts:Start");
87131
87332
  const files = args.source ? await Fsa.toArray(Fsa.list(args.source)) : args.paths;
87132
- const tileMatrix = TileMatrixSets.find(args.tileMatrix);
87133
- if (tileMatrix == null)
87134
- throw new Error(`Tile matrix ${args.tileMatrix} is not supported`);
87135
87333
  await (0, import_promises.mkdir)(tmpFolder, { recursive: true });
87136
87334
  const outputs = /* @__PURE__ */ new Set();
87137
87335
  const toProcess = files.map(
@@ -87150,39 +87348,15 @@ var ChartsCreationCommand = (0, import_cmd_ts2.command)({
87150
87348
  }
87151
87349
  const sourceTiff = await downloadCharts(file, filename, logger);
87152
87350
  const cutline = await downloadCutlines(new import_url2.URL(args.cutline.href), chartCode, logger);
87153
- logger.info({ file: file.href, tileMatrix: tileMatrix.identifier, chartCode }, "Charts:Processing");
87351
+ logger.info({ file: file.href, chartCode }, "Charts:Processing");
87154
87352
  const tiff = await new Tiff(Fsa.source(file)).init();
87155
87353
  const backUpUrl = new import_url2.URL(filename, args.backup);
87156
87354
  const metadata = await fetchMetadata(tiff, backUpUrl, logger);
87157
- const bufferedCutline = await prepareCutline(
87158
- cutline,
87159
- chartCode,
87160
- tileMatrix,
87161
- metadata.gsd,
87162
- args.bufferPixels,
87163
- logger
87164
- );
87165
- const { item, collection } = await createStacFiles(
87166
- chartCode,
87167
- tileMatrix,
87168
- metadata,
87169
- cutline,
87170
- bufferedCutline,
87171
- logger
87172
- );
87173
- logger.info({ file: file.href, chartCode, tileMatrix: tileMatrix.identifier }, "Charts:GdalBuildCharts");
87174
- const cogFile = new import_url2.URL(`${chartCode}-${tileMatrix.identifier}.tif`, tmpFolder);
87175
- await new GdalRunner(gdalBuildChartsCommand(cogFile, sourceTiff, bufferedCutline, tileMatrix)).run(logger);
87176
- const targetPath = new import_url2.URL(`${tileMatrix.projection.code}/${chartCode}/`, args.target);
87177
- logger.info({ file: file.href, target: targetPath.href }, "Charts:Uploading");
87355
+ const bufferedCutline = await prepareCutline(cutline, chartCode, metadata.gsd, args.bufferPixels, logger);
87356
+ const targetPath = new import_url2.URL(`${GoogleTms.projection.code}/${CliId}/${chartCode}/`, args.target);
87178
87357
  if (targetPath.protocol === "file:")
87179
87358
  await (0, import_promises.mkdir)(targetPath, { recursive: true });
87180
- const targetTiff = new import_url2.URL(filename, targetPath);
87181
- await Fsa.write(targetTiff, Fsa.readStream(cogFile));
87182
- const targetItem = new import_url2.URL(`${chartCode}.json`, targetPath);
87183
- await Fsa.write(targetItem, JSON.stringify(item, null, 2));
87184
- const targetCollection = new import_url2.URL("collection.json", targetPath);
87185
- await Fsa.write(targetCollection, JSON.stringify(collection, null, 2));
87359
+ await createCogs(sourceTiff, cutline, chartCode, bufferedCutline, metadata, targetPath, logger);
87186
87360
  outputs.add(urlToString(targetPath));
87187
87361
  await tiff.source.close?.();
87188
87362
  })
@@ -87215,17 +87389,17 @@ async function downloadCutlines(cutline, chartCode, logger) {
87215
87389
  }
87216
87390
  return new import_url2.URL(`${chartCode}.shp`, tmpFolder);
87217
87391
  }
87218
- async function prepareCutline(cutline, chartCode, tileMatrix, gsd, bufferPixels, logger) {
87392
+ async function prepareCutline(cutline, chartCode, gsd, bufferPixels, logger) {
87219
87393
  logger.info({ cutline: cutline.href, chartCode }, "Charts:WrapCutline");
87220
87394
  const wrappedCutline = new import_url2.URL(`${chartCode}-wrapped.shp`, tmpFolder);
87221
87395
  await new GdalRunner(wrapCutline(wrappedCutline, cutline)).run(logger);
87222
- logger.info({ cutline: cutline.href, chartCode, tileMatrix: tileMatrix.identifier }, "Charts:ReprojectCutline");
87223
- const reprojectedCutline = new import_url2.URL(`${chartCode}-${tileMatrix.identifier}.shp`, tmpFolder);
87224
- await new GdalRunner(reprojectCutline(reprojectedCutline, wrappedCutline, tileMatrix)).run(logger);
87396
+ logger.info({ cutline: cutline.href, chartCode, tileMatrix: GoogleTms.identifier }, "Charts:ReprojectCutline");
87397
+ const reprojectedCutline = new import_url2.URL(`${chartCode}-${GoogleTms.identifier}.shp`, tmpFolder);
87398
+ await new GdalRunner(reprojectCutline(reprojectedCutline, wrappedCutline, GoogleTms)).run(logger);
87225
87399
  logger.info({ cutline: cutline.href, chartCode, gsd, bufferPixels }, "Charts:BufferCutline");
87226
87400
  const bufferedCutline = new import_url2.URL(`${chartCode}-buffered.geojson`, tmpFolder);
87227
87401
  await new GdalRunner(
87228
- bufferCutline(bufferedCutline, reprojectedCutline, `${chartCode}-${tileMatrix.identifier}`, gsd, bufferPixels)
87402
+ bufferCutline(bufferedCutline, reprojectedCutline, `${chartCode}-${GoogleTms.identifier}`, gsd, bufferPixels)
87229
87403
  ).run(logger);
87230
87404
  return bufferedCutline;
87231
87405
  }
@@ -87276,9 +87450,9 @@ function geojsonToBbox(geojson) {
87276
87450
  throw new Error("No union found in GeoJSON features");
87277
87451
  return union2.toBbox();
87278
87452
  }
87279
- async function createStacFiles(chartCode, tileMatrix, metadata, sourceCutline, bufferedCutline, logger) {
87453
+ async function createStacItem(chartCode, metadata, sourceCutline, cutline, logger) {
87454
+ const geojson = await Fsa.readJson(cutline);
87280
87455
  logger.info({ chartCode }, "Charts:CreateStacItem");
87281
- const geojson = await Fsa.readJson(bufferedCutline);
87282
87456
  const item = {
87283
87457
  id: `${CliId}/${chartCode}`,
87284
87458
  type: "Feature",
@@ -87305,9 +87479,9 @@ async function createStacFiles(chartCode, tileMatrix, metadata, sourceCutline, b
87305
87479
  ],
87306
87480
  properties: {
87307
87481
  datetime: CliDate,
87308
- "proj:epsg": tileMatrix.projection.code,
87482
+ "proj:epsg": GoogleTms.projection.code,
87309
87483
  "linz_basemaps:options": {
87310
- tileMatrix: tileMatrix.identifier,
87484
+ tileMatrix: GoogleTms.identifier,
87311
87485
  sourceEpsg: metadata.epsg
87312
87486
  },
87313
87487
  "linz_basemaps:generated": {
@@ -87319,6 +87493,9 @@ async function createStacFiles(chartCode, tileMatrix, metadata, sourceCutline, b
87319
87493
  },
87320
87494
  assets: {}
87321
87495
  };
87496
+ return item;
87497
+ }
87498
+ function CreateStacCollection(chartCode, items, logger) {
87322
87499
  logger.info({ chartCode }, "Charts:CreateStacCollection");
87323
87500
  const collection = {
87324
87501
  id: CliId,
@@ -87327,21 +87504,73 @@ async function createStacFiles(chartCode, tileMatrix, metadata, sourceCutline, b
87327
87504
  stac_extensions: [],
87328
87505
  license: "CC-BY-4.0",
87329
87506
  title: `New Zealand Charts Mapsheets - ${chartCode}`,
87330
- description: `New Zealand Charts Mapsheets - ${chartCode} - ${tileMatrix.identifier}`,
87507
+ description: `New Zealand Charts Mapsheets - ${chartCode} - ${GoogleTms.identifier}`,
87331
87508
  extent: {
87332
- spatial: { bbox: [item.bbox] },
87509
+ spatial: { bbox: items.map((i) => i.bbox) },
87333
87510
  temporal: { interval: [[CliDate, null]] }
87334
87511
  },
87335
87512
  links: [
87336
87513
  { rel: "self", href: "./collection.json", type: "application/json" },
87337
- {
87514
+ ...items.map((item) => ({
87338
87515
  href: `./${item.id}.json`,
87339
87516
  rel: "item",
87340
87517
  type: "application/json"
87341
- }
87518
+ }))
87342
87519
  ]
87343
87520
  };
87344
- return { item, collection };
87521
+ for (const item of items) {
87522
+ collection.extent.spatial.bbox.push(item.bbox);
87523
+ }
87524
+ return collection;
87525
+ }
87526
+ async function createCogs(sourceTiff, sourceCutline, chartCode, bufferedCutline, metadata, targetPath, logger) {
87527
+ const geojson = await Fsa.readJson(bufferedCutline);
87528
+ let index = 0;
87529
+ const items = [];
87530
+ for (const feature of geojson.features) {
87531
+ if (feature.geometry.type === "Polygon") {
87532
+ const splitCutline = {
87533
+ type: "FeatureCollection",
87534
+ crs: { type: "name", properties: { name: "urn:ogc:def:crs:EPSG::3857" } },
87535
+ features: [feature]
87536
+ };
87537
+ const cutlineFile = new import_url2.URL(`cutline-${index}.geojson`, tmpFolder);
87538
+ await Fsa.write(cutlineFile, JSON.stringify(splitCutline));
87539
+ const item = await createStacItem(`${chartCode}-${index}`, metadata, sourceCutline, cutlineFile, logger);
87540
+ await Fsa.write(new import_url2.URL(`${chartCode}-${index}.json`, targetPath), JSON.stringify(item, null, 2));
87541
+ items.push(item);
87542
+ const cog = new import_url2.URL(`${chartCode}-${index}.tif`, tmpFolder);
87543
+ await new GdalRunner(gdalBuildChartsCommand(cog, sourceTiff, cutlineFile, GoogleTms)).run(logger);
87544
+ await Fsa.write(new import_url2.URL(`${chartCode}-${index}.tif`, targetPath), Fsa.readStream(cog));
87545
+ index++;
87546
+ } else if (feature.geometry.type === "MultiPolygon") {
87547
+ for (const coords of feature.geometry.coordinates) {
87548
+ const splitCutline = {
87549
+ type: "FeatureCollection",
87550
+ crs: { type: "name", properties: { name: "urn:ogc:def:crs:EPSG::3857" } },
87551
+ features: [
87552
+ {
87553
+ type: "Feature",
87554
+ geometry: { type: "Polygon", coordinates: coords }
87555
+ }
87556
+ ]
87557
+ };
87558
+ const cutlineFile = new import_url2.URL(`cutline-${index}.geojson`, tmpFolder);
87559
+ await Fsa.write(cutlineFile, JSON.stringify(splitCutline));
87560
+ const item = await createStacItem(`${chartCode}-${index}`, metadata, sourceCutline, cutlineFile, logger);
87561
+ await Fsa.write(new import_url2.URL(`${chartCode}-${index}.json`, targetPath), JSON.stringify(item, null, 2));
87562
+ items.push(item);
87563
+ const cog = new import_url2.URL(`${chartCode}-${index}.tif`, tmpFolder);
87564
+ await new GdalRunner(gdalBuildChartsCommand(cog, sourceTiff, cutlineFile, GoogleTms)).run(logger);
87565
+ await Fsa.write(new import_url2.URL(`${chartCode}-${index}.tif`, targetPath), Fsa.readStream(cog));
87566
+ index++;
87567
+ }
87568
+ } else {
87569
+ throw new Error("GeoJSON must contain Polygon or MultiPolygon geometries");
87570
+ }
87571
+ }
87572
+ const collection = CreateStacCollection(chartCode, items, logger);
87573
+ await Fsa.write(new import_url2.URL("collection.json", targetPath), JSON.stringify(collection, null, 2));
87345
87574
  }
87346
87575
 
87347
87576
  // ../config-loader/build/json/json.config.js
@@ -87511,8 +87740,15 @@ function ensureBandsSimilar(tiff, existingBands, newBands) {
87511
87740
  const bB = newBands[i];
87512
87741
  if (bA == null || bB == null)
87513
87742
  continue;
87514
- if (bA !== bB) {
87515
- throw new Error(`Band:${i} datatype mismatch: ${tiff.source.url.href} ${bA} vs ${bB}`);
87743
+ if (bA.type !== bB.type) {
87744
+ throw new Error(`Band:${i} datatype mismatch: ${tiff.source.url.href} ${bA.type} vs ${bB.type}`);
87745
+ }
87746
+ if (bA.stats == null && bB.stats) {
87747
+ bA.stats = bB.stats;
87748
+ } else if (bA.stats && bB.stats) {
87749
+ bA.stats.min = Math.min(bA.stats.min, bB.stats.min);
87750
+ bA.stats.max = Math.max(bA.stats.max, bB.stats.max);
87751
+ bA.stats.mean = (bA.stats.mean + bB.stats.mean) / 2;
87516
87752
  }
87517
87753
  }
87518
87754
  if (existingBands.length >= newBands.length)
@@ -87533,12 +87769,13 @@ async function computeTiffSummary(target, tiffs) {
87533
87769
  else if (res.projection !== epsg) {
87534
87770
  throw new Error(`ESPG projection mismatch on imagery ${res.projection} vs ${epsg} source: ${tiff.source.url.href}`);
87535
87771
  }
87536
- const [dataType, bitsPerSample, noData] = await Promise.all([
87772
+ const [dataType, bitsPerSample, noData, gdalMetadata] = await Promise.all([
87537
87773
  /** firstImage.fetch(TiffTag.Photometric), **/
87538
87774
  // TODO enable RGB detection
87539
87775
  firstImage.fetch(TiffTag.SampleFormat),
87540
87776
  firstImage.fetch(TiffTag.BitsPerSample),
87541
- firstImage.fetch(TiffTag.GdalNoData)
87777
+ firstImage.fetch(TiffTag.GdalNoData),
87778
+ firstImage.fetch(TiffTag.GdalMetadata)
87542
87779
  ]);
87543
87780
  if (bitsPerSample == null) {
87544
87781
  throw new Error(`Failed to extract band information from : ${tiff.source.url.href}`);
@@ -87550,8 +87787,9 @@ async function computeTiffSummary(target, tiffs) {
87550
87787
  for (let i = 0; i < bitsPerSample.length; i++) {
87551
87788
  const type = getDataType(dataType ? dataType[i] : SampleFormat.Uint);
87552
87789
  const bits = bitsPerSample[i];
87553
- imageBands.push(`${type}${bits}`);
87790
+ imageBands.push({ type: `${type}${bits}` });
87554
87791
  }
87792
+ parseGdalMetadata(gdalMetadata, imageBands);
87555
87793
  res.bands = ensureBandsSimilar(tiff, res.bands, imageBands);
87556
87794
  const imageNoData = noData ? Number(noData) : null;
87557
87795
  if (res.noData != null && imageNoData !== res.noData) {
@@ -87596,6 +87834,62 @@ async function computeTiffSummary(target, tiffs) {
87596
87834
  throw new Error(`Failed to extract imagery from: ${target.href}`);
87597
87835
  return res;
87598
87836
  }
87837
+ var MetadataSetter = {
87838
+ COLORINTERP: (val2, meta) => meta.color = val2.toLowerCase(),
87839
+ STATISTICS_MEAN: (val2, meta) => {
87840
+ meta.stats = meta.stats ?? { min: NaN, mean: NaN, max: NaN, stddev: NaN };
87841
+ meta.stats.mean = Number(val2);
87842
+ },
87843
+ STATISTICS_MAXIMUM: (val2, meta) => {
87844
+ meta.stats = meta.stats ?? { min: NaN, mean: NaN, max: NaN, stddev: NaN };
87845
+ meta.stats.max = Number(val2);
87846
+ },
87847
+ STATISTICS_MINIMUM: (val2, meta) => {
87848
+ meta.stats = meta.stats ?? { min: NaN, mean: NaN, max: NaN, stddev: NaN };
87849
+ meta.stats.min = Number(val2);
87850
+ },
87851
+ // Ignored
87852
+ STATISTICS_STDDEV: (val2, meta) => {
87853
+ meta.stats = meta.stats ?? { min: NaN, mean: NaN, max: NaN, stddev: NaN };
87854
+ meta.stats.stddev = Number(val2);
87855
+ },
87856
+ STATISTICS_VALIDPERCENT: () => {
87857
+ }
87858
+ };
87859
+ function parseGdalMetadata(dat, bands, log) {
87860
+ if (dat == null)
87861
+ return;
87862
+ const items = dat.split("\n").map((f) => f.trim()).filter((f) => f.startsWith("<Item"));
87863
+ for (const item of items) {
87864
+ const name = item.match(/name="([A-Z_]+)"/);
87865
+ if (name == null)
87866
+ continue;
87867
+ const sample = item.match(/sample="([0-9]+)"/);
87868
+ if (sample == null)
87869
+ continue;
87870
+ const value = item.match(/>(.*)<\/Item>/);
87871
+ if (value == null)
87872
+ continue;
87873
+ const sampleId = Number(sample[1]);
87874
+ if (isNaN(sampleId))
87875
+ continue;
87876
+ if (bands[sampleId] == null) {
87877
+ log?.warn({ sampleId }, "GdalMetadataParser:InvalidBand");
87878
+ return;
87879
+ }
87880
+ MetadataSetter[name[1]]?.(value[1], bands[sampleId]);
87881
+ if (MetadataSetter[name[1]] == null) {
87882
+ log?.debug({ gdalConfigName: name[1] }, "GdalMetadataParser:UnknownKey:" + name[1]);
87883
+ }
87884
+ }
87885
+ for (const band of bands) {
87886
+ if (band.stats == null)
87887
+ continue;
87888
+ if (isNaN(band.stats?.max) || isNaN(band.stats.min) || isNaN(band.stats.mean)) {
87889
+ delete band.stats;
87890
+ }
87891
+ }
87892
+ }
87599
87893
  function toRelative(base, other) {
87600
87894
  if (!other.href.startsWith(base.href))
87601
87895
  throw new Error("Paths are not relative");
@@ -87682,6 +87976,7 @@ async function initImageryFromTiffUrl(target, Q3, configCache, log) {
87682
87976
  const title = stac?.title ?? imageryName;
87683
87977
  const tileMatrix = params.projection === EpsgCode.Nztm2000 ? Nztm2000QuadTms : TileMatrixSets.tryGet(params.projection);
87684
87978
  const imagery = {
87979
+ v: 2,
87685
87980
  id: `im_${sha256base58(target.href)}`,
87686
87981
  name: imageryName,
87687
87982
  title,
@@ -87717,7 +88012,8 @@ async function initConfigFromUrls(provider, targets, concurrency = 25, configCac
87717
88012
  title: "Basemaps",
87718
88013
  category: "Basemaps",
87719
88014
  type: TileSetType.Raster,
87720
- layers: []
88015
+ layers: [],
88016
+ outputs: []
87721
88017
  };
87722
88018
  const elevationTileSet = {
87723
88019
  id: "ts_elevation",
@@ -87763,7 +88059,7 @@ function isRgbOrRgba(img) {
87763
88059
  if (img.bands.length > 4)
87764
88060
  return false;
87765
88061
  for (const b of img.bands) {
87766
- if (b !== "uint8")
88062
+ if (b.type !== "uint8")
87767
88063
  return false;
87768
88064
  }
87769
88065
  return true;
@@ -87923,11 +88219,12 @@ var ConfigJson = class _ConfigJson {
87923
88219
  const imageryFetch = [];
87924
88220
  if (ts.type === TileSetType.Raster) {
87925
88221
  for (const layer of ts.layers) {
88222
+ const category = layer.category ?? ts.category;
87926
88223
  if (layer[2193] != null) {
87927
- imageryFetch.push(this.loadImagery(stringToUrlFolder(layer[2193]), layer.name, layer.title));
88224
+ imageryFetch.push(this.loadImagery(stringToUrlFolder(layer[2193]), layer.name, layer.title, category));
87928
88225
  }
87929
88226
  if (layer[3857] != null) {
87930
- imageryFetch.push(this.loadImagery(stringToUrlFolder(layer[3857]), layer.name, layer.title));
88227
+ imageryFetch.push(this.loadImagery(stringToUrlFolder(layer[3857]), layer.name, layer.title, category));
87931
88228
  }
87932
88229
  }
87933
88230
  }
@@ -87992,10 +88289,10 @@ var ConfigJson = class _ConfigJson {
87992
88289
  tileSet.aliases = ts.aliases;
87993
88290
  return tileSet;
87994
88291
  }
87995
- loadImagery(url, name, title) {
88292
+ loadImagery(url, name, title, category) {
87996
88293
  let existing = this.cache.get(url.href);
87997
88294
  if (existing == null) {
87998
- existing = this._loadImagery(url, name, title);
88295
+ existing = this._loadImagery(url, name, title, category);
87999
88296
  this.cache.set(url.href, existing);
88000
88297
  }
88001
88298
  return existing;
@@ -88004,7 +88301,7 @@ var ConfigJson = class _ConfigJson {
88004
88301
  initImageryFromTiffUrl(url) {
88005
88302
  return initImageryFromTiffUrl(url, this.Q, this.imageryConfigCache, this.logger);
88006
88303
  }
88007
- async _loadImagery(url, name, title) {
88304
+ async _loadImagery(url, name, title, category) {
88008
88305
  const imageId = guessIdFromUri(url.href) ?? sha256base58(url.href);
88009
88306
  const id = ConfigId.prefix(ConfigPrefix.Imagery, imageId);
88010
88307
  this.logger.trace({ url: url.href, imageId: id }, "Imagery:Fetch");
@@ -88012,6 +88309,8 @@ var ConfigJson = class _ConfigJson {
88012
88309
  img.id = id;
88013
88310
  img.name = name;
88014
88311
  img.title = title;
88312
+ if (category)
88313
+ img.category = category;
88015
88314
  delete img.collection;
88016
88315
  this.mem.put(img);
88017
88316
  return img;
@@ -88618,7 +88917,11 @@ async function createCog(ctx) {
88618
88917
  if (tileMatrix == null)
88619
88918
  throw new Error("Failed to find tile matrix: " + options.tileMatrix);
88620
88919
  logger?.debug({ tileId }, "Cog:Create:VrtSource");
88621
- const vrtSourceCommand = gdalBuildVrt(new URL(`${tileId}-source.vrt`, ctx.tempFolder), ctx.sourceFiles);
88920
+ let addAlpha = false;
88921
+ if (options.presetBands?.includes("alpha") && options.presetBands.length > options.sourceBands.length) {
88922
+ addAlpha = true;
88923
+ }
88924
+ const vrtSourceCommand = gdalBuildVrt(new URL(`${tileId}-source.vrt`, ctx.tempFolder), ctx.sourceFiles, addAlpha);
88622
88925
  await new GdalRunner(vrtSourceCommand).run(logger);
88623
88926
  logger?.debug({ tileId }, "Cog:Create:VrtWarp");
88624
88927
  const cutlineProperties = { url: null, blend: ctx.cutline.blend };
@@ -88648,10 +88951,11 @@ async function createCog(ctx) {
88648
88951
  }
88649
88952
  const gdalCreateCommand = gdalCreate(new URL(`${tileId}-bg.tiff`, ctx.tempFolder), options.background, options);
88650
88953
  await new GdalRunner(gdalCreateCommand).run(logger);
88651
- const vrtMergeCommand = gdalBuildVrt(new URL(`${tileId}-merged.vrt`, ctx.tempFolder), [
88652
- gdalCreateCommand.output,
88653
- vrtWarpCommand.output
88654
- ]);
88954
+ const vrtMergeCommand = gdalBuildVrt(
88955
+ new URL(`${tileId}-merged.vrt`, ctx.tempFolder),
88956
+ [gdalCreateCommand.output, vrtWarpCommand.output],
88957
+ false
88958
+ );
88655
88959
  await new GdalRunner(vrtMergeCommand).run(logger);
88656
88960
  const cogCreateCommand = gdalBuildCog(new URL(`${tileId}.tiff`, ctx.tempFolder), vrtMergeCommand.output, options);
88657
88961
  await new GdalRunner(cogCreateCommand).run(logger);
@@ -88841,10 +89145,16 @@ function getTargetBaseZoom(tileMatrix, resolution, targetZoomOffset) {
88841
89145
  return Projection.getTiffResZoom(tileMatrix, resolution);
88842
89146
  return Projection.getTiffResZoom(tileMatrix, resolution) + targetZoomOffset;
88843
89147
  }
89148
+ var TargetZoomOffset = {
89149
+ zstd_17: 6,
89150
+ lzw: 6
89151
+ };
88844
89152
  async function createTileCover(ctx) {
88845
89153
  await ProjectionLoader.load(ctx.imagery.projection);
89154
+ await ProjectionLoader.load(EpsgCode.Wgs84);
88846
89155
  const targetBaseZoom = getTargetBaseZoom(ctx.tileMatrix, ctx.imagery.gsd, ctx.targetZoomOffset);
88847
- const optimalCoveringZoom = Math.max(1, targetBaseZoom - 7);
89156
+ const targetZoomOffset = TargetZoomOffset[ctx.preset] ?? 7;
89157
+ const optimalCoveringZoom = Math.max(1, targetBaseZoom - targetZoomOffset);
88848
89158
  ctx.logger?.debug({ targetBaseZoom, cogOverZoom: optimalCoveringZoom }, "Imagery:ZoomLevel");
88849
89159
  const sourceBounds = projectPolygon(
88850
89160
  polygonFromBounds(ctx.imagery.files),
@@ -88881,8 +89191,10 @@ async function createTileCover(ctx) {
88881
89191
  for (const tile of covering) {
88882
89192
  const bounds = ctx.tileMatrix.tileToSourceBounds(tile);
88883
89193
  const scaledBounds = bounds.scaleFromCenter(1.05);
88884
- const tileBounds = Projection.get(ctx.tileMatrix).projectMultipolygon(
88885
- [scaledBounds.toPolygon()],
89194
+ const projection = Projection.get(ctx.tileMatrix);
89195
+ const wsg84Bounds = multiPolygonToWgs84([scaledBounds.toPolygon()], projection.toWgs84, true);
89196
+ const tileBounds = Projection.get(EpsgCode.Wgs84).projectMultipolygon(
89197
+ wsg84Bounds,
88886
89198
  Projection.get(ctx.imagery.projection)
88887
89199
  );
88888
89200
  const source = imageryBounds.filter((f) => intersection(tileBounds, f.polygon).length > 0);
@@ -88909,9 +89221,11 @@ async function createTileCover(ctx) {
88909
89221
  "linz_basemaps:options": {
88910
89222
  preset: ctx.preset,
88911
89223
  ...Presets[ctx.preset].options,
89224
+ presetBands: ctx.presetBands,
88912
89225
  tile,
88913
89226
  tileMatrix: ctx.tileMatrix.identifier,
88914
89227
  sourceEpsg: ctx.imagery.projection,
89228
+ sourceBands: ctx.imagery.bands,
88915
89229
  zoomLevel: targetBaseZoom
88916
89230
  },
88917
89231
  "linz_basemaps:generated": {
@@ -89027,6 +89341,11 @@ var BasemapsCogifyCoverCommand = (0, import_cmd_ts4.command)({
89027
89341
  defaultValue: () => "webp",
89028
89342
  defaultValueIsSerializable: true
89029
89343
  }),
89344
+ presetBand: (0, import_cmd_ts4.option)({
89345
+ type: (0, import_cmd_ts4.optional)((0, import_cmd_ts4.oneOf)(Object.keys(BandPresets))),
89346
+ long: "preset-bands",
89347
+ description: "Tell GDAL what the bands in the source image represent see `-colorinterp` in GDAL"
89348
+ }),
89030
89349
  tileMatrix: (0, import_cmd_ts4.option)({
89031
89350
  type: import_cmd_ts4.string,
89032
89351
  long: "tile-matrix",
@@ -89052,6 +89371,9 @@ var BasemapsCogifyCoverCommand = (0, import_cmd_ts4.command)({
89052
89371
  async handler(args) {
89053
89372
  const metrics = new Metrics();
89054
89373
  const logger = getLogger(this, args, "cli-raster");
89374
+ if (args.presetBand != null && !AllowedPresets[args.preset].includes(args.presetBand)) {
89375
+ throw new Error(`Preset ${args.preset} does not support band option ${args.presetBand}`);
89376
+ }
89055
89377
  const mem = new ConfigProviderMemory();
89056
89378
  metrics.start("imagery:load");
89057
89379
  const cfg = await initConfigFromUrls(mem, args.paths);
@@ -89060,6 +89382,21 @@ var BasemapsCogifyCoverCommand = (0, import_cmd_ts4.command)({
89060
89382
  throw new Error("No imagery found");
89061
89383
  const im = cfg.imagery[0];
89062
89384
  logger.info({ files: im.files.length, title: im.title, duration: imageryLoadTime }, "Imagery:Loaded");
89385
+ if (args.presetBand != null && im.bands != null) {
89386
+ const bandList = BandPresets[args.presetBand];
89387
+ if (bandList && im.bands.length !== bandList.length) {
89388
+ if (bandList.includes("alpha") && im.bands.length === bandList.length - 1) {
89389
+ logger.warn(
89390
+ { title: im.title, bands: im.bands.length, expectedBands: bandList.length },
89391
+ `Imagery:Bands:MissingAlpha - Adding alpha to source`
89392
+ );
89393
+ } else {
89394
+ throw new Error(
89395
+ `Imagery has ${im.bands.length} bands, preset ${args.presetBand} requires ${bandList.join(", ")}`
89396
+ );
89397
+ }
89398
+ }
89399
+ }
89063
89400
  if (im.collection == null && args.requireStacCollection) {
89064
89401
  throw new Error(`No collection.json found with imagery: ${im.url.href}`);
89065
89402
  }
@@ -89081,6 +89418,7 @@ var BasemapsCogifyCoverCommand = (0, import_cmd_ts4.command)({
89081
89418
  metrics,
89082
89419
  cutline,
89083
89420
  preset: args.preset,
89421
+ presetBands: BandPresets[args.presetBand] ?? void 0,
89084
89422
  background: args.background,
89085
89423
  targetZoomOffset: args.baseZoomOffset
89086
89424
  };
@@ -89169,12 +89507,12 @@ function extractBoundsFromTiff(tiff, logger) {
89169
89507
  }
89170
89508
  var GeotagToEpsgCode = {
89171
89509
  // global
89172
- "Universal Transverse Mercator Zone": 4326,
89510
+ "Universal Transverse Mercator Zone": EpsgCode.Wgs84,
89173
89511
  // antarctic
89174
89512
  "McMurdo Sound Lambert Conformal 2000": 5479,
89175
89513
  // new zealand
89176
- "Chatham Islands Transverse Mercator 2000": 2193,
89177
- "New Zealand Transverse Mercator 2000": 3793,
89514
+ "New Zealand Transverse Mercator 2000": EpsgCode.Nztm2000,
89515
+ "Chatham Islands Transverse Mercator 2000": EpsgCode.Citm2000,
89178
89516
  // new zealand offshore islands
89179
89517
  "Auckland Islands Transverse Mercator": 3788,
89180
89518
  "Campbell Island Transverse Mercator": 3789,
@@ -89484,6 +89822,7 @@ async function writeStacFiles(target, items, collection, logger) {
89484
89822
 
89485
89823
  // src/cogify/cli/cli.topo.ts
89486
89824
  var Q2 = pLimit(10);
89825
+ var Format = ["gridded", "gridless"];
89487
89826
  var MapSeries = ["topo25", "topo50", "topo250"];
89488
89827
  var MapSeriesTitle = {
89489
89828
  topo25: "Raster Topographic Maps 25k",
@@ -89509,7 +89848,12 @@ var TopoStacCreationCommand = (0, import_cmd_ts5.command)({
89509
89848
  mapSeries: (0, import_cmd_ts5.option)({
89510
89849
  type: (0, import_cmd_ts5.oneOf)(MapSeries),
89511
89850
  long: "map-series",
89512
- description: `Map series name. Either ${MapSeries.join(", ")}`
89851
+ description: `Map series scale. Either ${MapSeries.join(", ")}`
89852
+ }),
89853
+ format: (0, import_cmd_ts5.option)({
89854
+ type: (0, import_cmd_ts5.oneOf)(Format),
89855
+ long: "format",
89856
+ description: `Map sheet format. Either ${Format.join(", ")}`
89513
89857
  }),
89514
89858
  latestOnly: (0, import_cmd_ts5.flag)({
89515
89859
  type: import_cmd_ts5.boolean,
@@ -89533,6 +89877,7 @@ var TopoStacCreationCommand = (0, import_cmd_ts5.command)({
89533
89877
  const startTime = performance.now();
89534
89878
  logger.info("TopoCogify:Start");
89535
89879
  const mapSeries = args.mapSeries;
89880
+ const format = args.format;
89536
89881
  const title = args.title ?? MapSeriesTitle[mapSeries];
89537
89882
  const ctx = {
89538
89883
  latestOnly: args.latestOnly,
@@ -89540,6 +89885,7 @@ var TopoStacCreationCommand = (0, import_cmd_ts5.command)({
89540
89885
  target: args.target,
89541
89886
  title,
89542
89887
  mapSeries,
89888
+ format,
89543
89889
  output: args.output,
89544
89890
  logger
89545
89891
  };
@@ -89583,7 +89929,7 @@ async function loadTiffsToCreateStacs(ctx) {
89583
89929
  const epsgDirectoryPaths = [];
89584
89930
  const stacItemPaths = [];
89585
89931
  const mapSeries = ctx.mapSeries;
89586
- const resolution = "gridless_600dpi";
89932
+ const resolution = `${ctx.format}_600dpi`;
89587
89933
  for (const [epsg, tiffItems] of allTiffItems.entries()) {
89588
89934
  logger?.info({ epsg }, "CreateStacFiles:Start");
89589
89935
  const latestTiffItems = extractLatestTiffItemsByMapCode(tiffItems);