@hypersoniclabs/helix-cli 0.1.25-helix3.478 → 0.1.25-helix3.480

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 (127) hide show
  1. package/README.md +2 -1
  2. package/dist/city/dem/despike.d.ts +12 -0
  3. package/dist/city/dem/despike.js +83 -0
  4. package/dist/city/dem/despike.js.map +1 -0
  5. package/dist/city/dem/index.d.ts +8 -0
  6. package/dist/city/dem/index.js +22 -0
  7. package/dist/city/dem/index.js.map +1 -0
  8. package/dist/city/dem/layer.d.ts +46 -0
  9. package/dist/city/dem/layer.js +69 -0
  10. package/dist/city/dem/layer.js.map +1 -0
  11. package/dist/city/dem/mosaic.d.ts +60 -0
  12. package/dist/city/dem/mosaic.js +168 -0
  13. package/dist/city/dem/mosaic.js.map +1 -0
  14. package/dist/city/dem/rangeClient.d.ts +40 -0
  15. package/dist/city/dem/rangeClient.js +137 -0
  16. package/dist/city/dem/rangeClient.js.map +1 -0
  17. package/dist/city/dem/tiff.d.ts +50 -0
  18. package/dist/city/dem/tiff.js +314 -0
  19. package/dist/city/dem/tiff.js.map +1 -0
  20. package/dist/city/dem/tileIds.d.ts +19 -0
  21. package/dist/city/dem/tileIds.js +67 -0
  22. package/dist/city/dem/tileIds.js.map +1 -0
  23. package/dist/city/dem/window.d.ts +57 -0
  24. package/dist/city/dem/window.js +227 -0
  25. package/dist/city/dem/window.js.map +1 -0
  26. package/dist/city/import/osm/areas.d.ts +19 -0
  27. package/dist/city/import/osm/areas.js +308 -0
  28. package/dist/city/import/osm/areas.js.map +1 -0
  29. package/dist/city/import/osm/buildings.d.ts +9 -0
  30. package/dist/city/import/osm/buildings.js +277 -0
  31. package/dist/city/import/osm/buildings.js.map +1 -0
  32. package/dist/city/import/osm/command.d.ts +6 -0
  33. package/dist/city/import/osm/command.js +113 -0
  34. package/dist/city/import/osm/command.js.map +1 -0
  35. package/dist/city/import/osm/elevation.d.ts +12 -0
  36. package/dist/city/import/osm/elevation.js +105 -0
  37. package/dist/city/import/osm/elevation.js.map +1 -0
  38. package/dist/city/import/osm/emit.d.ts +14 -0
  39. package/dist/city/import/osm/emit.js +21 -0
  40. package/dist/city/import/osm/emit.js.map +1 -0
  41. package/dist/city/import/osm/geom.d.ts +42 -0
  42. package/dist/city/import/osm/geom.js +356 -0
  43. package/dist/city/import/osm/geom.js.map +1 -0
  44. package/dist/city/import/osm/index.d.ts +84 -0
  45. package/dist/city/import/osm/index.js +317 -0
  46. package/dist/city/import/osm/index.js.map +1 -0
  47. package/dist/city/import/osm/normalise.d.ts +19 -0
  48. package/dist/city/import/osm/normalise.js +26 -0
  49. package/dist/city/import/osm/normalise.js.map +1 -0
  50. package/dist/city/import/osm/overpass.d.ts +64 -0
  51. package/dist/city/import/osm/overpass.js +234 -0
  52. package/dist/city/import/osm/overpass.js.map +1 -0
  53. package/dist/city/import/osm/pbf.d.ts +53 -0
  54. package/dist/city/import/osm/pbf.js +657 -0
  55. package/dist/city/import/osm/pbf.js.map +1 -0
  56. package/dist/city/import/osm/pois.d.ts +9 -0
  57. package/dist/city/import/osm/pois.js +62 -0
  58. package/dist/city/import/osm/pois.js.map +1 -0
  59. package/dist/city/import/osm/polygons.d.ts +50 -0
  60. package/dist/city/import/osm/polygons.js +262 -0
  61. package/dist/city/import/osm/polygons.js.map +1 -0
  62. package/dist/city/import/osm/preview.d.ts +17 -0
  63. package/dist/city/import/osm/preview.js +124 -0
  64. package/dist/city/import/osm/preview.js.map +1 -0
  65. package/dist/city/import/osm/projection.d.ts +18 -0
  66. package/dist/city/import/osm/projection.js +95 -0
  67. package/dist/city/import/osm/projection.js.map +1 -0
  68. package/dist/city/import/osm/raw.d.ts +64 -0
  69. package/dist/city/import/osm/raw.js +42 -0
  70. package/dist/city/import/osm/raw.js.map +1 -0
  71. package/dist/city/import/osm/roads.d.ts +14 -0
  72. package/dist/city/import/osm/roads.js +516 -0
  73. package/dist/city/import/osm/roads.js.map +1 -0
  74. package/dist/city/import/osm/tags.d.ts +34 -0
  75. package/dist/city/import/osm/tags.js +210 -0
  76. package/dist/city/import/osm/tags.js.map +1 -0
  77. package/dist/city/import/osm/testkit.d.ts +13 -0
  78. package/dist/city/import/osm/testkit.js +49 -0
  79. package/dist/city/import/osm/testkit.js.map +1 -0
  80. package/dist/index.js +19 -2
  81. package/dist/index.js.map +1 -1
  82. package/dist/scenePerf/byteRules.d.ts +28 -0
  83. package/dist/scenePerf/byteRules.js +251 -0
  84. package/dist/scenePerf/byteRules.js.map +1 -0
  85. package/dist/scenePerf/commands.d.ts +3 -0
  86. package/dist/scenePerf/commands.js +95 -0
  87. package/dist/scenePerf/commands.js.map +1 -0
  88. package/dist/scenePerf/format.d.ts +3 -0
  89. package/dist/scenePerf/format.js +25 -0
  90. package/dist/scenePerf/format.js.map +1 -0
  91. package/dist/scenePerf/gate.d.ts +23 -0
  92. package/dist/scenePerf/gate.js +39 -0
  93. package/dist/scenePerf/gate.js.map +1 -0
  94. package/dist/scenePerf/glb.d.ts +44 -0
  95. package/dist/scenePerf/glb.js +179 -0
  96. package/dist/scenePerf/glb.js.map +1 -0
  97. package/dist/scenePerf/index.d.ts +5 -0
  98. package/dist/scenePerf/index.js +19 -0
  99. package/dist/scenePerf/index.js.map +1 -0
  100. package/dist/scenePerf/ktx2.d.ts +27 -0
  101. package/dist/scenePerf/ktx2.js +53 -0
  102. package/dist/scenePerf/ktx2.js.map +1 -0
  103. package/dist/scenePerf/run.d.ts +31 -0
  104. package/dist/scenePerf/run.js +65 -0
  105. package/dist/scenePerf/run.js.map +1 -0
  106. package/dist/scenePerf/source.d.ts +15 -0
  107. package/dist/scenePerf/source.js +73 -0
  108. package/dist/scenePerf/source.js.map +1 -0
  109. package/dist/tsconfig.build.tsbuildinfo +1 -1
  110. package/docs/city-import-osm.md +100 -0
  111. package/docs/scene-perf.md +59 -0
  112. package/package.json +5 -2
  113. package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
  114. package/plugins/beamng-vehicle/bin/__pycache__/beamng_audio.cpython-312.pyc +0 -0
  115. package/plugins/beamng-vehicle/bin/__pycache__/beamng_engine.cpython-312.pyc +0 -0
  116. package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
  117. package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
  118. package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
  119. package/plugins/beamng-vehicle/bin/__pycache__/beamng_stock_events.cpython-312.pyc +0 -0
  120. package/plugins/beamng-vehicle/bin/__pycache__/fmod_studio.cpython-312.pyc +0 -0
  121. package/plugins/beamng-vehicle/bin/__pycache__/fsb5.cpython-312.pyc +0 -0
  122. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio.cpython-312.pyc +0 -0
  123. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_ac.cpython-312.pyc +0 -0
  124. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_beamng.cpython-312.pyc +0 -0
  125. package/plugins/beamng-vehicle/bin/__pycache__/sim_audio_fixtures.cpython-312.pyc +0 -0
  126. package/plugins/beamng-vehicle/bin/__pycache__/test_beamng_engine.cpython-312.pyc +0 -0
  127. package/plugins/beamng-vehicle/bin/__pycache__/test_sim_audio.cpython-312.pyc +0 -0
