@tangram.gl/tangram-renderer 0.0.0-stage → 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (321) hide show
  1. package/README.md +59 -2
  2. package/dist/core.js +36923 -0
  3. package/dist/core.js.map +6 -0
  4. package/dist/index.js +32 -0
  5. package/dist/loaders-gl-worker.js +82 -0
  6. package/dist/loaders-gl-worker.js.map +6 -0
  7. package/dist/map-logic.js +2 -0
  8. package/dist/map-logic.js.map +6 -0
  9. package/dist/projected-basemaps-worker.js +3 -0
  10. package/dist/projected-basemaps-worker.js.map +6 -0
  11. package/dist/shader-modules.js +141 -0
  12. package/dist/shader-modules.js.map +6 -0
  13. package/dist/style-schema.js +126 -0
  14. package/dist/style-schema.js.map +6 -0
  15. package/dist/tangram-style.schema.json +882 -0
  16. package/dist/tangram.debug.mjs +37139 -0
  17. package/dist/tangram.debug.mjs.map +6 -0
  18. package/dist/tangram.min.mjs +4950 -0
  19. package/dist/types/builders/earcut.d.ts +15 -0
  20. package/dist/types/core.d.ts +29 -0
  21. package/dist/types/gl/constants.d.ts +2 -0
  22. package/dist/types/gl/globe_mesh.d.ts +48 -0
  23. package/dist/types/gl/glsl.d.ts +17 -0
  24. package/dist/types/gl/vertex-constants.d.ts +4 -0
  25. package/dist/types/gl/vertex-types.d.ts +34 -0
  26. package/dist/types/gl/vertex_data.d.ts +32 -0
  27. package/dist/types/gl/vertex_elements.d.ts +8 -0
  28. package/dist/types/gl/vertex_layout.d.ts +32 -0
  29. package/dist/types/glsl.d.ts +14 -0
  30. package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
  31. package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
  32. package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
  33. package/dist/types/index.d.ts +155 -0
  34. package/dist/types/lights/light-definitions.d.ts +121 -0
  35. package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
  36. package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
  37. package/dist/types/map-logic/globe-terrain.d.ts +54 -0
  38. package/dist/types/map-logic/index.d.ts +33 -0
  39. package/dist/types/map-logic/label-identity.d.ts +22 -0
  40. package/dist/types/map-logic/label-placement.d.ts +32 -0
  41. package/dist/types/map-logic/screen-bounds.d.ts +15 -0
  42. package/dist/types/map-logic/terrain-data.d.ts +55 -0
  43. package/dist/types/map-logic/terrain-error.d.ts +51 -0
  44. package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
  45. package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
  46. package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
  47. package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
  48. package/dist/types/map-logic/terrain-query.d.ts +33 -0
  49. package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
  50. package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
  51. package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
  52. package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
  53. package/dist/types/map-logic/tile-residency.d.ts +17 -0
  54. package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
  55. package/dist/types/procedures/js-yaml.d.ts +15 -0
  56. package/dist/types/procedures/mesh-projector.d.ts +107 -0
  57. package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
  58. package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
  59. package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
  60. package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
  61. package/dist/types/procedures/terrain-draping.d.ts +165 -0
  62. package/dist/types/procedures/terrain-globe.d.ts +6 -0
  63. package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
  64. package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
  65. package/dist/types/procedures/web-mercator-math.d.ts +4 -0
  66. package/dist/types/scene/globals.d.ts +11 -0
  67. package/dist/types/scene/ground_footprint.d.ts +16 -0
  68. package/dist/types/scene/host_frame.d.ts +54 -0
  69. package/dist/types/scene/projection_math.d.ts +52 -0
  70. package/dist/types/scene/scene-resource-types.d.ts +177 -0
  71. package/dist/types/scene/scene_bundle.d.ts +69 -0
  72. package/dist/types/scene/visibility_adapter.d.ts +106 -0
  73. package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
  74. package/dist/types/selection/terrain_surface.d.ts +149 -0
  75. package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
  76. package/dist/types/shader-modules/height-decode.d.ts +13 -0
  77. package/dist/types/shader-modules/hillshade.d.ts +13 -0
  78. package/dist/types/shader-modules/index.d.ts +6 -0
  79. package/dist/types/shader-modules/planar.d.ts +12 -0
  80. package/dist/types/shader-modules/sphere-map.d.ts +13 -0
  81. package/dist/types/shader-modules/triplanar.d.ts +15 -0
  82. package/dist/types/sources/decoded_tile_store.d.ts +95 -0
  83. package/dist/types/sources/tile_source_metadata.d.ts +32 -0
  84. package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
  85. package/dist/types/tile/tile_id.d.ts +37 -0
  86. package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
  87. package/dist/types/types.d.ts +293 -0
  88. package/dist/types/utils/geo.d.ts +57 -0
  89. package/dist/types/utils/hash.d.ts +1 -0
  90. package/dist/types/utils/log.d.ts +27 -0
  91. package/dist/types/utils/props.d.ts +6 -0
  92. package/dist/types/utils/subscribe.d.ts +10 -0
  93. package/dist/types/utils/thread.d.ts +6 -0
  94. package/dist/types/utils/urls.d.ts +26 -0
  95. package/dist/types/utils/utils.d.ts +26 -0
  96. package/dist/types/utils/version.d.ts +2 -0
  97. package/dist/types/utils/worker-types.d.ts +68 -0
  98. package/dist/types/utils/worker_broker.d.ts +3 -0
  99. package/package.json +103 -3
  100. package/src/builders/common.ts +41 -0
  101. package/src/builders/earcut.d.ts +15 -0
  102. package/src/builders/geometry-types.ts +60 -0
  103. package/src/builders/points.ts +115 -0
  104. package/src/builders/polygons.ts +225 -0
  105. package/src/builders/polylines.ts +682 -0
  106. package/src/builders/wireframe.ts +31 -0
  107. package/src/core.d.ts +29 -0
  108. package/src/core.ts +33 -0
  109. package/src/experimental/loaders-gl-worker.ts +19 -0
  110. package/src/experimental/projected-basemaps-worker.ts +13 -0
  111. package/src/experimental/projected-mesh-cache.ts +137 -0
  112. package/src/experimental/projected-mesh-domain.ts +110 -0
  113. package/src/experimental/projected-mesh-refinement.ts +193 -0
  114. package/src/experimental/projected-mesh.ts +399 -0
  115. package/src/experimental/projected-normal.ts +26 -0
  116. package/src/experimental/projected-symbol-mesh.ts +74 -0
  117. package/src/gl/constants.ts +18 -0
  118. package/src/gl/context.ts +103 -0
  119. package/src/gl/extensions.ts +30 -0
  120. package/src/gl/globe_mesh.ts +285 -0
  121. package/src/gl/glsl.ts +257 -0
  122. package/src/gl/mesh-types.ts +56 -0
  123. package/src/gl/render_state.ts +111 -0
  124. package/src/gl/shader_program.ts +1310 -0
  125. package/src/gl/texture.ts +814 -0
  126. package/src/gl/uniform_buffer.ts +340 -0
  127. package/src/gl/vao.ts +154 -0
  128. package/src/gl/vbo_mesh.ts +349 -0
  129. package/src/gl/vertex-constants.ts +19 -0
  130. package/src/gl/vertex-types.ts +35 -0
  131. package/src/gl/vertex_data.ts +126 -0
  132. package/src/gl/vertex_elements.ts +54 -0
  133. package/src/gl/vertex_layout.ts +241 -0
  134. package/src/glsl.d.ts +14 -0
  135. package/src/gpu/luma_device_renderer.ts +579 -0
  136. package/src/gpu/pixel_readback.ts +51 -0
  137. package/src/gpu/resource_lifecycle.ts +32 -0
  138. package/src/gpu/tangram_gpu_backend.ts +195 -0
  139. package/src/index.d.ts +155 -0
  140. package/src/index.ts +105 -0
  141. package/src/labels/collision-types.ts +94 -0
  142. package/src/labels/collision.ts +273 -0
  143. package/src/labels/collision_grid.ts +61 -0
  144. package/src/labels/intersect.ts +30 -0
  145. package/src/labels/label-types.ts +91 -0
  146. package/src/labels/label.ts +207 -0
  147. package/src/labels/label_line.ts +801 -0
  148. package/src/labels/label_point.ts +131 -0
  149. package/src/labels/main-pass-types.ts +73 -0
  150. package/src/labels/main_pass.ts +221 -0
  151. package/src/labels/point_anchor.ts +100 -0
  152. package/src/labels/point_placement.ts +150 -0
  153. package/src/labels/projected-pass.ts +259 -0
  154. package/src/labels/repeat_group.ts +72 -0
  155. package/src/lights/ambient_light.glsl +18 -0
  156. package/src/lights/directional_light.glsl +39 -0
  157. package/src/lights/geographic-lights.ts +21 -0
  158. package/src/lights/light-definitions.ts +285 -0
  159. package/src/lights/light.ts +590 -0
  160. package/src/lights/lighting-uniforms.ts +80 -0
  161. package/src/lights/lighting-wgsl.ts +116 -0
  162. package/src/lights/material.glsl +237 -0
  163. package/src/lights/material.ts +145 -0
  164. package/src/lights/native-falloff.ts +37 -0
  165. package/src/lights/point_light.glsl +96 -0
  166. package/src/lights/spot_light.glsl +111 -0
  167. package/src/map-logic/draped-terrain-bounds.ts +71 -0
  168. package/src/map-logic/globe-terrain-preparation.ts +217 -0
  169. package/src/map-logic/globe-terrain.ts +296 -0
  170. package/src/map-logic/index.ts +37 -0
  171. package/src/map-logic/label-identity.ts +36 -0
  172. package/src/map-logic/label-placement.ts +117 -0
  173. package/src/map-logic/screen-bounds.ts +34 -0
  174. package/src/map-logic/terrain-data.ts +107 -0
  175. package/src/map-logic/terrain-error.ts +134 -0
  176. package/src/map-logic/terrain-ground-topology.ts +164 -0
  177. package/src/map-logic/terrain-height-field.ts +254 -0
  178. package/src/map-logic/terrain-navigation.ts +50 -0
  179. package/src/map-logic/terrain-neighborhood.ts +43 -0
  180. package/src/map-logic/terrain-query.ts +51 -0
  181. package/src/map-logic/terrain-resource-budget.ts +124 -0
  182. package/src/map-logic/terrain-transition-topology.ts +164 -0
  183. package/src/map-logic/tile-build-queue.ts +110 -0
  184. package/src/map-logic/tile-cache-policy.ts +89 -0
  185. package/src/map-logic/tile-residency.ts +50 -0
  186. package/src/map-logic/triangle-query-index.ts +265 -0
  187. package/src/procedures/js-yaml.d.ts +15 -0
  188. package/src/procedures/mesh-projector.ts +191 -0
  189. package/src/procedures/mlt-loaders.ts +55 -0
  190. package/src/procedures/mvt-dependencies.d.ts +20 -0
  191. package/src/procedures/mvt-legacy.ts +150 -0
  192. package/src/procedures/mvt-loaders.ts +53 -0
  193. package/src/procedures/mvt-parser.ts +48 -0
  194. package/src/procedures/mvt-properties.ts +36 -0
  195. package/src/procedures/mvt-tile-provider.ts +72 -0
  196. package/src/procedures/pmtiles-loader.ts +75 -0
  197. package/src/procedures/projected-coordinate-transform.ts +79 -0
  198. package/src/procedures/projection-batch-executor.ts +140 -0
  199. package/src/procedures/scene-yaml-legacy.ts +16 -0
  200. package/src/procedures/scene-yaml-loaders.ts +10 -0
  201. package/src/procedures/terrain-draping.ts +713 -0
  202. package/src/procedures/terrain-globe.ts +32 -0
  203. package/src/procedures/terrain-styled-topology.ts +184 -0
  204. package/src/procedures/web-mercator-legacy.ts +24 -0
  205. package/src/procedures/web-mercator-math.ts +50 -0
  206. package/src/scene/camera.ts +298 -0
  207. package/src/scene/camera_base.ts +116 -0
  208. package/src/scene/classic_scene.ts +20 -0
  209. package/src/scene/external_camera.ts +101 -0
  210. package/src/scene/globals.ts +120 -0
  211. package/src/scene/ground_footprint.ts +139 -0
  212. package/src/scene/host_frame.ts +326 -0
  213. package/src/scene/projected_tile_lod.ts +125 -0
  214. package/src/scene/projection_math.ts +142 -0
  215. package/src/scene/projection_shaders.ts +183 -0
  216. package/src/scene/renderer.ts +516 -0
  217. package/src/scene/scene-resource-types.ts +174 -0
  218. package/src/scene/scene.ts +2378 -0
  219. package/src/scene/scene_bundle.ts +304 -0
  220. package/src/scene/scene_debug.ts +129 -0
  221. package/src/scene/scene_loader.ts +402 -0
  222. package/src/scene/scene_worker.ts +528 -0
  223. package/src/scene/view.ts +683 -0
  224. package/src/scene/visibility_adapter.ts +319 -0
  225. package/src/scene/worker_url.ts +15 -0
  226. package/src/selection/draped-terrain-surface.ts +377 -0
  227. package/src/selection/selection-types.ts +79 -0
  228. package/src/selection/selection.ts +542 -0
  229. package/src/selection/selection_fragment.glsl +18 -0
  230. package/src/selection/selection_globals.glsl +9 -0
  231. package/src/selection/selection_pixels.ts +59 -0
  232. package/src/selection/selection_vertex.glsl +15 -0
  233. package/src/selection/terrain_surface.ts +298 -0
  234. package/src/shader-modules/globe-horizon.ts +52 -0
  235. package/src/shader-modules/height-decode.ts +41 -0
  236. package/src/shader-modules/hillshade.ts +70 -0
  237. package/src/shader-modules/index.ts +10 -0
  238. package/src/shader-modules/planar.ts +24 -0
  239. package/src/shader-modules/sphere-map.ts +38 -0
  240. package/src/shader-modules/triplanar.ts +45 -0
  241. package/src/sources/aligned_tile_source.ts +122 -0
  242. package/src/sources/data_source.ts +655 -0
  243. package/src/sources/decoded_tile_store.ts +245 -0
  244. package/src/sources/geojson.ts +300 -0
  245. package/src/sources/mvt.ts +247 -0
  246. package/src/sources/raster.ts +341 -0
  247. package/src/sources/shared_tile_source_adapter.ts +117 -0
  248. package/src/sources/sources.ts +10 -0
  249. package/src/sources/tile_source_adapter.ts +84 -0
  250. package/src/sources/tile_source_metadata.ts +65 -0
  251. package/src/sources/tile_source_state.ts +112 -0
  252. package/src/sources/topojson.ts +90 -0
  253. package/src/styles/feature_annotations.ts +16 -0
  254. package/src/styles/filter.ts +190 -0
  255. package/src/styles/geometry-style-types.ts +209 -0
  256. package/src/styles/globe_visibility_wgsl.ts +22 -0
  257. package/src/styles/layer-types.ts +45 -0
  258. package/src/styles/layer.ts +586 -0
  259. package/src/styles/lines/dasharray.ts +49 -0
  260. package/src/styles/lines/lines.ts +707 -0
  261. package/src/styles/lines/lines_wgsl.ts +224 -0
  262. package/src/styles/points/point-runtime-types.ts +125 -0
  263. package/src/styles/points/points.ts +1079 -0
  264. package/src/styles/points/points_fragment.glsl +126 -0
  265. package/src/styles/points/points_vertex.glsl +230 -0
  266. package/src/styles/points/points_wgsl.ts +189 -0
  267. package/src/styles/polygons/polygons.ts +268 -0
  268. package/src/styles/polygons/polygons_fragment.glsl +173 -0
  269. package/src/styles/polygons/polygons_vertex.glsl +213 -0
  270. package/src/styles/polygons/polygons_wgsl.ts +127 -0
  271. package/src/styles/property-types.ts +125 -0
  272. package/src/styles/raster/raster.ts +33 -0
  273. package/src/styles/raster/raster_globals.glsl +46 -0
  274. package/src/styles/style-mixing-types.ts +101 -0
  275. package/src/styles/style-runtime-types.ts +220 -0
  276. package/src/styles/style-schema.ts +133 -0
  277. package/src/styles/style.ts +1033 -0
  278. package/src/styles/style_globals.glsl +69 -0
  279. package/src/styles/style_manager.ts +464 -0
  280. package/src/styles/style_parser.ts +620 -0
  281. package/src/styles/text/font_manager.ts +180 -0
  282. package/src/styles/text/fontfaceobserver.d.ts +10 -0
  283. package/src/styles/text/text-types.ts +220 -0
  284. package/src/styles/text/text.ts +378 -0
  285. package/src/styles/text/text_canvas.ts +773 -0
  286. package/src/styles/text/text_labels.ts +436 -0
  287. package/src/styles/text/text_segments.ts +142 -0
  288. package/src/styles/text/text_settings.ts +228 -0
  289. package/src/styles/text/text_wgsl.ts +159 -0
  290. package/src/styles/text/text_wrap.ts +194 -0
  291. package/src/tile/globe_tile_preload.ts +40 -0
  292. package/src/tile/tangram_tileset_2d.ts +108 -0
  293. package/src/tile/tile-build-types.ts +155 -0
  294. package/src/tile/tile.ts +807 -0
  295. package/src/tile/tile_id.ts +172 -0
  296. package/src/tile/tile_manager.ts +889 -0
  297. package/src/tile/tile_pyramid.ts +119 -0
  298. package/src/tile/tile_resource_cache.ts +91 -0
  299. package/src/tile/tile_traversal_adapter.ts +132 -0
  300. package/src/types.ts +332 -0
  301. package/src/utils/debounce.ts +17 -0
  302. package/src/utils/debug_settings.ts +43 -0
  303. package/src/utils/errors.ts +12 -0
  304. package/src/utils/functions.ts +95 -0
  305. package/src/utils/geo.ts +364 -0
  306. package/src/utils/hash.ts +14 -0
  307. package/src/utils/log.ts +108 -0
  308. package/src/utils/media_capture.ts +193 -0
  309. package/src/utils/merge.ts +44 -0
  310. package/src/utils/obb.ts +134 -0
  311. package/src/utils/props.ts +43 -0
  312. package/src/utils/slice.ts +14 -0
  313. package/src/utils/subscribe.ts +58 -0
  314. package/src/utils/task.ts +154 -0
  315. package/src/utils/thread.ts +26 -0
  316. package/src/utils/urls.ts +223 -0
  317. package/src/utils/utils.ts +310 -0
  318. package/src/utils/vector.ts +197 -0
  319. package/src/utils/version.ts +8 -0
  320. package/src/utils/worker-types.ts +79 -0
  321. package/src/utils/worker_broker.ts +523 -0
