leaflet-city-mapper 0.0.5 → 0.0.6

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.
@@ -214,6 +214,14 @@
214
214
  }
215
215
  });
216
216
 
217
+ // src/weakmaps/cameraHandlersByMap.js
218
+ var cameraHandlersByMap;
219
+ var init_cameraHandlersByMap = __esm({
220
+ "src/weakmaps/cameraHandlersByMap.js"() {
221
+ cameraHandlersByMap = /* @__PURE__ */ new WeakMap();
222
+ }
223
+ });
224
+
217
225
  // src/weakmaps/rotateControlByMap.js
218
226
  var rotateControlByMap;
219
227
  var init_rotateControlByMap = __esm({
@@ -222,6 +230,22 @@
222
230
  }
223
231
  });
224
232
 
233
+ // src/weakmaps/touchPitchByMap.js
234
+ var touchPitchByMap;
235
+ var init_touchPitchByMap = __esm({
236
+ "src/weakmaps/touchPitchByMap.js"() {
237
+ touchPitchByMap = /* @__PURE__ */ new WeakMap();
238
+ }
239
+ });
240
+
241
+ // src/weakmaps/touchRotateByMap.js
242
+ var touchRotateByMap;
243
+ var init_touchRotateByMap = __esm({
244
+ "src/weakmaps/touchRotateByMap.js"() {
245
+ touchRotateByMap = /* @__PURE__ */ new WeakMap();
246
+ }
247
+ });
248
+
225
249
  // src/globals/Compass.js
226
250
  var Compass;
227
251
  var init_Compass = __esm({
@@ -1032,22 +1056,6 @@
1032
1056
  }
1033
1057
  });
1034
1058
 
1035
- // src/weakmaps/touchPitchByMap.js
1036
- var touchPitchByMap;
1037
- var init_touchPitchByMap = __esm({
1038
- "src/weakmaps/touchPitchByMap.js"() {
1039
- touchPitchByMap = /* @__PURE__ */ new WeakMap();
1040
- }
1041
- });
1042
-
1043
- // src/weakmaps/touchRotateByMap.js
1044
- var touchRotateByMap;
1045
- var init_touchRotateByMap = __esm({
1046
- "src/weakmaps/touchRotateByMap.js"() {
1047
- touchRotateByMap = /* @__PURE__ */ new WeakMap();
1048
- }
1049
- });
1050
-
1051
1059
  // src/controls/startCameraTouch.js
1052
1060
  function startCameraTouch(event) {
1053
1061
  const state = touchByContainer.get(this), { map, container } = state;
@@ -1242,14 +1250,6 @@
1242
1250
  }
1243
1251
  });
1244
1252
 
1245
- // src/weakmaps/cameraHandlersByMap.js
1246
- var cameraHandlersByMap;
1247
- var init_cameraHandlersByMap = __esm({
1248
- "src/weakmaps/cameraHandlersByMap.js"() {
1249
- cameraHandlersByMap = /* @__PURE__ */ new WeakMap();
1250
- }
1251
- });
1252
-
1253
1253
  // src/controls/addCameraHandler.js