package/README.md CHANGED
@@ -104,7 +104,8 @@ The token is stored in `~/.helix/credentials.json` (mode `600`). It's sent as
104
104
  | `helix world audit\|source-audit\|perf-gate\|prove-live` | Run the blocking world QA, performance, and deployed-build identity gates. |
105
105
  | `helix world performance-report [bundle] [--scene <scene-v2.json>] [--package <package-version.json>] [--snapshot <inspect.json>] [--perf <receipt.json>]` | Analyze exact declared scene resources and existing reference-profile budgets, while keeping authored geometry/material/delivery estimates separate from supplied runtime claims. Prints actionable instancing, Palette, lighting, transfer, and visual-QA guidance; it never predicts FPS from static counts. |
106
106
  | `helix world scene-source inventory\|compile <helsinki\|bistro> <archive>` | Verify and safely inventory a known CC BY source archive, then compile deterministic local proxy/detail GLBs and an existing-schema Scene v2 package. |
107
- | `helix world scene-source publish <world-id> <package-dir> [--publish]` | Validate the Scene v2 package and print its multipart build receipt. Execution remains dry-run-only until backend private-pin entitlement lands; `--publish` currently fails closed, and access control never changes CC BY rights. |
107
+ | `helix world scene-source publish <world-id> <package-dir> [--publish]` | Validate the Scene v2 package, refuse it if it breaks the open-world performance rules (docs/scene-perf.md; no override), and print its multipart build receipt. Execution remains dry-run-only until backend private-pin entitlement lands; `--publish` currently fails closed, and access control never changes CC BY rights. |
108
+ | `helix scene validate <dir\|file> [--perf] [--tier desktop\|mobile\|desktop,mobile] [--out <report.json>]` | Check a Scene v2 package against the shared validator and, with `--perf`, the 14 open-world performance rules (textures, geometry, cells, draws, budget, first-playable bytes). Exit 0 pass, 1 refused, 2 cannot be judged. See `docs/scene-perf.md`. |
108
109
  | `helix continuum publish-source-map\|publish-scratch\|publish-package\|publish-glb\|make-item\|publish-owned-item` | Resume exact-CID object adoption/ingestion, Package lease/seal, optional explicitly authorized Listing, and WorldRelease publication through one creator-owned pipeline. |