@@ -0,0 +1,265 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {intersectRayBounds, intersectRayTriangle} from '@math.gl/culling/queries';
6
+
7
+ /** Nearest two-sided triangle; the ray parameter retains the supplied direction scale. */
8
+ export interface IndexedTriangleHit {
9
+ /** Original triangle offset, unaffected by bounds partitioning. */
10
+ triangleIndex: number;
11
+ /** Parameter along origin + direction × parameter. */
12
+ parameter: number;
13
+ /** Weights in the original index order. */
14
+ barycentric: [number, number, number];
15
+ }
16
+
17
+ /** Detached query result with deterministic work counts for conformance/performance tests. */
18
+ export interface TriangleQueryResult {
19
+ /** Nearest hit in the closed forward segment, or null. */
20
+ hit: IndexedTriangleHit | null;
21
+ /** Number of tested hierarchy boxes. */
22
+ testedBounds: number;
23
+ /** Number of actual triangle intersection tests. */
24
+ testedTriangles: number;
25
+ }
26
+
27
+ /** Internal binary bounds partition; leaves retain original triangle identities. */
28
+ interface TriangleBoundsNode {
29
+ /** Closed lower corner. */
30
+ minimum: [number, number, number];
31
+ /** Closed upper corner. */
32
+ maximum: [number, number, number];
33
+ /** First offset in the partitioned triangle list. */
34
+ start: number;
35
+ /** Exclusive last offset. */
36
+ end: number;
37
+ /** Child offsets, absent at leaves. */
38
+ children?: [number, number];
39
+ }
40
+
41
+ /** Optional lazy hierarchy policy; the immutable geometry is always retained. */
42
+ export interface TriangleQueryIndexOptions {
43
+ /** Build on the first bounds-intersecting query rather than at construction. */
44
+ lazy?: boolean;
45
+ /** Maximum hierarchy typed-array bytes; insufficient space uses exact linear queries. */
46
+ maxHierarchyBytes?: number;
47
+ /** Never build synchronously from a query; an explicit cooperative iterator warms acceleration. */
48
+ deferBuild?: boolean;
49
+ }
50
+
51
+ /** Immutable CPU triangle index; no projection, scene, DOM, GPU or source ownership. */
52
+ export class TriangleQueryIndex {
53
+ /** Exact packed acceleration bytes for the same balanced partition used by construction. */
54
+ static getRequiredHierarchyBytes(triangleCount: number, leafSize = 8): number {
55
+ if (!Number.isSafeInteger(triangleCount) || triangleCount < 0 || triangleCount > 0xffffffff || !Number.isSafeInteger(leafSize) || leafSize < 1 || leafSize > 64) throw new Error('Invalid triangle hierarchy dimensions');
56
+ const counts = new Map<number, number>();
57
+ const countNodes = (count: number): number => {
58
+ if (!count) return 0;
59
+ if (count <= leafSize) return 1;
60
+ const cached = counts.get(count);
61
+ if (cached !== undefined) return cached;
62
+ const half = Math.floor(count / 2), result = 1 + countNodes(half) + countNodes(count - half);
63
+ counts.set(count, result); return result;
64
+ };
65
+ return triangleCount * 4 + countNodes(triangleCount) * 64;
66
+ }
67
+ /** Original number of triangles, including degenerates. */
68
+ readonly triangleCount: number;
69
+ /** Exact retained geometry typed-array bytes, independent of lazy acceleration. */
70
+ readonly geometryBytes: number;
71
+ /** Exact required order and packed-node bytes, known before hierarchy allocation. */
72
+ readonly requiredHierarchyBytes: number;
73
+ /** Copied vertices and original topology. */
74
+ private readonly positions: Float64Array;
75
+ private readonly indices: Uint32Array;
76
+ /** Partitioned original triangle identifiers. */
77
+ private order?: Uint32Array;
78
+ /** Six Float64 bounds and four Uint32 topology offsets per node. */
79
+ private bounds?: Float64Array;
80
+ private topology?: Uint32Array;
81
+ /** In-progress storage is counted but never traversed until the whole hierarchy is ready. */
82
+ private preparing?: {order: Uint32Array; bounds: Float64Array; topology: Uint32Array};
83
+ private readonly leafSize: number;
84
+ private readonly nodeCount: number;
85
+ private readonly hierarchyAllowed: boolean;
86
+ /** Host-driven warming keeps interactive queries from allocating a synchronous hierarchy. */
87
+ private readonly deferBuild: boolean;
88
+ private readonly minimum: [number, number, number] = [Infinity, Infinity, Infinity];
89
+ private readonly maximum: [number, number, number] = [-Infinity, -Infinity, -Infinity];
90
+
91
+ /** Copy finite XYZ triangles and partition their bounds into small balanced leaves. */
92
+ constructor(positions: ArrayLike<number>, indices: ArrayLike<number>, leafSize = 8, options: TriangleQueryIndexOptions = {}) {
93
+ if (!Number.isSafeInteger(positions.length) || !Number.isSafeInteger(indices.length) || positions.length % 3 || indices.length % 3 ||
94
+ !Number.isSafeInteger(leafSize) || leafSize < 1 || leafSize > 64) throw new Error('Invalid triangle index dimensions or leaf size');
95
+ if (indices.length / 3 > 0xffffffff) throw new Error('Triangle positions and indices must be finite and in range');
96
+ if (options.maxHierarchyBytes !== undefined && (!Number.isSafeInteger(options.maxHierarchyBytes) || options.maxHierarchyBytes < 0)) throw new Error('Triangle hierarchy byte limit must be a non-negative integer');
97
+ this.positions = Float64Array.from(positions);
98
+ // Validate the owned snapshot, not a separate read of caller-owned ArrayLike values.
99
+ for (let offset = 0; offset < this.positions.length; offset++) if (!Number.isFinite(this.positions[offset])) throw new Error('Triangle positions and indices must be finite and in range');
100
+ this.indices = new Uint32Array(indices.length);
101
+ for (let offset = 0; offset < indices.length; offset++) {
102
+ const index = indices[offset];
103
+ if (!Number.isSafeInteger(index) || index < 0 || index >= this.positions.length / 3 || index > 0xffffffff) throw new Error('Triangle positions and indices must be finite and in range');
104
+ this.indices[offset] = index;
105
+ }
106
+ this.triangleCount = indices.length / 3;
107
+ this.leafSize = leafSize;
108
+ this.requiredHierarchyBytes = TriangleQueryIndex.getRequiredHierarchyBytes(this.triangleCount, leafSize);
109
+ this.nodeCount = (this.requiredHierarchyBytes - this.triangleCount * 4) / 64;
110
+ this.geometryBytes = this.positions.byteLength + this.indices.byteLength;
111
+ this.hierarchyAllowed = this.requiredHierarchyBytes <= (options.maxHierarchyBytes ?? Infinity);
112
+ this.deferBuild = options.deferBuild ?? false;
113
+ for (let offset = 0; offset < this.positions.length; offset++) {
114
+ const axis = offset % 3;
115
+ this.minimum[axis] = Math.min(this.minimum[axis], this.positions[offset]);
116
+ this.maximum[axis] = Math.max(this.maximum[axis], this.positions[offset]);
117
+ }
118
+ if (!options.lazy && this.hierarchyAllowed) this.buildHierarchy();
119
+ }
120
+
121
+ /** Exact currently retained backing arrays, excluding JavaScript objects and construction scratch. */
122
+ get arrayBytes(): number {return this.geometryBytes + this.hierarchyBytes;}
123
+
124
+ /** Exact allocated acceleration storage, including pending warming; zero before allocation or in linear mode. */
125
+ get hierarchyBytes(): number {return this.preparing ? this.requiredHierarchyBytes : (this.order?.byteLength ?? 0) + (this.bounds?.byteLength ?? 0) + (this.topology?.byteLength ?? 0);}
126
+
127
+ /** Detached conservative geometry bounds, including unreferenced finite vertices; null for an empty vertex buffer. */
128
+ getBounds(): {minimum: [number, number, number]; maximum: [number, number, number]} | null {
129
+ return this.positions.length ? {minimum: [...this.minimum], maximum: [...this.maximum]} : null;
130
+ }
131
+
132
+ /** Warm acceleration in bounded scanning/partition chunks; closing the iterator discards partial storage. */
133
+ *prepareHierarchySteps(): Generator<void, void, void> {
134
+ if (this.order || this.preparing || !this.hierarchyAllowed || !this.triangleCount) return;
135
+ const storage = {order: new Uint32Array(this.triangleCount), bounds: new Float64Array(this.nodeCount * 6), topology: new Uint32Array(this.nodeCount * 4)};
136
+ this.preparing = storage;
137
+ try {
138
+ for (let offset = 0; offset < storage.order.length; offset++) {
139
+ storage.order[offset] = offset;
140
+ if (offset % 128 === 127) yield;
141
+ }
142
+ yield* this.buildNodeSteps(storage, 0, this.triangleCount, {next: 0});
143
+ this.bounds = storage.bounds; this.topology = storage.topology; this.order = storage.order;
144
+ } finally {this.preparing = undefined;}
145
+ }
146
+
147
+ /** Detached indexed vertex and its original identity, without exposing mutable backing arrays. */
148
+ getTriangleVertex(triangle: number, vertex: number): {index: number; position: [number, number, number]} {
149
+ return {index: this.getVertexIndex(triangle, vertex), position: this.getVertex(triangle, vertex)};
150
+ }
151
+
152
+ /** Read one original vertex identity without allocating coordinates or exposing topology storage. */
153
+ getVertexIndex(triangle: number, vertex: number): number {
154
+ if (!Number.isSafeInteger(triangle) || triangle < 0 || triangle >= this.triangleCount || !Number.isSafeInteger(vertex) || vertex < 0 || vertex > 2) throw new Error('Triangle vertex is out of range');
155
+ return this.indices[triangle * 3 + vertex];
156
+ }
157
+
158
+ /** Two-sided math.gl queries with bounds pruning, finite clipping and stable tie-breaking. */
159
+ queryRay(origin: readonly [number, number, number], direction: readonly [number, number, number], maxParameter = Infinity,
160
+ acceptHit?: (hit: IndexedTriangleHit) => boolean): TriangleQueryResult {
161
+ if (maxParameter < 0 || Number.isNaN(maxParameter)) throw new Error('Triangle ray limit must be non-negative');
162
+ const result: TriangleQueryResult = {hit: null, testedBounds: 0, testedTriangles: 0};
163
+ const length = Math.hypot(...direction);
164
+ if (origin.length !== 3 || direction.length !== 3 || !origin.every(Number.isFinite) || !direction.every(Number.isFinite) || !Number.isFinite(length) || length === 0 || !this.triangleCount) return result;
165
+ if (intersectRayBounds(origin, direction, this.minimum, this.maximum, maxParameter) === null) return result;
166
+ if (this.hierarchyAllowed && !this.deferBuild && !this.order && !this.preparing) this.buildHierarchy();
167
+ /** Share hit acceptance and stable tie-breaking across accelerated and allocation-free fallback paths. */
168
+ const testTriangle = (triangleIndex: number): void => {
169
+ result.testedTriangles++;
170
+ const hit = intersectRayTriangle(origin, direction, this.getVertex(triangleIndex, 0), this.getVertex(triangleIndex, 1), this.getVertex(triangleIndex, 2), {maxT: result.hit?.parameter ?? maxParameter});
171
+ if (hit && (!result.hit || hit.t < result.hit.parameter || hit.t === result.hit.parameter && triangleIndex < result.hit.triangleIndex)) {
172
+ const candidate = {triangleIndex, parameter: hit.t, barycentric: hit.barycentric};
173
+ if (!acceptHit || acceptHit(candidate)) result.hit = candidate;
174
+ }
175
+ };
176
+ if (!this.order) {
177
+ for (let triangle = 0; triangle < this.triangleCount; triangle++) testTriangle(triangle);
178
+ return result;
179
+ }
180
+ const stack = [{index: 0, near: 0}];
181
+ result.testedBounds++;
182
+ while (stack.length) {
183
+ const {index, near} = stack.pop()!;
184
+ if (near > (result.hit?.parameter ?? maxParameter)) continue;
185
+ const node = this.readNode(index);
186
+ if (node.children) {
187
+ const candidates = node.children.flatMap(child => {
188
+ const bounds = this.readNode(child); result.testedBounds++;
189
+ const entry = intersectRayBounds(origin, direction, bounds.minimum, bounds.maximum, result.hit?.parameter ?? maxParameter);
190
+ return entry === null ? [] : [{index: child, near: entry}];
191
+ });
192
+ candidates.sort((first, second) => second.near - first.near || second.index - first.index);
193
+ stack.push(...candidates); continue;
194
+ }
195
+ for (let offset = node.start; offset < node.end; offset++) {
196
+ testTriangle(this.order[offset]);
197
+ }
198
+ }
199
+ return result;
200
+ }
201
+
202
+ /** Preserve synchronous construction/lazy-query compatibility by consuming the same cooperative builder. */
203
+ private buildHierarchy(): void {
204
+ for (const step of this.prepareHierarchySteps()) {void step;}
205
+ }
206
+
207
+ /** Decode a node without retaining per-node JavaScript objects. */
208
+ private readNode(index: number): TriangleBoundsNode {
209
+ const bounds = this.bounds!, topology = this.topology!, offset = index * 4;
210
+ return {minimum: [bounds[index * 6], bounds[index * 6 + 1], bounds[index * 6 + 2]], maximum: [bounds[index * 6 + 3], bounds[index * 6 + 4], bounds[index * 6 + 5]], start: topology[offset], end: topology[offset + 1], children: topology[offset + 2] ? [topology[offset + 2], topology[offset + 3]] : undefined};
211
+ }
212
+
213
+ /** Read a detached original vertex for bounds and triangle kernels. */
214
+ private getVertex(triangle: number, offset: number): [number, number, number] {
215
+ const start = this.indices[triangle * 3 + offset] * 3;
216
+ return [this.positions[start], this.positions[start + 1], this.positions[start + 2]];
217
+ }
218
+
219
+ /** Partition on the longest bounds axis; midpoint splitting bounds recursion depth. */
220
+ private *buildNodeSteps(storage: {order: Uint32Array; bounds: Float64Array; topology: Uint32Array}, start: number, end: number, cursor: {next: number}): Generator<void, number, void> {
221
+ const {order, bounds, topology} = storage;
222
+ const minimum: [number, number, number] = [Infinity, Infinity, Infinity];
223
+ const maximum: [number, number, number] = [-Infinity, -Infinity, -Infinity];
224
+ for (let offset = start; offset < end; offset++) {
225
+ for (let vertex = 0; vertex < 3; vertex++) {
226
+ const positionOffset = this.indices[order[offset] * 3 + vertex] * 3;
227
+ for (let axis = 0; axis < 3; axis++) {const value = this.positions[positionOffset + axis]; minimum[axis] = Math.min(minimum[axis], value); maximum[axis] = Math.max(maximum[axis], value);}
228
+ }
229
+ if ((offset - start) % 128 === 127) yield;
230
+ }
231
+ const index = cursor.next++;
232
+ bounds.set([...minimum, ...maximum], index * 6);
233
+ topology.set([start, end], index * 4);
234
+ if (end - start > this.leafSize) {
235
+ let axis = 0;
236
+ for (let candidate = 1; candidate < 3; candidate++) if (maximum[candidate] - minimum[candidate] > maximum[axis] - minimum[axis]) axis = candidate;
237
+ const centroid = (triangle: number) => (this.positions[this.indices[triangle * 3] * 3 + axis] + this.positions[this.indices[triangle * 3 + 1] * 3 + axis] + this.positions[this.indices[triangle * 3 + 2] * 3 + axis]) / 3;
238
+ const middle = start + Math.floor((end - start) / 2);
239
+ yield* this.partitionSteps(order, middle, start, end - 1, (first, second) => centroid(first) - centroid(second) || first - second);
240
+ const left = yield* this.buildNodeSteps(storage, start, middle, cursor);
241
+ const right = yield* this.buildNodeSteps(storage, middle, end, cursor);
242
+ topology.set([left, right], index * 4 + 2);
243
+ }
244
+ yield;
245
+ return index;
246
+ }
247
+
248
+ /** In-place median selection with cooperative comparisons and no temporary full-size JavaScript order array. */
249
+ private *partitionSteps(order: Uint32Array, middle: number, left: number, right: number, compare: (first: number, second: number) => number): Generator<void, void, void> {
250
+ let comparisons = 0;
251
+ while (left < right) {
252
+ const pivot = order[left + Math.floor((right - left) / 2)];
253
+ let lower = left, upper = right;
254
+ while (lower <= upper) {
255
+ while (compare(order[lower], pivot) < 0) {lower++; if (++comparisons % 128 === 0) yield;}
256
+ while (compare(order[upper], pivot) > 0) {upper--; if (++comparisons % 128 === 0) yield;}
257
+ if (lower <= upper) {const value = order[lower]; order[lower++] = order[upper]; order[upper--] = value;}
258
+ if (++comparisons % 128 === 0) yield;
259
+ }
260
+ if (middle <= upper) right = upper;
261
+ else if (middle >= lower) left = lower;
262
+ else return;
263
+ }
264
+ }
265
+ }
@@ -0,0 +1,15 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ declare module 'js-yaml' {
6
+ type SafeLoadOptions = {
7
+ json?: boolean;
8
+ };
9
+
10
+ const yaml: {
11
+ safeLoad(source: string, options?: SafeLoadOptions): Record<string, any>;
12
+ };
13
+
14
+ export default yaml;
15
+ }
@@ -0,0 +1,191 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type VertexLayout from '../gl/vertex_layout';
6
+
7
+ /** Detached accounting for optional projection-independent worker preparation. */
8
+ export interface MeshPreparationStatistics {
9
+ /** Number of retained source meshes. */
10
+ entries: number;
11
+ /** Retained original/prepared typed-array bytes. */
12
+ bytes: number;
13
+ /** Successful preparation reuses. */
14
+ hits: number;
15
+ /** Requests requiring a new preparation. */
16
+ misses: number;
17
+ /** Optional detached statistics for the independently bounded, opt-in projected output cache. */
18
+ projectedResults?: {entries: number; bytes: number; hits: number; misses: number};
19
+ }
20
+
21
+ /** Cumulative projection work in one worker, reset with source preparation; not resident GPU memory. */
22
+ export interface MeshProjectionStatistics {
23
+ /** Meshes whose projection completed successfully. */
24
+ completedMeshes: number;
25
+ /** Requests rejected by preparation, refinement or the projection engine. */
26
+ failedMeshes: number;
27
+ /** Original packed vertices across completed requests. */
28
+ sourceVertices: number;
29
+ /** Final vertices across completed requests, including unchanged originals. */
30
+ outputVertices: number;
31
+ /** Final triangles across completed requests. */
32
+ outputTriangles: number;
33
+ /** Projector batches submitted, including samples and failed requests. */
34
+ projectionBatches: number;
35
+ /** Coordinate pairs submitted to local or host kernels. */
36
+ projectedPositions: number;
37
+ /** Successful sampled edge-refinement rounds. */
38
+ edgeRounds: number;
39
+ /** Successful sampled interior-refinement rounds. */
40
+ interiorRounds: number;
41
+ }
42
+
43
+ /** Initial CPU basemap projections; no arbitrary CRS or dynamic camera projection. */
44
+ export type ProjectedBasemapOptions = {
45
+ /** Projection with fixed documented ellipsoid, origin and standard parallels. */
46
+ type: 'equal-earth' | 'albers' | 'equirectangular' | 'mercator' | 'web-mercator';
47
+ /** Maximum Mercator angular edge span before projection; defaults to four degrees. */
48
+ maxAngularSpan?: number;
49
+ /** Per-mesh additional vertex budget; defaults to 65,536. */
50
+ maxAdditionalVertices?: number;
51
+ /** Optional sampled edge/interior error in common units; ribbons use edges only. */
52
+ maxProjectedError?: number;
53
+ /** Reuse completed projected meshes; host engines require a stable worker-local cache identity. */
54
+ cacheProjectedMeshes?: boolean;
55
+ /** Opt into packed surface/annotation elevation and polygon extrusion; ground-only by default. */
56
+ allowElevation?: boolean;
57
+ };
58
+
59
+ /** Worker-local packed triangle input; the original tile coordinates must survive. */
60
+ export interface MeshProjectionRequest {
61
+ /** Finalized interleaved vertex bytes. */
62
+ vertices: Uint8Array;
63
+ /** Triangle indices or an unindexed triangle stream. */
64
+ indices: Uint16Array | Uint32Array | false;
65
+ /** Layout shared by main-thread and worker polygon styles. */
66
+ layout: VertexLayout;
67
+ /** Source tile's north-west EPSG:3857 origin and actual data zoom. */
68
+ tile: {min: {x: number; y: number}; coords: {z: number}; overzoom2?: number; style_z?: number};
69
+ /** Lines carry centerline positions and separate packed extrusion; other meshes are already expanded. */
70
+ geometry?: 'polygons' | 'raster' | 'lines' | 'points' | 'text';
71
+ /** Validated scene-wide projection and refinement limits. */
72
+ projection: ProjectedBasemapOptions;
73
+ /** Optional worker-local bridge to a caller-owned host engine; never serialized with the scene. */
74
+ projectPositions?: (coordinates: Float64Array) => Promise<Float64Array>;
75
+ /** Immutable host transform identity for opt-in result caching; absence bypasses host result caching. */
76
+ projectionCacheKey?: string;
77
+ }
78
+
79
+ /** Completed packed mesh, independent of projection backend ownership. */
80
+ export type ProjectedMesh = {
81
+ vertices: Uint8Array;
82
+ indices: Uint16Array | Uint32Array;
83
+ };
84
+
85
+ /** Worker procedure; a host-injected backend may require an asynchronous batch round trip. */
86
+ export type MeshProjector = (request: MeshProjectionRequest) => ProjectedMesh | Promise<ProjectedMesh>;
87
+
88
+ let meshProjector: MeshProjector | undefined;
89
+
90
+ /** Projected lighting currently supports ambient and common-space directional lights only. */
91
+ export function validateProjectedLights(value: unknown): void {
92
+ if (value === undefined || value === null) return;
93
+ if (typeof value !== 'object') throw new Error('Projected lights must be an array or record');
94
+ for (const definition of Object.values(value)) {
95
+ const light = definition && typeof definition === 'object' && 'luma' in definition ? definition.luma : definition;
96
+ if (!light || typeof light !== 'object' || !('type' in light) ||
97
+ !['ambient', 'directional'].includes(String(light.type)) ||
98
+ ('directionSpace' in light && light.directionSpace !== 'common')) {
99
+ throw new Error('Projected lighting supports ambient and common-space directional lights only');
100
+ }
101
+ }
102
+ }
103
+
104
+ /** Validate fixed or strictly ordered zoom-stop distances with one unit across every stop. */
105
+ export function getProjectedRoadUnit(value: unknown, positive = true): 'm' | 'px' {
106
+ const parse = (distance: unknown): 'm' | 'px' => {
107
+ const match = typeof distance === 'string' ? /^\s*([+-]?(?:\d+(?:\.\d*)?|\.\d+))\s*(m|px)\s*$/.exec(distance) : null;
108
+ const number = typeof distance === 'number' ? distance : match ? Number(match[1]) : NaN;
109
+ if (!Number.isFinite(number) || (positive && number <= 0)) throw new Error('Projected roads require finite distances and positive widths');
110
+ return match?.[2] === 'px' ? 'px' : 'm';
111
+ };
112
+ if (!Array.isArray(value)) return parse(value);
113
+ if (!value.length) throw new Error('Projected road zoom stops must not be empty');
114
+ let previousZoom = -Infinity;
115
+ let unit: 'm' | 'px' | undefined;
116
+ for (const stop of value) {
117
+ if (!Array.isArray(stop) || stop.length !== 2 || typeof stop[0] !== 'number' ||
118
+ !Number.isFinite(stop[0]) || stop[0] < 0 || stop[0] > 22 || stop[0] <= previousZoom) {
119
+ throw new Error('Projected road zoom stops require strictly increasing levels in [0, 22]');
120
+ }
121
+ previousZoom = stop[0];
122
+ const current = parse(stop[1]);
123
+ if (unit && unit !== current) throw new Error('Projected road zoom stops must keep one unit');
124
+ unit = current;
125
+ }
126
+ if (!unit) throw new Error('Projected road zoom stops must not be empty');
127
+ return unit;
128
+ }
129
+ let clearPreparation: (() => void) | undefined;
130
+ let getPreparationStatistics: (() => MeshPreparationStatistics) | undefined;
131
+ let getProjectionStatistics: (() => MeshProjectionStatistics) | undefined;
132
+
133
+ /** Install the optional CPU projector in this worker without importing math.gl into normal bundles. */
134
+ export function registerMeshProjector(projector: MeshProjector, clear?: () => void,
135
+ getStatistics?: () => MeshPreparationStatistics, getWorkStatistics?: () => MeshProjectionStatistics): void {
136
+ if (typeof projector !== 'function') throw new Error('Mesh projector requires a function');
137
+ if (meshProjector) throw new Error('Mesh projector is already registered');
138
+ meshProjector = projector;
139
+ clearPreparation = clear;
140
+ getPreparationStatistics = getStatistics;
141
+ getProjectionStatistics = getWorkStatistics;
142
+ }
143
+
144
+ /** Release optional worker preparation at reset without importing its implementation in the core. */
145
+ export function clearMeshProjectorPreparation(): void {
146
+ clearPreparation?.();
147
+ }
148
+
149
+ /** Snapshot optional preparation diagnostics without importing the projected worker in the core. */
150
+ export function getMeshProjectorPreparationStatistics(): MeshPreparationStatistics | undefined {
151
+ return getPreparationStatistics?.();
152
+ }
153
+
154
+ /** Snapshot optional projection work without importing projection kernels into renderer core. */
155
+ export function getMeshProjectorWorkStatistics(): MeshProjectionStatistics | undefined {
156
+ return getProjectionStatistics?.();
157
+ }
158
+
159
+ /** Fail explicitly when a projected scene omitted its opt-in worker script. */
160
+ export function projectTileMesh(request: MeshProjectionRequest): ReturnType<MeshProjector> {
161
+ if (!meshProjector) throw new Error('CPU projection requires the projected-basemaps worker script');
162
+ return meshProjector(request);
163
+ }
164
+
165
+ /** Validate serialized scene options before layouts, shaders or meshes are created. */
166
+ export function normalizeProjectedBasemapOptions(value: unknown): ProjectedBasemapOptions {
167
+ if (!value || typeof value !== 'object' || !('type' in value) ||
168
+ !['equal-earth', 'albers', 'equirectangular', 'mercator', 'web-mercator'].includes(String(value.type))) {
169
+ throw new Error('CPU projection requires equal-earth, albers, equirectangular, mercator or web-mercator');
170
+ }
171
+ const maxAngularSpan = 'maxAngularSpan' in value ? value.maxAngularSpan : 4;
172
+ const maxAdditionalVertices = 'maxAdditionalVertices' in value ? value.maxAdditionalVertices : 65536;
173
+ const maxProjectedError = 'maxProjectedError' in value ? value.maxProjectedError : undefined;
174
+ const cacheProjectedMeshes = 'cacheProjectedMeshes' in value ? value.cacheProjectedMeshes : false;
175
+ const allowElevation = 'allowElevation' in value ? value.allowElevation : false;
176
+ if (typeof allowElevation !== 'boolean') throw new Error('CPU projection elevation must be boolean');
177
+ if (typeof cacheProjectedMeshes !== 'boolean') throw new Error('CPU projection mesh caching must be boolean');
178
+ if (maxProjectedError !== undefined && (typeof maxProjectedError !== 'number' || !Number.isFinite(maxProjectedError) || maxProjectedError <= 0)) {
179
+ throw new Error('CPU projection requires a positive finite projected error');
180
+ }
181
+ if (typeof maxAngularSpan !== 'number' || !Number.isFinite(maxAngularSpan) || maxAngularSpan < 1 || maxAngularSpan > 30 ||
182
+ typeof maxAdditionalVertices !== 'number' || !Number.isSafeInteger(maxAdditionalVertices) ||
183
+ maxAdditionalVertices < 0 || maxAdditionalVertices > 262144) {
184
+ throw new Error('CPU projection requires angular span in [1, 30] and vertex budget in [0, 262144]');
185
+ }
186
+ const type = value.type;
187
+ if (type !== 'equal-earth' && type !== 'albers' && type !== 'equirectangular' &&
188
+ type !== 'mercator' && type !== 'web-mercator') throw new Error('Invalid CPU projection');
189
+ return {type, maxAngularSpan, maxAdditionalVertices, ...(maxProjectedError === undefined ? {} : {maxProjectedError}),
190
+ ...(cacheProjectedMeshes ? {cacheProjectedMeshes: true} : {}), ...(allowElevation ? {allowElevation: true} : {})};
191
+ }
@@ -0,0 +1,55 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {MLTLoader} from '@loaders.gl/mlt/bundled';
6
+ import Geo from '../utils/geo';
7
+ import {parseMvtJsonProperties, type ParseJsonOption} from './mvt-properties';
8
+
9
+ const LAYER_PROPERTY = '__tangram_layer';
10
+ type GeoJsonFeature = {
11
+ type: 'Feature';
12
+ geometry: {type: string; coordinates: unknown} | null;
13
+ id?: string | number;
14
+ properties: Record<string, unknown>;
15
+ };
16
+ type GeoJsonFeatureCollection = {type: 'FeatureCollection'; features: GeoJsonFeature[]};
17
+
18
+ /** Parse MLT bytes with loaders.gl and normalize the result for Tangram's MVT source. */
19
+ export function parseMltWithLoaders(
20
+ response: ArrayBuffer | Uint8Array,
21
+ options: {parseJson?: ParseJsonOption} = {}
22
+ ): Record<string, GeoJsonFeatureCollection> {
23
+ const tileBuffer: ArrayBuffer = response instanceof Uint8Array
24
+ ? Uint8Array.from(response).buffer as ArrayBuffer
25
+ : response;
26
+ const parsed = MLTLoader.parseSync!(tileBuffer, {
27
+ mlt: {shape: 'geojson-table', coordinates: 'local', layerProperty: LAYER_PROPERTY}
28
+ }) as {features: GeoJsonFeature[]};
29
+
30
+ const layers: Record<string, GeoJsonFeatureCollection> = {};
31
+ for (const feature of parsed.features) {
32
+ const layerName = String(feature.properties[LAYER_PROPERTY] || '');
33
+ delete feature.properties[LAYER_PROPERTY];
34
+ if (!layerName) {
35
+ continue;
36
+ }
37
+ if (feature.geometry) {
38
+ feature.geometry.coordinates = scaleCoordinates(feature.geometry.coordinates, Geo.tile_scale);
39
+ }
40
+ parseMvtJsonProperties(feature, options.parseJson);
41
+ layers[layerName] ||= {type: 'FeatureCollection', features: []};
42
+ layers[layerName].features.push(feature);
43
+ }
44
+ return layers;
45
+ }
46
+
47
+ function scaleCoordinates(coordinates: unknown, scale: number): unknown {
48
+ if (!Array.isArray(coordinates)) {
49
+ return coordinates;
50
+ }
51
+ if (coordinates.length >= 2 && coordinates.every(value => typeof value === 'number')) {
52
+ return coordinates.map(value => (value as number) * scale);
53
+ }
54
+ return coordinates.map(value => scaleCoordinates(value, scale));
55
+ }
@@ -0,0 +1,20 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ declare module 'pbf' {
6
+ export default class Pbf {
7
+ constructor (buffer: Uint8Array);
8
+ }
9
+ }
10
+
11
+ declare module '@mapbox/vector-tile' {
12
+ export class VectorTile {
13
+ layers: Record<string, unknown>;
14
+ constructor (pbf: unknown);
15
+ }
16
+
17
+ export class VectorTileFeature {
18
+ static types: Record<number, string>;
19
+ }
20
+ }