1254
1254
  function addCameraHandler(map, name, Type) {
1255
1255
  let handlers = cameraHandlersByMap.get(map);
@@ -1282,6 +1282,8 @@
1282
1282
  addCameraHandler(map, "compassBearing", Compass.value);
1283
1283
  addCameraHandler(map, "touchRotate", TouchMode.value);
1284
1284
  addCameraHandler(map, "touchPitch", TouchMode.value);
1285
+ touchRotateByMap.set(map, cameraHandlersByMap.get(map).get("touchRotate"));
1286
+ touchPitchByMap.set(map, cameraHandlersByMap.get(map).get("touchPitch"));
1285
1287
  const container = map.getContainer();
1286
1288
  const state = (
1287
1289
  /* alloc */
@@ -1296,6 +1298,7 @@
1296
1298
  container.addEventListener("touchcancel", endCameraTouchEvent, true);
1297
1299
  window.addEventListener("blur", endAllCameraTouches);
1298
1300
  map.on("cameratouchchange", changeCameraTouch, map);
1301
+ changeCameraTouch.call(map);
1299
1302
  if (map.options.rotateControl) {
1300
1303
  rotateControlByMap.set(
1301
1304
  map,
@@ -1309,7 +1312,10 @@
1309
1312
  var init_cameraControls = __esm({
1310
1313
  "src/controls/cameraControls.js"() {
1311
1314
  init_cameraTouchStateByMap();
1315
+ init_cameraHandlersByMap();
1312
1316
  init_rotateControlByMap();
1317
+ init_touchPitchByMap();
1318
+ init_touchRotateByMap();
1313
1319
  init_L();
1314
1320
  init_Compass();
1315
1321
  init_ContainerResize();
@@ -1707,16 +1713,16 @@
1707
1713
  }
1708
1714
 
1709
1715
  // src/terrain/getElevation.js
1710
- function getElevation(layer, location) {
1716
+ function getElevation(layer, location2) {
1711
1717
  if (!layer._map || !terrainReadyByLayer.get(layer)) return null;
1712
- const point = location instanceof L.Point ? location : layer._map.project(location, terrainZoomByLayer.get(layer));
1718
+ const point = location2 instanceof L.Point ? location2 : layer._map.project(location2, terrainZoomByLayer.get(layer));
1713
1719
  return bridgeElevation(point, bridgesByLayer.get(layer)) ?? terrainElevation(point, terrainTilesByLayer.get(layer));
1714
1720
  }
1715
1721
 
1716
1722
  // src/terrain/mapElevationOf.js
1717
- function mapElevationOf(map, location) {
1723
+ function mapElevationOf(map, location2) {
1718
1724
  const terrain = terrain3DByMap.get(map);
1719
- return terrain && getElevation(terrain, location);
1725
+ return terrain && getElevation(terrain, location2);
1720
1726
  }
1721
1727
 
1722
1728
  // src/weakmaps/walkByMap.js
@@ -6154,18 +6160,24 @@
6154
6160
  return !(far < near);
6155
6161
  }
6156
6162
 
6163
+ // src/collision/vertexCount.js
6164
+ function vertexCount(position, index) {
6165
+ return index?.count ?? index?.data.length ?? position.count ?? position.data.length / (position.size ?? 3);
6166
+ }
6167
+
6157
6168
  // src/collision/readTriangle.js
6158
6169
  function readTriangle(mesh, i, a, b, c) {
6159
6170
  const { position, index, instanceOffset } = mesh.geometry.attributes;
6160
- const vertices = index?.count || position.count;
6171
+ const size = position.size ?? 3;
6172
+ const vertices = vertexCount(position, index);
6161
6173
  const vertex = instanceOffset ? i % vertices : i;
6162
6174
  const offset = instanceOffset ? Math.floor(i / vertices) * instanceOffset.size : 0;
6163
6175
  const x = instanceOffset?.data[offset] ?? 0;
6164
6176
  const y = instanceOffset?.data[offset + 1] ?? 0;
6165
6177
  const z = instanceOffset?.data[offset + 2] ?? 0;
6166
- const ai = (index ? index.data[vertex] : vertex) * position.size;
6167
- const bi = (index ? index.data[vertex + 1] : vertex + 1) * position.size;
6168
- const ci = (index ? index.data[vertex + 2] : vertex + 2) * position.size;
6178
+ const ai = (index ? index.data[vertex] : vertex) * size;
6179
+ const bi = (index ? index.data[vertex + 1] : vertex + 1) * size;
6180
+ const ci = (index ? index.data[vertex + 2] : vertex + 2) * size;
6169
6181
  a[0] = position.data[ai];
6170
6182
  a[1] = position.data[ai + 1];
6171
6183
  a[2] = position.data[ai + 2];
@@ -7621,1274 +7633,1325 @@
7621
7633
  layer.fire("progress", progress);
7622
7634
  }
7623
7635
 
7624
- // src/sources/buildAreaFeature.js
7625
- init_L();
7626
-
7627
- // src/projections/projectTreePoint.js
7628
- init_L();
7629
- function projectedTree(context, lon, lat) {
7630
- const { backend: layer, crs, origin, metres, map } = context;
7631
- const location = (
7636
+ // src/geometry/instanceAreaGeometry.js
7637
+ function instanceAreaGeometry(context) {
7638
+ const { positions, normals, colors, featureFaces } = context;
7639
+ if (context.backend.options.nightLight || featureFaces.length < 2) return context;
7640
+ const vertices = featureFaces[0].vertexEnd;
7641
+ const length = vertices * 3;
7642
+ if (!vertices || positions.length !== length * featureFaces.length) return context;
7643
+ const originX = Math.trunc(positions[0]), originY = Math.trunc(positions[1]), originZ = Math.trunc(positions[2]);
7644
+ const template = (
7632
7645
  /* alloc */
7633
- new L.LatLng(lat, lon)
7646
+ new Float32Array(length)
7647
+ );
7648
+ let i, instance, face, start2;
7649
+ for (i = 0; i < length; i++) template[i] = positions[i] - (i % 3 === 0 ? originX : i % 3 === 1 ? originY : originZ);
7650
+ const offsets = (
7651
+ /* alloc */
7652
+ new Float32Array(featureFaces.length * 4)
7634
7653
  );
7635
- const point = crs.project(location);
7636
- const elevation = mapElevationOf(map, location), root = rootByLayer.get(layer);
7654
+ for (instance = 0; instance < featureFaces.length; instance++) {
7655
+ face = featureFaces[instance];
7656
+ start2 = instance * length;
7657
+ if (face.vertexStart !== instance * vertices || face.vertexEnd !== (instance + 1) * vertices) return context;
7658
+ offsets[instance * 4] = originX + positions[start2] - positions[0];
7659
+ offsets[instance * 4 + 1] = originY + positions[start2 + 1] - positions[1];
7660
+ offsets[instance * 4 + 2] = originZ + positions[start2 + 2] - positions[2];
7661
+ offsets[instance * 4 + 3] = instance * vertices;
7662
+ for (i = 0; i < length; i++) {
7663
+ if (Math.fround(template[i] + offsets[instance * 4 + i % 3]) !== Math.fround(positions[start2 + i]) || Math.fround(normals[i]) !== Math.fround(normals[start2 + i]) || Math.fround(colors[i]) !== Math.fround(colors[start2 + i])) return context;
7664
+ }
7665
+ }
7637
7666
  return (
7638
7667
  /* alloc */
7639
7668
  {
7640
- x: (point.x - origin.x) * metres,
7641
- y: (point.y - origin.y) * metres,
7642
- ground: Number.isFinite(elevation) && Number.isFinite(root.position[1]) ? elevation - root.position[1] : 0
7669
+ positions: template,
7670
+ normals: normals.slice(0, length),
7671
+ colors: colors.slice(0, length),
7672
+ instanceOffsets: offsets
7643
7673
  }
7644
7674
  );
7645
7675
  }
7646
7676
 
7647
- // src/globals/triangleContexts.js
7648
- var triangleContexts = [];
7649
-
7650
- // src/sources/groundOffset.js
7651
- function groundOffset(point) {
7652
- return point.ground || 0;
7653
- }
7654
-
7655
- // src/geometry/appendAreaTriangle.js
7656
- function addTriangle(context, a, ah, b, bh, c, ch, nx, ny, nz, color) {
7657
- const { backend: layer, positions, normals, colors, uvs } = context;
7658
- const ux = b.x - a.x, uy = bh - ah, uz = a.y - b.y;
7659
- const vx = c.x - a.x, vy = ch - ah, vz = a.y - c.y;
7660
- if ((uy * vz - uz * vy) * nx + (uz * vx - ux * vz) * ny + (ux * vy - uy * vx) * nz < 0) {
7661
- const point = b, height = bh;
7662
- b = c;
7663
- bh = ch;
7664
- c = point;
7665
- ch = height;
7666
- }
7667
- positions.push(
7668
- a.x,
7669
- ah + groundOffset(a),
7670
- -a.y,
7671
- b.x,
7672
- bh + groundOffset(b),
7673
- -b.y,
7674
- c.x,
7675
- ch + groundOffset(c),
7676
- -c.y
7677
- );
7678
- normals.push(nx, ny, nz, nx, ny, nz, nx, ny, nz);
7679
- colors.push(color.r, color.g, color.b, color.r, color.g, color.b, color.r, color.g, color.b);
7680
- if (layer.options.nightLight) uvs.push(...a.uv, ...b.uv, ...c.uv);
7681
- }
7682
-
7683
- // src/geometry/addGeneratedTriangle.js
7684
- function addGeneratedTriangle(...args) {
7685
- addTriangle(triangleContexts[triangleContexts.length - 1], ...args);
7686
- }
7687
-
7688
- // src/geometry/generateTreeGeometry.js
7689
- function generateTreeGeometry(context, generator, point, style) {
7690
- triangleContexts.push(context);
7691
- try {
7692
- generator(point, style, addGeneratedTriangle);
7693
- } finally {
7694
- triangleContexts.pop();
7677
+ // src/sources/attachAreaMesh.js
7678
+ function attachAreaMesh(context) {
7679
+ const { backend: layer, area: area2 } = context;
7680
+ if (!context.positions.length) {
7681
+ area2.mesh = null, area2.buildings = 0, area2.skipped = context.skipped, area2.done = true;
7682
+ return;
7695
7683
  }
7696
- }
7697
-
7698
- // src/globals/lampDirections.js
7699
- var lampDirections = [
7700
- { x: Math.cos(0 * Math.PI / 3), y: Math.sin(0 * Math.PI / 3), nx: Math.cos(0.5 * Math.PI / 3), nz: -Math.sin(0.5 * Math.PI / 3) },
7701
- { x: Math.cos(1 * Math.PI / 3), y: Math.sin(1 * Math.PI / 3), nx: Math.cos(1.5 * Math.PI / 3), nz: -Math.sin(1.5 * Math.PI / 3) },
7702
- { x: Math.cos(2 * Math.PI / 3), y: Math.sin(2 * Math.PI / 3), nx: Math.cos(2.5 * Math.PI / 3), nz: -Math.sin(2.5 * Math.PI / 3) },
7703
- { x: Math.cos(3 * Math.PI / 3), y: Math.sin(3 * Math.PI / 3), nx: Math.cos(3.5 * Math.PI / 3), nz: -Math.sin(3.5 * Math.PI / 3) },
7704
- { x: Math.cos(4 * Math.PI / 3), y: Math.sin(4 * Math.PI / 3), nx: Math.cos(4.5 * Math.PI / 3), nz: -Math.sin(4.5 * Math.PI / 3) },
7705
- { x: Math.cos(5 * Math.PI / 3), y: Math.sin(5 * Math.PI / 3), nx: Math.cos(5.5 * Math.PI / 3), nz: -Math.sin(5.5 * Math.PI / 3) }
7706
- ];
7707
-
7708
- // src/geometry/createLampRing.js
7709
- function ring(point, radius) {
7710
- const points = (
7684
+ const device = rendererByLayer.get(layer).device;
7685
+ const data = context.preparedGeometry || instanceAreaGeometry(context);
7686
+ const attributes = (
7711
7687
  /* alloc */
7712
- new Array(lampDirections.length)
7713
- );
7714
- let i;
7715
- for (i = 0; i < points.length; i++) {
7716
- points[i] = /* alloc */
7717
7688
  {
7718
- x: point.x + lampDirections[i].x * radius,
7719
- y: point.y + lampDirections[i].y * radius,
7720
- ground: point.ground,
7721
- uv: (
7689
+ position: (
7722
7690
  /* alloc */
7723
- [3, 3]
7691
+ { size: 3, data: data.positions instanceof Float32Array ? data.positions : (
7692
+ /* alloc */
7693
+ new Float32Array(data.positions)
7694
+ ) }
7695
+ ),
7696
+ normal: (
7697
+ /* alloc */
7698
+ { size: 3, data: data.normals instanceof Float32Array ? data.normals : (
7699
+ /* alloc */
7700
+ new Float32Array(data.normals)
7701
+ ) }
7702
+ ),
7703
+ color: (
7704
+ /* alloc */
7705
+ { size: 3, data: data.colors instanceof Float32Array ? data.colors : (
7706
+ /* alloc */
7707
+ new Float32Array(data.colors)
7708
+ ) }
7724
7709
  )
7725
- };
7726
- }
7727
- return points;
7728
- }
7729
-
7730
- // src/geometry/lampPostGeometry.js
7731
- function lampPostGeometry(point, style, addTriangle2) {
7732
- const color = parseColor(5264986), radius = style.poleRadius;
7733
- if (!Number.isFinite(radius) || radius <= 0) {
7734
- throw (
7710
+ }
7711
+ );
7712
+ if (data.instanceOffsets) attributes.instanceOffset = /* alloc */
7713
+ { size: 4, data: data.instanceOffsets };
7714
+ if (layer.options.nightLight) attributes.uv = /* alloc */
7715
+ {
7716
+ size: 2,
7717
+ data: context.uvs instanceof Float32Array ? context.uvs : (
7735
7718
  /* alloc */
7736
- new Error("Invalid lamp pole radius")
7719
+ new Float32Array(context.uvs)
7720
+ )
7721
+ };
7722
+ let name, attribute;
7723
+ for (name in attributes) {
7724
+ attribute = attributes[name];
7725
+ attribute.count = attribute.data.length / attribute.size;
7726
+ attribute.buffer = device.createBuffer(
7727
+ /* alloc */
7728
+ {
7729
+ size: Math.max(4, attribute.data.byteLength),
7730
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
7731
+ }
7737
7732
  );
7733
+ device.queue.writeBuffer(attribute.buffer, 0, attribute.data);
7738
7734
  }
7739
- const center = (
7735
+ const meshGeometry = (
7740
7736
  /* alloc */
7741
7737
  {
7742
- ...point,
7743
- uv: (
7738
+ device,
7739
+ bounds: data.bounds,
7740
+ attributes,
7741
+ drawRange: (
7744
7742
  /* alloc */
7745
- [3, 3]
7743
+ { start: 0, count: attributes.position.count }
7746
7744
  )
7747
7745
  }
7748
7746
  );
7749
- let r, low, high, points, i, a, b, nx, nz, section;
7750
- for (section = 0; section < 2; section++) {
7751
- r = section ? radius * 2.5 : radius;
7752
- low = section ? style.top - 0.25 : 0;
7753
- high = section ? style.top + 0.15 : style.top;
7754
- points = ring(point, r);
7755
- for (i = 0; i < 6; i++) {
7756
- a = points[i], b = points[(i + 1) % 6];
7757
- nx = lampDirections[i].nx, nz = lampDirections[i].nz;
7758
- addTriangle2(a, low, b, low, b, high, nx, 0, nz, color);
7759
- addTriangle2(a, low, b, high, a, high, nx, 0, nz, color);
7760
- addTriangle2(center, high, a, high, b, high, 0, 1, 0, color);
7761
- addTriangle2(center, low, b, low, a, low, 0, -1, 0, color);
7762
- }
7763
- }
7764
- }
7765
-
7766
- // src/geometry/quad.js
7767
- function quad(addTriangle2, color, a, b, c, d, height, nx, ny, nz) {
7768
- if (typeof nx !== "number") {
7769
- addTriangle2(a, height, b, height, c, height, ...nx, color);
7770
- addTriangle2(a, height, c, height, d, height, ...nx, color);
7771
- } else {
7772
- addTriangle2(a, height, b, height, c, height, nx, ny, nz, color);
7773
- addTriangle2(a, height, c, height, d, height, nx, ny, nz, color);
7774
- }
7775
- }
7776
-
7777
- // src/geometry/emitter.js
7778
- function emitter(point, u, v, axis) {
7779
- const r = 0.65;
7780
- return (
7781
- /* alloc */
7782
- {
7783
- x: point.x + (axis === 2 ? 0 : u * r),
7784
- y: point.y + (axis === 1 ? 0 : axis === 2 ? u * r : v * r),
7785
- ground: point.ground + (axis === 0 ? 0 : v * r),
7786
- uv: (
7787
- /* alloc */
7788
- [u, v]
7789
- )
7790
- }
7791
- );
7792
- }
7793
-
7794
- // src/geometry/lightGeometry.js
7795
- function lightGeometry(point, style, addTriangle2) {
7796
- if (!Number.isFinite(style.lightRadius) || style.lightRadius <= 0) throw (
7797
- /* alloc */
7798
- new Error("Invalid light radius")
7799
- );
7800
- const color = parseColor(style.color);
7801
- let axis;
7802
- for (axis = 0; axis < 3; axis++) {
7803
- quad(
7804
- addTriangle2,
7805
- color,
7806
- emitter(point, -1, -1, axis),
7807
- emitter(point, 1, -1, axis),
7808
- emitter(point, 1, 1, axis),
7809
- emitter(point, -1, 1, axis),
7810
- style.top,
7811
- axis === 2 ? 1 : 0,
7812
- axis === 0 ? 1 : 0,
7813
- axis === 1 ? 1 : 0
7814
- );
7815
- }
7816
- }
7817
-
7818
- // src/globals/roundProfile.js
7819
- var roundProfile = [
7820
- [0, 0],
7821
- [0.35, 0.8],
7822
- [0.6, 1],
7823
- [0.85, 0.7],
7824
- [1, 0]
7825
- ];
7826
-
7827
- // src/globals/ringDirections.js
7828
- var ringDirections = [
7829
- {
7830
- x: Math.cos(0 * Math.PI / 4),
7831
- y: Math.sin(0 * Math.PI / 4),
7832
- nx: Math.cos((0 + 0.5) * Math.PI / 4),
7833
- nz: -Math.sin((0 + 0.5) * Math.PI / 4)
7834
- },
7835
- {
7836
- x: Math.cos(1 * Math.PI / 4),
7837
- y: Math.sin(1 * Math.PI / 4),
7838
- nx: Math.cos((1 + 0.5) * Math.PI / 4),
7839
- nz: -Math.sin((1 + 0.5) * Math.PI / 4)
7840
- },
7841
- {
7842
- x: Math.cos(2 * Math.PI / 4),
7843
- y: Math.sin(2 * Math.PI / 4),
7844
- nx: Math.cos((2 + 0.5) * Math.PI / 4),
7845
- nz: -Math.sin((2 + 0.5) * Math.PI / 4)
7846
- },
7847
- {
7848
- x: Math.cos(3 * Math.PI / 4),
7849
- y: Math.sin(3 * Math.PI / 4),
7850
- nx: Math.cos((3 + 0.5) * Math.PI / 4),
7851
- nz: -Math.sin((3 + 0.5) * Math.PI / 4)
7852
- },
7853
- {
7854
- x: Math.cos(4 * Math.PI / 4),
7855
- y: Math.sin(4 * Math.PI / 4),
7856
- nx: Math.cos((4 + 0.5) * Math.PI / 4),
7857
- nz: -Math.sin((4 + 0.5) * Math.PI / 4)
7858
- },
7859
- {
7860
- x: Math.cos(5 * Math.PI / 4),
7861
- y: Math.sin(5 * Math.PI / 4),
7862
- nx: Math.cos((5 + 0.5) * Math.PI / 4),
7863
- nz: -Math.sin((5 + 0.5) * Math.PI / 4)
7864
- },
7865
- {
7866
- x: Math.cos(6 * Math.PI / 4),
7867
- y: Math.sin(6 * Math.PI / 4),
7868
- nx: Math.cos((6 + 0.5) * Math.PI / 4),
7869
- nz: -Math.sin((6 + 0.5) * Math.PI / 4)
7870
- },
7747
+ area2.mesh = /* alloc */
7871
7748
  {
7872
- x: Math.cos(7 * Math.PI / 4),
7873
- y: Math.sin(7 * Math.PI / 4),
7874
- nx: Math.cos((7 + 0.5) * Math.PI / 4),
7875
- nz: -Math.sin((7 + 0.5) * Math.PI / 4)
7876
- }
7877
- ];
7878
-
7879
- // src/geometry/createTreeRing.js
7880
- function ring2(point, radius) {
7881
- const result = (
7882
- /* alloc */
7883
- new Array(ringDirections.length)
7884
- );
7885
- let i;
7886
- for (i = 0; i < result.length; i++) {
7887
- result[i] = /* alloc */
7888
- {
7889
- x: point.x + ringDirections[i].x * radius,
7890
- y: point.y + ringDirections[i].y * radius,
7891
- ground: point.ground
7892
- };
7893
- }
7894
- return result;
7749
+ ...emptyNode(),
7750
+ device,
7751
+ geometry: meshGeometry,
7752
+ program: createProgram(
7753
+ layer,
7754
+ /* alloc */
7755
+ {
7756
+ instanced: Boolean(data.instanceOffsets),
7757
+ vertexColors: true,
7758
+ roofTexture: layer.options.roofTexture,
7759
+ wallTexture: layer.options.wallTexture,
7760
+ nightLight: Boolean(layer.options.nightLight),
7761
+ alphaMode: layer.options.nightLight ? "BLEND" : void 0,
7762
+ doubleSided: Boolean(layer.options.nightLight)
7763
+ }
7764
+ ),
7765
+ id: nextId.value++,
7766
+ renderOrder: 0,
7767
+ fadeDuration: 250,
7768
+ extras: (
7769
+ /* alloc */
7770
+ { featureFaces: context.featureFaces, lights: context.lights }
7771
+ )
7772
+ };
7773
+ area2.buildings = context.featureFaces.length;
7774
+ area2.skipped = context.skipped;
7775
+ area2.done = true;
7776
+ setParent(area2.mesh, rootByLayer.get(layer));
7777
+ update(layer);
7895
7778
  }
7896
7779
 
7897
- // src/geometry/surface.js
7898
- function surface(point, addTriangle2, lowRadius, low, highRadius, high, color) {
7899
- const lower = ring2(point, lowRadius), upper = ring2(point, highRadius);
7900
- let i, next, slope, length, nx, nz, ny, normalX, normalZ;
7901
- for (i = 0; i < 8; i++) {
7902
- next = (i + 1) % 8;
7903
- slope = (lowRadius - highRadius) / (high - low);
7904
- length = Math.hypot(1, slope);
7905
- ({ nx, nz } = ringDirections[i]);
7906
- ny = slope / length;
7907
- normalX = nx / length, normalZ = nz / length;
7908
- if (lowRadius) addTriangle2(lower[i], low, lower[next], low, upper[next], high, normalX, ny, normalZ, color);
7909
- if (highRadius) addTriangle2(lower[i], low, upper[next], high, upper[i], high, normalX, ny, normalZ, color);
7910
- }
7911
- }
7780
+ // src/globals/archiveDecoder.js
7781
+ var archiveDecoder = new TextDecoder();
7912
7782
 
7913
- // src/geometry/validateTree.js
7914
- function validateTree(trunkRadius, crownRadius, crownBottom, bottom, top, crownShape) {
7915
- if (!Number.isFinite(trunkRadius) || !Number.isFinite(crownRadius) || !Number.isFinite(crownBottom) || trunkRadius <= 0 || crownRadius < trunkRadius || crownBottom <= bottom || crownBottom >= top || crownShape !== "round" && crownShape !== "cone") {
7783
+ // src/archives/tileId.js
7784
+ function tileId(z, x, y) {
7785
+ if (!Number.isInteger(z) || !Number.isInteger(x) || !Number.isInteger(y) || z < 0 || z > 26 || x < 0 || y < 0 || x >= 2 ** z || y >= 2 ** z) {
7916
7786
  throw (
7917
7787
  /* alloc */
7918
- new Error("Invalid tree dimensions or crown shape")
7788
+ new Error("Invalid PMTiles tile coordinates")
7919
7789
  );
7920
7790
  }
7791
+ let id = (4 ** z - 1) / 3;
7792
+ const n = 2 ** z;
7793
+ let s, rx, ry, swap;
7794
+ for (s = n / 2; s >= 1; s /= 2) {
7795
+ rx = x & s ? 1 : 0, ry = y & s ? 1 : 0;
7796
+ id += s * s * (3 * rx ^ ry);
7797
+ if (!ry) {
7798
+ if (rx) {
7799
+ x = n - 1 - x;
7800
+ y = n - 1 - y;
7801
+ }
7802
+ swap = x;
7803
+ x = y;
7804
+ y = swap;
7805
+ }
7806
+ }
7807
+ return id;
7921
7808
  }
7922
7809
 
7923
- // src/geometry/treeGeometry.js
7924
- function treeGeometry(point, style, addTriangle2) {
7925
- const { top, bottom = 0, trunkRadius, crownRadius, crownBottom, crownShape = "round" } = style;
7926
- validateTree(trunkRadius, crownRadius, crownBottom, bottom, top, crownShape);
7927
- const trunk = parseColor(style.trunkColor ?? 7491898);
7928
- const crown = parseColor(style.color ?? 5210683);
7929
- surface(point, addTriangle2, trunkRadius, bottom, trunkRadius, crownBottom + (top - crownBottom) * 0.25, trunk);
7930
- let i, low, lr, high, hr;
7931
- if (crownShape === "cone") {
7932
- const base = ring2(point, crownRadius);
7933
- for (i = 0; i < 8; i++) addTriangle2(
7934
- point,
7935
- crownBottom,
7936
- base[i],
7937
- crownBottom,
7938
- base[(i + 1) % 8],
7939
- crownBottom,
7940
- 0,
7941
- -1,
7942
- 0,
7943
- crown
7944
- );
7945
- surface(point, addTriangle2, crownRadius, crownBottom, 0, top, crown);
7946
- } else {
7947
- for (i = 1; i < roundProfile.length; i++) {
7948
- [low, lr] = roundProfile[i - 1], [high, hr] = roundProfile[i];
7949
- surface(
7950
- point,
7951
- addTriangle2,
7952
- lr * crownRadius,
7953
- crownBottom + low * (top - crownBottom),
7954
- hr * crownRadius,
7955
- crownBottom + high * (top - crownBottom),
7956
- crown
7810
+ // src/globals/directoryReader.js
7811
+ var directoryReader = {
7812
+ bytes: null,
7813
+ cursor: 0
7814
+ };
7815
+
7816
+ // src/archives/readVarint.js
7817
+ function varint(reader) {
7818
+ const { bytes } = reader;
7819
+ let value = 0;
7820
+ let factor, byte;
7821
+ for (factor = 1; factor <= 2 ** 49; factor *= 128) {
7822
+ if (reader.cursor >= bytes.length) {
7823
+ throw (
7824
+ /* alloc */
7825
+ new Error("Truncated PMTiles directory")
7826
+ );
7827
+ }
7828
+ byte = bytes[reader.cursor++];
7829
+ value += (byte & 127) * factor;
7830
+ if (!Number.isSafeInteger(value)) {
7831
+ throw (
7832
+ /* alloc */
7833
+ new Error("Invalid PMTiles directory integer")
7957
7834
  );
7958
7835
  }
7836
+ if (byte < 128) return value;
7959
7837
  }
7838
+ throw (
7839
+ /* alloc */
7840
+ new Error("Invalid PMTiles directory integer")
7841
+ );
7960
7842
  }
7961
7843
 
7962
- // src/geometry/buildProjectedPoint.js
7963
- function buildProjectedPoint(context, projected, style, baseColor, support) {
7964
- const { top } = style;
7965
- if (context.backend.options.nightLight) {
7966
- if (!Number.isFinite(style.lightFlux) || style.lightFlux <= 0) throw (
7967
- /* alloc */
7968
- new Error("Invalid light flux")
7969
- );
7970
- if (style.poleRadius > 0 && !context.supports.has(support)) {
7971
- generateTreeGeometry(context, lampPostGeometry, projected, style);
7972
- context.supports.add(support);
7844
+ // src/archives/decodeDirectory.js
7845
+ function decodeDirectory(data) {
7846
+ const reader = directoryReader;
7847
+ const bytes = (
7848
+ /* alloc */
7849
+ new Uint8Array(data)
7850
+ );
7851
+ reader.bytes = bytes;
7852
+ reader.cursor = 0;
7853
+ try {
7854
+ const count = varint(reader);
7855
+ if (count > (bytes.length - reader.cursor) / 4) {
7856
+ throw (
7857
+ /* alloc */
7858
+ new Error("Invalid PMTiles directory count")
7859
+ );
7973
7860
  }
7974
- generateTreeGeometry(context, lightGeometry, projected, style);
7975
- context.lights.push(
7861
+ const entries = (
7976
7862
  /* alloc */
7977
- {
7978
- position: (
7863
+ new Array(count)
7864
+ );
7865
+ let tileId2 = 0;
7866
+ let i, entry, value;
7867
+ for (i = 0; i < count; i++) {
7868
+ tileId2 += varint(reader);
7869
+ if (!Number.isSafeInteger(tileId2) || i && tileId2 <= entries[i - 1].tileId) {
7870
+ throw (
7979
7871
  /* alloc */
7980
- [projected.x, projected.ground + top, -projected.y]
7981
- ),
7982
- radius: Math.hypot(top, style.lightRadius),
7983
- color: (
7872
+ new Error("Invalid PMTiles directory tile IDs")
7873
+ );
7874
+ }
7875
+ entries[i] = /* alloc */
7876
+ { tileId: tileId2, runLength: 0, length: 0, offset: 0 };
7877
+ }
7878
+ for (entry of entries) entry.runLength = varint(reader);
7879
+ for (entry of entries) {
7880
+ entry.length = varint(reader);
7881
+ if (!entry.length) {
7882
+ throw (
7984
7883
  /* alloc */
7985
- [baseColor.r, baseColor.g, baseColor.b]
7986
- ),
7987
- flux: style.lightFlux
7884
+ new Error("Invalid PMTiles directory length")
7885
+ );
7988
7886
  }
7989
- );
7990
- } else generateTreeGeometry(context, treeGeometry, projected, style);
7991
- }
7992
-
7993
- // src/sources/buildAreaPoint.js
7994
- function buildAreaPoint(context, feature, lon, lat, style, baseColor) {
7995
- const projected = projectedTree(context, lon, lat);
7996
- buildProjectedPoint(context, projected, style, baseColor, feature.properties?.cod_ouvrag || `${lon},${lat}`);
7997
- }
7998
-
7999
- // src/projections/projectRing.js
8000
- init_L();
8001
- function projectRing(context, ring3) {
8002
- const { backend: layer, crs, origin, metres, map } = context;
8003
- if (ring3.length < 4) throw (
8004
- /* alloc */
8005
- new Error("Invalid GeoJSON polygon ring")
8006
- );
8007
- const path = (
8008
- /* alloc */
8009
- []
8010
- );
8011
- const root = rootByLayer.get(layer);
8012
- let i, lng, lat, location, point, elevation;
8013
- for (i = 0; i < ring3.length - 1; i++) {
8014
- lng = ring3[i][0], lat = ring3[i][1];
8015
- if (!Number.isFinite(lng) || !Number.isFinite(lat)) throw (
8016
- /* alloc */
8017
- new Error("Invalid GeoJSON coordinates")
8018
- );
8019
- location = /* alloc */
8020
- new L.LatLng(lat, lng);
8021
- point = crs.project(location);
8022
- elevation = mapElevationOf(map, location);
8023
- path.push(
8024
- /* alloc */
8025
- {
8026
- x: (point.x - origin.x) * metres,
8027
- y: (point.y - origin.y) * metres,
8028
- ground: Number.isFinite(elevation) && Number.isFinite(root.position[1]) ? elevation - root.position[1] : 0
7887
+ }
7888
+ for (i = 0; i < entries.length; i++) {
7889
+ entry = entries[i];
7890
+ value = varint(reader);
7891
+ if (!value && !i) {
7892
+ throw (
7893
+ /* alloc */
7894
+ new Error("Invalid PMTiles directory offset")
7895
+ );
8029
7896
  }
8030
- );
7897
+ entry.offset = value ? value - 1 : entries[i - 1].offset + entries[i - 1].length;
7898
+ }
7899
+ if (reader.cursor !== bytes.length) {
7900
+ throw (
7901
+ /* alloc */
7902
+ new Error("Trailing PMTiles directory data")
7903
+ );
7904
+ }
7905
+ return entries;
7906
+ } finally {
7907
+ reader.bytes = null;
7908
+ reader.cursor = 0;
8031
7909
  }
8032
- return path;
8033
7910
  }
8034
7911
 
8035
- // src/geometry/buildProjectedPolygon.js
8036
- function buildProjectedPolygon(context, paths, top, bottom, baseColor) {
8037
- const points = (
7912
+ // src/archives/decompress.js
7913
+ async function decompress(data, compression) {
7914
+ if (compression === 1) return data;
7915
+ if (compression !== 2) {
7916
+ throw (
7917
+ /* alloc */
7918
+ new Error(`Unsupported PMTiles compression: ${compression}`)
7919
+ );
7920
+ }
7921
+ const blob = (
8038
7922
  /* alloc */
8039
- []
7923
+ new Blob(
7924
+ /* alloc */
7925
+ [data]
7926
+ )
8040
7927
  );
8041
- let path, point, a, b, c, index, sign, i, length, nx, nz;
8042
- for (path of paths) for (point of path) points.push(point);
8043
- for ([a, b, c] of triangulate(paths[0], paths.slice(1))) {
8044
- addTriangle(context, points[a], top, points[b], top, points[c], top, 0, 1, 0, baseColor);
8045
- if (bottom > 0) addTriangle(context, points[a], bottom, points[b], bottom, points[c], bottom, 0, -1, 0, baseColor);
8046
- }
8047
- for (index = 0; index < paths.length; index++) {
8048
- path = paths[index];
8049
- sign = isClockWise(path) === (index === 0) ? -1 : 1;
8050
- for (i = 0; i < path.length; i++) {
8051
- a = path[i], b = path[(i + 1) % path.length];
8052
- length = Math.hypot(b.x - a.x, b.y - a.y);
8053
- if (!length) continue;
8054
- nx = sign * (b.y - a.y) / length, nz = sign * (b.x - a.x) / length;
8055
- addTriangle(context, a, bottom, b, bottom, b, top, nx, 0, nz, baseColor);
8056
- addTriangle(context, a, bottom, b, top, a, top, nx, 0, nz, baseColor);
8057
- }
8058
- }
8059
- }
8060
-
8061
- // src/geometry/buildExtrudedPolygon.js
8062
- function buildAreaPolygon(context, rings, top, bottom, baseColor) {
8063
- if (!rings.length) throw (
7928
+ const stream = (
8064
7929
  /* alloc */
8065
- new Error("Empty GeoJSON polygon")
7930
+ new DecompressionStream("gzip")
8066
7931
  );
8067
- const paths = (
7932
+ return (
8068
7933
  /* alloc */
8069
- []
7934
+ new Response(blob.stream().pipeThrough(stream)).arrayBuffer()
8070
7935
  );
8071
- let ring3;
8072
- for (ring3 of rings) paths.push(projectRing(context, ring3));
8073
- buildProjectedPolygon(context, paths, top, bottom, baseColor);
8074
7936
  }
8075
7937
 
8076
- // src/sources/featureAnchor.js
8077
- function featureAnchor(feature, nightLight) {
8078
- const geometry = feature?.geometry, point = geometry?.type === "Point";
8079
- if (nightLight && !point) throw (
8080
- /* alloc */
8081
- new Error("Night lights require Point geometry")
8082
- );
8083
- if (geometry?.type !== "Polygon" && geometry?.type !== "MultiPolygon" && !point) throw (
8084
- /* alloc */
8085
- new Error("Unsupported GeoJSON geometry")
8086
- );
8087
- const coordinates = point ? geometry.coordinates : geometry.type === "Polygon" ? geometry.coordinates[0]?.[0] : geometry.coordinates[0]?.[0]?.[0];
8088
- if (!Number.isFinite(coordinates?.[0]) || !Number.isFinite(coordinates?.[1])) throw (
8089
- /* alloc */
8090
- new Error("Invalid GeoJSON coordinates")
8091
- );
8092
- return coordinates;
7938
+ // src/archives/loadDirectory.js
7939
+ async function loadDirectory(layer, offset, length, compression) {
7940
+ return decodeDirectory(await decompress(await read(layer, offset, length), compression));
8093
7941
  }
8094
7942
 
8095
- // src/sources/extrusionStyle.js
8096
- function extrusionStyle(layer, feature) {
8097
- const style = layer.options.style(feature);
8098
- if (style === null) return null;
8099
- if (!style || typeof style !== "object") throw (
8100
- /* alloc */
8101
- new Error("style must return an extrusion style or null")
8102
- );
8103
- const { top, bottom = 0 } = style;
8104
- if (!Number.isFinite(top) || !Number.isFinite(bottom) || bottom < 0 || top <= bottom) {
8105
- throw (
8106
- /* alloc */
8107
- new Error("Invalid extrusion heights: expected top > bottom >= 0")
8108
- );
7943
+ // src/archives/getDirectoryEntries.js
7944
+ async function getDirectoryEntries(layer, offset, length, compression) {
7945
+ const directories = directoriesByLayer.get(layer);
7946
+ const key = `${offset}:${length}`;
7947
+ if (!directories.has(key)) {
7948
+ if (directories.size >= 32) directories.delete(directories.keys().next().value);
7949
+ directories.set(key, loadDirectory(layer, offset, length, compression));
7950
+ }
7951
+ const pending = directories.get(key);
7952
+ try {
7953
+ return await pending;
7954
+ } catch (error) {
7955
+ if (directories.get(key) === pending) directories.delete(key);
7956
+ throw error;
8109
7957
  }
8110
- return style;
8111
7958
  }
8112
7959
 
8113
- // src/sources/prepareAreaGeometryJob.js
8114
- function prepareAreaGeometryJob(context, feature, lon, lat, style, baseColor) {
8115
- const geometry = feature.geometry;
8116
- const polygons = (
8117
- /* alloc */
8118
- []
8119
- );
8120
- const rings = geometry.type === "Polygon" ? (
8121
- /* alloc */
8122
- [geometry.coordinates]
8123
- ) : geometry.type === "MultiPolygon" ? geometry.coordinates : (
8124
- /* alloc */
8125
- []
8126
- );
8127
- let polygon2, ring3, paths;
8128
- for (polygon2 of rings) {
8129
- if (!polygon2.length) throw (
8130
- /* alloc */
8131
- new Error("Empty GeoJSON polygon")
8132
- );
8133
- paths = /* alloc */
8134
- [];
8135
- for (ring3 of polygon2) paths.push(projectRing(context, ring3));
8136
- polygons.push(paths);
7960
+ // src/archives/findDirectoryEntry.js
7961
+ function findDirectoryEntry(entries, id) {
7962
+ let low = 0, high = entries.length - 1, mid;
7963
+ while (low <= high) {
7964
+ mid = low + high >> 1;
7965
+ if (entries[mid].tileId <= id) low = mid + 1;
7966
+ else high = mid - 1;
8137
7967
  }
8138
- const point = geometry.type === "Point" ? projectedTree(context, lon, lat) : null;
8139
- const nightLight = context.backend.options.nightLight;
8140
- const crownColor = point ? parseColor(style.color ?? (nightLight ? void 0 : 5210683)) : baseColor;
8141
- const trunkColor = point && !nightLight ? parseColor(style.trunkColor ?? 7491898) : null;
8142
- context.jobs.push(
8143
- /* alloc */
8144
- {
8145
- point,
8146
- polygons,
8147
- baseColor: (
8148
- /* alloc */
8149
- { r: baseColor.r, g: baseColor.g, b: baseColor.b }
8150
- ),
8151
- support: feature.properties?.cod_ouvrag || `${lon},${lat}`,
8152
- style: (
8153
- /* alloc */
8154
- {
8155
- top: style.top,
8156
- bottom: style.bottom ?? 0,
8157
- color: (
8158
- /* alloc */
8159
- { r: crownColor.r, g: crownColor.g, b: crownColor.b }
8160
- ),
8161
- trunkColor: trunkColor ? (
8162
- /* alloc */
8163
- { r: trunkColor.r, g: trunkColor.g, b: trunkColor.b }
8164
- ) : void 0,
8165
- trunkRadius: style.trunkRadius,
8166
- crownRadius: style.crownRadius,
8167
- crownBottom: style.crownBottom,
8168
- crownShape: style.crownShape,
8169
- poleRadius: style.poleRadius,
8170
- lightRadius: style.lightRadius,
8171
- lightFlux: style.lightFlux
8172
- }
8173
- )
8174
- }
8175
- );
7968
+ return entries[high];
8176
7969
  }
8177
7970
 
8178
- // src/sources/buildAreaFeature.js
8179
- function buildAreaFeature(context, feature) {
8180
- const { backend: layer, area: area2, map } = context;
8181
- const coords = area2.coords;
8182
- const coordinates = featureAnchor(feature, layer.options.nightLight);
8183
- const geometry = feature.geometry, isTree = geometry.type === "Point";
8184
- const lon = coordinates[0], lat = coordinates[1];
8185
- if (coords) {
8186
- const anchor = map.project(
8187
- /* alloc */
8188
- new L.LatLng(lat, lon),
8189
- coords.z
8190
- );
8191
- if (Math.floor(anchor.x / 256) !== coords.x || Math.floor(anchor.y / 256) !== coords.y) return;
8192
- }
8193
- const style = extrusionStyle(layer, feature);
8194
- if (style === null) {
8195
- context.skipped++;
8196
- return;
8197
- }
8198
- const { top, bottom = 0, color = 14267276 } = style;
8199
- const baseColor = parseColor(color);
8200
- if (context.jobs) {
8201
- prepareAreaGeometryJob(context, feature, lon, lat, style, baseColor);
8202
- context.featureFaces.push(
7971
+ // src/archives/getTileData.js
7972
+ async function getTileData(layer, header, entry, id, signal) {
7973
+ if (id - entry.tileId >= entry.runLength) return;
7974
+ if (entry.offset + entry.length > header.tileDataLength) {
7975
+ throw (
8203
7976
  /* alloc */
8204
- {
8205
- id: feature.id ?? feature.properties?.objectid ?? JSON.stringify(geometry),
8206
- properties: feature.properties,
8207
- baseColor
8208
- }
7977
+ new Error("PMTiles tile outside data section")
8209
7978
  );
8210
- return;
8211
7979
  }
8212
- const vertexStart = context.positions.length / 3;
8213
- if (isTree) buildAreaPoint(context, feature, lon, lat, style, baseColor);
8214
- let polygon2;
8215
- if (geometry.type === "Polygon") buildAreaPolygon(context, geometry.coordinates, top, bottom, baseColor);
8216
- else if (geometry.type === "MultiPolygon") for (polygon2 of geometry.coordinates) buildAreaPolygon(context, polygon2, top, bottom, baseColor);
8217
- const vertexEnd = context.positions.length / 3;
8218
- context.featureFaces.push(
7980
+ const data = await decompress(
7981
+ await read(layer, header.tileDataOffset + entry.offset, entry.length, signal),
7982
+ header.tileCompression
7983
+ );
7984
+ signal?.throwIfAborted();
7985
+ return (
8219
7986
  /* alloc */
8220
- {
8221
- id: feature.id ?? feature.properties?.objectid ?? JSON.stringify(geometry),
8222
- properties: feature.properties,
8223
- baseColor,
8224
- faceStart: vertexStart / 3,
8225
- faceEnd: vertexEnd / 3,
8226
- vertexStart,
8227
- vertexEnd
7987
+ { data }
7988
+ );
7989
+ }
7990
+
7991
+ // src/archives/findTile.js
7992
+ async function findTile(layer, header, id, signal) {
7993
+ let offset = header.rootDirectoryOffset, length = header.rootDirectoryLength;
7994
+ let firstOffset = -1, secondOffset = -1, thirdOffset = -1;
7995
+ let depth, entries, entry, tile;
7996
+ for (depth = 0; depth < 4; depth++) {
7997
+ if (offset === firstOffset || offset === secondOffset || offset === thirdOffset) {
7998
+ throw (
7999
+ /* alloc */
8000
+ new Error("Cyclic PMTiles directory")
8001
+ );
8002
+ }
8003
+ if (depth === 0) firstOffset = offset;
8004
+ else if (depth === 1) secondOffset = offset;
8005
+ else if (depth === 2) thirdOffset = offset;
8006
+ entries = await getDirectoryEntries(layer, offset, length, header.internalCompression);
8007
+ signal?.throwIfAborted();
8008
+ entry = findDirectoryEntry(entries, id);
8009
+ if (!entry) return;
8010
+ if (entry.runLength) {
8011
+ tile = await getTileData(layer, header, entry, id, signal);
8012
+ return tile;
8013
+ }
8014
+ if (entry.offset + entry.length > header.leafDirectoryLength) {
8015
+ throw (
8016
+ /* alloc */
8017
+ new Error("PMTiles directory outside leaf section")
8018
+ );
8228
8019
  }
8020
+ offset = header.leafDirectoryOffset + entry.offset;
8021
+ length = entry.length;
8022
+ }
8023
+ throw (
8024
+ /* alloc */
8025
+ new Error("PMTiles directory depth exceeds four levels")
8229
8026
  );
8230
8027
  }
8231
8028
 
8232
- // src/geometry/instanceAreaGeometry.js
8233
- function instanceAreaGeometry(context) {
8234
- const { positions, normals, colors, featureFaces } = context;
8235
- if (context.backend.options.nightLight || featureFaces.length < 2) return context;
8236
- const vertices = featureFaces[0].vertexEnd;
8237
- const length = vertices * 3;
8238
- if (!vertices || positions.length !== length * featureFaces.length) return context;
8239
- const originX = Math.trunc(positions[0]), originY = Math.trunc(positions[1]), originZ = Math.trunc(positions[2]);
8240
- const template = (
8241
- /* alloc */
8242
- new Float32Array(length)
8243
- );
8244
- let i, instance, face, start2;
8245
- for (i = 0; i < length; i++) template[i] = positions[i] - (i % 3 === 0 ? originX : i % 3 === 1 ? originY : originZ);
8246
- const offsets = (
8247
- /* alloc */
8248
- new Float32Array(featureFaces.length * 4)
8249
- );
8250
- for (instance = 0; instance < featureFaces.length; instance++) {
8251
- face = featureFaces[instance];
8252
- start2 = instance * length;
8253
- if (face.vertexStart !== instance * vertices || face.vertexEnd !== (instance + 1) * vertices) return context;
8254
- offsets[instance * 4] = originX + positions[start2] - positions[0];
8255
- offsets[instance * 4 + 1] = originY + positions[start2 + 1] - positions[1];
8256
- offsets[instance * 4 + 2] = originZ + positions[start2 + 2] - positions[2];
8257
- offsets[instance * 4 + 3] = instance * vertices;
8258
- for (i = 0; i < length; i++) {
8259
- if (Math.fround(template[i] + offsets[instance * 4 + i % 3]) !== Math.fround(positions[start2 + i]) || Math.fround(normals[i]) !== Math.fround(normals[start2 + i]) || Math.fround(colors[i]) !== Math.fround(colors[start2 + i])) return context;
8029
+ // src/archives/getZxy.js
8030
+ async function getZxy(layer, z, x, y, signal) {
8031
+ if (!sceneLayers.has(layer)) return layer.getZxy(z, x, y, signal);
8032
+ if (layer.options.archive && layer.options.archive !== layer) {
8033
+ return getZxy(layer.options.archive, z, x, y, signal);
8034
+ }
8035
+ const id = tileId(z, x, y);
8036
+ signal?.throwIfAborted();
8037
+ const header = await getHeader(layer);
8038
+ signal?.throwIfAborted();
8039
+ if (z < header.minZoom || z > header.maxZoom) return;
8040
+ return findTile(layer, header, id, signal);
8041
+ }
8042
+
8043
+ // src/archives/loadSource.js
8044
+ async function loadSource(layer, area2) {
8045
+ if (geojsonByLayer.get(layer)) {
8046
+ const { data, url } = layer.options;
8047
+ if (data !== void 0 && url) {
8048
+ throw (
8049
+ /* alloc */
8050
+ new Error("GeoJSON source requires either data or url")
8051
+ );
8052
+ }
8053
+ if (data !== void 0) return data;
8054
+ if (!url) {
8055
+ throw (
8056
+ /* alloc */
8057
+ new Error("GeoJSON source requires either data or url")
8058
+ );
8059
+ }
8060
+ const sourceUrl = (
8061
+ /* alloc */
8062
+ new URL(url, document.baseURI)
8063
+ );
8064
+ const response = await fetch(
8065
+ sourceUrl,
8066
+ /* alloc */
8067
+ { signal: area2.request.signal }
8068
+ );
8069
+ if (!response.ok) {
8070
+ throw (
8071
+ /* alloc */
8072
+ new Error(`Unable to load GeoJSON: HTTP ${response.status}`)
8073
+ );
8260
8074
  }
8075
+ return response.json();
8261
8076
  }
8262
- return (
8077
+ const result = await getZxy(layer, area2.coords.z, area2.coords.x, area2.coords.y, area2.request.signal);
8078
+ return result ? JSON.parse(archiveDecoder.decode(result.data)) : (
8263
8079
  /* alloc */
8264
8080
  {
8265
- positions: template,
8266
- normals: normals.slice(0, length),
8267
- colors: colors.slice(0, length),
8268
- instanceOffsets: offsets
8081
+ type: "FeatureCollection",
8082
+ features: (
8083
+ /* alloc */
8084
+ []
8085
+ )
8269
8086
  }
8270
8087
  );
8271
8088
  }
8272
8089
 
8273
- // src/sources/attachAreaMesh.js
8274
- function attachAreaMesh(context) {
8275
- const { backend: layer, area: area2 } = context;
8276
- if (!context.positions.length) {
8277
- area2.mesh = null, area2.buildings = 0, area2.skipped = context.skipped, area2.done = true;
8278
- return;
8279
- }
8280
- const device = rendererByLayer.get(layer).device;
8281
- const data = context.preparedGeometry || instanceAreaGeometry(context);
8282
- const attributes = (
8090
+ // src/sources/sourceAreaContext.js
8091
+ init_radians();
8092
+ function sourceAreaContext(layer, area2, map) {
8093
+ const crs = map.options.crs, origin = crs.project(locationByLayer.get(layer));
8094
+ return (
8283
8095
  /* alloc */
8284
8096
  {
8285
- position: (
8097
+ backend: layer,
8098
+ area: area2,
8099
+ map,
8100
+ crs,
8101
+ origin,
8102
+ metres: Math.cos(locationByLayer.get(layer).lat * radians),
8103
+ positions: (
8286
8104
  /* alloc */
8287
- { size: 3, data: data.positions instanceof Float32Array ? data.positions : (
8288
- /* alloc */
8289
- new Float32Array(data.positions)
8290
- ) }
8105
+ []
8291
8106
  ),
8292
- normal: (
8107
+ normals: (
8293
8108
  /* alloc */
8294
- { size: 3, data: data.normals instanceof Float32Array ? data.normals : (
8295
- /* alloc */
8296
- new Float32Array(data.normals)
8297
- ) }
8109
+ []
8298
8110
  ),
8299
- color: (
8111
+ colors: (
8300
8112
  /* alloc */
8301
- { size: 3, data: data.colors instanceof Float32Array ? data.colors : (
8302
- /* alloc */
8303
- new Float32Array(data.colors)
8304
- ) }
8305
- )
8113
+ []
8114
+ ),
8115
+ uvs: (
8116
+ /* alloc */
8117
+ []
8118
+ ),
8119
+ featureFaces: (
8120
+ /* alloc */
8121
+ []
8122
+ ),
8123
+ lights: (
8124
+ /* alloc */
8125
+ []
8126
+ ),
8127
+ supports: (
8128
+ /* alloc */
8129
+ /* @__PURE__ */ new Set()
8130
+ ),
8131
+ skipped: 0
8306
8132
  }
8307
8133
  );
8308
- if (data.instanceOffsets) attributes.instanceOffset = /* alloc */
8309
- { size: 4, data: data.instanceOffsets };
8310
- if (layer.options.nightLight) attributes.uv = /* alloc */
8311
- {
8312
- size: 2,
8313
- data: context.uvs instanceof Float32Array ? context.uvs : (
8314
- /* alloc */
8315
- new Float32Array(context.uvs)
8316
- )
8317
- };
8318
- let name, attribute;
8319
- for (name in attributes) {
8320
- attribute = attributes[name];
8321
- attribute.count = attribute.data.length / attribute.size;
8322
- attribute.buffer = device.createBuffer(
8134
+ }
8135
+
8136
+ // src/globals/geometryWorkerUrl.js
8137
+ var geometryWorkerUrl = void 0 ? new URL("../workers/geometryWorker.js", null.url).href : new URL("leaflet-city-mapper-worker.js", document.currentScript?.src || document.baseURI).href;
8138
+
8139
+ // src/workers/settleGeometryWorker.js
8140
+ function settleGeometryWorker(task, event) {
8141
+ if (event.type === "abort") task.reject(task.signal.reason);
8142
+ else if (event.type === "error") task.reject(
8143
+ /* alloc */
8144
+ new Error(`Geometry worker failed: ${event.message}`)
8145
+ );
8146
+ else if (event.type === "messageerror") task.reject(
8147
+ /* alloc */
8148
+ new Error("Unable to receive geometry worker buffers")
8149
+ );
8150
+ else if (event.data.error) {
8151
+ const error = (
8323
8152
  /* alloc */
8324
- {
8325
- size: Math.max(4, attribute.data.byteLength),
8326
- usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
8327
- }
8153
+ new Error(event.data.error.message)
8328
8154
  );
8329
- device.queue.writeBuffer(attribute.buffer, 0, attribute.data);
8330
- }
8331
- const meshGeometry = (
8155
+ error.name = event.data.error.name;
8156
+ task.reject(error);
8157
+ } else task.resolve(event.data);
8158
+ }
8159
+
8160
+ // src/workers/startGeometryWorker.js
8161
+ function startGeometryWorker(task, resolve, reject) {
8162
+ task.resolve = resolve;
8163
+ task.reject = reject;
8164
+ task.onEvent = settleGeometryWorker.bind(null, task);
8165
+ task.worker.addEventListener("message", task.onEvent);
8166
+ task.worker.addEventListener("error", task.onEvent);
8167
+ task.worker.addEventListener("messageerror", task.onEvent);
8168
+ task.signal.addEventListener(
8169
+ "abort",
8170
+ task.onEvent,
8332
8171
  /* alloc */
8333
- {
8334
- device,
8335
- bounds: data.bounds,
8336
- attributes,
8337
- drawRange: (
8338
- /* alloc */
8339
- { start: 0, count: attributes.position.count }
8340
- )
8341
- }
8172
+ { once: true }
8342
8173
  );
8343
- area2.mesh = /* alloc */
8344
- {
8345
- ...emptyNode(),
8346
- device,
8347
- geometry: meshGeometry,
8348
- program: createProgram(
8349
- layer,
8350
- /* alloc */
8351
- {
8352
- instanced: Boolean(data.instanceOffsets),
8353
- vertexColors: true,
8354
- roofTexture: layer.options.roofTexture,
8355
- wallTexture: layer.options.wallTexture,
8356
- nightLight: Boolean(layer.options.nightLight),
8357
- alphaMode: layer.options.nightLight ? "BLEND" : void 0,
8358
- doubleSided: Boolean(layer.options.nightLight)
8359
- }
8360
- ),
8361
- id: nextId.value++,
8362
- renderOrder: 0,
8363
- fadeDuration: 250,
8364
- extras: (
8365
- /* alloc */
8366
- { featureFaces: context.featureFaces, lights: context.lights }
8367
- )
8368
- };
8369
- area2.buildings = context.featureFaces.length;
8370
- area2.skipped = context.skipped;
8371
- area2.done = true;
8372
- setParent(area2.mesh, rootByLayer.get(layer));
8373
- update(layer);
8174
+ if (task.signal.aborted) {
8175
+ reject(task.signal.reason);
8176
+ return;
8177
+ }
8178
+ task.worker.postMessage(task.input);
8374
8179
  }
8375
8180
 
8376
- // src/globals/archiveDecoder.js
8377
- var archiveDecoder = new TextDecoder();
8181
+ // src/weakmaps/geometryWorkersByMap.js
8182
+ var geometryWorkersByMap = /* @__PURE__ */ new WeakMap();
8378
8183
 
8379
- // src/archives/tileId.js
8380
- function tileId(z, x, y) {
8381
- if (!Number.isInteger(z) || !Number.isInteger(x) || !Number.isInteger(y) || z < 0 || z > 26 || x < 0 || y < 0 || x >= 2 ** z || y >= 2 ** z) {
8382
- throw (
8184
+ // src/workers/closeGeometryWorkers.js
8185
+ function closeGeometryWorkers(pool) {
8186
+ pool.closed = true;
8187
+ let entry;
8188
+ for (entry of pool.entries) {
8189
+ entry.closed = true;
8190
+ entry.worker.terminate();
8191
+ if (entry.task) entry.task.reject(
8383
8192
  /* alloc */
8384
- new Error("Invalid PMTiles tile coordinates")
8193
+ new DOMException("Map removed", "AbortError")
8385
8194
  );
8386
8195
  }
8387
- let id = (4 ** z - 1) / 3;
8388
- const n = 2 ** z;
8389
- let s, rx, ry, swap;
8390
- for (s = n / 2; s >= 1; s /= 2) {
8391
- rx = x & s ? 1 : 0, ry = y & s ? 1 : 0;
8392
- id += s * s * (3 * rx ^ ry);
8393
- if (!ry) {
8394
- if (rx) {
8395
- x = n - 1 - x;
8396
- y = n - 1 - y;
8397
- }
8398
- swap = x;
8399
- x = y;
8400
- y = swap;
8401
- }
8402
- }
8403
- return id;
8196
+ pool.entries.clear();
8197
+ pool.idle.length = 0;
8404
8198
  }
8405
8199
 
8406
- // src/globals/directoryReader.js
8407
- var directoryReader = {
8408
- bytes: null,
8409
- cursor: 0
8410
- };
8411
-
8412
- // src/archives/readVarint.js
8413
- function varint(reader) {
8414
- const { bytes } = reader;
8415
- let value = 0;
8416
- let factor, byte;
8417
- for (factor = 1; factor <= 2 ** 49; factor *= 128) {
8418
- if (reader.cursor >= bytes.length) {
8419
- throw (
8420
- /* alloc */
8421
- new Error("Truncated PMTiles directory")
8422
- );
8423
- }
8424
- byte = bytes[reader.cursor++];
8425
- value += (byte & 127) * factor;
8426
- if (!Number.isSafeInteger(value)) {
8427
- throw (
8200
+ // src/workers/createGeometryWorker.js
8201
+ function createGeometryWorker(url, pool) {
8202
+ let workerUrl = url, blobUrl, worker;
8203
+ if (globalThis.location) {
8204
+ const resolved = (
8205
+ /* alloc */
8206
+ new URL(url, location.href)
8207
+ );
8208
+ if ((resolved.protocol === "https:" || resolved.protocol === "http:") && resolved.origin !== location.origin) {
8209
+ blobUrl = URL.createObjectURL(
8428
8210
  /* alloc */
8429
- new Error("Invalid PMTiles directory integer")
8211
+ new Blob(
8212
+ /* alloc */
8213
+ [`import ${JSON.stringify(resolved.href)};`],
8214
+ /* alloc */
8215
+ { type: "text/javascript" }
8216
+ )
8430
8217
  );
8218
+ workerUrl = blobUrl;
8431
8219
  }
8432
- if (byte < 128) return value;
8433
8220
  }
8434
- throw (
8221
+ try {
8222
+ worker = /* alloc */
8223
+ new Worker(
8224
+ workerUrl,
8225
+ /* alloc */
8226
+ { type: "module" }
8227
+ );
8228
+ } finally {
8229
+ if (!worker && blobUrl) URL.revokeObjectURL(blobUrl);
8230
+ }
8231
+ const entry = (
8435
8232
  /* alloc */
8436
- new Error("Invalid PMTiles directory integer")
8233
+ { url, pool, worker, blobUrl }
8437
8234
  );
8235
+ if (pool) pool.entries.add(entry);
8236
+ return entry;
8438
8237
  }
8439
8238
 
8440
- // src/archives/decodeDirectory.js
8441
- function decodeDirectory(data) {
8442
- const reader = directoryReader;
8443
- const bytes = (
8444
- /* alloc */
8445
- new Uint8Array(data)
8446
- );
8447
- reader.bytes = bytes;
8448
- reader.cursor = 0;
8449
- try {
8450
- const count = varint(reader);
8451
- if (count > (bytes.length - reader.cursor) / 4) {
8452
- throw (
8453
- /* alloc */
8454
- new Error("Invalid PMTiles directory count")
8455
- );
8456
- }
8457
- const entries = (
8239
+ // src/workers/acquireGeometryWorker.js
8240
+ function acquireGeometryWorker(map, url) {
8241
+ let pool = map && geometryWorkersByMap.get(map);
8242
+ if (map && !pool) {
8243
+ pool = /* alloc */
8244
+ { entries: (
8458
8245
  /* alloc */
8459
- new Array(count)
8246
+ /* @__PURE__ */ new Set()
8247
+ ), idle: (
8248
+ /* alloc */
8249
+ []
8250
+ ), closed: false };
8251
+ geometryWorkersByMap.set(map, pool);
8252
+ map.once("unload", closeGeometryWorkers.bind(null, pool));
8253
+ }
8254
+ let entry, i;
8255
+ if (pool) {
8256
+ if (pool.closed) throw (
8257
+ /* alloc */
8258
+ new DOMException("Map removed", "AbortError")
8460
8259
  );
8461
- let tileId2 = 0;
8462
- let i, entry, value;
8463
- for (i = 0; i < count; i++) {
8464
- tileId2 += varint(reader);
8465
- if (!Number.isSafeInteger(tileId2) || i && tileId2 <= entries[i - 1].tileId) {
8466
- throw (
8467
- /* alloc */
8468
- new Error("Invalid PMTiles directory tile IDs")
8469
- );
8470
- }
8471
- entries[i] = /* alloc */
8472
- { tileId: tileId2, runLength: 0, length: 0, offset: 0 };
8473
- }
8474
- for (entry of entries) entry.runLength = varint(reader);
8475
- for (entry of entries) {
8476
- entry.length = varint(reader);
8477
- if (!entry.length) {
8478
- throw (
8479
- /* alloc */
8480
- new Error("Invalid PMTiles directory length")
8481
- );
8482
- }
8260
+ for (i = 0; i < pool.idle.length; i++) {
8261
+ if (pool.idle[i].url !== url) continue;
8262
+ entry = pool.idle[i];
8263
+ pool.idle.splice(i, 1);
8264
+ return entry;
8483
8265
  }
8484
- for (i = 0; i < entries.length; i++) {
8485
- entry = entries[i];
8486
- value = varint(reader);
8487
- if (!value && !i) {
8488
- throw (
8489
- /* alloc */
8490
- new Error("Invalid PMTiles directory offset")
8491
- );
8492
- }
8493
- entry.offset = value ? value - 1 : entries[i - 1].offset + entries[i - 1].length;
8266
+ entry = pool.idle.pop();
8267
+ if (entry) {
8268
+ entry.closed = true;
8269
+ entry.worker.terminate();
8270
+ pool.entries.delete(entry);
8494
8271
  }
8495
- if (reader.cursor !== bytes.length) {
8496
- throw (
8272
+ }
8273
+ return createGeometryWorker(url, pool);
8274
+ }
8275
+
8276
+ // src/workers/buildAreaInWorker.js
8277
+ async function buildAreaInWorker(context, signal) {
8278
+ const entry = acquireGeometryWorker(context.map || context.backend._map, context.backend.options.workerUrl || geometryWorkerUrl);
8279
+ const { worker, pool } = entry;
8280
+ const task = (
8281
+ /* alloc */
8282
+ {
8283
+ worker,
8284
+ signal,
8285
+ input: (
8497
8286
  /* alloc */
8498
- new Error("Trailing PMTiles directory data")
8499
- );
8287
+ { jobs: context.jobs, nightLight: Boolean(context.backend.options.nightLight) }
8288
+ )
8500
8289
  }
8501
- return entries;
8290
+ );
8291
+ entry.task = task;
8292
+ let completed = false;
8293
+ try {
8294
+ const data = await /* alloc */
8295
+ new Promise(startGeometryWorker.bind(null, task));
8296
+ let i;
8297
+ for (i = 0; i < data.ranges.length; i++) Object.assign(context.featureFaces[i], data.ranges[i]);
8298
+ context.positions = data.positions;
8299
+ context.uvs = data.uvs;
8300
+ context.lights = data.lights;
8301
+ context.preparedGeometry = data;
8302
+ completed = true;
8303
+ } finally {
8304
+ worker.removeEventListener("message", task.onEvent);
8305
+ worker.removeEventListener("error", task.onEvent);
8306
+ worker.removeEventListener("messageerror", task.onEvent);
8307
+ signal.removeEventListener("abort", task.onEvent);
8308
+ entry.task = null;
8309
+ if (completed && pool && !pool.closed && !signal.aborted) pool.idle.push(entry);
8310
+ else {
8311
+ if (!entry.closed) worker.terminate();
8312
+ if (pool) pool.entries.delete(entry);
8313
+ }
8314
+ if (entry.blobUrl) {
8315
+ URL.revokeObjectURL(entry.blobUrl);
8316
+ entry.blobUrl = null;
8317
+ }
8318
+ context.jobs = null;
8319
+ }
8320
+ }
8321
+
8322
+ // src/projections/projectTreePoint.js
8323
+ init_L();
8324
+ function projectedTree(context, lon, lat) {
8325
+ const { backend: layer, crs, origin, metres, map } = context;
8326
+ const location2 = (
8327
+ /* alloc */
8328
+ new L.LatLng(lat, lon)
8329
+ );
8330
+ const point = crs.project(location2);
8331
+ const elevation = mapElevationOf(map, location2), root = rootByLayer.get(layer);
8332
+ return (
8333
+ /* alloc */
8334
+ {
8335
+ x: (point.x - origin.x) * metres,
8336
+ y: (point.y - origin.y) * metres,
8337
+ ground: Number.isFinite(elevation) && Number.isFinite(root.position[1]) ? elevation - root.position[1] : 0
8338
+ }
8339
+ );
8340
+ }
8341
+
8342
+ // src/globals/triangleContexts.js
8343
+ var triangleContexts = [];
8344
+
8345
+ // src/sources/groundOffset.js
8346
+ function groundOffset(point) {
8347
+ return point.ground || 0;
8348
+ }
8349
+
8350
+ // src/geometry/appendAreaTriangle.js
8351
+ function addTriangle(context, a, ah, b, bh, c, ch, nx, ny, nz, color) {
8352
+ const { backend: layer, positions, normals, colors, uvs } = context;
8353
+ const ux = b.x - a.x, uy = bh - ah, uz = a.y - b.y;
8354
+ const vx = c.x - a.x, vy = ch - ah, vz = a.y - c.y;
8355
+ if ((uy * vz - uz * vy) * nx + (uz * vx - ux * vz) * ny + (ux * vy - uy * vx) * nz < 0) {
8356
+ const point = b, height = bh;
8357
+ b = c;
8358
+ bh = ch;
8359
+ c = point;
8360
+ ch = height;
8361
+ }
8362
+ positions.push(
8363
+ a.x,
8364
+ ah + groundOffset(a),
8365
+ -a.y,
8366
+ b.x,
8367
+ bh + groundOffset(b),
8368
+ -b.y,
8369
+ c.x,
8370
+ ch + groundOffset(c),
8371
+ -c.y
8372
+ );
8373
+ normals.push(nx, ny, nz, nx, ny, nz, nx, ny, nz);
8374
+ colors.push(color.r, color.g, color.b, color.r, color.g, color.b, color.r, color.g, color.b);
8375
+ if (layer.options.nightLight) uvs.push(...a.uv, ...b.uv, ...c.uv);
8376
+ }
8377
+
8378
+ // src/geometry/addGeneratedTriangle.js
8379
+ function addGeneratedTriangle(...args) {
8380
+ addTriangle(triangleContexts[triangleContexts.length - 1], ...args);
8381
+ }
8382
+
8383
+ // src/geometry/generateTreeGeometry.js
8384
+ function generateTreeGeometry(context, generator, point, style) {
8385
+ triangleContexts.push(context);
8386
+ try {
8387
+ generator(point, style, addGeneratedTriangle);
8502
8388
  } finally {
8503
- reader.bytes = null;
8504
- reader.cursor = 0;
8389
+ triangleContexts.pop();
8505
8390
  }
8506
8391
  }
8507
8392
 
8508
- // src/archives/decompress.js
8509
- async function decompress(data, compression) {
8510
- if (compression === 1) return data;
8511
- if (compression !== 2) {
8393
+ // src/globals/lampDirections.js
8394
+ var lampDirections = [
8395
+ { x: Math.cos(0 * Math.PI / 3), y: Math.sin(0 * Math.PI / 3), nx: Math.cos(0.5 * Math.PI / 3), nz: -Math.sin(0.5 * Math.PI / 3) },
8396
+ { x: Math.cos(1 * Math.PI / 3), y: Math.sin(1 * Math.PI / 3), nx: Math.cos(1.5 * Math.PI / 3), nz: -Math.sin(1.5 * Math.PI / 3) },
8397
+ { x: Math.cos(2 * Math.PI / 3), y: Math.sin(2 * Math.PI / 3), nx: Math.cos(2.5 * Math.PI / 3), nz: -Math.sin(2.5 * Math.PI / 3) },
8398
+ { x: Math.cos(3 * Math.PI / 3), y: Math.sin(3 * Math.PI / 3), nx: Math.cos(3.5 * Math.PI / 3), nz: -Math.sin(3.5 * Math.PI / 3) },
8399
+ { x: Math.cos(4 * Math.PI / 3), y: Math.sin(4 * Math.PI / 3), nx: Math.cos(4.5 * Math.PI / 3), nz: -Math.sin(4.5 * Math.PI / 3) },
8400
+ { x: Math.cos(5 * Math.PI / 3), y: Math.sin(5 * Math.PI / 3), nx: Math.cos(5.5 * Math.PI / 3), nz: -Math.sin(5.5 * Math.PI / 3) }
8401
+ ];
8402
+
8403
+ // src/geometry/createLampRing.js
8404
+ function ring(point, radius) {
8405
+ const points = (
8406
+ /* alloc */
8407
+ new Array(lampDirections.length)
8408
+ );
8409
+ let i;
8410
+ for (i = 0; i < points.length; i++) {
8411
+ points[i] = /* alloc */
8412
+ {
8413
+ x: point.x + lampDirections[i].x * radius,
8414
+ y: point.y + lampDirections[i].y * radius,
8415
+ ground: point.ground,
8416
+ uv: (
8417
+ /* alloc */
8418
+ [3, 3]
8419
+ )
8420
+ };
8421
+ }
8422
+ return points;
8423
+ }
8424
+
8425
+ // src/geometry/lampPostGeometry.js
8426
+ function lampPostGeometry(point, style, addTriangle2) {
8427
+ const color = parseColor(5264986), radius = style.poleRadius;
8428
+ if (!Number.isFinite(radius) || radius <= 0) {
8512
8429
  throw (
8513
8430
  /* alloc */
8514
- new Error(`Unsupported PMTiles compression: ${compression}`)
8431
+ new Error("Invalid lamp pole radius")
8515
8432
  );
8516
8433
  }
8517
- const blob = (
8518
- /* alloc */
8519
- new Blob(
8520
- /* alloc */
8521
- [data]
8522
- )
8523
- );
8524
- const stream = (
8434
+ const center = (
8525
8435
  /* alloc */
8526
- new DecompressionStream("gzip")
8436
+ {
8437
+ ...point,
8438
+ uv: (
8439
+ /* alloc */
8440
+ [3, 3]
8441
+ )
8442
+ }
8527
8443
  );
8444
+ let r, low, high, points, i, a, b, nx, nz, section;
8445
+ for (section = 0; section < 2; section++) {
8446
+ r = section ? radius * 2.5 : radius;
8447
+ low = section ? style.top - 0.25 : 0;
8448
+ high = section ? style.top + 0.15 : style.top;
8449
+ points = ring(point, r);
8450
+ for (i = 0; i < 6; i++) {
8451
+ a = points[i], b = points[(i + 1) % 6];
8452
+ nx = lampDirections[i].nx, nz = lampDirections[i].nz;
8453
+ addTriangle2(a, low, b, low, b, high, nx, 0, nz, color);
8454
+ addTriangle2(a, low, b, high, a, high, nx, 0, nz, color);
8455
+ addTriangle2(center, high, a, high, b, high, 0, 1, 0, color);
8456
+ addTriangle2(center, low, b, low, a, low, 0, -1, 0, color);
8457
+ }
8458
+ }
8459
+ }
8460
+
8461
+ // src/geometry/quad.js
8462
+ function quad(addTriangle2, color, a, b, c, d, height, nx, ny, nz) {
8463
+ if (typeof nx !== "number") {
8464
+ addTriangle2(a, height, b, height, c, height, ...nx, color);
8465
+ addTriangle2(a, height, c, height, d, height, ...nx, color);
8466
+ } else {
8467
+ addTriangle2(a, height, b, height, c, height, nx, ny, nz, color);
8468
+ addTriangle2(a, height, c, height, d, height, nx, ny, nz, color);
8469
+ }
8470
+ }
8471
+
8472
+ // src/geometry/emitter.js
8473
+ function emitter(point, u, v, axis) {
8474
+ const r = 0.65;
8528
8475
  return (
8529
8476
  /* alloc */
8530
- new Response(blob.stream().pipeThrough(stream)).arrayBuffer()
8477
+ {
8478
+ x: point.x + (axis === 2 ? 0 : u * r),
8479
+ y: point.y + (axis === 1 ? 0 : axis === 2 ? u * r : v * r),
8480
+ ground: point.ground + (axis === 0 ? 0 : v * r),
8481
+ uv: (
8482
+ /* alloc */
8483
+ [u, v]
8484
+ )
8485
+ }
8531
8486
  );
8532
8487
  }
8533
8488
 
8534
- // src/archives/loadDirectory.js
8535
- async function loadDirectory(layer, offset, length, compression) {
8536
- return decodeDirectory(await decompress(await read(layer, offset, length), compression));
8489
+ // src/geometry/lightGeometry.js
8490
+ function lightGeometry(point, style, addTriangle2) {
8491
+ if (!Number.isFinite(style.lightRadius) || style.lightRadius <= 0) throw (
8492
+ /* alloc */
8493
+ new Error("Invalid light radius")
8494
+ );
8495
+ const color = parseColor(style.color);
8496
+ let axis;
8497
+ for (axis = 0; axis < 3; axis++) {
8498
+ quad(
8499
+ addTriangle2,
8500
+ color,
8501
+ emitter(point, -1, -1, axis),
8502
+ emitter(point, 1, -1, axis),
8503
+ emitter(point, 1, 1, axis),
8504
+ emitter(point, -1, 1, axis),
8505
+ style.top,
8506
+ axis === 2 ? 1 : 0,
8507
+ axis === 0 ? 1 : 0,
8508
+ axis === 1 ? 1 : 0
8509
+ );
8510
+ }
8537
8511
  }
8538
8512
 
8539
- // src/archives/getDirectoryEntries.js
8540
- async function getDirectoryEntries(layer, offset, length, compression) {
8541
- const directories = directoriesByLayer.get(layer);
8542
- const key = `${offset}:${length}`;
8543
- if (!directories.has(key)) {
8544
- if (directories.size >= 32) directories.delete(directories.keys().next().value);
8545
- directories.set(key, loadDirectory(layer, offset, length, compression));
8546
- }
8547
- const pending = directories.get(key);
8548
- try {
8549
- return await pending;
8550
- } catch (error) {
8551
- if (directories.get(key) === pending) directories.delete(key);
8552
- throw error;
8513
+ // src/globals/roundProfile.js
8514
+ var roundProfile = [
8515
+ [0, 0],
8516
+ [0.35, 0.8],
8517
+ [0.6, 1],
8518
+ [0.85, 0.7],
8519
+ [1, 0]
8520
+ ];
8521
+
8522
+ // src/globals/ringDirections.js
8523
+ var ringDirections = [
8524
+ {
8525
+ x: Math.cos(0 * Math.PI / 4),
8526
+ y: Math.sin(0 * Math.PI / 4),
8527
+ nx: Math.cos((0 + 0.5) * Math.PI / 4),
8528
+ nz: -Math.sin((0 + 0.5) * Math.PI / 4)
8529
+ },
8530
+ {
8531
+ x: Math.cos(1 * Math.PI / 4),
8532
+ y: Math.sin(1 * Math.PI / 4),
8533
+ nx: Math.cos((1 + 0.5) * Math.PI / 4),
8534
+ nz: -Math.sin((1 + 0.5) * Math.PI / 4)
8535
+ },
8536
+ {
8537
+ x: Math.cos(2 * Math.PI / 4),
8538
+ y: Math.sin(2 * Math.PI / 4),
8539
+ nx: Math.cos((2 + 0.5) * Math.PI / 4),
8540
+ nz: -Math.sin((2 + 0.5) * Math.PI / 4)
8541
+ },
8542
+ {
8543
+ x: Math.cos(3 * Math.PI / 4),
8544
+ y: Math.sin(3 * Math.PI / 4),
8545
+ nx: Math.cos((3 + 0.5) * Math.PI / 4),
8546
+ nz: -Math.sin((3 + 0.5) * Math.PI / 4)
8547
+ },
8548
+ {
8549
+ x: Math.cos(4 * Math.PI / 4),
8550
+ y: Math.sin(4 * Math.PI / 4),
8551
+ nx: Math.cos((4 + 0.5) * Math.PI / 4),
8552
+ nz: -Math.sin((4 + 0.5) * Math.PI / 4)
8553
+ },
8554
+ {
8555
+ x: Math.cos(5 * Math.PI / 4),
8556
+ y: Math.sin(5 * Math.PI / 4),
8557
+ nx: Math.cos((5 + 0.5) * Math.PI / 4),
8558
+ nz: -Math.sin((5 + 0.5) * Math.PI / 4)
8559
+ },
8560
+ {
8561
+ x: Math.cos(6 * Math.PI / 4),
8562
+ y: Math.sin(6 * Math.PI / 4),
8563
+ nx: Math.cos((6 + 0.5) * Math.PI / 4),
8564
+ nz: -Math.sin((6 + 0.5) * Math.PI / 4)
8565
+ },
8566
+ {
8567
+ x: Math.cos(7 * Math.PI / 4),
8568
+ y: Math.sin(7 * Math.PI / 4),
8569
+ nx: Math.cos((7 + 0.5) * Math.PI / 4),
8570
+ nz: -Math.sin((7 + 0.5) * Math.PI / 4)
8553
8571
  }
8554
- }
8572
+ ];
8555
8573
 
8556
- // src/archives/findDirectoryEntry.js
8557
- function findDirectoryEntry(entries, id) {
8558
- let low = 0, high = entries.length - 1, mid;
8559
- while (low <= high) {
8560
- mid = low + high >> 1;
8561
- if (entries[mid].tileId <= id) low = mid + 1;
8562
- else high = mid - 1;
8574
+ // src/geometry/createTreeRing.js
8575
+ function ring2(point, radius) {
8576
+ const result = (
8577
+ /* alloc */
8578
+ new Array(ringDirections.length)
8579
+ );
8580
+ let i;
8581
+ for (i = 0; i < result.length; i++) {
8582
+ result[i] = /* alloc */
8583
+ {
8584
+ x: point.x + ringDirections[i].x * radius,
8585
+ y: point.y + ringDirections[i].y * radius,
8586
+ ground: point.ground
8587
+ };
8563
8588
  }
8564
- return entries[high];
8589
+ return result;
8565
8590
  }
8566
8591
 
8567
- // src/archives/getTileData.js
8568
- async function getTileData(layer, header, entry, id, signal) {
8569
- if (id - entry.tileId >= entry.runLength) return;
8570
- if (entry.offset + entry.length > header.tileDataLength) {
8592
+ // src/geometry/surface.js
8593
+ function surface(point, addTriangle2, lowRadius, low, highRadius, high, color) {
8594
+ const lower = ring2(point, lowRadius), upper = ring2(point, highRadius);
8595
+ let i, next, slope, length, nx, nz, ny, normalX, normalZ;
8596
+ for (i = 0; i < 8; i++) {
8597
+ next = (i + 1) % 8;
8598
+ slope = (lowRadius - highRadius) / (high - low);
8599
+ length = Math.hypot(1, slope);
8600
+ ({ nx, nz } = ringDirections[i]);
8601
+ ny = slope / length;
8602
+ normalX = nx / length, normalZ = nz / length;
8603
+ if (lowRadius) addTriangle2(lower[i], low, lower[next], low, upper[next], high, normalX, ny, normalZ, color);
8604
+ if (highRadius) addTriangle2(lower[i], low, upper[next], high, upper[i], high, normalX, ny, normalZ, color);
8605
+ }
8606
+ }
8607
+
8608
+ // src/geometry/validateTree.js
8609
+ function validateTree(trunkRadius, crownRadius, crownBottom, bottom, top, crownShape) {
8610
+ if (!Number.isFinite(trunkRadius) || !Number.isFinite(crownRadius) || !Number.isFinite(crownBottom) || trunkRadius <= 0 || crownRadius < trunkRadius || crownBottom <= bottom || crownBottom >= top || crownShape !== "round" && crownShape !== "cone") {
8571
8611
  throw (
8572
8612
  /* alloc */
8573
- new Error("PMTiles tile outside data section")
8613
+ new Error("Invalid tree dimensions or crown shape")
8574
8614
  );
8575
8615
  }
8576
- const data = await decompress(
8577
- await read(layer, header.tileDataOffset + entry.offset, entry.length, signal),
8578
- header.tileCompression
8579
- );
8580
- signal?.throwIfAborted();
8581
- return (
8582
- /* alloc */
8583
- { data }
8584
- );
8585
8616
  }
8586
8617
 
8587
- // src/archives/findTile.js
8588
- async function findTile(layer, header, id, signal) {
8589
- let offset = header.rootDirectoryOffset, length = header.rootDirectoryLength;
8590
- let firstOffset = -1, secondOffset = -1, thirdOffset = -1;
8591
- let depth, entries, entry, tile;
8592
- for (depth = 0; depth < 4; depth++) {
8593
- if (offset === firstOffset || offset === secondOffset || offset === thirdOffset) {
8594
- throw (
8595
- /* alloc */
8596
- new Error("Cyclic PMTiles directory")
8597
- );
8598
- }
8599
- if (depth === 0) firstOffset = offset;
8600
- else if (depth === 1) secondOffset = offset;
8601
- else if (depth === 2) thirdOffset = offset;
8602
- entries = await getDirectoryEntries(layer, offset, length, header.internalCompression);
8603
- signal?.throwIfAborted();
8604
- entry = findDirectoryEntry(entries, id);
8605
- if (!entry) return;
8606
- if (entry.runLength) {
8607
- tile = await getTileData(layer, header, entry, id, signal);
8608
- return tile;
8609
- }
8610
- if (entry.offset + entry.length > header.leafDirectoryLength) {
8611
- throw (
8612
- /* alloc */
8613
- new Error("PMTiles directory outside leaf section")
8618
+ // src/geometry/treeGeometry.js
8619
+ function treeGeometry(point, style, addTriangle2) {
8620
+ const { top, bottom = 0, trunkRadius, crownRadius, crownBottom, crownShape = "round" } = style;
8621
+ validateTree(trunkRadius, crownRadius, crownBottom, bottom, top, crownShape);
8622
+ const trunk = parseColor(style.trunkColor ?? 7491898);
8623
+ const crown = parseColor(style.color ?? 5210683);
8624
+ surface(point, addTriangle2, trunkRadius, bottom, trunkRadius, crownBottom + (top - crownBottom) * 0.25, trunk);
8625
+ let i, low, lr, high, hr;
8626
+ if (crownShape === "cone") {
8627
+ const base = ring2(point, crownRadius);
8628
+ for (i = 0; i < 8; i++) addTriangle2(
8629
+ point,
8630
+ crownBottom,
8631
+ base[i],
8632
+ crownBottom,
8633
+ base[(i + 1) % 8],
8634
+ crownBottom,
8635
+ 0,
8636
+ -1,
8637
+ 0,
8638
+ crown
8639
+ );
8640
+ surface(point, addTriangle2, crownRadius, crownBottom, 0, top, crown);
8641
+ } else {
8642
+ for (i = 1; i < roundProfile.length; i++) {
8643
+ [low, lr] = roundProfile[i - 1], [high, hr] = roundProfile[i];
8644
+ surface(
8645
+ point,
8646
+ addTriangle2,
8647
+ lr * crownRadius,
8648
+ crownBottom + low * (top - crownBottom),
8649
+ hr * crownRadius,
8650
+ crownBottom + high * (top - crownBottom),
8651
+ crown
8614
8652
  );
8615
8653
  }
8616
- offset = header.leafDirectoryOffset + entry.offset;
8617
- length = entry.length;
8618
8654
  }
8619
- throw (
8620
- /* alloc */
8621
- new Error("PMTiles directory depth exceeds four levels")
8622
- );
8623
8655
  }
8624
8656
 
8625
- // src/archives/getZxy.js
8626
- async function getZxy(layer, z, x, y, signal) {
8627
- if (!sceneLayers.has(layer)) return layer.getZxy(z, x, y, signal);
8628
- if (layer.options.archive && layer.options.archive !== layer) {
8629
- return getZxy(layer.options.archive, z, x, y, signal);
8630
- }
8631
- const id = tileId(z, x, y);
8632
- signal?.throwIfAborted();
8633
- const header = await getHeader(layer);
8634
- signal?.throwIfAborted();
8635
- if (z < header.minZoom || z > header.maxZoom) return;
8636
- return findTile(layer, header, id, signal);
8657
+ // src/geometry/buildProjectedPoint.js
8658
+ function buildProjectedPoint(context, projected, style, baseColor, support) {
8659
+ const { top } = style;
8660
+ if (context.backend.options.nightLight) {
8661
+ if (!Number.isFinite(style.lightFlux) || style.lightFlux <= 0) throw (
8662
+ /* alloc */
8663
+ new Error("Invalid light flux")
8664
+ );
8665
+ if (style.poleRadius > 0 && !context.supports.has(support)) {
8666
+ generateTreeGeometry(context, lampPostGeometry, projected, style);
8667
+ context.supports.add(support);
8668
+ }
8669
+ generateTreeGeometry(context, lightGeometry, projected, style);
8670
+ context.lights.push(
8671
+ /* alloc */
8672
+ {
8673
+ position: (
8674
+ /* alloc */
8675
+ [projected.x, projected.ground + top, -projected.y]
8676
+ ),
8677
+ radius: Math.hypot(top, style.lightRadius),
8678
+ color: (
8679
+ /* alloc */
8680
+ [baseColor.r, baseColor.g, baseColor.b]
8681
+ ),
8682
+ flux: style.lightFlux
8683
+ }
8684
+ );
8685
+ } else generateTreeGeometry(context, treeGeometry, projected, style);
8637
8686
  }
8638
8687
 
8639
- // src/archives/loadSource.js
8640
- async function loadSource(layer, area2) {
8641
- if (geojsonByLayer.get(layer)) {
8642
- const { data, url } = layer.options;
8643
- if (data !== void 0 && url) {
8644
- throw (
8645
- /* alloc */
8646
- new Error("GeoJSON source requires either data or url")
8647
- );
8648
- }
8649
- if (data !== void 0) return data;
8650
- if (!url) {
8651
- throw (
8652
- /* alloc */
8653
- new Error("GeoJSON source requires either data or url")
8654
- );
8655
- }
8656
- const sourceUrl = (
8688
+ // src/sources/buildAreaPoint.js
8689
+ function buildAreaPoint(context, feature, lon, lat, style, baseColor) {
8690
+ const projected = projectedTree(context, lon, lat);
8691
+ buildProjectedPoint(context, projected, style, baseColor, feature.properties?.cod_ouvrag || `${lon},${lat}`);
8692
+ }
8693
+
8694
+ // src/projections/projectRing.js
8695
+ init_L();
8696
+ function projectRing(context, ring3) {
8697
+ const { backend: layer, crs, origin, metres, map } = context;
8698
+ if (ring3.length < 4) throw (
8699
+ /* alloc */
8700
+ new Error("Invalid GeoJSON polygon ring")
8701
+ );
8702
+ const path = (
8703
+ /* alloc */
8704
+ []
8705
+ );
8706
+ const root = rootByLayer.get(layer);
8707
+ let i, lng, lat, location2, point, elevation;
8708
+ for (i = 0; i < ring3.length - 1; i++) {
8709
+ lng = ring3[i][0], lat = ring3[i][1];
8710
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) throw (
8657
8711
  /* alloc */
8658
- new URL(url, document.baseURI)
8712
+ new Error("Invalid GeoJSON coordinates")
8659
8713
  );
8660
- const response = await fetch(
8661
- sourceUrl,
8714
+ location2 = /* alloc */
8715
+ new L.LatLng(lat, lng);
8716
+ point = crs.project(location2);
8717
+ elevation = mapElevationOf(map, location2);
8718
+ path.push(
8662
8719
  /* alloc */
8663
- { signal: area2.request.signal }
8720
+ {
8721
+ x: (point.x - origin.x) * metres,
8722
+ y: (point.y - origin.y) * metres,
8723
+ ground: Number.isFinite(elevation) && Number.isFinite(root.position[1]) ? elevation - root.position[1] : 0
8724
+ }
8664
8725
  );
8665
- if (!response.ok) {
8666
- throw (
8667
- /* alloc */
8668
- new Error(`Unable to load GeoJSON: HTTP ${response.status}`)
8669
- );
8670
- }
8671
- return response.json();
8672
8726
  }
8673
- const result = await getZxy(layer, area2.coords.z, area2.coords.x, area2.coords.y, area2.request.signal);
8674
- return result ? JSON.parse(archiveDecoder.decode(result.data)) : (
8675
- /* alloc */
8676
- {
8677
- type: "FeatureCollection",
8678
- features: (
8679
- /* alloc */
8680
- []
8681
- )
8682
- }
8683
- );
8727
+ return path;
8684
8728
  }
8685
8729
 
8686
- // src/sources/sourceAreaContext.js
8687
- init_radians();
8688
- function sourceAreaContext(layer, area2, map) {
8689
- const crs = map.options.crs, origin = crs.project(locationByLayer.get(layer));
8690
- return (
8730
+ // src/geometry/buildProjectedPolygon.js
8731
+ function buildProjectedPolygon(context, paths, top, bottom, baseColor) {
8732
+ const points = (
8691
8733
  /* alloc */
8692
- {
8693
- backend: layer,
8694
- area: area2,
8695
- map,
8696
- crs,
8697
- origin,
8698
- metres: Math.cos(locationByLayer.get(layer).lat * radians),
8699
- positions: (
8700
- /* alloc */
8701
- []
8702
- ),
8703
- normals: (
8704
- /* alloc */
8705
- []
8706
- ),
8707
- colors: (
8708
- /* alloc */
8709
- []
8710
- ),
8711
- uvs: (
8712
- /* alloc */
8713
- []
8714
- ),
8715
- featureFaces: (
8716
- /* alloc */
8717
- []
8718
- ),
8719
- lights: (
8720
- /* alloc */
8721
- []
8722
- ),
8723
- supports: (
8724
- /* alloc */
8725
- /* @__PURE__ */ new Set()
8726
- ),
8727
- skipped: 0
8734
+ []
8735
+ );
8736
+ let path, point, a, b, c, index, sign, i, length, nx, nz;
8737
+ for (path of paths) for (point of path) points.push(point);
8738
+ for ([a, b, c] of triangulate(paths[0], paths.slice(1))) {
8739
+ addTriangle(context, points[a], top, points[b], top, points[c], top, 0, 1, 0, baseColor);
8740
+ if (bottom > 0) addTriangle(context, points[a], bottom, points[b], bottom, points[c], bottom, 0, -1, 0, baseColor);
8741
+ }
8742
+ for (index = 0; index < paths.length; index++) {
8743
+ path = paths[index];
8744
+ sign = isClockWise(path) === (index === 0) ? -1 : 1;
8745
+ for (i = 0; i < path.length; i++) {
8746
+ a = path[i], b = path[(i + 1) % path.length];
8747
+ length = Math.hypot(b.x - a.x, b.y - a.y);
8748
+ if (!length) continue;
8749
+ nx = sign * (b.y - a.y) / length, nz = sign * (b.x - a.x) / length;
8750
+ addTriangle(context, a, bottom, b, bottom, b, top, nx, 0, nz, baseColor);
8751
+ addTriangle(context, a, bottom, b, top, a, top, nx, 0, nz, baseColor);
8728
8752
  }
8729
- );
8753
+ }
8730
8754
  }
8731
8755
 
8732
- // src/globals/geometryWorkerUrl.js
8733
- var geometryWorkerUrl = void 0 ? new URL("../workers/geometryWorker.js", null.url).href : new URL("leaflet-city-mapper-worker.js", document.currentScript?.src || document.baseURI).href;
8734
-
8735
- // src/workers/settleGeometryWorker.js
8736
- function settleGeometryWorker(task, event) {
8737
- if (event.type === "abort") task.reject(task.signal.reason);
8738
- else if (event.type === "error") task.reject(
8756
+ // src/geometry/buildExtrudedPolygon.js
8757
+ function buildAreaPolygon(context, rings, top, bottom, baseColor) {
8758
+ if (!rings.length) throw (
8739
8759
  /* alloc */
8740
- new Error(`Geometry worker failed: ${event.message}`)
8760
+ new Error("Empty GeoJSON polygon")
8741
8761
  );
8742
- else if (event.type === "messageerror") task.reject(
8762
+ const paths = (
8743
8763
  /* alloc */
8744
- new Error("Unable to receive geometry worker buffers")
8764
+ []
8745
8765
  );
8746
- else if (event.data.error) {
8747
- const error = (
8748
- /* alloc */
8749
- new Error(event.data.error.message)
8750
- );
8751
- error.name = event.data.error.name;
8752
- task.reject(error);
8753
- } else task.resolve(event.data);
8766
+ let ring3;
8767
+ for (ring3 of rings) paths.push(projectRing(context, ring3));
8768
+ buildProjectedPolygon(context, paths, top, bottom, baseColor);
8754
8769
  }
8755
8770
 
8756
- // src/workers/startGeometryWorker.js
8757
- function startGeometryWorker(task, resolve, reject) {
8758
- task.resolve = resolve;
8759
- task.reject = reject;
8760
- task.onEvent = settleGeometryWorker.bind(null, task);
8761
- task.worker.addEventListener("message", task.onEvent);
8762
- task.worker.addEventListener("error", task.onEvent);
8763
- task.worker.addEventListener("messageerror", task.onEvent);
8764
- task.signal.addEventListener(
8765
- "abort",
8766
- task.onEvent,
8771
+ // src/sources/featureAnchor.js
8772
+ function featureAnchor(feature, nightLight) {
8773
+ const geometry = feature?.geometry, point = geometry?.type === "Point";
8774
+ if (nightLight && !point) throw (
8767
8775
  /* alloc */
8768
- { once: true }
8776
+ new Error("Night lights require Point geometry")
8769
8777
  );
8770
- if (task.signal.aborted) {
8771
- reject(task.signal.reason);
8772
- return;
8773
- }
8774
- task.worker.postMessage(task.input);
8778
+ if (geometry?.type !== "Polygon" && geometry?.type !== "MultiPolygon" && !point) throw (
8779
+ /* alloc */
8780
+ new Error("Unsupported GeoJSON geometry")
8781
+ );
8782
+ const coordinates = point ? geometry.coordinates : geometry.type === "Polygon" ? geometry.coordinates[0]?.[0] : geometry.coordinates[0]?.[0]?.[0];
8783
+ if (!Number.isFinite(coordinates?.[0]) || !Number.isFinite(coordinates?.[1])) throw (
8784
+ /* alloc */
8785
+ new Error("Invalid GeoJSON coordinates")
8786
+ );
8787
+ return coordinates;
8775
8788
  }
8776
8789
 
8777
- // src/weakmaps/geometryWorkersByMap.js
8778
- var geometryWorkersByMap = /* @__PURE__ */ new WeakMap();
8779
-
8780
- // src/workers/closeGeometryWorkers.js
8781
- function closeGeometryWorkers(pool) {
8782
- pool.closed = true;
8783
- let entry;
8784
- for (entry of pool.entries) {
8785
- entry.closed = true;
8786
- entry.worker.terminate();
8787
- if (entry.task) entry.task.reject(
8790
+ // src/sources/extrusionStyle.js
8791
+ function extrusionStyle(layer, feature) {
8792
+ const style = layer.options.style(feature);
8793
+ if (style === null) return null;
8794
+ if (!style || typeof style !== "object") throw (
8795
+ /* alloc */
8796
+ new Error("style must return an extrusion style or null")
8797
+ );
8798
+ const { top, bottom = 0 } = style;
8799
+ if (!Number.isFinite(top) || !Number.isFinite(bottom) || bottom < 0 || top <= bottom) {
8800
+ throw (
8788
8801
  /* alloc */
8789
- new DOMException("Map removed", "AbortError")
8802
+ new Error("Invalid extrusion heights: expected top > bottom >= 0")
8790
8803
  );
8791
8804
  }
8792
- pool.entries.clear();
8793
- pool.idle.length = 0;
8805
+ return style;
8794
8806
  }
8795
8807
 
8796
- // src/workers/acquireGeometryWorker.js
8797
- function acquireGeometryWorker(map, url) {
8798
- let pool = map && geometryWorkersByMap.get(map);
8799
- if (map && !pool) {
8800
- pool = /* alloc */
8801
- { entries: (
8802
- /* alloc */
8803
- /* @__PURE__ */ new Set()
8804
- ), idle: (
8805
- /* alloc */
8806
- []
8807
- ), closed: false };
8808
- geometryWorkersByMap.set(map, pool);
8809
- map.once("unload", closeGeometryWorkers.bind(null, pool));
8810
- }
8811
- let entry, i;
8812
- if (pool) {
8813
- if (pool.closed) throw (
8808
+ // src/sources/projectAreaPolygons.js
8809
+ function projectAreaPolygons(context, geometry) {
8810
+ const rings = geometry.type === "Polygon" ? (
8811
+ /* alloc */
8812
+ [geometry.coordinates]
8813
+ ) : geometry.type === "MultiPolygon" ? geometry.coordinates : (
8814
+ /* alloc */
8815
+ []
8816
+ );
8817
+ const polygons = (
8818
+ /* alloc */
8819
+ []
8820
+ );
8821
+ let polygon2, ring3, paths;
8822
+ for (polygon2 of rings) {
8823
+ if (!polygon2.length) throw (
8814
8824
  /* alloc */
8815
- new DOMException("Map removed", "AbortError")
8825
+ new Error("Empty GeoJSON polygon")
8816
8826
  );
8817
- for (i = 0; i < pool.idle.length; i++) {
8818
- if (pool.idle[i].url !== url) continue;
8819
- entry = pool.idle[i];
8820
- pool.idle.splice(i, 1);
8821
- return entry;
8822
- }
8823
- entry = pool.idle.pop();
8824
- if (entry) {
8825
- entry.closed = true;
8826
- entry.worker.terminate();
8827
- pool.entries.delete(entry);
8828
- }
8827
+ paths = /* alloc */
8828
+ [];
8829
+ for (ring3 of polygon2) paths.push(projectRing(context, ring3));
8830
+ polygons.push(paths);
8829
8831
  }
8830
- entry = /* alloc */
8831
- {
8832
- url,
8833
- pool,
8834
- worker: (
8835
- /* alloc */
8836
- new Worker(
8837
- url,
8838
- /* alloc */
8839
- { type: "module" }
8840
- )
8841
- )
8842
- };
8843
- if (pool) pool.entries.add(entry);
8844
- return entry;
8832
+ return polygons;
8845
8833
  }
8846
8834
 
8847
- // src/workers/buildAreaInWorker.js
8848
- async function buildAreaInWorker(context, signal) {
8849
- const entry = acquireGeometryWorker(context.map || context.backend._map, context.backend.options.workerUrl || geometryWorkerUrl);
8850
- const { worker, pool } = entry;
8851
- const task = (
8835
+ // src/sources/prepareAreaGeometryJob.js
8836
+ function prepareAreaGeometryJob(context, feature, lon, lat, style, baseColor) {
8837
+ const geometry = feature.geometry;
8838
+ const polygons = projectAreaPolygons(context, geometry);
8839
+ const point = geometry.type === "Point" ? projectedTree(context, lon, lat) : null;
8840
+ const nightLight = context.backend.options.nightLight;
8841
+ const crownColor = point ? parseColor(style.color ?? (nightLight ? void 0 : 5210683)) : baseColor;
8842
+ const trunkColor = point && !nightLight ? parseColor(style.trunkColor ?? 7491898) : null;
8843
+ context.jobs.push(
8852
8844
  /* alloc */
8853
8845
  {
8854
- worker,
8855
- signal,
8856
- input: (
8846
+ point,
8847
+ polygons,
8848
+ baseColor: (
8857
8849
  /* alloc */
8858
- { jobs: context.jobs, nightLight: Boolean(context.backend.options.nightLight) }
8850
+ { r: baseColor.r, g: baseColor.g, b: baseColor.b }
8851
+ ),
8852
+ support: feature.properties?.cod_ouvrag || `${lon},${lat}`,
8853
+ style: (
8854
+ /* alloc */
8855
+ {
8856
+ top: style.top,
8857
+ bottom: style.bottom ?? 0,
8858
+ color: (
8859
+ /* alloc */
8860
+ { r: crownColor.r, g: crownColor.g, b: crownColor.b }
8861
+ ),
8862
+ trunkColor: trunkColor ? (
8863
+ /* alloc */
8864
+ { r: trunkColor.r, g: trunkColor.g, b: trunkColor.b }
8865
+ ) : void 0,
8866
+ trunkRadius: style.trunkRadius,
8867
+ crownRadius: style.crownRadius,
8868
+ crownBottom: style.crownBottom,
8869
+ crownShape: style.crownShape,
8870
+ poleRadius: style.poleRadius,
8871
+ lightRadius: style.lightRadius,
8872
+ lightFlux: style.lightFlux
8873
+ }
8859
8874
  )
8860
8875
  }
8861
8876
  );
8862
- entry.task = task;
8863
- let completed = false;
8864
- try {
8865
- const data = await /* alloc */
8866
- new Promise(startGeometryWorker.bind(null, task));
8867
- let i;
8868
- for (i = 0; i < data.ranges.length; i++) Object.assign(context.featureFaces[i], data.ranges[i]);
8869
- context.positions = data.positions;
8870
- context.uvs = data.uvs;
8871
- context.lights = data.lights;
8872
- context.preparedGeometry = data;
8873
- completed = true;
8874
- } finally {
8875
- worker.removeEventListener("message", task.onEvent);
8876
- worker.removeEventListener("error", task.onEvent);
8877
- worker.removeEventListener("messageerror", task.onEvent);
8878
- signal.removeEventListener("abort", task.onEvent);
8879
- entry.task = null;
8880
- if (completed && pool && !pool.closed && !signal.aborted) pool.idle.push(entry);
8881
- else {
8882
- if (!entry.closed) worker.terminate();
8883
- if (pool) pool.entries.delete(entry);
8877
+ }
8878
+
8879
+ // src/sources/matchesAreaTile.js
8880
+ init_L();
8881
+ function matchesAreaTile(map, coords, lon, lat) {
8882
+ if (!coords) return true;
8883
+ const anchor = map.project(
8884
+ /* alloc */
8885
+ new L.LatLng(lat, lon),
8886
+ coords.z
8887
+ );
8888
+ return Math.floor(anchor.x / 256) === coords.x && Math.floor(anchor.y / 256) === coords.y;
8889
+ }
8890
+
8891
+ // src/sources/buildAreaFeature.js
8892
+ function buildAreaFeature(context, feature) {
8893
+ const { backend: layer, area: area2, map } = context;
8894
+ const coords = area2.coords;
8895
+ const coordinates = featureAnchor(feature, layer.options.nightLight);
8896
+ const geometry = feature.geometry, isTree = geometry.type === "Point";
8897
+ const lon = coordinates[0], lat = coordinates[1];
8898
+ if (!matchesAreaTile(map, coords, lon, lat)) return;
8899
+ const style = extrusionStyle(layer, feature);
8900
+ if (style === null) {
8901
+ context.skipped++;
8902
+ return;
8903
+ }
8904
+ const { top, bottom = 0, color = 14267276 } = style;
8905
+ const baseColor = parseColor(color);
8906
+ if (context.jobs) {
8907
+ prepareAreaGeometryJob(context, feature, lon, lat, style, baseColor);
8908
+ context.featureFaces.push(
8909
+ /* alloc */
8910
+ {
8911
+ id: feature.id ?? feature.properties?.objectid ?? JSON.stringify(geometry),
8912
+ properties: feature.properties,
8913
+ baseColor
8914
+ }
8915
+ );
8916
+ return;
8917
+ }
8918
+ const vertexStart = context.positions.length / 3;
8919
+ if (isTree) buildAreaPoint(context, feature, lon, lat, style, baseColor);
8920
+ let polygon2;
8921
+ if (geometry.type === "Polygon") buildAreaPolygon(context, geometry.coordinates, top, bottom, baseColor);
8922
+ else if (geometry.type === "MultiPolygon") for (polygon2 of geometry.coordinates) buildAreaPolygon(context, polygon2, top, bottom, baseColor);
8923
+ const vertexEnd = context.positions.length / 3;
8924
+ context.featureFaces.push(
8925
+ /* alloc */
8926
+ {
8927
+ id: feature.id ?? feature.properties?.objectid ?? JSON.stringify(geometry),
8928
+ properties: feature.properties,
8929
+ baseColor,
8930
+ faceStart: vertexStart / 3,
8931
+ faceEnd: vertexEnd / 3,
8932
+ vertexStart,
8933
+ vertexEnd
8884
8934
  }
8885
- context.jobs = null;
8935
+ );
8936
+ }
8937
+
8938
+ // src/sources/buildAreaFeatures.js
8939
+ async function buildAreaFeatures(context, area2, features) {
8940
+ let deadline = performance.now() + 4, feature;
8941
+ for (feature of features) {
8942
+ if (area2.request.signal.aborted) return;
8943
+ if (performance.now() >= deadline) {
8944
+ await /* alloc */
8945
+ new Promise(requestAnimationFrame);
8946
+ if (area2.request.signal.aborted) return;
8947
+ deadline = performance.now() + 4;
8948
+ }
8949
+ buildAreaFeature(context, feature);
8886
8950
  }
8887
8951
  }
8888
8952
 
8889
8953
  // src/sources/buildArea.js
8890
8954
  async function buildArea(layer, area2, map) {
8891
- let feature;
8892
8955
  try {
8893
8956
  await /* alloc */
8894
8957
  new Promise(requestAnimationFrame);
@@ -8913,17 +8976,8 @@
8913
8976
  context.jobs = /* alloc */
8914
8977
  [];
8915
8978
  }
8916
- let deadline = performance.now() + 4;
8917
- for (feature of data.features) {
8918
- if (area2.request.signal.aborted) return;
8919
- if (performance.now() >= deadline) {
8920
- await /* alloc */
8921
- new Promise(requestAnimationFrame);
8922
- if (area2.request.signal.aborted) return;
8923
- deadline = performance.now() + 4;
8924
- }
8925
- buildAreaFeature(context, feature);
8926
- }
8979
+ await buildAreaFeatures(context, area2, data.features);
8980
+ if (area2.request.signal.aborted) return;
8927
8981
  if (context.jobs) await buildAreaInWorker(context, area2.request.signal);
8928
8982
  if (area2.request.signal.aborted) return;
8929
8983
  attachAreaMesh(context);
@@ -10261,26 +10315,26 @@
10261
10315
  /* alloc */
10262
10316
  new Error("glTF mesh has no positions")
10263
10317
  );
10264
- const vertexCount = attributes.position.data.length / 3;
10265
- if (attributes.position.size !== 3 || vertexCount < 3) throw (
10318
+ const vertexCount2 = attributes.position.data.length / 3;
10319
+ if (attributes.position.size !== 3 || vertexCount2 < 3) throw (
10266
10320
  /* alloc */
10267
10321
  new Error("Invalid glTF vertex attributes")
10268
10322
  );
10269
10323
  let attribute;
10270
10324
  for (attribute of Object.values(attributes)) {
10271
- if (attribute.data.length / attribute.size !== vertexCount) throw (
10325
+ if (attribute.data.length / attribute.size !== vertexCount2) throw (
10272
10326
  /* alloc */
10273
10327
  new Error("Invalid glTF vertex attributes")
10274
10328
  );
10275
10329
  }
10276
10330
  return (
10277
10331
  /* alloc */
10278
- { attributes, vertexCount }
10332
+ { attributes, vertexCount: vertexCount2 }
10279
10333
  );
10280
10334
  }
10281
10335
 
10282
10336
  // src/models/readModelIndices.js
10283
- function readModelIndices(description, primitive, attributes, vertexCount) {
10337
+ function readModelIndices(description, primitive, attributes, vertexCount2) {
10284
10338
  if (primitive.indices !== void 0) {
10285
10339
  const index = readAccessor(description, primitive.indices);
10286
10340
  attributes.index = /* alloc */
@@ -10292,11 +10346,11 @@
10292
10346
  );
10293
10347
  }
10294
10348
  let value;
10295
- for (value of index.indices) if (value >= vertexCount) throw (
10349
+ for (value of index.indices) if (value >= vertexCount2) throw (
10296
10350
  /* alloc */
10297
10351
  new Error("Invalid glTF triangle indices")
10298
10352
  );
10299
- } else if (vertexCount % 3) {
10353
+ } else if (vertexCount2 % 3) {
10300
10354
  throw (
10301
10355
  /* alloc */
10302
10356
  new Error("Incomplete glTF triangle")
@@ -10383,13 +10437,13 @@
10383
10437
  }
10384
10438
  );
10385
10439
  group.matrix = nodeMatrix(spec);
10386
- let primitive, attributeData, attributes, vertexCount, built, mesh, child;
10440
+ let primitive, attributeData, attributes, vertexCount2, built, mesh, child;
10387
10441
  if (spec.mesh !== void 0) {
10388
10442
  for (primitive of description.meshes[spec.mesh].primitives) {
10389
10443
  attributeData = readModelAttributes(description, primitive);
10390
10444
  attributes = attributeData.attributes;
10391
- vertexCount = attributeData.vertexCount;
10392
- readModelIndices(description, primitive, attributes, vertexCount);
10445
+ vertexCount2 = attributeData.vertexCount;
10446
+ readModelIndices(description, primitive, attributes, vertexCount2);
10393
10447
  built = createModelGeometry(context, primitive, attributes);
10394
10448
  mesh = /* alloc */
10395
10449
  {
@@ -14446,8 +14500,8 @@
14446
14500
  function local2(output) {
14447
14501
  const { layer, state } = this;
14448
14502
  const map = layer._map, crs = map.options.crs;
14449
- const location = locationByLayer.get(layer), root = rootByLayer.get(layer);
14450
- const origin = crs.project(location), metres = Math.cos(location.lat * radians);
14503
+ const location2 = locationByLayer.get(layer), root = rootByLayer.get(layer);
14504
+ const origin = crs.project(location2), metres = Math.cos(location2.lat * radians);
14451
14505
  const p = crs.project(state.latlng);
14452
14506
  const ground = mapElevationOf(map, state.latlng) || 0;
14453
14507
  output[0] = (p.x - origin.x) * metres;
@@ -14534,49 +14588,21 @@
14534
14588
  init_mapGetBearing();
14535
14589
  init_mapSetPitch();
14536
14590
 
14537
- // src/utils/wrap.js
14538
- function wrap(angle) {
14539
- return Math.atan2(Math.sin(angle), Math.cos(angle));
14540
- }
14541
-
14542
- // src/picking/shouldSkipOther.js
14543
- function shouldSkipOther(other, layer) {
14544
- return other === layer || typeByLayer.get(other) === "terrain" || typeByLayer.get(other) === "traffic";
14545
- }
14546
-
14547
- // src/walk/landWalker.js
14548
- function land(previousY) {
14549
- const ray = raycasterByLayer.get(this.layer);
14550
- const feet = local2.call(this, this.local), root = rootByLayer.get(this.layer);
14551
- const x = root.position[0] + feet[0] * root.scale[0];
14552
- const y = root.position[1] + feet[1] * root.scale[1];
14553
- const z = root.position[2] + feet[2] * root.scale[2];
14554
- if (y > previousY || this.verticalSpeed > 0) return;
14555
- updateWorldMatrix(sceneByLayer.get(this.layer));
14556
- ray.origin[0] = x;
14557
- ray.origin[1] = previousY + 0.01;
14558
- ray.origin[2] = z;
14559
- ray.direction[0] = 0;
14560
- ray.direction[1] = -1;
14561
- ray.direction[2] = 0;
14562
- ray.meshes.length = 0;
14563
- let layer, hit2;
14564
- for (layer of layersByLayer.get(sharedByLayer.get(this.layer))) {
14565
- if (shouldSkipOther(layer, this.layer) || kindByLayer.get(layer) === "raster" || layer.options.nightLight) continue;
14566
- traverse(rootByLayer.get(layer), collectVisibleMesh2, ray.meshes);
14567
- }
14568
- for (hit2 of intersectMeshes(ray, ray.meshes)) {
14569
- if (hit2.point[1] < y - 1e-3) break;
14570
- this.state.height += (hit2.point[1] - y) / root.scale[1];
14571
- this.verticalSpeed = 0;
14572
- break;
14591
+ // src/walk/stepWalkerHeight.js
14592
+ function stepWalkerHeight(dt) {
14593
+ if (this.keys.has("Space")) {
14594
+ this.verticalSpeed = 6;
14595
+ this.state.height += this.verticalSpeed * dt;
14596
+ return true;
14573
14597
  }
14574
- ray.meshes.length = 0;
14598
+ if (!this.verticalSpeed && !this.state.height) return false;
14599
+ this.state.height = Math.max(0, this.state.height + this.verticalSpeed * dt - 9.81 * dt * dt / 2);
14600
+ this.verticalSpeed = this.state.height > 0 ? this.verticalSpeed - 9.81 * dt : 0;
14601
+ return true;
14575
14602
  }
14576
14603
 
14577
- // src/walk/moveWalker.js
14604
+ // src/walk/resolveWalkerPosition.js
14578
14605
  init_L();
14579
- init_radians();
14580
14606
 
14581
14607
  // src/globals/CELL.js
14582
14608
  var CELL = 8;
@@ -14603,8 +14629,8 @@
14603
14629
  function collider(mesh) {
14604
14630
  let c = colliders.get(mesh.geometry);
14605
14631
  if (c) return c;
14606
- const m = mesh.worldMatrix, { position: { data: p }, index, normal, uv, instanceOffset } = mesh.geometry.attributes;
14607
- const vertices = index ? index.count : p.length / 3;
14632
+ const m = mesh.worldMatrix, { position, index, normal, uv, instanceOffset } = mesh.geometry.attributes;
14633
+ const vertices = vertexCount(position, index);
14608
14634
  const count = vertices * (instanceOffset?.count ?? 1);
14609
14635
  const tri = (
14610
14636
  /* alloc */
@@ -14667,34 +14693,269 @@
14667
14693
  nrm.push(nx / h, nz / h);
14668
14694
  indexTriangle(cells, v, t);
14669
14695
  }
14670
- c = /* alloc */
14671
- {
14672
- tri: Float32Array.from(tri),
14673
- nrm: Float32Array.from(nrm),
14674
- cells,
14675
- maxX,
14676
- origin: (
14677
- /* alloc */
14678
- [m[12], m[13], m[14]]
14679
- )
14680
- };
14681
- colliders.set(mesh.geometry, c);
14682
- return c;
14696
+ c = /* alloc */
14697
+ {
14698
+ tri: Float32Array.from(tri),
14699
+ nrm: Float32Array.from(nrm),
14700
+ cells,
14701
+ maxX,
14702
+ origin: (
14703
+ /* alloc */
14704
+ [m[12], m[13], m[14]]
14705
+ )
14706
+ };
14707
+ colliders.set(mesh.geometry, c);
14708
+ return c;
14709
+ }
14710
+
14711
+ // src/collision/horizontalRay.js
14712
+ function horizontalRay(v, t, ox, oy, oz, dx, dz) {
14713
+ const i = t * 9, ex = v[i + 3] - v[i], ey = v[i + 4] - v[i + 1], ez = v[i + 5] - v[i + 2];
14714
+ const fx = v[i + 6] - v[i], fy = v[i + 7] - v[i + 1], fz = v[i + 8] - v[i + 2];
14715
+ const px = -dz * fy, py = dz * fx - dx * fz, pz = dx * fy, det = ex * px + ey * py + ez * pz;
14716
+ if (Math.abs(det) < 1e-9) return -1;
14717
+ const tx = ox - v[i], ty = oy - v[i + 1], tz = oz - v[i + 2], u = (tx * px + ty * py + tz * pz) / det;
14718
+ if (u < 0 || u > 1) return -1;
14719
+ const qx = ty * ez - tz * ey, qy = tz * ex - tx * ez, qz = tx * ey - ty * ex, w = (dx * qx + dz * qz) / det;
14720
+ if (w < 0 || u + w > 1) return -1;
14721
+ return (fx * qx + fy * qy + fz * qz) / det;
14722
+ }
14723
+
14724
+ // src/collision/collectIntersections.js
14725
+ function collectIntersections(shape, x, y, z, seen, hits, end, iz) {
14726
+ let ix, cell, triangle3, distance;
14727
+ for (ix = Math.floor(x / CELL); ix <= end; ix++) {
14728
+ cell = shape.cells.get(`${ix},${iz}`);
14729
+ if (!cell) continue;
14730
+ for (triangle3 of cell) {
14731
+ if (seen.has(triangle3)) continue;
14732
+ seen.add(triangle3);
14733
+ distance = horizontalRay(shape.tri, triangle3, x, y, z, 1, 0);
14734
+ if (distance > 1e-4) hits.push(distance);
14735
+ }
14736
+ }
14737
+ }
14738
+
14739
+ // src/collision/countCrossings.js
14740
+ function countCrossings(hits) {
14741
+ let ix, j, distance;
14742
+ for (ix = 1; ix < hits.length; ix++) {
14743
+ distance = hits[ix];
14744
+ j = ix - 1;
14745
+ while (j >= 0 && hits[j] > distance) hits[j + 1] = hits[j--];
14746
+ hits[j + 1] = distance;
14747
+ }
14748
+ let crossings = 0, previous = -Infinity;
14749
+ for (distance of hits) {
14750
+ if (distance - previous > 1e-4) crossings++;
14751
+ previous = distance;
14752
+ }
14753
+ return crossings;
14754
+ }
14755
+
14756
+ // src/collision/insideMesh.js
14757
+ function insideMesh(mesh, x, y, z, seen, intersections) {
14758
+ const geometry = mesh.geometry;
14759
+ if (!mesh.visible || !geometry?.attributes?.position?.data || mesh.program?.depthTest === false) return false;
14760
+ if (!geometry.bounds) computeBounds(geometry);
14761
+ const bounds = geometry.bounds;
14762
+ const world = mesh.worldMatrix;
14763
+ const center = bounds.center;
14764
+ const cx = world[0] * center[0] + world[4] * center[1] + world[8] * center[2] + world[12];
14765
+ const cy = world[1] * center[0] + world[5] * center[1] + world[9] * center[2] + world[13];
14766
+ const cz = world[2] * center[0] + world[6] * center[1] + world[10] * center[2] + world[14];
14767
+ const radius = bounds.radius * Math.max(
14768
+ Math.hypot(world[0], world[1], world[2]),
14769
+ Math.hypot(world[4], world[5], world[6]),
14770
+ Math.hypot(world[8], world[9], world[10])
14771
+ );
14772
+ if (Math.hypot(cx - x, cy - y, cz - z) > radius) return false;
14773
+ const shape = collider(mesh);
14774
+ const end = Math.floor(shape.maxX / CELL);
14775
+ if (x >= shape.maxX) return false;
14776
+ const iz = Math.floor(z / CELL);
14777
+ const hits = intersections;
14778
+ hits.length = 0;
14779
+ seen.clear();
14780
+ collectIntersections(shape, x, y, z, seen, hits, end, iz);
14781
+ return countCrossings(hits) % 2 === 1;
14782
+ }
14783
+
14784
+ // src/collision/setTrafficNormal.js
14785
+ function setTrafficNormal(data, x, z, distance, dx, dz) {
14786
+ if (distance < 1e-6) {
14787
+ data.normal[0] = -dx;
14788
+ data.normal[1] = -dz;
14789
+ } else {
14790
+ data.normal[0] = x / distance;
14791
+ data.normal[1] = z / distance;
14792
+ }
14793
+ }
14794
+
14795
+ // src/collision/trafficCollision.js
14796
+ function trafficCollision(layer, x, y, z, dx, dz, reach, data) {
14797
+ const state = trafficByLayer.get(layer);
14798
+ if (!state?.mesh || !state.trips.length) return false;
14799
+ let bounds = state.collisionBounds;
14800
+ if (!bounds) {
14801
+ bounds = state.collisionBounds = /* alloc */
14802
+ { radius: 0, minY: Infinity, maxY: -Infinity };
14803
+ let i, px2, py, pz2;
14804
+ for (i = 0; i < state.template.position.length; i += 3) {
14805
+ px2 = state.template.position[i];
14806
+ py = state.template.position[i + 1];
14807
+ pz2 = state.template.position[i + 2];
14808
+ bounds.radius = Math.max(bounds.radius, Math.hypot(px2, pz2));
14809
+ bounds.minY = Math.min(bounds.minY, py);
14810
+ bounds.maxY = Math.max(bounds.maxY, py);
14811
+ }
14812
+ }
14813
+ const root = rootByLayer.get(layer), position = state.position ||= /* alloc */
14814
+ {};
14815
+ const scale = Math.max(root.scale[0], root.scale[1], root.scale[2]);
14816
+ const radius = bounds.radius * scale + 0.35;
14817
+ let trip, projection, closest, rx, rz, perpendicular, entry, distance, bottom, top, px, pz;
14818
+ for (trip of state.trips) {
14819
+ ({ x: px, y: bottom, z: pz } = locateTraffic(state, trip, position));
14820
+ bottom = root.position[1] + (bottom + bounds.minY) * root.scale[1];
14821
+ top = root.position[1] + (position.y + bounds.maxY) * root.scale[1];
14822
+ if (bottom > y + 1.7 * scale || top < y + 0.2 * scale) continue;
14823
+ px = root.position[0] + px * root.scale[0];
14824
+ pz = root.position[2] + pz * root.scale[2];
14825
+ projection = (px - x) * dx + (pz - z) * dz;
14826
+ closest = Math.max(0, Math.min(reach, projection));
14827
+ rx = x + closest * dx - px;
14828
+ rz = z + closest * dz - pz;
14829
+ perpendicular = rx * rx + rz * rz;
14830
+ if (perpendicular >= radius * radius) continue;
14831
+ if (reach === 0) {
14832
+ data.found = true;
14833
+ distance = Math.hypot(x - px, z - pz);
14834
+ data.normal[0] = distance ? (x - px) / distance : 0;
14835
+ data.normal[1] = distance ? (z - pz) / distance : 0;
14836
+ continue;
14837
+ }
14838
+ entry = Math.max(0, projection - Math.sqrt(radius * radius - perpendicular));
14839
+ if (entry > reach || entry >= data.best) continue;
14840
+ data.best = entry;
14841
+ data.found = true;
14842
+ distance = Math.hypot(x + entry * dx - px, z + entry * dz - pz);
14843
+ setTrafficNormal(data, x + entry * dx - px, z + entry * dz - pz, distance, dx, dz);
14844
+ }
14845
+ return data.found;
14846
+ }
14847
+
14848
+ // src/collision/insideWalker.js
14849
+ function insideWalker(context) {
14850
+ const { layer, local: localBuffer, hitData } = context;
14851
+ const shared = sharedByLayer.get(layer);
14852
+ if (!shared || !layersByLayer.get(shared)) return false;
14853
+ updateWorldMatrix(sceneByLayer.get(layer));
14854
+ const root = rootByLayer.get(layer);
14855
+ const feet = local2.call(context, localBuffer);
14856
+ const x = root.position[0] + feet[0] * root.scale[0];
14857
+ const y = root.position[1] + feet[1] * root.scale[1];
14858
+ const z = root.position[2] + feet[2] * root.scale[2];
14859
+ const layers = layersByLayer.get(shared);
14860
+ let other, node, child, nodes;
14861
+ for (other of layers) {
14862
+ if (other === layer || kindByLayer.get(other) === "terrain") continue;
14863
+ if (kindByLayer.get(other) === "traffic") {
14864
+ hitData.best = 0;
14865
+ hitData.found = false;
14866
+ if (trafficCollision(other, x, y, z, 0, 0, 0, hitData)) return true;
14867
+ continue;
14868
+ }
14869
+ nodes = hitData.nodes;
14870
+ nodes.length = 0;
14871
+ if (rootByLayer.get(other)) nodes.push(rootByLayer.get(other));
14872
+ while (nodes.length) {
14873
+ node = nodes.pop();
14874
+ if (!node.visible) continue;
14875
+ if (node.geometry && (insideMesh(node, x, y + 0.45 * root.scale[1], z, hitData.seen, hitData.intersections) || insideMesh(node, x, y + 1.35 * root.scale[1], z, hitData.seen, hitData.intersections))) return true;
14876
+ for (child of node.children) nodes.push(child);
14877
+ }
14878
+ }
14879
+ return false;
14880
+ }
14881
+
14882
+ // src/walk/resolveWalkerPosition.js
14883
+ function resolveWalkerPosition() {
14884
+ if (!this.saved || !insideWalker(this)) return false;
14885
+ const map = this.layer._map;
14886
+ const origin = this.state.latlng;
14887
+ const height = this.state.height;
14888
+ let distance, angle, candidate;
14889
+ for (distance = 0.5; distance <= 20; distance += 0.5) {
14890
+ for (angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) {
14891
+ candidate = /* alloc */
14892
+ new L.LatLng(
14893
+ origin.lat + Math.cos(angle) * distance / 111320,
14894
+ origin.lng + Math.sin(angle) * distance / (111320 * Math.cos(origin.lat * Math.PI / 180))
14895
+ );
14896
+ this.state.latlng = candidate;
14897
+ this.state.height = 0;
14898
+ if (!insideWalker(this)) {
14899
+ this.verticalSpeed = 0;
14900
+ map.setView(
14901
+ candidate,
14902
+ map.getZoom(),
14903
+ /* alloc */
14904
+ { animate: false }
14905
+ );
14906
+ return true;
14907
+ }
14908
+ }
14909
+ }
14910
+ this.state.latlng = origin;
14911
+ this.state.height = height;
14912
+ return false;
14683
14913
  }
14684
14914
 
14685
- // src/collision/horizontalRay.js
14686
- function horizontalRay(v, t, ox, oy, oz, dx, dz) {
14687
- const i = t * 9, ex = v[i + 3] - v[i], ey = v[i + 4] - v[i + 1], ez = v[i + 5] - v[i + 2];
14688
- const fx = v[i + 6] - v[i], fy = v[i + 7] - v[i + 1], fz = v[i + 8] - v[i + 2];
14689
- const px = -dz * fy, py = dz * fx - dx * fz, pz = dx * fy, det = ex * px + ey * py + ez * pz;
14690
- if (Math.abs(det) < 1e-9) return -1;
14691
- const tx = ox - v[i], ty = oy - v[i + 1], tz = oz - v[i + 2], u = (tx * px + ty * py + tz * pz) / det;
14692
- if (u < 0 || u > 1) return -1;
14693
- const qx = ty * ez - tz * ey, qy = tz * ex - tx * ez, qz = tx * ey - ty * ex, w = (dx * qx + dz * qz) / det;
14694
- if (w < 0 || u + w > 1) return -1;
14695
- return (fx * qx + fy * qy + fz * qz) / det;
14915
+ // src/utils/wrap.js
14916
+ function wrap(angle) {
14917
+ return Math.atan2(Math.sin(angle), Math.cos(angle));
14918
+ }
14919
+
14920
+ // src/picking/shouldSkipOther.js
14921
+ function shouldSkipOther(other, layer) {
14922
+ return other === layer || typeByLayer.get(other) === "terrain" || typeByLayer.get(other) === "traffic";
14923
+ }
14924
+
14925
+ // src/walk/landWalker.js
14926
+ function land(previousY) {
14927
+ const ray = raycasterByLayer.get(this.layer);
14928
+ const feet = local2.call(this, this.local), root = rootByLayer.get(this.layer);
14929
+ const x = root.position[0] + feet[0] * root.scale[0];
14930
+ const y = root.position[1] + feet[1] * root.scale[1];
14931
+ const z = root.position[2] + feet[2] * root.scale[2];
14932
+ if (y > previousY || this.verticalSpeed > 0) return;
14933
+ updateWorldMatrix(sceneByLayer.get(this.layer));
14934
+ ray.origin[0] = x;
14935
+ ray.origin[1] = previousY + 0.01;
14936
+ ray.origin[2] = z;
14937
+ ray.direction[0] = 0;
14938
+ ray.direction[1] = -1;
14939
+ ray.direction[2] = 0;
14940
+ ray.meshes.length = 0;
14941
+ let layer, hit2;
14942
+ for (layer of layersByLayer.get(sharedByLayer.get(this.layer))) {
14943
+ if (shouldSkipOther(layer, this.layer) || kindByLayer.get(layer) === "raster" || layer.options.nightLight) continue;
14944
+ traverse(rootByLayer.get(layer), collectVisibleMesh2, ray.meshes);
14945
+ }
14946
+ for (hit2 of intersectMeshes(ray, ray.meshes)) {
14947
+ if (hit2.point[1] < y - 1e-3) break;
14948
+ this.state.height += (hit2.point[1] - y) / root.scale[1];
14949
+ this.verticalSpeed = 0;
14950
+ break;
14951
+ }
14952
+ ray.meshes.length = 0;
14696
14953
  }
14697
14954
 
14955
+ // src/walk/moveWalker.js
14956
+ init_L();
14957
+ init_radians();
14958
+
14698
14959
  // src/picking/hitColliderCell.js
14699
14960
  function hitColliderCell(cell, c, data, ox, oy, oz) {
14700
14961
  const { dx, dz, seen } = data;
@@ -14734,63 +14995,6 @@
14734
14995
  }
14735
14996
  }
14736
14997
 
14737
- // src/collision/trafficCollision.js
14738
- function trafficCollision(layer, x, y, z, dx, dz, reach, data) {
14739
- const state = trafficByLayer.get(layer);
14740
- if (!state?.mesh || !state.trips.length) return false;
14741
- let bounds = state.collisionBounds;
14742
- if (!bounds) {
14743
- bounds = state.collisionBounds = { radius: 0, minY: Infinity, maxY: -Infinity };
14744
- let i, px2, py, pz2;
14745
- for (i = 0; i < state.template.position.length; i += 3) {
14746
- px2 = state.template.position[i];
14747
- py = state.template.position[i + 1];
14748
- pz2 = state.template.position[i + 2];
14749
- bounds.radius = Math.max(bounds.radius, Math.hypot(px2, pz2));
14750
- bounds.minY = Math.min(bounds.minY, py);
14751
- bounds.maxY = Math.max(bounds.maxY, py);
14752
- }
14753
- }
14754
- const root = rootByLayer.get(layer), position = state.position ||= {};
14755
- const scale = Math.max(root.scale[0], root.scale[1], root.scale[2]);
14756
- const radius = bounds.radius * scale + 0.35;
14757
- let trip, projection, closest, rx, rz, perpendicular, entry, distance, bottom, top, px, pz;
14758
- for (trip of state.trips) {
14759
- ({ x: px, y: bottom, z: pz } = locateTraffic(state, trip, position));
14760
- bottom = root.position[1] + (bottom + bounds.minY) * root.scale[1];
14761
- top = root.position[1] + (position.y + bounds.maxY) * root.scale[1];
14762
- if (bottom > y + 1.7 * scale || top < y + 0.2 * scale) continue;
14763
- px = root.position[0] + px * root.scale[0];
14764
- pz = root.position[2] + pz * root.scale[2];
14765
- projection = (px - x) * dx + (pz - z) * dz;
14766
- closest = Math.max(0, Math.min(reach, projection));
14767
- rx = x + closest * dx - px;
14768
- rz = z + closest * dz - pz;
14769
- perpendicular = rx * rx + rz * rz;
14770
- if (perpendicular >= radius * radius) continue;
14771
- if (reach === 0) {
14772
- data.found = true;
14773
- distance = Math.hypot(x - px, z - pz);
14774
- data.normal[0] = distance ? (x - px) / distance : 0;
14775
- data.normal[1] = distance ? (z - pz) / distance : 0;
14776
- continue;
14777
- }
14778
- entry = Math.max(0, projection - Math.sqrt(radius * radius - perpendicular));
14779
- if (entry > reach || entry >= data.best) continue;
14780
- data.best = entry;
14781
- data.found = true;
14782
- distance = Math.hypot(x + entry * dx - px, z + entry * dz - pz);
14783
- if (distance < 1e-6) {
14784
- data.normal[0] = -dx;
14785
- data.normal[1] = -dz;
14786
- } else {
14787
- data.normal[0] = (x + entry * dx - px) / distance;
14788
- data.normal[1] = (z + entry * dz - pz) / distance;
14789
- }
14790
- }
14791
- return data.found;
14792
- }
14793
-
14794
14998
  // src/picking/hit.js
14795
14999
  function hit(layer, x, y, z, dx, dz, reach, data) {
14796
15000
  updateWorldMatrix(sceneByLayer.get(layer));
@@ -14834,19 +15038,6 @@
14834
15038
  this.state.phase += Math.hypot(east, north) / 1.6 * Math.PI;
14835
15039
  }
14836
15040
 
14837
- // src/walk/stepWalkerHeight.js
14838
- function stepWalkerHeight(dt) {
14839
- if (this.keys.has("Space")) {
14840
- this.verticalSpeed = 6;
14841
- this.state.height += this.verticalSpeed * dt;
14842
- return true;
14843
- }
14844
- if (!this.verticalSpeed && !this.state.height) return false;
14845
- this.state.height = Math.max(0, this.state.height + this.verticalSpeed * dt - 9.81 * dt * dt / 2);
14846
- this.verticalSpeed = this.state.height > 0 ? this.verticalSpeed - 9.81 * dt : 0;
14847
- return true;
14848
- }
14849
-
14850
15041
  // src/walk/updateWalkerView.js
14851
15042
  init_degrees();
14852
15043
  init_radians();
@@ -14873,123 +15064,19 @@
14873
15064
  return false;
14874
15065
  }
14875
15066
 
14876
- // src/walk/resolveWalkerPosition.js
14877
- init_L();
14878
-
14879
- // src/collision/insideMesh.js
14880
- function insideMesh(mesh, x, y, z, seen, intersections) {
14881
- const geometry = mesh.geometry;
14882
- if (!mesh.visible || !geometry?.attributes?.position?.data || mesh.program?.depthTest === false) return false;
14883
- if (!geometry.bounds) computeBounds(geometry);
14884
- const bounds = geometry.bounds;
14885
- const world = mesh.worldMatrix;
14886
- const center = bounds.center;
14887
- const cx = world[0] * center[0] + world[4] * center[1] + world[8] * center[2] + world[12];
14888
- const cy = world[1] * center[0] + world[5] * center[1] + world[9] * center[2] + world[13];
14889
- const cz = world[2] * center[0] + world[6] * center[1] + world[10] * center[2] + world[14];
14890
- const radius = bounds.radius * Math.max(
14891
- Math.hypot(world[0], world[1], world[2]),
14892
- Math.hypot(world[4], world[5], world[6]),
14893
- Math.hypot(world[8], world[9], world[10])
14894
- );
14895
- if (Math.hypot(cx - x, cy - y, cz - z) > radius) return false;
14896
- const shape = collider(mesh);
14897
- const end = Math.floor(shape.maxX / CELL);
14898
- if (x >= shape.maxX) return false;
14899
- const iz = Math.floor(z / CELL);
14900
- const hits = intersections;
14901
- hits.length = 0;
14902
- seen.clear();
14903
- let ix, cell, triangle3, distance;
14904
- for (ix = Math.floor(x / CELL); ix <= end; ix++) {
14905
- cell = shape.cells.get(`${ix},${iz}`);
14906
- if (!cell) continue;
14907
- for (triangle3 of cell) {
14908
- if (seen.has(triangle3)) continue;
14909
- seen.add(triangle3);
14910
- distance = horizontalRay(shape.tri, triangle3, x, y, z, 1, 0);
14911
- if (distance > 1e-4) hits.push(distance);
14912
- }
14913
- }
14914
- for (ix = 1; ix < hits.length; ix++) {
14915
- distance = hits[ix];
14916
- let j = ix - 1;
14917
- while (j >= 0 && hits[j] > distance) hits[j + 1] = hits[j--];
14918
- hits[j + 1] = distance;
14919
- }
14920
- let crossings = 0, previous = -Infinity;
14921
- for (distance of hits) {
14922
- if (distance - previous > 1e-4) crossings++;
14923
- previous = distance;
14924
- }
14925
- return crossings % 2 === 1;
14926
- }
14927
-
14928
- // src/collision/insideWalker.js
14929
- function insideWalker(context) {
14930
- const { layer, local: localBuffer, hitData } = context;
14931
- const shared = sharedByLayer.get(layer);
14932
- if (!shared || !layersByLayer.get(shared)) return false;
14933
- updateWorldMatrix(sceneByLayer.get(layer));
14934
- const root = rootByLayer.get(layer);
14935
- const feet = local2.call(context, localBuffer);
14936
- const x = root.position[0] + feet[0] * root.scale[0];
14937
- const y = root.position[1] + feet[1] * root.scale[1];
14938
- const z = root.position[2] + feet[2] * root.scale[2];
14939
- const layers = layersByLayer.get(shared);
14940
- let other, node, child;
14941
- for (other of layers) {
14942
- if (other === layer || kindByLayer.get(other) === "terrain") continue;
14943
- if (kindByLayer.get(other) === "traffic") {
14944
- hitData.best = 0;
14945
- hitData.found = false;
14946
- if (trafficCollision(other, x, y, z, 0, 0, 0, hitData)) return true;
14947
- continue;
14948
- }
14949
- const nodes = hitData.nodes;
14950
- nodes.length = 0;
14951
- if (rootByLayer.get(other)) nodes.push(rootByLayer.get(other));
14952
- while (nodes.length) {
14953
- node = nodes.pop();
14954
- if (!node.visible) continue;
14955
- if (node.geometry && (insideMesh(node, x, y + 0.45 * root.scale[1], z, hitData.seen, hitData.intersections) || insideMesh(node, x, y + 1.35 * root.scale[1], z, hitData.seen, hitData.intersections))) return true;
14956
- for (child of node.children) nodes.push(child);
14957
- }
15067
+ // src/walk/updateWalkerFrame.js
15068
+ function updateWalkerFrame(walker, previousY, forward, turn, dt, changed) {
15069
+ if (turn) {
15070
+ walker.state.heading = wrap(walker.state.heading + turn * 2.5 * dt);
15071
+ changed = true;
14958
15072
  }
14959
- return false;
14960
- }
14961
-
14962
- // src/walk/resolveWalkerPosition.js
14963
- function resolveWalkerPosition() {
14964
- if (!this.saved || !insideWalker(this)) return false;
14965
- const map = this.layer._map;
14966
- const origin = this.state.latlng;
14967
- const height = this.state.height;
14968
- let distance, angle, candidate;
14969
- for (distance = 0.5; distance <= 20; distance += 0.5) {
14970
- for (angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) {
14971
- candidate = /* alloc */
14972
- new L.LatLng(
14973
- origin.lat + Math.cos(angle) * distance / 111320,
14974
- origin.lng + Math.sin(angle) * distance / (111320 * Math.cos(origin.lat * Math.PI / 180))
14975
- );
14976
- this.state.latlng = candidate;
14977
- this.state.height = 0;
14978
- if (!insideWalker(this)) {
14979
- this.verticalSpeed = 0;
14980
- map.setView(
14981
- candidate,
14982
- map.getZoom(),
14983
- /* alloc */
14984
- { animate: false }
14985
- );
14986
- return true;
14987
- }
14988
- }
15073
+ if (forward) {
15074
+ moveWalker.call(walker, forward, dt);
15075
+ changed = true;
14989
15076
  }
14990
- this.state.latlng = origin;
14991
- this.state.height = height;
14992
- return false;
15077
+ if (changed) land.call(walker, previousY);
15078
+ if (updateWalkerView.call(walker, forward, turn, dt)) changed = true;
15079
+ if (changed) place.call(walker);
14993
15080
  }
14994
15081
 
14995
15082
  // src/walk/animateWalker.js
@@ -15020,17 +15107,7 @@
15020
15107
  }
15021
15108
  forward = Math.sign(forward);
15022
15109
  turn = Math.sign(turn);
15023
- if (turn) {
15024
- this.state.heading = wrap(this.state.heading + turn * 2.5 * dt);
15025
- changed = true;
15026
- }
15027
- if (forward) {
15028
- moveWalker.call(this, forward, dt);
15029
- changed = true;
15030
- }
15031
- if (changed) land.call(this, previousY);
15032
- if (updateWalkerView.call(this, forward, turn, dt)) changed = true;
15033
- if (changed) place.call(this);
15110
+ updateWalkerFrame(this, previousY, forward, turn, dt, changed);
15034
15111
  }
15035
15112
 
15036
15113
  // src/walk/startWalking.js