@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,150 @@
1
+ // Tangram
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) 2013-2016 Brett Camper and Mapzen
4
+ // Copyright (c) 2026 vis.gl contributors
5
+
6
+ import Pbf from 'pbf';
7
+ import {VectorTile, VectorTileFeature} from '@mapbox/vector-tile';
8
+ import Geo from '../utils/geo';
9
+ import {parseMvtJsonProperties} from './mvt-properties';
10
+
11
+ interface MvtOptions {
12
+ parseJson?: boolean | readonly string[];
13
+ }
14
+
15
+ interface MvtPoint {
16
+ x: number;
17
+ y: number;
18
+ }
19
+
20
+ interface MvtFeature {
21
+ id?: string | number;
22
+ type: number;
23
+ properties: Record<string, unknown>;
24
+ loadGeometry(): MvtPoint[][];
25
+ }
26
+
27
+ interface MvtLayer {
28
+ extent: number;
29
+ length: number;
30
+ feature(index: number): MvtFeature;
31
+ }
32
+
33
+ /** Mapbox Vector Tile object produced by the legacy vector-tile decoder. */
34
+ export interface MvtTile {
35
+ layers: Record<string, MvtLayer>;
36
+ }
37
+
38
+ interface TangramGeometry {
39
+ type: string;
40
+ coordinates: [number, number] | [number, number][] | [number, number][][] | [number, number][][][];
41
+ }
42
+
43
+ interface TangramFeature {
44
+ type: 'Feature';
45
+ geometry: TangramGeometry | null;
46
+ id?: string | number;
47
+ properties: Record<string, unknown>;
48
+ }
49
+
50
+ export interface TangramFeatureCollection {
51
+ type: 'FeatureCollection';
52
+ features: TangramFeature[];
53
+ }
54
+
55
+ /** Parse MVT bytes using Tangram's established Mapbox vector-tile implementation. */
56
+ export function parseMvtWithLegacy(response: ArrayBuffer | ArrayLike<number>, options: MvtOptions = {}): Record<string, TangramFeatureCollection> {
57
+ const tile = new VectorTile(new Pbf(new Uint8Array(response))) as unknown as MvtTile;
58
+ return convertMvtTileWithLegacy(tile, options);
59
+ }
60
+
61
+ /** Convert a Mapbox VectorTile object to Tangram's layer-indexed GeoJSON shape. */
62
+ export function convertMvtTileWithLegacy(tile: MvtTile, options: MvtOptions = {}): Record<string, TangramFeatureCollection> {
63
+ const layers: Record<string, TangramFeatureCollection> = {};
64
+ for (const layerName in tile.layers) {
65
+ const layer = tile.layers[layerName];
66
+ const scale = Geo.tile_scale / layer.extent;
67
+ const layerGeoJson: TangramFeatureCollection = {type: 'FeatureCollection', features: []};
68
+
69
+ for (let featureIndex = 0; featureIndex < layer.length; featureIndex++) {
70
+ const feature = layer.feature(featureIndex);
71
+ const featureGeoJson: TangramFeature = {
72
+ type: 'Feature', geometry: {type: '', coordinates: []}, id: feature.id, properties: feature.properties
73
+ };
74
+ parseMvtJsonProperties(featureGeoJson, options.parseJson);
75
+
76
+ const coordinates: [number, number][][] = feature.loadGeometry().map(ring =>
77
+ ring.map(point => [point.x * scale, point.y * scale])
78
+ );
79
+ let geometry = featureGeoJson.geometry;
80
+ if (!geometry) {
81
+ continue;
82
+ }
83
+ geometry.coordinates = coordinates;
84
+
85
+ if (VectorTileFeature.types[feature.type] === 'Point') {
86
+ if (coordinates.length === 1) {
87
+ geometry.type = 'Point';
88
+ geometry.coordinates = geometry.coordinates[0][0];
89
+ }
90
+ else {
91
+ geometry.type = 'MultiPoint';
92
+ geometry.coordinates = geometry.coordinates[0];
93
+ }
94
+ }
95
+ else if (VectorTileFeature.types[feature.type] === 'LineString') {
96
+ if (coordinates.length === 1) {
97
+ geometry.type = 'LineString';
98
+ geometry.coordinates = geometry.coordinates[0];
99
+ }
100
+ else {
101
+ geometry.type = 'MultiLineString';
102
+ }
103
+ }
104
+ else if (VectorTileFeature.types[feature.type] === 'Polygon') {
105
+ geometry = decodeMultiPolygon(geometry);
106
+ }
107
+
108
+ featureGeoJson.geometry = geometry;
109
+ layerGeoJson.features.push(featureGeoJson);
110
+ }
111
+ layers[layerName] = layerGeoJson;
112
+ }
113
+ return layers;
114
+ }
115
+
116
+ /** Decode flattened MVT polygon rings into Polygon or MultiPolygon coordinates. */
117
+ export function decodeMultiPolygon(geometry: TangramGeometry): TangramGeometry | null {
118
+ const polygons: [number, number][][][] = [];
119
+ let polygon: [number, number][][] = [];
120
+ let outerWinding: 'CW' | 'CCW' | null = null;
121
+ const rings = geometry.coordinates as [number, number][][];
122
+ for (const ring of rings) {
123
+ const winding = Geo.ringWinding(ring);
124
+ if (winding == null) {
125
+ continue;
126
+ }
127
+ outerWinding = outerWinding || winding;
128
+ if (winding === outerWinding && polygon.length > 0) {
129
+ polygons.push(polygon);
130
+ polygon = [];
131
+ }
132
+ polygon.push(ring);
133
+ }
134
+ if (polygon.length > 0) {
135
+ polygons.push(polygon);
136
+ }
137
+
138
+ if (polygons.length === 1) {
139
+ geometry.type = 'Polygon';
140
+ geometry.coordinates = polygons[0];
141
+ }
142
+ else if (polygons.length > 1) {
143
+ geometry.type = 'MultiPolygon';
144
+ geometry.coordinates = polygons;
145
+ }
146
+ else {
147
+ return null;
148
+ }
149
+ return geometry;
150
+ }
@@ -0,0 +1,53 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {MVTGeoJSONLoaderWithParser} from '@loaders.gl/mvt/mvt-geojson-loader';
6
+ import Geo from '../utils/geo';
7
+ import {parseMvtJsonProperties, type ParseJsonOption} from './mvt-properties';
8
+
9
+ const LAYER_PROPERTY = '__tangram_layer';
10
+ type GeoJsonGeometry = {type: string; coordinates: unknown};
11
+ type GeoJsonFeature = {
12
+ type: 'Feature';
13
+ geometry: GeoJsonGeometry | null;
14
+ id?: string | number;
15
+ properties: Record<string, unknown>;
16
+ };
17
+ type GeoJsonFeatureCollection = {type: 'FeatureCollection'; features: GeoJsonFeature[]};
18
+
19
+ /** Parse MVT bytes with loaders.gl and normalize them to Tangram's layer-indexed GeoJSON shape. */
20
+ export function parseMvtWithLoaders(
21
+ response: ArrayBuffer | Uint8Array,
22
+ options: {parseJson?: ParseJsonOption} = {}
23
+ ): Record<string, GeoJsonFeatureCollection> {
24
+ const tileBuffer: ArrayBuffer = response instanceof Uint8Array
25
+ ? Uint8Array.from(response).buffer as ArrayBuffer
26
+ : response;
27
+ const parsed = MVTGeoJSONLoaderWithParser.parseSync(tileBuffer, {
28
+ mvt: {coordinates: 'local', layerProperty: LAYER_PROPERTY}
29
+ }) as GeoJsonFeatureCollection;
30
+
31
+ const layers: Record<string, GeoJsonFeatureCollection> = {};
32
+ for (const feature of parsed.features) {
33
+ const layerName = String(feature.properties[LAYER_PROPERTY]);
34
+ delete feature.properties[LAYER_PROPERTY];
35
+ if (feature.geometry) {
36
+ feature.geometry.coordinates = scaleCoordinates(feature.geometry.coordinates, Geo.tile_scale);
37
+ }
38
+ parseMvtJsonProperties(feature, options.parseJson);
39
+ layers[layerName] ||= {type: 'FeatureCollection', features: []};
40
+ layers[layerName].features.push(feature);
41
+ }
42
+ return layers;
43
+ }
44
+
45
+ function scaleCoordinates(coordinates: unknown, scale: number): unknown {
46
+ if (!Array.isArray(coordinates)) {
47
+ return coordinates;
48
+ }
49
+ if (coordinates.length >= 2 && coordinates.every(value => typeof value === 'number')) {
50
+ return coordinates.map(value => value * scale);
51
+ }
52
+ return coordinates.map(value => scaleCoordinates(value, scale));
53
+ }
@@ -0,0 +1,48 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {ParseJsonOption} from './mvt-properties';
6
+
7
+ /** Tangram-compatible decoded MVT layers in local tile coordinates. */
8
+ export type MvtLayers = Record<string, {
9
+ type: 'FeatureCollection';
10
+ features: unknown[];
11
+ }>;
12
+
13
+ /** Signature required of a registered MVT decoder. */
14
+ export type MvtDecoder = (
15
+ response: ArrayBuffer | Uint8Array,
16
+ options: {parseJson?: ParseJsonOption}
17
+ ) => MvtLayers;
18
+
19
+ const mvtDecoders = new Map<string, MvtDecoder>();
20
+
21
+ /** Register an MVT decoder and return a function that removes that registration. */
22
+ export function registerMvtDecoder(name: string, decoder: MvtDecoder): () => void {
23
+ if (!name || typeof decoder !== 'function') {
24
+ throw new Error('MVT decoder registration requires a name and decoder function');
25
+ }
26
+ if (mvtDecoders.has(name)) {
27
+ throw new Error(`MVT decoder '${name}' is already registered`);
28
+ }
29
+ mvtDecoders.set(name, decoder);
30
+ return () => {
31
+ if (mvtDecoders.get(name) === decoder) {
32
+ mvtDecoders.delete(name);
33
+ }
34
+ };
35
+ }
36
+
37
+ /** Decode one MVT payload with the selected registered decoder. */
38
+ export function parseMvt(
39
+ name: string,
40
+ response: ArrayBuffer | Uint8Array,
41
+ options: {parseJson?: ParseJsonOption}
42
+ ): MvtLayers {
43
+ const decoder = mvtDecoders.get(name);
44
+ if (!decoder) {
45
+ throw new Error(`MVT decoder '${name}' is not registered in this worker`);
46
+ }
47
+ return decoder(response, options);
48
+ }
@@ -0,0 +1,36 @@
1
+ // Tangram
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) 2013-2016 Brett Camper and Mapzen
4
+ // Copyright (c) 2026 vis.gl contributors
5
+
6
+ /** Tangram's supported `parse_json` source option. */
7
+ export type ParseJsonOption = boolean | readonly string[] | undefined;
8
+
9
+ type FeatureWithProperties = {properties: Record<string, unknown>};
10
+
11
+ /** Parse JSON-encoded MVT properties according to Tangram's `parse_json` option. */
12
+ export function parseMvtJsonProperties(
13
+ feature: FeatureWithProperties,
14
+ parseJson: ParseJsonOption
15
+ ): void {
16
+ if (!parseJson) {
17
+ return;
18
+ }
19
+
20
+ const properties = feature.properties;
21
+ const propertyNames = Array.isArray(parseJson) ? parseJson : Object.keys(properties);
22
+ for (const propertyName of propertyNames) {
23
+ const value = properties[propertyName];
24
+ if (typeof value !== 'string') {
25
+ continue;
26
+ }
27
+ if (parseJson === true && value[0] !== '{' && value[0] !== '[') {
28
+ continue;
29
+ }
30
+ try {
31
+ properties[propertyName] = JSON.parse(value);
32
+ } catch {
33
+ // Preserve the original value when it is not valid JSON.
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,72 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ /** Tile coordinates passed to a registered MVT tile provider. */
6
+ export type MvtTileIndex = {x: number; y: number; z: number};
7
+
8
+ /** Loads encoded vector-tile bytes for a Tangram source URL and tile index. */
9
+ export type MvtTileProvider = (
10
+ url: string,
11
+ tileIndex: MvtTileIndex
12
+ ) => Promise<ArrayBuffer | Uint8Array | null> | ArrayBuffer | Uint8Array | null;
13
+
14
+ /** Metadata supplied by TileJSON or an archive without depending on a renderer. */
15
+ export interface MvtProviderMetadata {
16
+ /** Container format, distinct from the encoded tile MIME type. */
17
+ format?: string;
18
+ /** Encoded tile MIME type, when known. */
19
+ tileMIMEType?: string;
20
+ /** Provider's human-readable name. */
21
+ name?: string;
22
+ /** Required provider credits, never interpreted as trusted HTML. */
23
+ attributions?: readonly string[];
24
+ /** Lowest advertised tile level. */
25
+ minZoom?: number;
26
+ /** Highest advertised tile level. */
27
+ maxZoom?: number;
28
+ /** Geographic extent, including west greater than east at the antimeridian. */
29
+ boundingBox?: readonly [readonly [number, number], readonly [number, number]];
30
+ }
31
+
32
+ /** Source-owned handle for archive metadata, cancellable requests and explicit teardown. */
33
+ export interface MvtTileProviderSource {
34
+ /** Acquire encoded bytes; a canceled request must not publish a decoded tile. */
35
+ getTile(index: MvtTileIndex, signal?: AbortSignal): Promise<ArrayBuffer | Uint8Array | null>;
36
+ /** Resolve source capabilities and credits without fetching tile payloads. */
37
+ getMetadata?(): Promise<MvtProviderMetadata>;
38
+ /** Cancel pending transport work and release this source's archive/cache references. */
39
+ dispose(): void;
40
+ }
41
+
42
+ /** Factory isolates archive ownership by Tangram source instance rather than global URL. */
43
+ export interface MvtTileProviderFactory {
44
+ /** Create a handle for the resolved URL and optional authenticated request headers. */
45
+ createSource(url: string, options: {headers?: Record<string, string>}): MvtTileProviderSource;
46
+ }
47
+
48
+ /** Existing stateless functions remain valid; factories add optional lifecycle capabilities. */
49
+ export type MvtTileProviderRegistration = MvtTileProvider | MvtTileProviderFactory;
50
+
51
+ const mvtTileProviders = new Map<string, MvtTileProviderRegistration>();
52
+
53
+ /** Register an MVT tile provider and return a function that removes that registration. */
54
+ export function registerMvtTileProvider(name: string, provider: MvtTileProviderRegistration): () => void {
55
+ if (!name || !(typeof provider === 'function' || provider && typeof provider.createSource === 'function')) {
56
+ throw new Error('MVT tile provider registration requires a name and provider function or source factory');
57
+ }
58
+ if (mvtTileProviders.has(name)) {
59
+ throw new Error(`MVT tile provider '${name}' is already registered`);
60
+ }
61
+ mvtTileProviders.set(name, provider);
62
+ return () => {
63
+ if (mvtTileProviders.get(name) === provider) {
64
+ mvtTileProviders.delete(name);
65
+ }
66
+ };
67
+ }
68
+
69
+ /** Find a registered MVT tile provider by name. */
70
+ export function getMvtTileProvider(name: string): MvtTileProviderRegistration | undefined {
71
+ return mvtTileProviders.get(name);
72
+ }
@@ -0,0 +1,75 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {PMTilesSourceLoader} from '@loaders.gl/pmtiles';
6
+ import type {MvtProviderMetadata, MvtTileProviderFactory, MvtTileProviderSource} from './mvt-tile-provider';
7
+
8
+ type PMTilesTileSource = ReturnType<typeof PMTilesSourceLoader.createDataSource>;
9
+
10
+ /** PMTiles transport factory; every Tangram source owns exactly one archive handle. */
11
+ export const pmtilesTileProvider: MvtTileProviderFactory = {createSource: createPMTilesSource};
12
+
13
+ /** Acquire archive metadata and raw tile bytes without a global URL cache or a decoder switch. */
14
+ export function createPMTilesSource(url: string, options: {headers?: Record<string, string>} = {}): MvtTileProviderSource {
15
+ const lifetime = new AbortController();
16
+ let source: PMTilesTileSource | undefined = PMTilesSourceLoader.createDataSource(url, {
17
+ core: {fetch: async (requestUrl: string, requestOptions: RequestInit = {}) => {
18
+ const controller = new AbortController();
19
+ const abort = (): void => controller.abort();
20
+ const requestSignal = requestOptions.signal;
21
+ if (lifetime.signal.aborted || requestSignal?.aborted) controller.abort();
22
+ lifetime.signal.addEventListener('abort', abort, {once: true});
23
+ requestSignal?.addEventListener('abort', abort, {once: true});
24
+ try {
25
+ const headers = new Headers(requestOptions.headers);
26
+ for (const [name, value] of Object.entries(options.headers ?? {})) headers.set(name, value);
27
+ const response = await fetch(requestUrl, {...requestOptions, headers, signal: controller.signal});
28
+ // Range transports consume the body after fetch resolves. Keep the
29
+ // lifetime signal bound until that body finishes, not just headers.
30
+ const bytes = await response.arrayBuffer();
31
+ return new Response(bytes, {status: response.status, statusText: response.statusText, headers: response.headers});
32
+ } finally {
33
+ lifetime.signal.removeEventListener('abort', abort);
34
+ requestSignal?.removeEventListener('abort', abort);
35
+ }
36
+ }}
37
+ });
38
+ // loaders.gl starts metadata eagerly; retain the rejection for getMetadata(),
39
+ // but do not emit an unhandled rejection when a source is disposed before use.
40
+ const metadata = source.metadata;
41
+ void metadata.catch(() => {});
42
+ let capabilities: Promise<MvtProviderMetadata> | undefined;
43
+ return {
44
+ async getTile(index, signal) {
45
+ if (!source || signal?.aborted) throw createAbortError();
46
+ const bytes = await source.getTile({...index, signal});
47
+ if (!source || signal?.aborted) throw createAbortError();
48
+ return bytes;
49
+ },
50
+ async getMetadata() {
51
+ if (!source) throw createAbortError();
52
+ if (!capabilities) {
53
+ // alpha.8 drops credits in both normalized metadata surfaces.
54
+ // Keep the raw archive JSON as well, without changing its parser.
55
+ capabilities = Promise.all([metadata, source.pmtiles.getMetadata()]).then(([value, raw]) => {
56
+ const rawAttribution = raw && typeof raw === 'object' && 'attribution' in raw ? raw.attribution : undefined;
57
+ const nestedAttribution = value.tilejson?.htmlAttribution;
58
+ return {...value, attributions: [...new Set([...(value.attributions ?? []),
59
+ ...[nestedAttribution, rawAttribution].filter((credit): credit is string => typeof credit === 'string')])]};
60
+ });
61
+ }
62
+ const value = await capabilities;
63
+ if (!source) throw createAbortError();
64
+ return value;
65
+ },
66
+ dispose() { lifetime.abort(); source = undefined; }
67
+ };
68
+ }
69
+
70
+ /** Cancellation stays distinguishable from missing archive content. */
71
+ function createAbortError(): Error {
72
+ const error = new Error('PMTiles source request aborted');
73
+ error.name = 'AbortError';
74
+ return error;
75
+ }
@@ -0,0 +1,79 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {ProjectionEngineOptions, ProjectionEngine, ProjectionEngineTransform} from '../types';
6
+ import {normalizeProjectedBasemapOptions} from './mesh-projector';
7
+ import type {ProjectedBasemapOptions} from './mesh-projector';
8
+ import {ProjectionBatchExecutor} from './projection-batch-executor';
9
+ import type {ProjectionExecutionOptions, ProjectionExecutionStatistics} from './projection-batch-executor';
10
+
11
+ /** Tangram's projected common-space units per projected meter. */
12
+ export const PROJECTED_COMMON_SCALE = 256 / 6378137;
13
+
14
+ /** Compile the same geographic-degree to north-positive meter contract on either thread. */
15
+ export function getProjectedCoordinateOptions(type: ProjectedBasemapOptions['type']): ProjectionEngineOptions {
16
+ normalizeProjectedBasemapOptions({type});
17
+ const parameters = type === 'equal-earth' ? '+proj=eqearth +lon_0=0' :
18
+ type === 'albers' ? '+proj=aea +lon_0=-96 +lat_0=37.5 +lat_1=29.5 +lat_2=45.5' :
19
+ type === 'mercator' || type === 'web-mercator' ? '+proj=merc +lon_0=0 +k_0=1 +over' :
20
+ '+proj=eqc +lon_0=0 +lat_0=0 +lat_ts=0';
21
+ const geometry = type === 'mercator' ? '+ellps=WGS84' : '+R=6378137';
22
+ return {from: `+proj=longlat ${geometry}`, to: `${parameters} ${geometry} +units=m`};
23
+ }
24
+
25
+ /** Reject transform instances and malformed factories before allocating renderer resources. */
26
+ export function validateProjectionEngine(engine: ProjectionEngine | undefined): void {
27
+ if (engine !== undefined && (!engine || typeof engine.createProjection !== 'function' ||
28
+ typeof engine.createProjectionAsync !== 'function')) {
29
+ throw new Error('projectionEngine must implement the math.gl ProjectionEngine factory contract');
30
+ }
31
+ }
32
+
33
+ /** Scene-owned transforms; the caller owns the engine and its registrations/grids. */
34
+ export class HostProjectionEngineAdapter {
35
+ /** Independent asynchronous compilation per CRS pair, shared across meshes and workers. */
36
+ private readonly transforms = new Map<ProjectedBasemapOptions['type'], Promise<ProjectionEngineTransform>>();
37
+ /** Prevent late worker batches from publishing after scene teardown. */
38
+ private disposed = false;
39
+ /** Cooperative execution and cancellation for scene-owned host requests. */
40
+ private readonly executor: ProjectionBatchExecutor;
41
+
42
+ /** Retain, but never mutate or dispose, the supplied factory. */
43
+ constructor(private readonly engine: ProjectionEngine, options: ProjectionExecutionOptions = {}) {
44
+ validateProjectionEngine(engine);
45
+ this.executor = new ProjectionBatchExecutor(options);
46
+ }
47
+
48
+ /** Project one packed longitude/latitude batch to common coordinates without modifying input. */
49
+ async projectPositions(coordinates: Float64Array, type: ProjectedBasemapOptions['type'], signal?: AbortSignal): Promise<Float64Array> {
50
+ if (this.disposed) throw new Error('Projection engine adapter is disposed');
51
+ const options = getProjectedCoordinateOptions(type);
52
+ if (!(coordinates instanceof Float64Array) || coordinates.length % 2 !== 0 ||
53
+ !coordinates.every((value, index) => Number.isFinite(value) && Math.abs(value) <=
54
+ (index % 2 === 0 ? 180 : 85.0511287798066))) {
55
+ throw new Error('Projection engine requires packed finite geographic degrees within the tile domain');
56
+ }
57
+ return this.executor.execute(coordinates, () => {
58
+ let pending = this.transforms.get(type);
59
+ if (!pending) {
60
+ pending = Promise.resolve().then(() => this.engine.createProjectionAsync(options));
61
+ this.transforms.set(type, pending);
62
+ pending.catch(() => {if (this.transforms.get(type) === pending) this.transforms.delete(type);});
63
+ }
64
+ return pending;
65
+ }, {signal, scale: PROJECTED_COMMON_SCALE, maxAbsoluteValue: 3.4028234663852886e38});
66
+ }
67
+
68
+ /** Detached host execution counters, separate from mesh and decoded-source residency. */
69
+ getStatistics(): ProjectionExecutionStatistics {
70
+ return this.executor.getStatistics();
71
+ }
72
+
73
+ /** Release scene-owned transforms without taking ownership of the injected factory. */
74
+ dispose(): void {
75
+ this.disposed = true;
76
+ this.executor.dispose();
77
+ this.transforms.clear();
78
+ }
79
+ }
@@ -0,0 +1,140 @@
1
+ // tangram-layers
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {ProjectionEngineTransform} from '../types';
6
+
7
+ /** Cooperative host-side execution policy; does not limit tile loading or mesh-build capacity. */
8
+ export interface ProjectionExecutionOptions {
9
+ /** Maximum coordinate pairs per synchronous kernel call, 1–65,536; defaults to 4,096. */
10
+ maxBatchPositions?: number;
11
+ }
12
+
13
+ /** Per-operation cancellation and output coordinate contract. */
14
+ export interface ProjectionBatchRequestOptions {
15
+ /** Cancel this operation without cancelling shared compilation or other requests. */
16
+ signal?: AbortSignal;
17
+ /** Multiply kernel output by this finite scale; defaults to one. */
18
+ scale?: number;
19
+ /** Optional positive finite output magnitude bound, checked after scaling. */
20
+ maxAbsoluteValue?: number;
21
+ /** Preserve nonfinite inverse-domain sentinels for caller validation; defaults to false. */
22
+ allowNonfinite?: boolean;
23
+ }
24
+
25
+ /** Detached cumulative host work; counts kernel submissions, not worker/GPU memory or wall-clock time. */
26
+ export interface ProjectionExecutionStatistics {
27
+ /** Requests still awaiting compilation or cooperative execution. */
28
+ activeRequests: number;
29
+ /** Requests that returned a fully validated output. */
30
+ completedRequests: number;
31
+ /** Requests rejected by compilation, kernels or invalid output. */
32
+ failedRequests: number;
33
+ /** Requests cancelled by a signal or executor teardown. */
34
+ cancelledRequests: number;
35
+ /** Coordinate pairs submitted to kernels, including requests that subsequently fail/cancel. */
36
+ submittedPositions: number;
37
+ /** Synchronous kernel calls attempted, including failed calls. */
38
+ batches: number;
39
+ /** Event-loop yields scheduled between chunks. */
40
+ yieldCount: number;
41
+ /** Configured per-call coordinate pair limit. */
42
+ maxBatchPositions: number;
43
+ }
44
+
45
+ /** Validate policy before allocating a renderer, worker endpoint or navigation helper. */
46
+ export function normalizeProjectionExecutionOptions(options: ProjectionExecutionOptions = {}): Required<ProjectionExecutionOptions> {
47
+ const maxBatchPositions = options.maxBatchPositions === undefined ? 4096 : options.maxBatchPositions;
48
+ if (!Number.isSafeInteger(maxBatchPositions) || maxBatchPositions < 1 || maxBatchPositions > 65536) {
49
+ throw new Error('Projection execution requires maxBatchPositions in [1, 65536]');
50
+ }
51
+ return {maxBatchPositions};
52
+ }
53
+
54
+ /** Run independent packed batches with detached input, cooperative yields and prompt cancellation. */
55
+ export class ProjectionBatchExecutor {
56
+ /** Cancels pending operations without taking ownership of transforms or factories. */
57
+ private readonly lifetime = new AbortController();
58
+ /** Detached snapshots are returned instead of exposing these mutable counters. */
59
+ private readonly statistics: ProjectionExecutionStatistics;
60
+
61
+ /** Capture an immutable per-kernel-call execution policy. */
62
+ constructor(options: ProjectionExecutionOptions = {}) {
63
+ this.statistics = {...normalizeProjectionExecutionOptions(options), activeRequests: 0,
64
+ completedRequests: 0, failedRequests: 0, cancelledRequests: 0, submittedPositions: 0,
65
+ batches: 0, yieldCount: 0};
66
+ }
67
+
68
+ /** Capture input before compilation; abort only this output, never a shared factory promise. */
69
+ async execute(coordinates: Float64Array, getTransform: () => Promise<ProjectionEngineTransform>,
70
+ options: ProjectionBatchRequestOptions = {}): Promise<Float64Array> {
71
+ const scale = options.scale ?? 1;
72
+ const maximum = options.maxAbsoluteValue ?? Infinity;
73
+ const allowNonfinite = options.allowNonfinite ?? false;
74
+ if (!(coordinates instanceof Float64Array) || coordinates.length % 2 !== 0 ||
75
+ !coordinates.every(Number.isFinite) || !Number.isFinite(scale) ||
76
+ maximum <= 0 || (options.maxAbsoluteValue !== undefined && !Number.isFinite(maximum))) {
77
+ throw new Error('Projection batches require finite coordinate pairs, scale and output bounds');
78
+ }
79
+ const signals = [this.lifetime.signal, ...(options.signal ? [options.signal] : [])];
80
+ const cancelled = () => signals.some(signal => signal.aborted);
81
+ const abortError = () => {const error = new Error(this.lifetime.signal.aborted ? 'Projection operation disposed' : 'Projection operation aborted'); error.name = 'AbortError'; return error;};
82
+ if (cancelled()) throw abortError();
83
+ const result = coordinates.slice();
84
+ let rejectCancellation: (error: Error) => void = () => {};
85
+ const cancellation = new Promise<never>((_resolve, reject) => {rejectCancellation = reject;});
86
+ const onAbort = () => rejectCancellation(abortError());
87
+ for (const signal of signals) signal.addEventListener('abort', onAbort, {once: true});
88
+ this.statistics.activeRequests++;
89
+ try {
90
+ const work = async () => {
91
+ // Stop awaiting a caller-owned compilation on abort; do not retain this
92
+ // request's coordinate snapshot until an unrelated factory settles.
93
+ const transform = await Promise.race([getTransform(), cancellation]);
94
+ if (cancelled()) throw abortError();
95
+ const stride = this.statistics.maxBatchPositions * 2;
96
+ for (let offset = 0; offset < result.length; offset += stride) {
97
+ if (cancelled()) throw abortError();
98
+ const chunk = result.subarray(offset, Math.min(result.length, offset + stride));
99
+ this.statistics.batches++;
100
+ this.statistics.submittedPositions += chunk.length / 2;
101
+ transform.projectFlatSync(chunk, 2);
102
+ for (let index = 0; index < chunk.length; index++) {
103
+ chunk[index] *= scale;
104
+ if ((!allowNonfinite && !Number.isFinite(chunk[index])) || Math.abs(chunk[index]) > maximum) {
105
+ throw new Error(maximum === Infinity ?
106
+ 'Projection engine produced nonfinite output coordinates' : 'Projection engine produced an invalid Float32 common position');
107
+ }
108
+ }
109
+ if (offset + stride < result.length) {
110
+ this.statistics.yieldCount++;
111
+ await new Promise<void>(resolve => setTimeout(resolve, 0));
112
+ }
113
+ }
114
+ if (cancelled()) throw abortError();
115
+ return result;
116
+ };
117
+ const output = await Promise.race([work(), cancellation]);
118
+ if (cancelled()) throw abortError();
119
+ this.statistics.completedRequests++;
120
+ return output;
121
+ } catch (error) {
122
+ if (cancelled()) this.statistics.cancelledRequests++;
123
+ else this.statistics.failedRequests++;
124
+ throw error;
125
+ } finally {
126
+ this.statistics.activeRequests--;
127
+ for (const signal of signals) signal.removeEventListener('abort', onAbort);
128
+ }
129
+ }
130
+
131
+ /** Return detached counters; no factory objects, coordinate buffers or mutable references escape. */
132
+ getStatistics(): ProjectionExecutionStatistics {
133
+ return {...this.statistics};
134
+ }
135
+
136
+ /** Reject pending requests immediately; synchronous kernels cannot be interrupted mid-call. */
137
+ dispose(): void {
138
+ this.lifetime.abort();
139
+ }
140
+ }
@@ -0,0 +1,16 @@
1
+ // Tangram
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) 2013-2016 Brett Camper and Mapzen
4
+ // Copyright (c) 2026 vis.gl contributors
5
+
6
+ import yaml from 'js-yaml';
7
+
8
+ /**
9
+ * Parse a Tangram scene using the renderer's established js-yaml implementation.
10
+ * @param {string} source YAML scene source.
11
+ * @returns Parsed YAML value; scene interpretation and schema validation are separate boundaries.
12
+ */
13
+ export function parseSceneYamlLegacy(source: string): unknown {
14
+ // Existing Tangram behavior allows duplicate keys despite the YAML specification.
15
+ return yaml.safeLoad(source, { json: true });
16
+ }