109
110
  | `helix continuum receipt <job-id>` | Read the sanitized durable receipt for a completed Continuum publication. |
110
111
  | `helix unreal init\|validate\|doctor\|package [project]` | Create and gate a shared-runtime or native-game publish manifest, check optional editor MCP, and assemble a local upload-shaped package. |
@@ -0,0 +1,12 @@
1
+ import type { DemWindow } from './window';
2
+ /**
3
+ * GLO-30 is a surface model, and in a skyscraper canyon the radar returns small pits (downtown San Francisco has 3-4 sample wide
4
+ * holes 18 m below sea level on a 20 m hill). A pit is a connected cluster of samples more than `depthM` under the median of
5
+ * their 7 x 7 neighbourhood; clusters of at most `maxSamples` are filled with that median, while larger ones are real basins
6
+ * (quarries, docks, lakes under the surface model) and stay. Mutates `window.heights` in place, so `sampleAt` sees it, and
7
+ * recomputes `min`/`max`. Returns how many samples were filled.
8
+ */
9
+ export declare function despikeDemWindow(window: DemWindow, opts?: {
10
+ depthM?: number;
11
+ maxSamples?: number;
12
+ }): number;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.despikeDemWindow = despikeDemWindow;
4
+ /**
5
+ * GLO-30 is a surface model, and in a skyscraper canyon the radar returns small pits (downtown San Francisco has 3-4 sample wide
6
+ * holes 18 m below sea level on a 20 m hill). A pit is a connected cluster of samples more than `depthM` under the median of
7
+ * their 7 x 7 neighbourhood; clusters of at most `maxSamples` are filled with that median, while larger ones are real basins
8
+ * (quarries, docks, lakes under the surface model) and stay. Mutates `window.heights` in place, so `sampleAt` sees it, and
9
+ * recomputes `min`/`max`. Returns how many samples were filled.
10
+ */
11
+ function despikeDemWindow(window, opts = {}) {
12
+ const depthM = opts.depthM ?? 8;
13
+ const maxSamples = opts.maxSamples ?? 40;
14
+ const { samplesX: w, samplesZ: h, heights } = window;
15
+ const median = new Float32Array(heights.length);
16
+ const gather = [];
17
+ for (let j = 0; j < h; j += 1) {
18
+ for (let i = 0; i < w; i += 1) {
19
+ gather.length = 0;
20
+ for (let dj = -3; dj <= 3; dj += 1) {
21
+ for (let di = -3; di <= 3; di += 1) {
22
+ const x = i + di;
23
+ const z = j + dj;
24
+ if (x >= 0 && z >= 0 && x < w && z < h)
25
+ gather.push(heights[z * w + x]);
26
+ }
27
+ }
28
+ gather.sort((a, b) => a - b);
29
+ median[j * w + i] = gather[gather.length >> 1];
30
+ }
31
+ }
32
+ const pit = new Uint8Array(heights.length);
33
+ for (let k = 0; k < heights.length; k += 1)
34
+ if (median[k] - heights[k] > depthM)
35
+ pit[k] = 1;
36
+ const seen = new Uint8Array(heights.length);
37
+ let filled = 0;
38
+ for (let start = 0; start < pit.length; start += 1) {
39
+ if (!pit[start] || seen[start])
40
+ continue;
41
+ const cluster = [];
42
+ const stack = [start];
43
+ seen[start] = 1;
44
+ while (stack.length > 0) {
45
+ const at = stack.pop();
46
+ cluster.push(at);
47
+ const x = at % w;
48
+ const z = (at - x) / w;
49
+ for (let dj = -1; dj <= 1; dj += 1) {
50
+ for (let di = -1; di <= 1; di += 1) {
51
+ const nx = x + di;
52
+ const nz = z + dj;
53
+ if (nx < 0 || nz < 0 || nx >= w || nz >= h)
54
+ continue;
55
+ const n = nz * w + nx;
56
+ if (pit[n] && !seen[n]) {
57
+ seen[n] = 1;
58
+ stack.push(n);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ if (cluster.length <= maxSamples) {
64
+ for (const at of cluster)
65
+ heights[at] = median[at];
66
+ filled += cluster.length;
67
+ }
68
+ }
69
+ if (filled > 0) {
70
+ let min = Infinity;
71
+ let max = -Infinity;
72
+ for (const value of heights) {
73
+ if (value < min)
74
+ min = value;
75
+ if (value > max)
76
+ max = value;
77
+ }
78
+ window.min = min;
79
+ window.max = max;
80
+ }
81
+ return filled;
82
+ }
83
+ //# sourceMappingURL=despike.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"despike.js","sourceRoot":"","sources":["../../../src/city/dem/despike.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export { copernicusTileIds, demCellId, MAX_DEM_CELLS } from './tileIds';
2
+ export { COPERNICUS_ATTRIBUTION, COPERNICUS_BASE_URL, fetchCopernicusWindow } from './window';
3
+ export type { DemTileReport, DemWindow, DemWindowRequest } from './window';
4
+ export { DEM_LAYER_SCHEMA, DEM_RASTER_FILE, encodeDemLayer, writeDemLayer } from './layer';
5
+ export type { DemLayer, DemLayerFiles, DemLayerMeta } from './layer';
6
+ export { decodeTile, parseCogHeader } from './tiff';
7
+ export type { CogHeader } from './tiff';
8
+ export { despikeDemWindow } from './despike';
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.despikeDemWindow = exports.parseCogHeader = exports.decodeTile = exports.writeDemLayer = exports.encodeDemLayer = exports.DEM_RASTER_FILE = exports.DEM_LAYER_SCHEMA = exports.fetchCopernicusWindow = exports.COPERNICUS_BASE_URL = exports.COPERNICUS_ATTRIBUTION = exports.MAX_DEM_CELLS = exports.demCellId = exports.copernicusTileIds = void 0;
4
+ var tileIds_1 = require("./tileIds");
5
+ Object.defineProperty(exports, "copernicusTileIds", { enumerable: true, get: function () { return tileIds_1.copernicusTileIds; } });
6
+ Object.defineProperty(exports, "demCellId", { enumerable: true, get: function () { return tileIds_1.demCellId; } });
7
+ Object.defineProperty(exports, "MAX_DEM_CELLS", { enumerable: true, get: function () { return tileIds_1.MAX_DEM_CELLS; } });
8
+ var window_1 = require("./window");
9
+ Object.defineProperty(exports, "COPERNICUS_ATTRIBUTION", { enumerable: true, get: function () { return window_1.COPERNICUS_ATTRIBUTION; } });
10
+ Object.defineProperty(exports, "COPERNICUS_BASE_URL", { enumerable: true, get: function () { return window_1.COPERNICUS_BASE_URL; } });
11
+ Object.defineProperty(exports, "fetchCopernicusWindow", { enumerable: true, get: function () { return window_1.fetchCopernicusWindow; } });
12
+ var layer_1 = require("./layer");
13
+ Object.defineProperty(exports, "DEM_LAYER_SCHEMA", { enumerable: true, get: function () { return layer_1.DEM_LAYER_SCHEMA; } });
14
+ Object.defineProperty(exports, "DEM_RASTER_FILE", { enumerable: true, get: function () { return layer_1.DEM_RASTER_FILE; } });
15
+ Object.defineProperty(exports, "encodeDemLayer", { enumerable: true, get: function () { return layer_1.encodeDemLayer; } });
16
+ Object.defineProperty(exports, "writeDemLayer", { enumerable: true, get: function () { return layer_1.writeDemLayer; } });
17
+ var tiff_1 = require("./tiff");
18
+ Object.defineProperty(exports, "decodeTile", { enumerable: true, get: function () { return tiff_1.decodeTile; } });
19
+ Object.defineProperty(exports, "parseCogHeader", { enumerable: true, get: function () { return tiff_1.parseCogHeader; } });
20
+ var despike_1 = require("./despike");
21
+ Object.defineProperty(exports, "despikeDemWindow", { enumerable: true, get: function () { return despike_1.despikeDemWindow; } });
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/city/dem/index.ts"],"names":[],"mappings":";;;AAAA,qCAAwE;AAA/D,4GAAA,iBAAiB,OAAA;AAAE,oGAAA,SAAS,OAAA;AAAE,wGAAA,aAAa,OAAA;AACpD,mCAA8F;AAArF,gHAAA,sBAAsB,OAAA;AAAE,6GAAA,mBAAmB,OAAA;AAAE,+GAAA,qBAAqB,OAAA;AAE3E,iCAA2F;AAAlF,yGAAA,gBAAgB,OAAA;AAAE,wGAAA,eAAe,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,sGAAA,aAAa,OAAA;AAEzE,+BAAoD;AAA3C,kGAAA,UAAU,OAAA;AAAE,sGAAA,cAAc,OAAA;AAEnC,qCAA6C;AAApC,2GAAA,gBAAgB,OAAA"}
@@ -0,0 +1,46 @@
1
+ import { type DemWindow } from './window';
2
+ /** The `terrain/dem.json` sidecar of `helix.city-source/1` (lane city-source's `CityDemMeta`). */
3
+ export interface DemLayerMeta {
4
+ schema: 'helix.city-source-dem/1';
5
+ file: string;
6
+ format: 'r16';
7
+ /** [columns, rows] */
8
+ samples: [number, number];
9
+ metresPerSample: number;
10
+ originXZ: [number, number];
11
+ /** Scene Y metres that raw 0 and raw 65535 stand for (already minus the manifest's originAltitudeM). */
12
+ heightRangeM: [number, number];
13
+ /** Over the raster bytes. */
14
+ sha256: string;
15
+ note?: string;
16
+ }
17
+ export interface DemLayer {
18
+ meta: DemLayerMeta;
19
+ r16: Buffer;
20
+ originAltitudeM: number;
21
+ }
22
+ export interface DemLayerFiles {
23
+ r16Path: string;
24
+ jsonPath: string;
25
+ sha256: {
26
+ r16: string;
27
+ json: string;
28
+ };
29
+ originAltitudeM: number;
30
+ heightRange: [number, number];
31
+ }
32
+ export declare const DEM_LAYER_SCHEMA: "helix.city-source-dem/1";
33
+ export declare const DEM_RASTER_FILE = "dem.r16";
34
+ /**
35
+ * The window as a city-source terrain layer: little-endian uint16 R16, row 0 north, encoding altitude relative to the window's
36
+ * floor over `[0, span]` in whole metres (as the terrain-world engine script does), so
37
+ * `helix terrain import dem.r16 --metres-per-sample N --samples <columns> --height-range 0,<span>` reads it back.
38
+ * Pure and deterministic: the same window gives the same bytes, and dem.json carries no timestamps.
39
+ */
40
+ export declare function encodeDemLayer(window: DemWindow, opts?: {
41
+ originAltitudeM?: number;
42
+ }): DemLayer;
43
+ /** Writes `dir/dem.r16` and `dir/dem.json` for a standalone consumer (the city-source writer writes its own copy). */
44
+ export declare function writeDemLayer(window: DemWindow, dir: string, opts?: {
45
+ originAltitudeM?: number;
46
+ }): Promise<DemLayerFiles>;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEM_RASTER_FILE = exports.DEM_LAYER_SCHEMA = void 0;
4
+ exports.encodeDemLayer = encodeDemLayer;
5
+ exports.writeDemLayer = writeDemLayer;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const promises_1 = require("node:fs/promises");
8
+ const node_path_1 = require("node:path");
9
+ const window_1 = require("./window");
10
+ exports.DEM_LAYER_SCHEMA = 'helix.city-source-dem/1';
11
+ exports.DEM_RASTER_FILE = 'dem.r16';
12
+ /**
13
+ * The window as a city-source terrain layer: little-endian uint16 R16, row 0 north, encoding altitude relative to the window's
14
+ * floor over `[0, span]` in whole metres (as the terrain-world engine script does), so
15
+ * `helix terrain import dem.r16 --metres-per-sample N --samples <columns> --height-range 0,<span>` reads it back.
16
+ * Pure and deterministic: the same window gives the same bytes, and dem.json carries no timestamps.
17
+ */
18
+ function encodeDemLayer(window, opts) {
19
+ const floor = Math.floor(window.min);
20
+ const originAltitudeM = opts?.originAltitudeM ?? floor;
21
+ if (!Number.isFinite(originAltitudeM))
22
+ throw new Error(`encodeDemLayer: originAltitudeM must be finite, received ${originAltitudeM}`);
23
+ if (originAltitudeM > window.min) {
24
+ // R16 has no room below zero: a higher origin would clamp the lowest ground flat, silently.
25
+ throw new Error(`encodeDemLayer: originAltitudeM ${originAltitudeM} is above the window's lowest ground (${window.min} m)`);
26
+ }
27
+ const span = Math.max(1, Math.ceil(window.max) - originAltitudeM);
28
+ if (window.heights.length !== window.samplesX * window.samplesZ) {
29
+ throw new Error(`encodeDemLayer: ${window.heights.length} heights is not ${window.samplesX} x ${window.samplesZ}`);
30
+ }
31
+ const r16 = Buffer.alloc(window.heights.length * 2);
32
+ for (let index = 0; index < window.heights.length; index += 1) {
33
+ const encoded = Math.round(((window.heights[index] - originAltitudeM) / span) * 65535);
34
+ r16.writeUInt16LE(Math.min(65535, Math.max(0, encoded)), index * 2);
35
+ }
36
+ return {
37
+ meta: {
38
+ schema: exports.DEM_LAYER_SCHEMA,
39
+ file: exports.DEM_RASTER_FILE,
40
+ format: 'r16',
41
+ samples: [window.samplesX, window.samplesZ],
42
+ metresPerSample: window.metresPerSample,
43
+ originXZ: [window.originXZ[0], window.originXZ[1]],
44
+ heightRangeM: [0, span],
45
+ sha256: (0, node_crypto_1.createHash)('sha256').update(r16).digest('hex'),
46
+ note: `Copernicus GLO-30 DSM (EGM2008), altitude ${originAltitudeM} m is scene Y 0; ${window_1.COPERNICUS_ATTRIBUTION}`,
47
+ },
48
+ r16,
49
+ originAltitudeM,
50
+ };
51
+ }
52
+ /** Writes `dir/dem.r16` and `dir/dem.json` for a standalone consumer (the city-source writer writes its own copy). */
53
+ async function writeDemLayer(window, dir, opts) {
54
+ const layer = encodeDemLayer(window, opts);
55
+ const json = `${JSON.stringify(layer.meta, null, 2)}\n`;
56
+ await (0, promises_1.mkdir)(dir, { recursive: true });
57
+ const r16Path = (0, node_path_1.join)(dir, layer.meta.file);
58
+ const jsonPath = (0, node_path_1.join)(dir, 'dem.json');
59
+ await (0, promises_1.writeFile)(r16Path, layer.r16);
60
+ await (0, promises_1.writeFile)(jsonPath, json);
61
+ return {
62
+ r16Path,
63
+ jsonPath,
64
+ sha256: { r16: layer.meta.sha256, json: (0, node_crypto_1.createHash)('sha256').update(json).digest('hex') },
65
+ originAltitudeM: layer.originAltitudeM,
66
+ heightRange: layer.meta.heightRangeM,
67
+ };
68
+ }
69
+ //# sourceMappingURL=layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layer.js","sourceRoot":"","sources":["../../../src/city/dem/layer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import { type DemCell } from './tileIds';
2
+ import { type CogHeader } from './tiff';
3
+ /** One 1x1 degree GLO-30 cell once its header is known (`header` undefined means the bucket has no such tile: open ocean). */
4
+ export interface LoadedCell {
5
+ cell: DemCell;
6
+ id: string;
7
+ url: string;
8
+ header?: CogHeader;
9
+ note?: string;
10
+ /** Decoded internal tiles by tile index; undefined until fetched. NaN marks nodata. */
11
+ tiles: Array<Float32Array | undefined>;
12
+ /** Every byte range this build used (header at 0, then tiles), keyed by start offset: the cache-independent record the digest covers. */
13
+ ranges: Map<number, Uint8Array>;
14
+ bytesFetched: number;
15
+ }
16
+ export declare function cellKey(cell: DemCell): number;
17
+ /** Raster <-> geographic mapping of one cell's header. */
18
+ export interface CellGeometry {
19
+ colOf(lon: number): number;
20
+ rowOf(lat: number): number;
21
+ lonOf(col: number): number;
22
+ latOf(row: number): number;
23
+ }
24
+ /**
25
+ * Copernicus GLO-30 COGs carry GTRasterTypeGeoKey = 2 (PixelIsPoint) with the tie point raster (0,0) -> (cell west lon,
26
+ * cell north lat): sample (0,0) is CENTRED on the integer-degree north-west corner, so column c sits at lon = west + c *
27
+ * scale. (Seen in the real N37 W123 and N46 E007 headers; GDAL reports the same file with a half-pixel-shifted geotransform
28
+ * and AREA_OR_POINT=Point.) A PixelIsArea raster's tie point names the pixel CORNER, so its centre is half a pixel further in.
29
+ */
30
+ export declare function cellGeometry(header: CogHeader): CellGeometry;
31
+ /**
32
+ * The cells as one continuous sample field. A sample is looked up in the cell that owns it (west edge inclusive, north
33
+ * edge inclusive: the cell's row 0 sits ON its north degree line). The bilinear footprint of a sample near a seam reaches
34
+ * one pixel into the neighbour; that pixel is the neighbour's nearest sample to the exact position (the neighbour may have
35
+ * a coarser longitude posting at high latitude), so a seam interpolates across both tiles. If the neighbour was not loaded
36
+ * the footprint clamps to the edge of its own cell instead; a missing (ocean) neighbour reads as 0 m.
37
+ *
38
+ * Two passes share this code: `collectNeeded` records which internal tiles a window touches without reading any, then the
39
+ * caller fetches exactly those and runs `heightAt` for real.
40
+ */
41
+ export declare class Mosaic {
42
+ private readonly cells;
43
+ /** Output samples that needed any nodata fill. */
44
+ filled: number;
45
+ private collected;
46
+ private readonly geometry;
47
+ constructor(cells: Map<number, LoadedCell>);
48
+ startCollecting(): void;
49
+ /** Ends the collecting pass and returns, per cell, the internal tile indices it touched. */
50
+ finishCollecting(): Map<LoadedCell, Set<number>>;
51
+ private geometryOf;
52
+ private cellAt;
53
+ /** Bilinear height at lon/lat (0 over a missing tile); NaN never leaves. */
54
+ heightAt(lon: number, lat: number): number;
55
+ /** The valid sample nearest `(col, row)` by Euclidean distance within a bounded search, walking square rings outward. */
56
+ private nearestValid;
57
+ /** One source sample, crossing into the neighbouring cell when the index is off this cell's raster. NaN = nodata / not loaded. */
58
+ private pixel;
59
+ private read;
60
+ }
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Mosaic = void 0;
4
+ exports.cellKey = cellKey;
5
+ exports.cellGeometry = cellGeometry;
6
+ const tileIds_1 = require("./tileIds");
7
+ const tiff_1 = require("./tiff");
8
+ /** How far a nodata sample looks for a valid neighbour before the void is declared sea level. */
9
+ const NODATA_SEARCH_PIXELS = 16;
10
+ function cellKey(cell) {
11
+ return (cell.latIdx + 90) * 400 + (cell.lonIdx + 180);
12
+ }
13
+ /**
14
+ * Copernicus GLO-30 COGs carry GTRasterTypeGeoKey = 2 (PixelIsPoint) with the tie point raster (0,0) -> (cell west lon,
15
+ * cell north lat): sample (0,0) is CENTRED on the integer-degree north-west corner, so column c sits at lon = west + c *
16
+ * scale. (Seen in the real N37 W123 and N46 E007 headers; GDAL reports the same file with a half-pixel-shifted geotransform
17
+ * and AREA_OR_POINT=Point.) A PixelIsArea raster's tie point names the pixel CORNER, so its centre is half a pixel further in.
18
+ */
19
+ function cellGeometry(header) {
20
+ const shift = header.rasterType === 'area' ? 0.5 : 0;
21
+ const { tiePoint: tie, pixelScale: [sx, sy] } = header;
22
+ return {
23
+ colOf: (lon) => (lon - tie.lon) / sx + tie.i - shift,
24
+ rowOf: (lat) => (tie.lat - lat) / sy + tie.j - shift,
25
+ lonOf: (col) => tie.lon + (col + shift - tie.i) * sx,
26
+ latOf: (row) => tie.lat - (row + shift - tie.j) * sy,
27
+ };
28
+ }
29
+ /**
30
+ * The cells as one continuous sample field. A sample is looked up in the cell that owns it (west edge inclusive, north
31
+ * edge inclusive: the cell's row 0 sits ON its north degree line). The bilinear footprint of a sample near a seam reaches
32
+ * one pixel into the neighbour; that pixel is the neighbour's nearest sample to the exact position (the neighbour may have
33
+ * a coarser longitude posting at high latitude), so a seam interpolates across both tiles. If the neighbour was not loaded
34
+ * the footprint clamps to the edge of its own cell instead; a missing (ocean) neighbour reads as 0 m.
35
+ *
36
+ * Two passes share this code: `collectNeeded` records which internal tiles a window touches without reading any, then the
37
+ * caller fetches exactly those and runs `heightAt` for real.
38
+ */
39
+ class Mosaic {
40
+ cells;
41
+ /** Output samples that needed any nodata fill. */
42
+ filled = 0;
43
+ collected = null;
44
+ geometry = new Map();
45
+ constructor(cells) {
46
+ this.cells = cells;
47
+ }
48
+ startCollecting() {
49
+ this.collected = new Map();
50
+ }
51
+ /** Ends the collecting pass and returns, per cell, the internal tile indices it touched. */
52
+ finishCollecting() {
53
+ const result = this.collected ?? new Map();
54
+ this.collected = null;
55
+ return result;
56
+ }
57
+ geometryOf(cell) {
58
+ let geometry = this.geometry.get(cell);
59
+ if (geometry === undefined) {
60
+ geometry = cellGeometry(cell.header);
61
+ this.geometry.set(cell, geometry);
62
+ }
63
+ return geometry;
64
+ }
65
+ cellAt(lon, lat) {
66
+ return this.cells.get(cellKey((0, tileIds_1.owningCell)(lon, lat)));
67
+ }
68
+ /** Bilinear height at lon/lat (0 over a missing tile); NaN never leaves. */
69
+ heightAt(lon, lat) {
70
+ const cell = this.cellAt(lon, lat);
71
+ if (cell === undefined || cell.header === undefined)
72
+ return 0;
73
+ const geometry = this.geometryOf(cell);
74
+ const u = geometry.colOf(lon);
75
+ const v = geometry.rowOf(lat);
76
+ const c0 = Math.floor(u);
77
+ const r0 = Math.floor(v);
78
+ const fu = u - c0;
79
+ const fv = v - r0;
80
+ let sum = 0;
81
+ let weight = 0;
82
+ let sawNodata = false;
83
+ for (let dr = 0; dr < 2; dr += 1) {
84
+ for (let dc = 0; dc < 2; dc += 1) {
85
+ const w = (dc === 0 ? 1 - fu : fu) * (dr === 0 ? 1 - fv : fv);
86
+ if (w <= 0)
87
+ continue;
88
+ const value = this.pixel(cell, c0 + dc, r0 + dr);
89
+ if (Number.isNaN(value)) {
90
+ sawNodata = true;
91
+ continue;
92
+ }
93
+ sum += value * w;
94
+ weight += w;
95
+ }
96
+ }
97
+ // Interpolate over the valid corners only: a void must not drag its neighbours to a sentinel.
98
+ if (weight > 0) {
99
+ if (sawNodata)
100
+ this.filled += 1;
101
+ return sum / weight;
102
+ }
103
+ this.filled += 1;
104
+ return this.nearestValid(cell, Math.round(u), Math.round(v)) ?? 0;
105
+ }
106
+ /** The valid sample nearest `(col, row)` by Euclidean distance within a bounded search, walking square rings outward. */
107
+ nearestValid(cell, col, row) {
108
+ let bestSquared = Infinity;
109
+ let bestValue = null;
110
+ // Ring r is at least r away, so once r*r passes the best hit nothing beyond can beat it.
111
+ for (let radius = 1; radius <= NODATA_SEARCH_PIXELS && radius * radius <= bestSquared; radius += 1) {
112
+ for (let dr = -radius; dr <= radius; dr += 1) {
113
+ const step = Math.abs(dr) === radius ? 1 : 2 * radius;
114
+ for (let dc = -radius; dc <= radius; dc += step) {
115
+ const squared = dc * dc + dr * dr;
116
+ if (squared >= bestSquared)
117
+ continue;
118
+ const value = this.pixel(cell, col + dc, row + dr);
119
+ if (!Number.isNaN(value)) {
120
+ bestSquared = squared;
121
+ bestValue = value;
122
+ }
123
+ }
124
+ }
125
+ }
126
+ return bestValue;
127
+ }
128
+ /** One source sample, crossing into the neighbouring cell when the index is off this cell's raster. NaN = nodata / not loaded. */
129
+ pixel(cell, col, row) {
130
+ const header = cell.header;
131
+ if (col >= 0 && col < header.width && row >= 0 && row < header.height)
132
+ return this.read(cell, col, row);
133
+ const geometry = this.geometryOf(cell);
134
+ const lon = geometry.lonOf(col);
135
+ const lat = geometry.latOf(row);
136
+ const neighbour = this.cellAt(lon, lat);
137
+ if (neighbour === undefined || neighbour === cell) {
138
+ return this.read(cell, Math.min(Math.max(col, 0), header.width - 1), Math.min(Math.max(row, 0), header.height - 1));
139
+ }
140
+ if (neighbour.header === undefined)
141
+ return 0;
142
+ const other = this.geometryOf(neighbour);
143
+ const nc = Math.min(Math.max(Math.round(other.colOf(lon)), 0), neighbour.header.width - 1);
144
+ const nr = Math.min(Math.max(Math.round(other.rowOf(lat)), 0), neighbour.header.height - 1);
145
+ return this.read(neighbour, nc, nr);
146
+ }
147
+ read(cell, col, row) {
148
+ const header = cell.header;
149
+ const tx = Math.floor(col / header.tileWidth);
150
+ const ty = Math.floor(row / header.tileHeight);
151
+ const index = ty * (0, tiff_1.tilesAcross)(header) + tx;
152
+ if (this.collected !== null) {
153
+ let set = this.collected.get(cell);
154
+ if (set === undefined) {
155
+ set = new Set();
156
+ this.collected.set(cell, set);
157
+ }
158
+ set.add(index);
159
+ return 0;
160
+ }
161
+ const tile = cell.tiles[index];
162
+ if (tile === undefined)
163
+ return Number.NaN;
164
+ return tile[(row - ty * header.tileHeight) * header.tileWidth + (col - tx * header.tileWidth)];
165
+ }
166
+ }
167
+ exports.Mosaic = Mosaic;
168
+ //# sourceMappingURL=mosaic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mosaic.js","sourceRoot":"","sources":["../../../src/city/dem/mosaic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ export interface RangeClientOptions {
2
+ fetch: typeof fetch;
3
+ retries: number;
4
+ /** First backoff delay; doubles per attempt. Tests drop it to a millisecond. */
5
+ backoffBaseMs: number;
6
+ cacheDir?: string;
7
+ }
8
+ export type RangeResult = {
9
+ status: 'ok';
10
+ bytes: Uint8Array;
11
+ networkBytes: number;
12
+ }
13
+ /** 404 / 403: S3 answers 403 for an absent key when the bucket forbids listing, so both mean "no such object". */
14
+ | {
15
+ status: 'missing';
16
+ code: number;
17
+ networkBytes: number;
18
+ };
19
+ export interface RangeStats {
20
+ requests: number;
21
+ bytesFetched: number;
22
+ cacheHits: number;
23
+ }
24
+ /**
25
+ * Ranged GETs with a bounded retry and an optional on-disk cache of every range, so a rerun is offline-capable and
26
+ * polite to the bucket. A range is a pure function of (url, start, end) because the bucket's objects are immutable.
27
+ */
28
+ export declare class RangeClient {
29
+ private readonly options;
30
+ readonly stats: RangeStats;
31
+ constructor(options: RangeClientOptions);
32
+ /** Bytes `[start, endInclusive]` of `url`; a range past the end of a short object comes back short (the header read relies on that). */
33
+ getRange(url: string, start: number, endInclusive: number, exact: boolean): Promise<RangeResult>;
34
+ private fetchWithRetry;
35
+ private cachePath;
36
+ private readCache;
37
+ private writeCache;
38
+ }
39
+ /** Runs `worker` over `items` with at most `limit` in flight; results keep input order. */
40
+ export declare function mapPool<T, R>(items: readonly T[], limit: number, worker: (item: T, index: number) => Promise<R>): Promise<R[]>;