isard 1.6.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/dist/basemap/furniture.d.ts +20 -2
  3. package/dist/basemap/furniture.js +72 -3
  4. package/dist/basemap/furniture.js.map +1 -1
  5. package/dist/basemap/meshes.d.ts +21 -0
  6. package/dist/basemap/meshes.js +159 -0
  7. package/dist/basemap/meshes.js.map +1 -0
  8. package/dist/basemap/placed.d.ts +13 -0
  9. package/dist/basemap/placed.js +14 -0
  10. package/dist/basemap/placed.js.map +1 -0
  11. package/dist/basemap/trees.d.ts +4 -14
  12. package/dist/basemap/trees.js +12 -93
  13. package/dist/basemap/trees.js.map +1 -1
  14. package/dist/basemap/vectortiles/context.d.ts +4 -2
  15. package/dist/basemap/vectortiles/context.js.map +1 -1
  16. package/dist/basemap/vectortiles/extrusions.d.ts +1 -1
  17. package/dist/basemap/vectortiles/extrusions.js +7 -16
  18. package/dist/basemap/vectortiles/extrusions.js.map +1 -1
  19. package/dist/basemap/vectortiles/furniture.d.ts +19 -0
  20. package/dist/basemap/vectortiles/furniture.js +29 -0
  21. package/dist/basemap/vectortiles/furniture.js.map +1 -0
  22. package/dist/basemap/vectortiles/labels.js +8 -1
  23. package/dist/basemap/vectortiles/labels.js.map +1 -1
  24. package/dist/basemap/vectortiles/lamps.js +11 -76
  25. package/dist/basemap/vectortiles/lamps.js.map +1 -1
  26. package/dist/basemap/vectortiles/placed.d.ts +38 -0
  27. package/dist/basemap/vectortiles/placed.js +143 -0
  28. package/dist/basemap/vectortiles/placed.js.map +1 -0
  29. package/dist/basemap/vectortiles/street-wind.js +4 -6
  30. package/dist/basemap/vectortiles/street-wind.js.map +1 -1
  31. package/dist/basemap/vectortiles/trees.d.ts +2 -9
  32. package/dist/basemap/vectortiles/trees.js +34 -165
  33. package/dist/basemap/vectortiles/trees.js.map +1 -1
  34. package/dist/basemap/vectortiles/vectors.d.ts +4 -5
  35. package/dist/basemap/vectortiles/vectors.js +17 -16
  36. package/dist/basemap/vectortiles/vectors.js.map +1 -1
  37. package/dist/basemap/vectortiles.d.ts +14 -4
  38. package/dist/basemap/vectortiles.js +40 -12
  39. package/dist/basemap/vectortiles.js.map +1 -1
  40. package/dist/basemap/wmts.js +2 -2
  41. package/dist/basemap/wmts.js.map +1 -1
  42. package/dist/basemap.worker.js +2 -2
  43. package/dist/basemap.worker.js.map +18 -14
  44. package/dist/bind/layers.d.ts +10 -3
  45. package/dist/bind/layers.js +11 -4
  46. package/dist/bind/layers.js.map +1 -1
  47. package/dist/data/collada.js +3 -17
  48. package/dist/data/collada.js.map +1 -1
  49. package/dist/data/crs.js +1 -1
  50. package/dist/data/crs.js.map +1 -1
  51. package/dist/data/ifc.js +21 -22
  52. package/dist/data/ifc.js.map +1 -1
  53. package/dist/geoplateforme.d.ts +16 -4
  54. package/dist/geoplateforme.js +26 -4
  55. package/dist/geoplateforme.js.map +1 -1
  56. package/dist/index.d.ts +5 -4
  57. package/dist/index.js +3 -3
  58. package/dist/index.js.map +1 -1
  59. package/dist/isard_bg.wasm +0 -0
  60. package/dist/isard_core_bg.wasm +0 -0
  61. package/dist/isard_grid_bg.wasm +0 -0
  62. package/dist/isard_tess_bg.wasm +0 -0
  63. package/dist/layers/extrusions.js +5 -4
  64. package/dist/layers/extrusions.js.map +1 -1
  65. package/dist/layers/features.d.ts +0 -2
  66. package/dist/layers/features.js +5 -6
  67. package/dist/layers/features.js.map +1 -1
  68. package/dist/layers/layers.d.ts +2 -0
  69. package/dist/layers/layers.js +11 -0
  70. package/dist/layers/layers.js.map +1 -1
  71. package/dist/layers/panoramas.d.ts +32 -10
  72. package/dist/layers/panoramas.js +55 -28
  73. package/dist/layers/panoramas.js.map +1 -1
  74. package/dist/layers/photos.js +5 -6
  75. package/dist/layers/photos.js.map +1 -1
  76. package/dist/layers/trees.js +7 -7
  77. package/dist/layers/trees.js.map +1 -1
  78. package/dist/layers/vectors.d.ts +1 -0
  79. package/dist/layers/vectors.js +23 -8
  80. package/dist/layers/vectors.js.map +1 -1
  81. package/dist/map/frame-loop.d.ts +2 -0
  82. package/dist/map/frame-loop.js +3 -3
  83. package/dist/map/frame-loop.js.map +1 -1
  84. package/dist/map.js +17 -0
  85. package/dist/map.js.map +1 -1
  86. package/dist/panoramax.d.ts +19 -0
  87. package/dist/panoramax.js +48 -3
  88. package/dist/panoramax.js.map +1 -1
  89. package/dist/react/CustomLayer.js +5 -4
  90. package/dist/react/CustomLayer.js.map +1 -1
  91. package/dist/react/Layer.d.ts +1 -1
  92. package/dist/react/Layer.js +6 -5
  93. package/dist/react/Layer.js.map +1 -1
  94. package/dist/react/VectorGeometry.d.ts +18 -9
  95. package/dist/react/VectorGeometry.js +30 -15
  96. package/dist/react/VectorGeometry.js.map +1 -1
  97. package/dist/react/VectorTiles.d.ts +65 -0
  98. package/dist/react/VectorTiles.js +112 -0
  99. package/dist/react/VectorTiles.js.map +1 -0
  100. package/dist/react/context.d.ts +12 -0
  101. package/dist/react/context.js +40 -1
  102. package/dist/react/context.js.map +1 -1
  103. package/dist/react/index.d.ts +7 -1
  104. package/dist/react/index.js +7 -1
  105. package/dist/react/index.js.map +1 -1
  106. package/dist/types.d.ts +2 -0
  107. package/dist/types.js.map +1 -1
  108. package/dist/util/geo.d.ts +8 -0
  109. package/dist/util/geo.js +8 -0
  110. package/dist/util/geo.js.map +1 -1
  111. package/dist/util/gltf.d.ts +18 -1
  112. package/dist/util/gltf.js +26 -1
  113. package/dist/util/gltf.js.map +1 -1
  114. package/dist/util/roofs.d.ts +8 -0
  115. package/dist/util/roofs.js +16 -2
  116. package/dist/util/roofs.js.map +1 -1
  117. package/dist/util/style-floats.d.ts +33 -0
  118. package/dist/util/style-floats.js +34 -0
  119. package/dist/util/style-floats.js.map +1 -0
  120. package/dist/util/sun.js +1 -1
  121. package/dist/util/sun.js.map +1 -1
  122. package/dist/workers/basemap/furniture.d.ts +13 -0
  123. package/dist/workers/basemap/geometry.d.ts +2 -5
  124. package/dist/workers/basemap/labels.d.ts +5 -2
  125. package/dist/workers/basemap/lamps.d.ts +3 -3
  126. package/dist/workers/basemap/tiles.d.ts +2 -0
  127. package/dist/workers/basemap/trees.d.ts +3 -0
  128. package/dist/workers/basemap.worker.d.ts +17 -1
  129. package/dist/workers/basemap.worker.js +12 -1
  130. package/dist/workers/basemap.worker.js.map +1 -1
  131. package/package.json +1 -1
@@ -4,13 +4,12 @@
4
4
  * its camera, each pixel where the camera saw it. Seen from where it was taken, it lies over the buildings it shows.
5
5
  */
6
6
  import { withHeaders } from "../engine/requests.js";
7
+ import { RAD } from "../util/geo.js";
7
8
  import { bounds, glbOf } from "../util/gltf.js";
8
9
  /** Degrees between the rows and the columns of a 360° photo's band */
9
10
  const STEP = 5;
10
11
  /** Degrees of a 360° photo shown above and below the horizon */
11
12
  const TILT = 40;
12
- /** Degrees to radians */
13
- const rad = (degrees) => (degrees * Math.PI) / 180;
14
13
  /**
15
14
  * The surface a photo is drawn on, `aspect` its width over its height: a flat photo on a frame `distance` ahead
16
15
  * spanning its field of view; a 360° photo on a band `distance` round its camera, its columns at their bearings and its
@@ -34,7 +33,7 @@ export const photoMesh = (options, aspect) => {
34
33
  };
35
34
  };
36
35
  if (projection === 'flat') {
37
- const half = distance * Math.tan(rad(Math.min(fov, 170) / 2));
36
+ const half = distance * Math.tan((Math.min(fov, 170) / 2) * RAD);
38
37
  // Its left (u 0) at +x, glTF's left; its top (v 0) up
39
38
  return grid([0, 1], [0, 1], (u, v) => [half * (1 - 2 * u), height + (half / aspect) * (1 - 2 * v), distance, u, v]);
40
39
  }
@@ -42,9 +41,9 @@ export const photoMesh = (options, aspect) => {
42
41
  const half = Math.min(span, 360) / 2;
43
42
  // Bearing `theta` right of the heading, `phi` above the horizon: in the image at u 0.5 + theta / 360, v 0.5 - phi / 180
44
43
  return grid(range(-half, half), range(-TILT, TILT).reverse(), (theta, phi) => [
45
- -distance * Math.sin(rad(theta)),
46
- height + distance * Math.tan(rad(phi)),
47
- distance * Math.cos(rad(theta)),
44
+ -distance * Math.sin(theta * RAD),
45
+ height + distance * Math.tan(phi * RAD),
46
+ distance * Math.cos(theta * RAD),
48
47
  0.5 + theta / 360,
49
48
  0.5 - phi / 180,
50
49
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"photos.js","sourceRoot":"","sources":["../../src/layers/photos.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A photo standing where it was taken (a street-level photo: Panoramax's, a phone's), facing the way it looked, over\n * the 3D map: a flat photo on a frame across its field of view, a 360° photo (equirectangular) on a band curved round\n * its camera, each pixel where the camera saw it. Seen from where it was taken, it lies over the buildings it shows.\n */\n\nimport { withHeaders } from '../engine/requests.ts';\nimport type { LonLat } from '../types.ts';\nimport { bounds, glbOf } from '../util/gltf.ts';\nimport type { Layer, ModelLayer, ModelOptions } from './layers.ts';\n\n/** The options of `GeoMap.addPhoto` */\nexport interface PhotoOptions {\n /** The image (JPEG or PNG); or its bytes (`data`) */\n url?: string;\n data?: ArrayBuffer | ArrayBufferView<ArrayBuffer>;\n /** Where it was taken */\n lonLat: LonLat;\n /** Degrees clockwise from the north of the photo's middle (0 by default) */\n heading?: number;\n /** `'flat'` (the default) or `'equirectangular'`: a 360° photo, its middle at `heading` */\n projection?: 'flat' | 'equirectangular';\n /** A flat photo's field of view across, degrees (70 by default) */\n fov?: number;\n /** Degrees of a 360° photo shown round its heading (120 by default) */\n span?: number;\n /** Metres of its camera above the ground (2 by default) */\n height?: number;\n /** Metres from its camera to the photo drawn (15 by default) */\n distance?: number;\n before?: Layer;\n}\n\n/** A photo's surface: positions (glTF: x left, y up, z ahead, metres from its foot), image coordinates, triangles */\nexport interface PhotoMesh {\n positions: number[];\n uvs: number[];\n indices: number[];\n}\n\n/** Degrees between the rows and the columns of a 360° photo's band */\nconst STEP = 5;\n/** Degrees of a 360° photo shown above and below the horizon */\nconst TILT = 40;\n\n/** Degrees to radians */\nconst rad = (degrees: number): number => (degrees * Math.PI) / 180;\n\n/**\n * The surface a photo is drawn on, `aspect` its width over its height: a flat photo on a frame `distance` ahead\n * spanning its field of view; a 360° photo on a band `distance` round its camera, its columns at their bearings and its\n * rows at their heights over the horizon, as the camera saw them\n */\nexport const photoMesh = (options: Omit<PhotoOptions, 'lonLat'>, aspect: number): PhotoMesh => {\n const { projection = 'flat', fov = 70, span = 120, height = 2, distance = 15 } = options;\n // A grid of columns (left to right) and rows (top to bottom): its points and their image coordinates\n const grid = (\n columns: number[],\n rows: number[],\n at: (column: number, row: number) => [x: number, y: number, z: number, u: number, v: number],\n ): PhotoMesh => {\n const points = rows.flatMap((row) => columns.map((column) => at(column, row)));\n const indices = rows.slice(1).flatMap((_, r) =>\n columns.slice(1).flatMap((_, c) => {\n const [a, b] = [r * columns.length + c, r * columns.length + c + 1];\n const [d, e] = [a + columns.length, b + columns.length];\n\n // Facing the camera\n return [a, d, b, b, d, e];\n }),\n );\n\n return {\n positions: points.flatMap((p) => p.slice(0, 3)),\n uvs: points.flatMap(([, , , u, v]) => [u, v]),\n indices,\n };\n };\n\n if (projection === 'flat') {\n const half = distance * Math.tan(rad(Math.min(fov, 170) / 2));\n\n // Its left (u 0) at +x, glTF's left; its top (v 0) up\n return grid([0, 1], [0, 1], (u, v) => [half * (1 - 2 * u), height + (half / aspect) * (1 - 2 * v), distance, u, v]);\n }\n\n const range = (from: number, to: number) =>\n Array.from({ length: Math.round((to - from) / STEP) + 1 }, (_, i) => from + i * STEP);\n const half = Math.min(span, 360) / 2;\n\n // Bearing `theta` right of the heading, `phi` above the horizon: in the image at u 0.5 + theta / 360, v 0.5 - phi / 180\n return grid(range(-half, half), range(-TILT, TILT).reverse(), (theta, phi) => [\n -distance * Math.sin(rad(theta)),\n height + distance * Math.tan(rad(phi)),\n distance * Math.cos(rad(theta)),\n 0.5 + theta / 360,\n 0.5 - phi / 180,\n ]);\n};\n\n/** The width and height of a JPEG or a PNG from its header, undefined for another image */\nexport const imageSize = (bytes: Uint8Array): [width: number, height: number] | undefined => {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\n // PNG: its IHDR chunk first\n if (bytes.length >= 24 && view.getUint32(0) === 0x89504e47) return [view.getUint32(16), view.getUint32(20)];\n if (bytes.length < 4 || view.getUint16(0) !== 0xffd8) return undefined;\n\n // JPEG: the segments up to a start of frame (C0 to CF, but C4, C8 and CC)\n for (let at = 2; at + 9 < bytes.length; ) {\n const marker = view.getUint16(at);\n const length = view.getUint16(at + 2);\n\n if ((marker & 0xff00) !== 0xff00) return undefined;\n if (marker >= 0xffc0 && marker <= 0xffcf && ![0xffc4, 0xffc8, 0xffcc].includes(marker))\n return [view.getUint16(at + 7), view.getUint16(at + 5)];\n at += 2 + length;\n }\n\n return undefined;\n};\n\n/** A photo as a glTF binary: its surface (`photoMesh`) textured by its image, unlit, both sides drawn */\nexport const photoModel = (image: Uint8Array, options: Omit<PhotoOptions, 'lonLat'> = {}): Uint8Array<ArrayBuffer> => {\n const [width, height] = imageSize(image) ?? [4, 3];\n const mesh = photoMesh(options, width / height);\n const floats = new Float32Array([...mesh.positions, ...mesh.uvs]);\n const indices = new Uint32Array(mesh.indices);\n const parts = [new Uint8Array(floats.buffer), new Uint8Array(indices.buffer), image];\n // Each part 4-byte aligned in the buffer\n const offsets = parts.map((_, i) => parts.slice(0, i).reduce((n, p) => n + Math.ceil(p.byteLength / 4) * 4, 0));\n const bin = new Uint8Array((offsets.at(-1) ?? 0) + image.byteLength);\n\n parts.forEach((p, i) => {\n bin.set(p, offsets[i]);\n });\n\n const count = mesh.positions.length / 3;\n const gltf = {\n asset: { version: '2.0', generator: 'isard' },\n extensionsUsed: ['KHR_materials_unlit'],\n scene: 0,\n scenes: [{ nodes: [0] }],\n nodes: [{ mesh: 0 }],\n meshes: [{ primitives: [{ attributes: { POSITION: 0, TEXCOORD_0: 1 }, indices: 2, material: 0 }] }],\n materials: [\n {\n pbrMetallicRoughness: { baseColorTexture: { index: 0 }, metallicFactor: 0, roughnessFactor: 1 },\n extensions: { KHR_materials_unlit: {} },\n doubleSided: true,\n },\n ],\n textures: [{ source: 0, sampler: 0 }],\n samplers: [{ magFilter: 9729, minFilter: 9987, wrapS: 33071, wrapT: 33071 }],\n images: [{ bufferView: 3, mimeType: image[0] === 0x89 ? 'image/png' : 'image/jpeg' }],\n accessors: [\n { bufferView: 0, componentType: 5126, count, type: 'VEC3', ...bounds(mesh.positions) },\n { bufferView: 1, componentType: 5126, count, type: 'VEC2' },\n { bufferView: 2, componentType: 5125, count: indices.length, type: 'SCALAR' },\n ],\n bufferViews: [\n { buffer: 0, byteOffset: 0, byteLength: mesh.positions.length * 4, target: 34962 },\n { buffer: 0, byteOffset: mesh.positions.length * 4, byteLength: mesh.uvs.length * 4, target: 34962 },\n { buffer: 0, byteOffset: offsets[1], byteLength: indices.byteLength, target: 34963 },\n { buffer: 0, byteOffset: offsets[2], byteLength: image.byteLength },\n ],\n buffers: [{ byteLength: bin.byteLength }],\n };\n\n return glbOf(gltf, bin);\n};\n\n/** The bytes of a photo's image: given, or read from its URL (with the map's request headers) */\nconst imageOf = async ({ url, data }: PhotoOptions): Promise<Uint8Array> => {\n if (data)\n return ArrayBuffer.isView(data)\n ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n : new Uint8Array(data);\n if (!url) throw new Error('isard: a photo needs a url or data');\n\n const response = await fetch(url, withHeaders(url));\n\n if (!response.ok) throw new Error(`isard: ${url}: ${response.status}`);\n\n return new Uint8Array(await response.arrayBuffer());\n};\n\n/** The photo's layer (`addModels`: the map's), its image read */\nexport const addPhoto = async (\n addModels: (options: ModelOptions & { before?: Layer }) => ModelLayer,\n options: PhotoOptions,\n): Promise<ModelLayer> => {\n const data = photoModel(await imageOf(options), options);\n\n return addModels({\n data,\n instances: [{ lonLat: options.lonLat, heading: options.heading ?? 0 }],\n ...(options.before && { before: options.before }),\n });\n};\n"]}
1
+ {"version":3,"file":"photos.js","sourceRoot":"","sources":["../../src/layers/photos.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAgChD,sEAAsE;AACtE,MAAM,IAAI,GAAG,CAAC,CAAC;AACf,gEAAgE;AAChE,MAAM,IAAI,GAAG,EAAE,CAAC;AAEhB;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAqC,EAAE,MAAc,EAAa,EAAE;IAC5F,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,GAAG,GAAG,EAAE,EAAE,IAAI,GAAG,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC;IACzF,qGAAqG;IACrG,MAAM,IAAI,GAAG,CACX,OAAiB,EACjB,IAAc,EACd,EAA4F,EACjF,EAAE;QACb,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/E,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC7C,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAChC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YACpE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAExD,oBAAoB;YACpB,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5B,CAAC,CAAC,CACH,CAAC;QAEF,OAAO;YACL,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/C,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,AAAD,EAAG,AAAD,EAAG,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7C,OAAO;SACR,CAAC;IACJ,CAAC,CAAC;IAEF,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAEjE,sDAAsD;QACtD,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACtH,CAAC;IAED,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAU,EAAE,EAAE,CACzC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;IAErC,wHAAwH;IACxH,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC;QAC5E,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC;QACjC,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,CAAC;QACvC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC;QAChC,GAAG,GAAG,KAAK,GAAG,GAAG;QACjB,GAAG,GAAG,GAAG,GAAG,GAAG;KAChB,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,2FAA2F;AAC3F,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAiB,EAA+C,EAAE;IAC1F,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAE5E,4BAA4B;IAC5B,IAAI,KAAK,CAAC,MAAM,IAAI,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,UAAU;QAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5G,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,MAAM;QAAE,OAAO,SAAS,CAAC;IAEvE,0EAA0E;IAC1E,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,GAAI,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAEtC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,MAAM;YAAE,OAAO,SAAS,CAAC;QACnD,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,MAAM,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;YACpF,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1D,EAAE,IAAI,CAAC,GAAG,MAAM,CAAC;IACnB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,yGAAyG;AACzG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAiB,EAAE,UAAwC,EAAE,EAA2B,EAAE;IACnH,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC;IACrF,yCAAyC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAChH,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;IAErE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACrB,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG;QACX,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE;QAC7C,cAAc,EAAE,CAAC,qBAAqB,CAAC;QACvC,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACpB,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACnG,SAAS,EAAE;YACT;gBACE,oBAAoB,EAAE,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE;gBAC/F,UAAU,EAAE,EAAE,mBAAmB,EAAE,EAAE,EAAE;gBACvC,WAAW,EAAE,IAAI;aAClB;SACF;QACD,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QACrC,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC5E,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC;QACrF,SAAS,EAAE;YACT,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE;YACtF,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE;YAC3D,EAAE,UAAU,EAAE,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE;SAC9E;QACD,WAAW,EAAE;YACX,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE;YAClF,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE;YACpG,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE;YACpF,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE;SACpE;QACD,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;KAC1C,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,iGAAiG;AACjG,MAAM,OAAO,GAAG,KAAK,EAAE,EAAE,GAAG,EAAE,IAAI,EAAgB,EAAuB,EAAE;IACzE,IAAI,IAAI;QACN,OAAO,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;YAC7B,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC;YAC/D,CAAC,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IAEhE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAEpD,IAAI,CAAC,QAAQ,CAAC,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,UAAU,GAAG,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAEvE,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,iEAAiE;AACjE,MAAM,CAAC,MAAM,QAAQ,GAAG,KAAK,EAC3B,SAAqE,EACrE,OAAqB,EACA,EAAE;IACvB,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;IAEzD,OAAO,SAAS,CAAC;QACf,IAAI;QACJ,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC,EAAE,CAAC;QACtE,GAAG,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;KAClD,CAAC,CAAC;AACL,CAAC,CAAC","sourcesContent":["/**\n * A photo standing where it was taken (a street-level photo: Panoramax's, a phone's), facing the way it looked, over\n * the 3D map: a flat photo on a frame across its field of view, a 360° photo (equirectangular) on a band curved round\n * its camera, each pixel where the camera saw it. Seen from where it was taken, it lies over the buildings it shows.\n */\n\nimport { withHeaders } from '../engine/requests.ts';\nimport type { LonLat } from '../types.ts';\nimport { RAD } from '../util/geo.ts';\nimport { bounds, glbOf } from '../util/gltf.ts';\nimport type { Layer, ModelLayer, ModelOptions } from './layers.ts';\n\n/** The options of `GeoMap.addPhoto` */\nexport interface PhotoOptions {\n /** The image (JPEG or PNG); or its bytes (`data`) */\n url?: string;\n data?: ArrayBuffer | ArrayBufferView<ArrayBuffer>;\n /** Where it was taken */\n lonLat: LonLat;\n /** Degrees clockwise from the north of the photo's middle (0 by default) */\n heading?: number;\n /** `'flat'` (the default) or `'equirectangular'`: a 360° photo, its middle at `heading` */\n projection?: 'flat' | 'equirectangular';\n /** A flat photo's field of view across, degrees (70 by default) */\n fov?: number;\n /** Degrees of a 360° photo shown round its heading (120 by default) */\n span?: number;\n /** Metres of its camera above the ground (2 by default) */\n height?: number;\n /** Metres from its camera to the photo drawn (15 by default) */\n distance?: number;\n before?: Layer;\n}\n\n/** A photo's surface: positions (glTF: x left, y up, z ahead, metres from its foot), image coordinates, triangles */\nexport interface PhotoMesh {\n positions: number[];\n uvs: number[];\n indices: number[];\n}\n\n/** Degrees between the rows and the columns of a 360° photo's band */\nconst STEP = 5;\n/** Degrees of a 360° photo shown above and below the horizon */\nconst TILT = 40;\n\n/**\n * The surface a photo is drawn on, `aspect` its width over its height: a flat photo on a frame `distance` ahead\n * spanning its field of view; a 360° photo on a band `distance` round its camera, its columns at their bearings and its\n * rows at their heights over the horizon, as the camera saw them\n */\nexport const photoMesh = (options: Omit<PhotoOptions, 'lonLat'>, aspect: number): PhotoMesh => {\n const { projection = 'flat', fov = 70, span = 120, height = 2, distance = 15 } = options;\n // A grid of columns (left to right) and rows (top to bottom): its points and their image coordinates\n const grid = (\n columns: number[],\n rows: number[],\n at: (column: number, row: number) => [x: number, y: number, z: number, u: number, v: number],\n ): PhotoMesh => {\n const points = rows.flatMap((row) => columns.map((column) => at(column, row)));\n const indices = rows.slice(1).flatMap((_, r) =>\n columns.slice(1).flatMap((_, c) => {\n const [a, b] = [r * columns.length + c, r * columns.length + c + 1];\n const [d, e] = [a + columns.length, b + columns.length];\n\n // Facing the camera\n return [a, d, b, b, d, e];\n }),\n );\n\n return {\n positions: points.flatMap((p) => p.slice(0, 3)),\n uvs: points.flatMap(([, , , u, v]) => [u, v]),\n indices,\n };\n };\n\n if (projection === 'flat') {\n const half = distance * Math.tan((Math.min(fov, 170) / 2) * RAD);\n\n // Its left (u 0) at +x, glTF's left; its top (v 0) up\n return grid([0, 1], [0, 1], (u, v) => [half * (1 - 2 * u), height + (half / aspect) * (1 - 2 * v), distance, u, v]);\n }\n\n const range = (from: number, to: number) =>\n Array.from({ length: Math.round((to - from) / STEP) + 1 }, (_, i) => from + i * STEP);\n const half = Math.min(span, 360) / 2;\n\n // Bearing `theta` right of the heading, `phi` above the horizon: in the image at u 0.5 + theta / 360, v 0.5 - phi / 180\n return grid(range(-half, half), range(-TILT, TILT).reverse(), (theta, phi) => [\n -distance * Math.sin(theta * RAD),\n height + distance * Math.tan(phi * RAD),\n distance * Math.cos(theta * RAD),\n 0.5 + theta / 360,\n 0.5 - phi / 180,\n ]);\n};\n\n/** The width and height of a JPEG or a PNG from its header, undefined for another image */\nexport const imageSize = (bytes: Uint8Array): [width: number, height: number] | undefined => {\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n\n // PNG: its IHDR chunk first\n if (bytes.length >= 24 && view.getUint32(0) === 0x89504e47) return [view.getUint32(16), view.getUint32(20)];\n if (bytes.length < 4 || view.getUint16(0) !== 0xffd8) return undefined;\n\n // JPEG: the segments up to a start of frame (C0 to CF, but C4, C8 and CC)\n for (let at = 2; at + 9 < bytes.length; ) {\n const marker = view.getUint16(at);\n const length = view.getUint16(at + 2);\n\n if ((marker & 0xff00) !== 0xff00) return undefined;\n if (marker >= 0xffc0 && marker <= 0xffcf && ![0xffc4, 0xffc8, 0xffcc].includes(marker))\n return [view.getUint16(at + 7), view.getUint16(at + 5)];\n at += 2 + length;\n }\n\n return undefined;\n};\n\n/** A photo as a glTF binary: its surface (`photoMesh`) textured by its image, unlit, both sides drawn */\nexport const photoModel = (image: Uint8Array, options: Omit<PhotoOptions, 'lonLat'> = {}): Uint8Array<ArrayBuffer> => {\n const [width, height] = imageSize(image) ?? [4, 3];\n const mesh = photoMesh(options, width / height);\n const floats = new Float32Array([...mesh.positions, ...mesh.uvs]);\n const indices = new Uint32Array(mesh.indices);\n const parts = [new Uint8Array(floats.buffer), new Uint8Array(indices.buffer), image];\n // Each part 4-byte aligned in the buffer\n const offsets = parts.map((_, i) => parts.slice(0, i).reduce((n, p) => n + Math.ceil(p.byteLength / 4) * 4, 0));\n const bin = new Uint8Array((offsets.at(-1) ?? 0) + image.byteLength);\n\n parts.forEach((p, i) => {\n bin.set(p, offsets[i]);\n });\n\n const count = mesh.positions.length / 3;\n const gltf = {\n asset: { version: '2.0', generator: 'isard' },\n extensionsUsed: ['KHR_materials_unlit'],\n scene: 0,\n scenes: [{ nodes: [0] }],\n nodes: [{ mesh: 0 }],\n meshes: [{ primitives: [{ attributes: { POSITION: 0, TEXCOORD_0: 1 }, indices: 2, material: 0 }] }],\n materials: [\n {\n pbrMetallicRoughness: { baseColorTexture: { index: 0 }, metallicFactor: 0, roughnessFactor: 1 },\n extensions: { KHR_materials_unlit: {} },\n doubleSided: true,\n },\n ],\n textures: [{ source: 0, sampler: 0 }],\n samplers: [{ magFilter: 9729, minFilter: 9987, wrapS: 33071, wrapT: 33071 }],\n images: [{ bufferView: 3, mimeType: image[0] === 0x89 ? 'image/png' : 'image/jpeg' }],\n accessors: [\n { bufferView: 0, componentType: 5126, count, type: 'VEC3', ...bounds(mesh.positions) },\n { bufferView: 1, componentType: 5126, count, type: 'VEC2' },\n { bufferView: 2, componentType: 5125, count: indices.length, type: 'SCALAR' },\n ],\n bufferViews: [\n { buffer: 0, byteOffset: 0, byteLength: mesh.positions.length * 4, target: 34962 },\n { buffer: 0, byteOffset: mesh.positions.length * 4, byteLength: mesh.uvs.length * 4, target: 34962 },\n { buffer: 0, byteOffset: offsets[1], byteLength: indices.byteLength, target: 34963 },\n { buffer: 0, byteOffset: offsets[2], byteLength: image.byteLength },\n ],\n buffers: [{ byteLength: bin.byteLength }],\n };\n\n return glbOf(gltf, bin);\n};\n\n/** The bytes of a photo's image: given, or read from its URL (with the map's request headers) */\nconst imageOf = async ({ url, data }: PhotoOptions): Promise<Uint8Array> => {\n if (data)\n return ArrayBuffer.isView(data)\n ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength)\n : new Uint8Array(data);\n if (!url) throw new Error('isard: a photo needs a url or data');\n\n const response = await fetch(url, withHeaders(url));\n\n if (!response.ok) throw new Error(`isard: ${url}: ${response.status}`);\n\n return new Uint8Array(await response.arrayBuffer());\n};\n\n/** The photo's layer (`addModels`: the map's), its image read */\nexport const addPhoto = async (\n addModels: (options: ModelOptions & { before?: Layer }) => ModelLayer,\n options: PhotoOptions,\n): Promise<ModelLayer> => {\n const data = photoModel(await imageOf(options), options);\n\n return addModels({\n data,\n instances: [{ lonLat: options.lonLat, heading: options.heading ?? 0 }],\n ...(options.before && { before: options.before }),\n });\n};\n"]}
@@ -3,12 +3,12 @@
3
3
  * `natural=tree`), as the basemap's trees are drawn: a broadleaved model and a conifer, each on a layer in one instanced
4
4
  * draw, scaled to the tree's height when the data gives it.
5
5
  */
6
- import { coniferModel, treeModel } from "../basemap/trees.js";
6
+ import { turned } from "../basemap/placed.js";
7
+ import { coniferModel, TREE_METRES, treeModel } from "../basemap/trees.js";
8
+ import { isRecord } from "../util/guards.js";
7
9
  import { featuresOf } from "./features.js";
8
10
  /** The genera of conifers among street and park trees */
9
11
  const CONIFERS = /^(pinus|abies|picea|cedrus|larix|taxus|pseudotsuga|cupressus|sequoia|sequoiadendron|metasequoia|thuja|juniperus|chamaecyparis|cryptomeria|tsuga|araucaria|taxodium|ginkgo)\b/i;
10
- /** The heights of the models: what a tree's height scales them by */
11
- const MODEL_METRES = { broadleaved: 10.2, conifer: 11 };
12
12
  /** A tree's kind from its data: OpenStreetMap's `leaf_type`, else its genus */
13
13
  export const treeKind = (properties) => {
14
14
  if (properties.leaf_type === 'needleleaved')
@@ -31,16 +31,16 @@ const pointsOf = (geometry) => {
31
31
  };
32
32
  /** The broadleaved trees' layer (`addModels`: the map's), the conifers' with it (`Layer.companions`) */
33
33
  export const addTrees = (addModels, options) => {
34
- const kindOf = options.kind ?? ((p) => treeKind(p));
35
- const heightOf = options.height ?? ((p) => treeHeight(p));
34
+ const kindOf = options.kind ?? ((p) => treeKind(isRecord(p) ? p : {}));
35
+ const heightOf = options.height ?? ((p) => treeHeight(isRecord(p) ? p : {}));
36
36
  const instances = { broadleaved: [], conifer: [] };
37
37
  featuresOf(options.data).forEach((f, i) => {
38
38
  const points = pointsOf(f.geometry);
39
39
  const kind = kindOf(f.properties);
40
- const scale = heightOf(f.properties) / MODEL_METRES[kind];
40
+ const scale = heightOf(f.properties) / TREE_METRES[kind];
41
41
  points.forEach(([lon = 0, lat = 0]) => {
42
42
  // Each turned its own way, the same each time
43
- instances[kind].push({ lonLat: [lon, lat], heading: (i * 7919) % 360, scale });
43
+ instances[kind].push({ lonLat: [lon, lat], heading: turned(i), scale });
44
44
  });
45
45
  });
46
46
  const before = options.before ? { before: options.before } : {};
@@ -1 +1 @@
1
- {"version":3,"file":"trees.js","sourceRoot":"","sources":["../../src/layers/trees.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Trees at the points of a source that has them (a city's open data of its street trees, OpenStreetMap's\n * `natural=tree`), as the basemap's trees are drawn: a broadleaved model and a conifer, each on a layer in one instanced\n * draw, scaled to the tree's height when the data gives it.\n */\n\nimport { coniferModel, type TreeColors, treeModel } from '../basemap/trees.ts';\nimport { type FeatureData, featuresOf, type Geometry, type Position, type Properties } from './features.ts';\nimport type { Layer, ModelInstance, ModelLayer, ModelOptions } from './layers.ts';\n\n/** The kind of a tree */\nexport type TreeKind = 'broadleaved' | 'conifer';\n\n/** The options of `GeoMap.addTrees` */\nexport interface TreeOptions<P = Properties> {\n /** Points (and multipoints): a tree at each */\n data: FeatureData<P>;\n /** A tree's kind (default: its `leaf_type`, else a conifer if its genus is one: `genre`, `genus` or `species`\n * starting with Pinus, Abies, Picea, Cedrus…) */\n kind?: (properties: P) => TreeKind;\n /** A tree's height, metres (default: its `height`, `hauteur` or `hauteurenm`, else 10) */\n height?: (properties: P) => number;\n colors?: Partial<TreeColors>;\n before?: Layer;\n}\n\n/** The genera of conifers among street and park trees */\nconst CONIFERS =\n /^(pinus|abies|picea|cedrus|larix|taxus|pseudotsuga|cupressus|sequoia|sequoiadendron|metasequoia|thuja|juniperus|chamaecyparis|cryptomeria|tsuga|araucaria|taxodium|ginkgo)\\b/i;\n\n/** The heights of the models: what a tree's height scales them by */\nconst MODEL_METRES: Record<TreeKind, number> = { broadleaved: 10.2, conifer: 11 };\n\n/** A tree's kind from its data: OpenStreetMap's `leaf_type`, else its genus */\nexport const treeKind = (properties: Properties): TreeKind => {\n if (properties.leaf_type === 'needleleaved') return 'conifer';\n if (properties.leaf_type === 'broadleaved') return 'broadleaved';\n\n const genus = [properties.genre, properties.genus, properties.species].find((v) => typeof v === 'string');\n\n return typeof genus === 'string' && CONIFERS.test(genus.trim()) ? 'conifer' : 'broadleaved';\n};\n\n/** A tree's height from its data (metres), 10 without */\nconst treeHeight = (properties: Properties): number => {\n const height = Number(properties.height ?? properties.hauteur ?? properties.hauteurenm ?? Number.NaN);\n\n return height > 0 ? height : 10;\n};\n\n/** The points of a geometry: a point's, a multipoint's, none of another */\nconst pointsOf = (geometry: Geometry | null): Position[] => {\n if (geometry?.type === 'Point') return [geometry.coordinates];\n\n return geometry?.type === 'MultiPoint' ? geometry.coordinates : [];\n};\n\n/** The broadleaved trees' layer (`addModels`: the map's), the conifers' with it (`Layer.companions`) */\nexport const addTrees = <P = Properties>(\n addModels: (options: ModelOptions & { before?: Layer }) => ModelLayer,\n options: TreeOptions<P>,\n): ModelLayer => {\n const kindOf = options.kind ?? ((p: P) => treeKind(p as Properties));\n const heightOf = options.height ?? ((p: P) => treeHeight(p as Properties));\n const instances: Record<TreeKind, ModelInstance[]> = { broadleaved: [], conifer: [] };\n\n featuresOf(options.data).forEach((f, i) => {\n const points = pointsOf(f.geometry);\n const kind = kindOf(f.properties);\n const scale = heightOf(f.properties) / MODEL_METRES[kind];\n\n points.forEach(([lon = 0, lat = 0]) => {\n // Each turned its own way, the same each time\n instances[kind].push({ lonLat: [lon, lat], heading: (i * 7919) % 360, scale });\n });\n });\n\n const before = options.before ? { before: options.before } : {};\n const layer = addModels({ data: treeModel(options.colors), instances: instances.broadleaved, ...before });\n\n layer.companions.push(addModels({ data: coniferModel(options.colors), instances: instances.conifer, ...before }));\n\n return layer;\n};\n"]}
1
+ {"version":3,"file":"trees.js","sourceRoot":"","sources":["../../src/layers/trees.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Trees at the points of a source that has them (a city's open data of its street trees, OpenStreetMap's\n * `natural=tree`), as the basemap's trees are drawn: a broadleaved model and a conifer, each on a layer in one instanced\n * draw, scaled to the tree's height when the data gives it.\n */\n\nimport { turned } from '../basemap/placed.ts';\nimport { coniferModel, TREE_METRES, type TreeColors, treeModel } from '../basemap/trees.ts';\nimport { isRecord } from '../util/guards.ts';\nimport { type FeatureData, featuresOf, type Geometry, type Position, type Properties } from './features.ts';\nimport type { Layer, ModelInstance, ModelLayer, ModelOptions } from './layers.ts';\n\n/** The kind of a tree */\nexport type TreeKind = 'broadleaved' | 'conifer';\n\n/** The options of `GeoMap.addTrees` */\nexport interface TreeOptions<P = Properties> {\n /** Points (and multipoints): a tree at each */\n data: FeatureData<P>;\n /** A tree's kind (default: its `leaf_type`, else a conifer if its genus is one: `genre`, `genus` or `species`\n * starting with Pinus, Abies, Picea, Cedrus…) */\n kind?: (properties: P) => TreeKind;\n /** A tree's height, metres (default: its `height`, `hauteur` or `hauteurenm`, else 10) */\n height?: (properties: P) => number;\n colors?: Partial<TreeColors>;\n before?: Layer;\n}\n\n/** The genera of conifers among street and park trees */\nconst CONIFERS =\n /^(pinus|abies|picea|cedrus|larix|taxus|pseudotsuga|cupressus|sequoia|sequoiadendron|metasequoia|thuja|juniperus|chamaecyparis|cryptomeria|tsuga|araucaria|taxodium|ginkgo)\\b/i;\n\n/** A tree's kind from its data: OpenStreetMap's `leaf_type`, else its genus */\nexport const treeKind = (properties: Properties): TreeKind => {\n if (properties.leaf_type === 'needleleaved') return 'conifer';\n if (properties.leaf_type === 'broadleaved') return 'broadleaved';\n\n const genus = [properties.genre, properties.genus, properties.species].find((v) => typeof v === 'string');\n\n return typeof genus === 'string' && CONIFERS.test(genus.trim()) ? 'conifer' : 'broadleaved';\n};\n\n/** A tree's height from its data (metres), 10 without */\nconst treeHeight = (properties: Properties): number => {\n const height = Number(properties.height ?? properties.hauteur ?? properties.hauteurenm ?? Number.NaN);\n\n return height > 0 ? height : 10;\n};\n\n/** The points of a geometry: a point's, a multipoint's, none of another */\nconst pointsOf = (geometry: Geometry | null): Position[] => {\n if (geometry?.type === 'Point') return [geometry.coordinates];\n\n return geometry?.type === 'MultiPoint' ? geometry.coordinates : [];\n};\n\n/** The broadleaved trees' layer (`addModels`: the map's), the conifers' with it (`Layer.companions`) */\nexport const addTrees = <P = Properties>(\n addModels: (options: ModelOptions & { before?: Layer }) => ModelLayer,\n options: TreeOptions<P>,\n): ModelLayer => {\n const kindOf = options.kind ?? ((p: P) => treeKind(isRecord(p) ? p : {}));\n const heightOf = options.height ?? ((p: P) => treeHeight(isRecord(p) ? p : {}));\n const instances: Record<TreeKind, ModelInstance[]> = { broadleaved: [], conifer: [] };\n\n featuresOf(options.data).forEach((f, i) => {\n const points = pointsOf(f.geometry);\n const kind = kindOf(f.properties);\n const scale = heightOf(f.properties) / TREE_METRES[kind];\n\n points.forEach(([lon = 0, lat = 0]) => {\n // Each turned its own way, the same each time\n instances[kind].push({ lonLat: [lon, lat], heading: turned(i), scale });\n });\n });\n\n const before = options.before ? { before: options.before } : {};\n const layer = addModels({ data: treeModel(options.colors), instances: instances.broadleaved, ...before });\n\n layer.companions.push(addModels({ data: coniferModel(options.colors), instances: instances.conifer, ...before }));\n\n return layer;\n};\n"]}
@@ -68,6 +68,7 @@ export declare class VectorLayer extends Layer {
68
68
  private flush;
69
69
  /** Sends a tile to the engine (its blocks' first part); its key */
70
70
  private upload;
71
+ remove(): void;
71
72
  /** The feature of a picked index (no geometry: the tiles hold it cut), its properties and style layer. Tiles are
72
73
  * numbered one after the other, in the order they were first added */
73
74
  featureAt(index: number): VectorFeature | undefined;
@@ -4,7 +4,8 @@
4
4
  * tiles again. The workers cut each vector tile into polygons and lines and style its features; the engine draws the
5
5
  * style's layers in their order, each over its zoom range.
6
6
  */
7
- import { FLOATS, stops } from "./features.js";
7
+ import { FLOATS } from "../util/style-floats.js";
8
+ import { stops } from "./features.js";
8
9
  import { Layer } from "./layers.js";
9
10
  /** The main thread's time a frame gives to uploading tiles (ms): tiles arriving together are uploaded over several
10
11
  * frames, not all in the one (copying a tile's buffers to the GPU takes milliseconds) */
@@ -53,12 +54,15 @@ export class VectorLayer extends Layer {
53
54
  /** @internal Adds or replaces a tile, cut by the workers (and made ready for the GPU there, with its mesh): uploaded
54
55
  * on a coming frame, within its share of the frame (`UPLOAD_MS`); resolves once it is (or once dropped meanwhile) */
55
56
  setTile(tile, vectors) {
56
- return new Promise((uploaded) => {
57
+ if (this.removed)
58
+ return Promise.resolve();
59
+ // The map not idle until it is uploaded
60
+ return this.map.track(new Promise((uploaded) => {
57
61
  // A newer cut of a tile still waiting takes its place
58
62
  this.queue.get(tile)?.uploaded();
59
63
  this.queue.set(tile, { vectors, uploaded });
60
64
  this.flushSoon();
61
- });
65
+ }));
62
66
  }
63
67
  /** Uploads the waiting tiles on the next frame */
64
68
  flushSoon() {
@@ -73,6 +77,9 @@ export class VectorLayer extends Layer {
73
77
  /** Uploads waiting tiles for `UPLOAD_MS` at most (one at least, or a part of its blocks), the rest on the next
74
78
  * frames; a tile's `setTile` resolves once its blocks are all there */
75
79
  flush() {
80
+ // Removed meanwhile, or between two engines (the new one asks for the tiles again)
81
+ if (this.id < 0)
82
+ return;
76
83
  const start = performance.now();
77
84
  const within = () => performance.now() - start <= UPLOAD_MS;
78
85
  for (const [tile, waiting] of this.queue) {
@@ -115,6 +122,18 @@ export class VectorLayer extends Layer {
115
122
  this.map.engine.setFeatureStyle(this.id, styles, t.key);
116
123
  this.map.requestRender();
117
124
  }
125
+ remove() {
126
+ super.remove();
127
+ this.dispose();
128
+ }
129
+ /** @internal The tiles waiting resolved (dropped), none held */
130
+ dispose() {
131
+ this.queue.forEach((waiting) => {
132
+ waiting.uploaded();
133
+ });
134
+ this.queue.clear();
135
+ this.tiles.clear();
136
+ }
118
137
  /** @internal */
119
138
  removeTile(tile) {
120
139
  this.queue.get(tile)?.uploaded();
@@ -192,11 +211,7 @@ export class VectorLayer extends Layer {
192
211
  engine.setFacades(id, this.facades);
193
212
  // An engine made anew (after a context loss, or the one with 3D Tiles): its tiles cut again by the workers, once
194
213
  // the layer has its id there. Keeping their geometry here would double the memory they take
195
- this.tiles.clear();
196
- this.queue.forEach(({ uploaded }) => {
197
- uploaded();
198
- });
199
- this.queue.clear();
214
+ this.dispose();
200
215
  queueMicrotask(() => this.recut?.());
201
216
  return id;
202
217
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vectors.js","sourceRoot":"","sources":["../../src/layers/vectors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The fills and lines of a MapLibre style drawn as geometry, tile by tile (`VectorTiles.vectors`), instead of into the\n * images of a raster layer: sharp at any zoom and pitch, on the relief, picked by feature, restyled without cutting the\n * tiles again. The workers cut each vector tile into polygons and lines and style its features; the engine draws the\n * style's layers in their order, each over its zoom range.\n */\n\nimport type { LayerHost } from '../types.ts';\nimport type { Engine } from '../wasm/isard.js';\nimport type { TileVectors } from '../workers/basemap.worker.ts';\nimport { type Feature, FLOATS, type Size, stops } from './features.ts';\nimport { Layer } from './layers.ts';\n\n/** A style layer as the engine draws it: its kind, zoom range, visibility and dashes (dash, gap, dash, gap, in line\n * widths; zeros: solid) */\nexport interface VectorSublayer {\n /** The style layer's id */\n id: string;\n /** Its source layer, if any (not for a background) */\n sourceLayer: string | undefined;\n kind: 'fill' | 'line' | 'extrusion' | 'circle';\n minZoom: number;\n maxZoom: number;\n visible: boolean;\n /** Its opacity when it depends on the zoom only, evaluated at the camera's zoom (else 1: the features' colors hold\n * it) */\n opacity: Size;\n dash: [number, number, number, number];\n /** The caps of its lines' ends (`line-cap`) */\n cap: 'butt' | 'round' | 'square';\n /** The joins of its lines' pieces (`line-join`), a miter longer than `miterLimit` line widths bevelled\n * (`line-miter-limit`); round and 2 if left out */\n join?: 'miter' | 'bevel' | 'round';\n miterLimit?: number;\n /** How far its fills or lines move on screen (`fill-translate`, `line-translate`: CSS pixels, x right, y down),\n * turning with the map or staying on the screen (`*-translate-anchor`); none and `map` if left out */\n translate?: [number, number];\n translateAnchor?: 'map' | 'viewport';\n /** Its fills have patterns (`fill-pattern`, `background-pattern`): over the relief, the engine finds the ground under\n * them */\n patterned?: boolean;\n /** Its lines glow at night (the streets: its id names a road, not its casing; `setSun`) */\n lit: boolean;\n}\n\n/** The properties of a picked feature, with the style layer that drew it */\nexport type VectorFeature = Feature<Record<string, unknown>> & { layer: string; sourceLayer: string | undefined };\n\n/** The main thread's time a frame gives to uploading tiles (ms): tiles arriving together are uploaded over several\n * frames, not all in the one (copying a tile's buffers to the GPU takes milliseconds) */\nconst UPLOAD_MS = 4;\n\ninterface Tile {\n /** Its number in the engine: tiles are numbered for picking in this order */\n key: number;\n /** What picking needs (its geometry and styles are the engine's only: an engine made anew asks the tiles again) */\n vectors: Pick<TileVectors, 'properties' | 'sublayer'>;\n}\n\nexport class VectorLayer extends Layer {\n private readonly tiles = new Map<string, Tile>();\n /** The tiles waiting for their upload (`UPLOAD_MS` a frame), each with what resolves its `setTile`; `sent`: in the\n * engine already, its blocks still uploading */\n private readonly queue = new Map<string, { vectors: TileVectors; uploaded: () => void; sent?: number }>();\n private flushing = false;\n private sublayers: VectorSublayer[] = [];\n private nextKey = 0;\n /** The whole layer shown (`setVisible`), over each style layer's visibility */\n private shown = true;\n /** Its buildings' walls show their windows (`setFacades`) */\n private facades = false;\n /** @internal Resolved once the style is loaded and its layers drawn known (set by its `VectorTiles`) */\n styled: Promise<void> = Promise.resolve();\n /** @internal Changes a property of a style layer drawn (set by its `VectorTiles`) */\n restyle: ((layer: string, group: 'paint' | 'layout', name: string, value: unknown) => void) | undefined;\n /** @internal Asks the workers for the tiles in view again (set by its `VectorTiles`): an engine made anew has none */\n recut: (() => void) | undefined;\n\n /** @internal */\n constructor(map: LayerHost) {\n super(map);\n }\n\n /** Resolves once the style is loaded, its layers drawn known (`layers`), and the map has drawn the layer; rejected\n * if the style fails */\n override get ready(): Promise<void> {\n return this.styled.then(() => super.ready);\n }\n\n /** The style layers drawn, in order */\n get layers(): readonly VectorSublayer[] {\n return this.sublayers;\n }\n\n /** Keys of the tiles held (`z/x/y`) */\n get tileKeys(): string[] {\n return [...this.tiles.keys()];\n }\n\n /** @internal Sets the style layers drawn, in order (the tiles' sublayers) */\n setSublayers(sublayers: VectorSublayer[]): void {\n this.sublayers = sublayers;\n this.sendSublayers(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n /** @internal Adds or replaces a tile, cut by the workers (and made ready for the GPU there, with its mesh): uploaded\n * on a coming frame, within its share of the frame (`UPLOAD_MS`); resolves once it is (or once dropped meanwhile) */\n setTile(tile: string, vectors: TileVectors): Promise<void> {\n return new Promise((uploaded) => {\n // A newer cut of a tile still waiting takes its place\n this.queue.get(tile)?.uploaded();\n this.queue.set(tile, { vectors, uploaded });\n this.flushSoon();\n });\n }\n\n /** Uploads the waiting tiles on the next frame */\n private flushSoon(): void {\n if (this.flushing) return;\n this.flushing = true;\n requestAnimationFrame(() => {\n this.flushing = false;\n this.flush();\n });\n }\n\n /** Uploads waiting tiles for `UPLOAD_MS` at most (one at least, or a part of its blocks), the rest on the next\n * frames; a tile's `setTile` resolves once its blocks are all there */\n private flush(): void {\n const start = performance.now();\n const within = () => performance.now() - start <= UPLOAD_MS;\n\n for (const [tile, waiting] of this.queue) {\n waiting.sent ??= this.upload(tile, waiting.vectors);\n\n // A large tile's blocks (a pitched view's far tiles of a city: 10 MB and more), a part at a time\n let left = this.map.engine.uploadVectorTile(this.id, waiting.sent);\n\n while (left && within()) left = this.map.engine.uploadVectorTile(this.id, waiting.sent);\n if (left) break;\n this.queue.delete(tile);\n waiting.uploaded();\n if (!within()) break;\n }\n if (this.queue.size > 0) this.flushSoon();\n this.map.requestRender();\n }\n\n /** Sends a tile to the engine (its blocks' first part); its key */\n private upload(tile: string, vectors: TileVectors): number {\n const key = this.tiles.get(tile)?.key ?? this.nextKey++;\n const { properties, sublayer } = vectors;\n\n this.tiles.set(tile, { key, vectors: { properties, sublayer } });\n this.sendTile(this.map.engine, this.id, key, vectors);\n\n return key;\n }\n\n /** @internal New styles of a tile's features, as the workers evaluated them (the same features) */\n setTileStyles(tile: string, styles: Float32Array): void {\n const waiting = this.queue.get(tile);\n\n // Not uploaded yet: uploaded with them\n if (waiting && waiting.sent === undefined) {\n if (styles.length === waiting.vectors.properties.length * FLOATS) waiting.vectors.styles = styles;\n\n return;\n }\n\n const t = this.tiles.get(tile);\n\n if (!t || styles.length !== t.vectors.properties.length * FLOATS) return;\n this.map.engine.setFeatureStyle(this.id, styles, t.key);\n this.map.requestRender();\n }\n\n /** @internal */\n removeTile(tile: string): void {\n this.queue.get(tile)?.uploaded();\n this.queue.delete(tile);\n\n const t = this.tiles.get(tile);\n\n if (!t) return;\n this.tiles.delete(tile);\n this.map.engine.removeVectorTile(this.id, t.key);\n this.map.requestRender();\n }\n\n /** The feature of a picked index (no geometry: the tiles hold it cut), its properties and style layer. Tiles are\n * numbered one after the other, in the order they were first added */\n featureAt(index: number): VectorFeature | undefined {\n const tiles = [...this.tiles.values()].sort((a, b) => a.key - b.key);\n // The tile holding the index, and the index among its features (one pass, the features of the tiles before\n // taken off)\n const { tile, rest } = tiles.reduce<{ tile?: Tile; rest: number }>(\n (found, t) => {\n if (found.tile) return found;\n\n const count = t.vectors.properties.length;\n\n return found.rest < count ? { tile: t, rest: found.rest } : { rest: found.rest - count };\n },\n { rest: index },\n );\n\n if (!tile) return undefined;\n\n const properties = tile.vectors.properties[rest];\n const sublayer = this.sublayers[tile.vectors.sublayer[rest] ?? -1];\n\n if (properties === undefined || !sublayer) return undefined;\n\n return { type: 'Feature', properties, geometry: null, layer: sublayer.id, sourceLayer: sublayer.sourceLayer };\n }\n\n /** Changes a paint property of a style layer drawn, as MapLibre's `setPaintProperty`: the tiles are restyled, not cut\n * again */\n setPaintProperty(layer: string, name: string, value: unknown): void {\n this.restyle?.(layer, 'paint', name, value);\n }\n\n /** Changes a layout property of a style layer drawn (`visibility`: shown or hidden at once), as MapLibre's\n * `setLayoutProperty` */\n setLayoutProperty(layer: string, name: string, value: unknown): void {\n this.restyle?.(layer, 'layout', name, value);\n }\n\n /** Whether its buildings' walls show their windows: glass by day, some lit at night (only the lit ones without) */\n setFacades(facades: boolean): void {\n this.facades = facades;\n this.map.engine.setFacades(this.id, facades);\n this.map.requestRender();\n }\n\n setVisible(visible: boolean): void {\n this.shown = visible;\n this.sendSublayers(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n private sendSublayers(engine: Engine, id: number): void {\n const s = this.sublayers;\n\n engine.setVectorSublayers(\n id,\n Uint8Array.from(s, (l) => ({ fill: 0, line: 1, extrusion: 2, circle: 3 })[l.kind]),\n Float64Array.from(s.flatMap((l) => [l.minZoom, l.maxZoom])),\n Uint8Array.from(s, (l) => (l.visible && this.shown ? 1 : 0)),\n Float32Array.from(s.flatMap((l) => l.dash)),\n Float32Array.from(s.flatMap((l) => stops(l.opacity))),\n Uint8Array.from(s, (l) => ({ butt: 0, round: 1, square: 2 })[l.cap]),\n Uint8Array.from(s, (l) => (l.lit ? 1 : 0)),\n Float32Array.from(s.flatMap((l) => [{ round: 0, miter: 1, bevel: 2 }[l.join ?? 'round'], l.miterLimit ?? 2])),\n Float32Array.from(s.flatMap((l) => [...(l.translate ?? [0, 0]), l.translateAnchor === 'viewport' ? 1 : 0])),\n Uint8Array.from(s, (l) => (l.patterned ? 1 : 0)),\n );\n }\n\n /** A tile to the engine: its mesh uploaded as it is, or without one its geometry, which the engine makes ready */\n private sendTile(engine: Engine, id: number, key: number, vectors: TileVectors): void {\n const { fills, lines, circles, styles, properties, mesh } = vectors;\n\n if (mesh) {\n engine.setVectorTileMesh(\n id,\n key,\n fills.points,\n fills.ringStart,\n fills.polygonStart,\n fills.feature,\n fills.fillStart,\n mesh.vertices,\n mesh.indices,\n mesh.fillRanges,\n mesh.prism,\n mesh.pieces,\n mesh.lineTable,\n mesh.lineRanges,\n mesh.blocks,\n mesh.blockIndices,\n mesh.blockRanges,\n mesh.bounds,\n properties.length,\n );\n } else {\n engine.setVectorTile(\n id,\n key,\n fills.points,\n fills.ringStart,\n fills.polygonStart,\n fills.feature,\n fills.fillStart,\n lines.points,\n lines.start,\n lines.feature,\n lines.lineStart,\n properties.length,\n vectors.tile ?? new Float64Array(),\n fills.roofs ?? new Uint8Array(),\n fills.roofHeights ?? new Float32Array(),\n fills.roofOutlines ?? new Float64Array(),\n fills.roofOutlineStart ?? new Uint32Array(),\n fills.roofMetres ?? 0,\n );\n }\n // The circles' points, once the tile is there\n if (circles) engine.setVectorTilePoints(id, key, circles.points, circles.feature, circles.circleStart);\n engine.setFeatureStyle(id, styles, key);\n }\n\n /** @internal */\n protected create(engine: Engine): number {\n const id = engine.addVectorTiles();\n\n this.sendSublayers(engine, id);\n engine.setFacades(id, this.facades);\n // An engine made anew (after a context loss, or the one with 3D Tiles): its tiles cut again by the workers, once\n // the layer has its id there. Keeping their geometry here would double the memory they take\n this.tiles.clear();\n this.queue.forEach(({ uploaded }) => {\n uploaded();\n });\n this.queue.clear();\n queueMicrotask(() => this.recut?.());\n\n return id;\n }\n}\n"]}
1
+ {"version":3,"file":"vectors.js","sourceRoot":"","sources":["../../src/layers/vectors.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The fills and lines of a MapLibre style drawn as geometry, tile by tile (`VectorTiles.vectors`), instead of into the\n * images of a raster layer: sharp at any zoom and pitch, on the relief, picked by feature, restyled without cutting the\n * tiles again. The workers cut each vector tile into polygons and lines and style its features; the engine draws the\n * style's layers in their order, each over its zoom range.\n */\n\nimport type { LayerHost } from '../types.ts';\nimport { FLOATS } from '../util/style-floats.ts';\nimport type { Engine } from '../wasm/isard.js';\nimport type { TileVectors } from '../workers/basemap.worker.ts';\nimport { type Feature, type Size, stops } from './features.ts';\nimport { Layer } from './layers.ts';\n\n/** A style layer as the engine draws it: its kind, zoom range, visibility and dashes (dash, gap, dash, gap, in line\n * widths; zeros: solid) */\nexport interface VectorSublayer {\n /** The style layer's id */\n id: string;\n /** Its source layer, if any (not for a background) */\n sourceLayer: string | undefined;\n kind: 'fill' | 'line' | 'extrusion' | 'circle';\n minZoom: number;\n maxZoom: number;\n visible: boolean;\n /** Its opacity when it depends on the zoom only, evaluated at the camera's zoom (else 1: the features' colors hold\n * it) */\n opacity: Size;\n dash: [number, number, number, number];\n /** The caps of its lines' ends (`line-cap`) */\n cap: 'butt' | 'round' | 'square';\n /** The joins of its lines' pieces (`line-join`), a miter longer than `miterLimit` line widths bevelled\n * (`line-miter-limit`); round and 2 if left out */\n join?: 'miter' | 'bevel' | 'round';\n miterLimit?: number;\n /** How far its fills or lines move on screen (`fill-translate`, `line-translate`: CSS pixels, x right, y down),\n * turning with the map or staying on the screen (`*-translate-anchor`); none and `map` if left out */\n translate?: [number, number];\n translateAnchor?: 'map' | 'viewport';\n /** Its fills have patterns (`fill-pattern`, `background-pattern`): over the relief, the engine finds the ground under\n * them */\n patterned?: boolean;\n /** Its lines glow at night (the streets: its id names a road, not its casing; `setSun`) */\n lit: boolean;\n}\n\n/** The properties of a picked feature, with the style layer that drew it */\nexport type VectorFeature = Feature<Record<string, unknown>> & { layer: string; sourceLayer: string | undefined };\n\n/** The main thread's time a frame gives to uploading tiles (ms): tiles arriving together are uploaded over several\n * frames, not all in the one (copying a tile's buffers to the GPU takes milliseconds) */\nconst UPLOAD_MS = 4;\n\ninterface Tile {\n /** Its number in the engine: tiles are numbered for picking in this order */\n key: number;\n /** What picking needs (its geometry and styles are the engine's only: an engine made anew asks the tiles again) */\n vectors: Pick<TileVectors, 'properties' | 'sublayer'>;\n}\n\nexport class VectorLayer extends Layer {\n private readonly tiles = new Map<string, Tile>();\n /** The tiles waiting for their upload (`UPLOAD_MS` a frame), each with what resolves its `setTile`; `sent`: in the\n * engine already, its blocks still uploading */\n private readonly queue = new Map<string, { vectors: TileVectors; uploaded: () => void; sent?: number }>();\n private flushing = false;\n private sublayers: VectorSublayer[] = [];\n private nextKey = 0;\n /** The whole layer shown (`setVisible`), over each style layer's visibility */\n private shown = true;\n /** Its buildings' walls show their windows (`setFacades`) */\n private facades = false;\n /** @internal Resolved once the style is loaded and its layers drawn known (set by its `VectorTiles`) */\n styled: Promise<void> = Promise.resolve();\n /** @internal Changes a property of a style layer drawn (set by its `VectorTiles`) */\n restyle: ((layer: string, group: 'paint' | 'layout', name: string, value: unknown) => void) | undefined;\n /** @internal Asks the workers for the tiles in view again (set by its `VectorTiles`): an engine made anew has none */\n recut: (() => void) | undefined;\n\n /** @internal */\n constructor(map: LayerHost) {\n super(map);\n }\n\n /** Resolves once the style is loaded, its layers drawn known (`layers`), and the map has drawn the layer; rejected\n * if the style fails */\n override get ready(): Promise<void> {\n return this.styled.then(() => super.ready);\n }\n\n /** The style layers drawn, in order */\n get layers(): readonly VectorSublayer[] {\n return this.sublayers;\n }\n\n /** Keys of the tiles held (`z/x/y`) */\n get tileKeys(): string[] {\n return [...this.tiles.keys()];\n }\n\n /** @internal Sets the style layers drawn, in order (the tiles' sublayers) */\n setSublayers(sublayers: VectorSublayer[]): void {\n this.sublayers = sublayers;\n this.sendSublayers(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n /** @internal Adds or replaces a tile, cut by the workers (and made ready for the GPU there, with its mesh): uploaded\n * on a coming frame, within its share of the frame (`UPLOAD_MS`); resolves once it is (or once dropped meanwhile) */\n setTile(tile: string, vectors: TileVectors): Promise<void> {\n if (this.removed) return Promise.resolve();\n // The map not idle until it is uploaded\n return this.map.track(\n new Promise((uploaded) => {\n // A newer cut of a tile still waiting takes its place\n this.queue.get(tile)?.uploaded();\n this.queue.set(tile, { vectors, uploaded });\n this.flushSoon();\n }),\n );\n }\n\n /** Uploads the waiting tiles on the next frame */\n private flushSoon(): void {\n if (this.flushing) return;\n this.flushing = true;\n requestAnimationFrame(() => {\n this.flushing = false;\n this.flush();\n });\n }\n\n /** Uploads waiting tiles for `UPLOAD_MS` at most (one at least, or a part of its blocks), the rest on the next\n * frames; a tile's `setTile` resolves once its blocks are all there */\n private flush(): void {\n // Removed meanwhile, or between two engines (the new one asks for the tiles again)\n if (this.id < 0) return;\n\n const start = performance.now();\n const within = () => performance.now() - start <= UPLOAD_MS;\n\n for (const [tile, waiting] of this.queue) {\n waiting.sent ??= this.upload(tile, waiting.vectors);\n\n // A large tile's blocks (a pitched view's far tiles of a city: 10 MB and more), a part at a time\n let left = this.map.engine.uploadVectorTile(this.id, waiting.sent);\n\n while (left && within()) left = this.map.engine.uploadVectorTile(this.id, waiting.sent);\n if (left) break;\n this.queue.delete(tile);\n waiting.uploaded();\n if (!within()) break;\n }\n if (this.queue.size > 0) this.flushSoon();\n this.map.requestRender();\n }\n\n /** Sends a tile to the engine (its blocks' first part); its key */\n private upload(tile: string, vectors: TileVectors): number {\n const key = this.tiles.get(tile)?.key ?? this.nextKey++;\n const { properties, sublayer } = vectors;\n\n this.tiles.set(tile, { key, vectors: { properties, sublayer } });\n this.sendTile(this.map.engine, this.id, key, vectors);\n\n return key;\n }\n\n /** @internal New styles of a tile's features, as the workers evaluated them (the same features) */\n setTileStyles(tile: string, styles: Float32Array): void {\n const waiting = this.queue.get(tile);\n\n // Not uploaded yet: uploaded with them\n if (waiting && waiting.sent === undefined) {\n if (styles.length === waiting.vectors.properties.length * FLOATS) waiting.vectors.styles = styles;\n\n return;\n }\n\n const t = this.tiles.get(tile);\n\n if (!t || styles.length !== t.vectors.properties.length * FLOATS) return;\n this.map.engine.setFeatureStyle(this.id, styles, t.key);\n this.map.requestRender();\n }\n\n override remove(): void {\n super.remove();\n this.dispose();\n }\n\n /** @internal The tiles waiting resolved (dropped), none held */\n override dispose(): void {\n this.queue.forEach((waiting) => {\n waiting.uploaded();\n });\n this.queue.clear();\n this.tiles.clear();\n }\n\n /** @internal */\n removeTile(tile: string): void {\n this.queue.get(tile)?.uploaded();\n this.queue.delete(tile);\n\n const t = this.tiles.get(tile);\n\n if (!t) return;\n this.tiles.delete(tile);\n this.map.engine.removeVectorTile(this.id, t.key);\n this.map.requestRender();\n }\n\n /** The feature of a picked index (no geometry: the tiles hold it cut), its properties and style layer. Tiles are\n * numbered one after the other, in the order they were first added */\n featureAt(index: number): VectorFeature | undefined {\n const tiles = [...this.tiles.values()].sort((a, b) => a.key - b.key);\n // The tile holding the index, and the index among its features (one pass, the features of the tiles before\n // taken off)\n const { tile, rest } = tiles.reduce<{ tile?: Tile; rest: number }>(\n (found, t) => {\n if (found.tile) return found;\n\n const count = t.vectors.properties.length;\n\n return found.rest < count ? { tile: t, rest: found.rest } : { rest: found.rest - count };\n },\n { rest: index },\n );\n\n if (!tile) return undefined;\n\n const properties = tile.vectors.properties[rest];\n const sublayer = this.sublayers[tile.vectors.sublayer[rest] ?? -1];\n\n if (properties === undefined || !sublayer) return undefined;\n\n return { type: 'Feature', properties, geometry: null, layer: sublayer.id, sourceLayer: sublayer.sourceLayer };\n }\n\n /** Changes a paint property of a style layer drawn, as MapLibre's `setPaintProperty`: the tiles are restyled, not cut\n * again */\n setPaintProperty(layer: string, name: string, value: unknown): void {\n this.restyle?.(layer, 'paint', name, value);\n }\n\n /** Changes a layout property of a style layer drawn (`visibility`: shown or hidden at once), as MapLibre's\n * `setLayoutProperty` */\n setLayoutProperty(layer: string, name: string, value: unknown): void {\n this.restyle?.(layer, 'layout', name, value);\n }\n\n /** Whether its buildings' walls show their windows: glass by day, some lit at night (only the lit ones without) */\n setFacades(facades: boolean): void {\n this.facades = facades;\n this.map.engine.setFacades(this.id, facades);\n this.map.requestRender();\n }\n\n setVisible(visible: boolean): void {\n this.shown = visible;\n this.sendSublayers(this.map.engine, this.id);\n this.map.requestRender();\n }\n\n private sendSublayers(engine: Engine, id: number): void {\n const s = this.sublayers;\n\n engine.setVectorSublayers(\n id,\n Uint8Array.from(s, (l) => ({ fill: 0, line: 1, extrusion: 2, circle: 3 })[l.kind]),\n Float64Array.from(s.flatMap((l) => [l.minZoom, l.maxZoom])),\n Uint8Array.from(s, (l) => (l.visible && this.shown ? 1 : 0)),\n Float32Array.from(s.flatMap((l) => l.dash)),\n Float32Array.from(s.flatMap((l) => stops(l.opacity))),\n Uint8Array.from(s, (l) => ({ butt: 0, round: 1, square: 2 })[l.cap]),\n Uint8Array.from(s, (l) => (l.lit ? 1 : 0)),\n Float32Array.from(s.flatMap((l) => [{ round: 0, miter: 1, bevel: 2 }[l.join ?? 'round'], l.miterLimit ?? 2])),\n Float32Array.from(s.flatMap((l) => [...(l.translate ?? [0, 0]), l.translateAnchor === 'viewport' ? 1 : 0])),\n Uint8Array.from(s, (l) => (l.patterned ? 1 : 0)),\n );\n }\n\n /** A tile to the engine: its mesh uploaded as it is, or without one its geometry, which the engine makes ready */\n private sendTile(engine: Engine, id: number, key: number, vectors: TileVectors): void {\n const { fills, lines, circles, styles, properties, mesh } = vectors;\n\n if (mesh) {\n engine.setVectorTileMesh(\n id,\n key,\n fills.points,\n fills.ringStart,\n fills.polygonStart,\n fills.feature,\n fills.fillStart,\n mesh.vertices,\n mesh.indices,\n mesh.fillRanges,\n mesh.prism,\n mesh.pieces,\n mesh.lineTable,\n mesh.lineRanges,\n mesh.blocks,\n mesh.blockIndices,\n mesh.blockRanges,\n mesh.bounds,\n properties.length,\n );\n } else {\n engine.setVectorTile(\n id,\n key,\n fills.points,\n fills.ringStart,\n fills.polygonStart,\n fills.feature,\n fills.fillStart,\n lines.points,\n lines.start,\n lines.feature,\n lines.lineStart,\n properties.length,\n vectors.tile ?? new Float64Array(),\n fills.roofs ?? new Uint8Array(),\n fills.roofHeights ?? new Float32Array(),\n fills.roofOutlines ?? new Float64Array(),\n fills.roofOutlineStart ?? new Uint32Array(),\n fills.roofMetres ?? 0,\n );\n }\n // The circles' points, once the tile is there\n if (circles) engine.setVectorTilePoints(id, key, circles.points, circles.feature, circles.circleStart);\n engine.setFeatureStyle(id, styles, key);\n }\n\n /** @internal */\n protected create(engine: Engine): number {\n const id = engine.addVectorTiles();\n\n this.sendSublayers(engine, id);\n engine.setFacades(id, this.facades);\n // An engine made anew (after a context loss, or the one with 3D Tiles): its tiles cut again by the workers, once\n // the layer has its id there. Keeping their geometry here would double the memory they take\n this.dispose();\n queueMicrotask(() => this.recut?.());\n\n return id;\n }\n}\n"]}
@@ -21,6 +21,8 @@ export interface FrameHost {
21
21
  customLayers(): boolean;
22
22
  /** The engine with 3D Tiles is loading: not idle */
23
23
  upgrading(): boolean;
24
+ /** Work the page waits on for what it draws (the vector tiles cut in the workers, uploaded over frames): not idle */
25
+ working(): boolean;
24
26
  /** The frame is drawn: what follows it (the overlays placed, the hover picking asked for at `hovered`) */
25
27
  drawn(hovered: Point | undefined): void;
26
28
  }
@@ -131,9 +131,9 @@ export class FrameLoop {
131
131
  // A move goes on until a frame finds the camera still: that frame sends 'moveend'
132
132
  if (this.animation || (moved && this.moving))
133
133
  this.request();
134
- // Idle: nothing loading (the engine with 3D Tiles neither), no animation, no frame asked for (uploads waiting,
135
- // fades, listeners)
136
- if (loading || host.upgrading() || this.animation || this.requested !== 0) {
134
+ // Idle: nothing loading (the engine with 3D Tiles neither, nor the page's work for what it draws), no animation,
135
+ // no frame asked for (uploads waiting, fades, listeners)
136
+ if (loading || host.upgrading() || host.working() || this.animation || this.requested !== 0) {
137
137
  this.#settled = false;
138
138
  }
139
139
  else if (!this.#settled) {
@@ -1 +1 @@
1
- {"version":3,"file":"frame-loop.js","sourceRoot":"","sources":["../../src/map/frame-loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The frame loop of a map: frames drawn on demand only (an idle map costs nothing), the running animation stepped in\n * each, and the events of a frame sent with it: the camera's moves, the pointer's position (coalesced to one per\n * frame, so the values computed for it are drawn in the same frame), 'render', and 'idle' once everything is loaded.\n */\n\nimport type { Point } from '../types.ts';\nimport type { Step } from '../util/animation.ts';\nimport type { Camera, Engine } from '../wasm/isard.js';\nimport type { MapListeners } from './events.ts';\n\n/** What the frame loop needs of its map */\nexport interface FrameHost {\n /** The map's engine of the moment */\n engine(): Engine;\n /** The map's camera */\n camera(): Camera;\n /** The WebGL context is lost: nothing is drawn */\n lost(): boolean;\n /** The pointer or the keys are moving the camera: no 'moveend' yet */\n interacting(): boolean;\n /** Custom layers are drawn: they read the camera of the frame */\n customLayers(): boolean;\n /** The engine with 3D Tiles is loading: not idle */\n upgrading(): boolean;\n /** The frame is drawn: what follows it (the overlays placed, the hover picking asked for at `hovered`) */\n drawn(hovered: Point | undefined): void;\n}\n\n/** The frames of a map, asked for and drawn */\nexport class FrameLoop {\n private readonly host: FrameHost;\n private readonly listeners: MapListeners;\n /** Frame asked for, 0 if none */\n private requested = 0;\n private inFrame = false;\n private animation: Step | undefined;\n /** The pointer's position for the next frame */\n private hoverPoint: Point | undefined;\n private lastCamera = '';\n /** The camera moved since the last 'moveend' */\n private moving = false;\n #settled = false;\n #camera: Camera | undefined;\n\n /** `listeners`: the map's, told of the frame's events */\n constructor(host: FrameHost, listeners: MapListeners) {\n this.host = host;\n this.listeners = listeners;\n }\n\n /** The last frame found everything loaded ('idle' sent) */\n get settled(): boolean {\n return this.#settled;\n }\n\n /** The camera of the frame being drawn: what `getCamera` gives the custom layers while the engine is busy */\n get camera(): Camera | undefined {\n return this.#camera;\n }\n\n /** Asks for a frame; several calls before the next frame draw it once */\n request(): void {\n // Changes made by listeners during a frame are drawn by that frame; nothing is drawn without a context\n if (this.inFrame || this.host.lost()) return;\n this.requested ||= requestAnimationFrame((now) => this.frame(now));\n }\n\n /** Asks for a frame that sends 'idle' once everything is loaded, even if the last one already did: the waiters of\n * `whenIdle` on a map already idle (else no frame would change from loading to loaded, and none would send it) */\n requestIdle(): void {\n this.#settled = false;\n this.request();\n }\n\n /** Runs `step` every frame until it returns false; replaces the running animation */\n animate(step: Step | undefined): void {\n // A flight cut short no longer needs its destination\n if (this.animation) this.host.engine().clearPreload();\n this.animation = step;\n this.request();\n }\n\n /** The pointer's position for the next frame (undefined: none) */\n hover(point: Point | undefined): void {\n this.hoverPoint = point;\n }\n\n /** Draws a frame now, in place of the one asked for (a resized canvas is cleared: waiting one frame would flash) */\n drawNow(): void {\n cancelAnimationFrame(this.requested);\n this.frame(performance.now());\n }\n\n /** Cancels the frame asked for (the context is lost); a new one can be asked for */\n cancel(): void {\n cancelAnimationFrame(this.requested);\n this.requested = 0;\n }\n\n /** Cancels the frame asked for, for good (the map is destroyed): it stays marked as asked for */\n stop(): void {\n cancelAnimationFrame(this.requested);\n }\n\n private frame(now: number): void {\n this.requested = 0;\n try {\n this.inFrame = true;\n this.draw(now);\n } finally {\n // Whatever throws in a frame, the next ones are still drawn\n this.inFrame = false;\n }\n }\n\n private draw(now: number): void {\n if (this.animation && !this.animation(now)) this.animation = undefined;\n\n const { host, listeners } = this;\n const camera = host.camera();\n const key = `${camera.center}|${camera.zoom}|${camera.pitch}|${camera.bearing}`;\n const moved = key !== this.lastCamera;\n\n if (moved) {\n // The first frame is no move\n if (!this.moving && this.lastCamera !== '') {\n this.moving = true;\n listeners.emit('movestart', camera);\n }\n this.lastCamera = key;\n listeners.emit('move', camera);\n } else if (this.moving && !this.animation && !host.interacting()) {\n this.moving = false;\n listeners.emit('moveend', camera);\n }\n\n // Values for the pointer position are computed before drawing, in the same frame\n const hovered = this.hoverPoint;\n\n if (hovered) {\n this.hoverPoint = undefined;\n // The position under the pointer for its listeners only (a 'hoverfeature' alone does not need it)\n if (listeners.has('hover')) {\n listeners.emit('hover', { point: hovered, lonLat: host.engine().ground(hovered[0], hovered[1]) });\n }\n }\n\n const t0 = performance.now();\n\n // The engine asks for the next frame while it draws (uploads past the frame budget, fades): not ignored\n this.inFrame = false;\n // As the engine has it now (a listener may have moved it), for the custom layers drawn in the frame\n this.#camera = host.customLayers() ? host.engine().camera() : undefined;\n\n const render = (): boolean => {\n try {\n return host.engine().render();\n } finally {\n this.#camera = undefined;\n }\n };\n const loading = render();\n\n this.inFrame = true;\n host.drawn(hovered);\n listeners.emit('render', { ms: performance.now() - t0 });\n this.inFrame = false;\n // A move goes on until a frame finds the camera still: that frame sends 'moveend'\n if (this.animation || (moved && this.moving)) this.request();\n // Idle: nothing loading (the engine with 3D Tiles neither), no animation, no frame asked for (uploads waiting,\n // fades, listeners)\n if (loading || host.upgrading() || this.animation || this.requested !== 0) {\n this.#settled = false;\n } else if (!this.#settled) {\n this.#settled = true;\n listeners.emit('idle', {});\n }\n }\n}\n"]}
1
+ {"version":3,"file":"frame-loop.js","sourceRoot":"","sources":["../../src/map/frame-loop.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The frame loop of a map: frames drawn on demand only (an idle map costs nothing), the running animation stepped in\n * each, and the events of a frame sent with it: the camera's moves, the pointer's position (coalesced to one per\n * frame, so the values computed for it are drawn in the same frame), 'render', and 'idle' once everything is loaded.\n */\n\nimport type { Point } from '../types.ts';\nimport type { Step } from '../util/animation.ts';\nimport type { Camera, Engine } from '../wasm/isard.js';\nimport type { MapListeners } from './events.ts';\n\n/** What the frame loop needs of its map */\nexport interface FrameHost {\n /** The map's engine of the moment */\n engine(): Engine;\n /** The map's camera */\n camera(): Camera;\n /** The WebGL context is lost: nothing is drawn */\n lost(): boolean;\n /** The pointer or the keys are moving the camera: no 'moveend' yet */\n interacting(): boolean;\n /** Custom layers are drawn: they read the camera of the frame */\n customLayers(): boolean;\n /** The engine with 3D Tiles is loading: not idle */\n upgrading(): boolean;\n /** Work the page waits on for what it draws (the vector tiles cut in the workers, uploaded over frames): not idle */\n working(): boolean;\n /** The frame is drawn: what follows it (the overlays placed, the hover picking asked for at `hovered`) */\n drawn(hovered: Point | undefined): void;\n}\n\n/** The frames of a map, asked for and drawn */\nexport class FrameLoop {\n private readonly host: FrameHost;\n private readonly listeners: MapListeners;\n /** Frame asked for, 0 if none */\n private requested = 0;\n private inFrame = false;\n private animation: Step | undefined;\n /** The pointer's position for the next frame */\n private hoverPoint: Point | undefined;\n private lastCamera = '';\n /** The camera moved since the last 'moveend' */\n private moving = false;\n #settled = false;\n #camera: Camera | undefined;\n\n /** `listeners`: the map's, told of the frame's events */\n constructor(host: FrameHost, listeners: MapListeners) {\n this.host = host;\n this.listeners = listeners;\n }\n\n /** The last frame found everything loaded ('idle' sent) */\n get settled(): boolean {\n return this.#settled;\n }\n\n /** The camera of the frame being drawn: what `getCamera` gives the custom layers while the engine is busy */\n get camera(): Camera | undefined {\n return this.#camera;\n }\n\n /** Asks for a frame; several calls before the next frame draw it once */\n request(): void {\n // Changes made by listeners during a frame are drawn by that frame; nothing is drawn without a context\n if (this.inFrame || this.host.lost()) return;\n this.requested ||= requestAnimationFrame((now) => this.frame(now));\n }\n\n /** Asks for a frame that sends 'idle' once everything is loaded, even if the last one already did: the waiters of\n * `whenIdle` on a map already idle (else no frame would change from loading to loaded, and none would send it) */\n requestIdle(): void {\n this.#settled = false;\n this.request();\n }\n\n /** Runs `step` every frame until it returns false; replaces the running animation */\n animate(step: Step | undefined): void {\n // A flight cut short no longer needs its destination\n if (this.animation) this.host.engine().clearPreload();\n this.animation = step;\n this.request();\n }\n\n /** The pointer's position for the next frame (undefined: none) */\n hover(point: Point | undefined): void {\n this.hoverPoint = point;\n }\n\n /** Draws a frame now, in place of the one asked for (a resized canvas is cleared: waiting one frame would flash) */\n drawNow(): void {\n cancelAnimationFrame(this.requested);\n this.frame(performance.now());\n }\n\n /** Cancels the frame asked for (the context is lost); a new one can be asked for */\n cancel(): void {\n cancelAnimationFrame(this.requested);\n this.requested = 0;\n }\n\n /** Cancels the frame asked for, for good (the map is destroyed): it stays marked as asked for */\n stop(): void {\n cancelAnimationFrame(this.requested);\n }\n\n private frame(now: number): void {\n this.requested = 0;\n try {\n this.inFrame = true;\n this.draw(now);\n } finally {\n // Whatever throws in a frame, the next ones are still drawn\n this.inFrame = false;\n }\n }\n\n private draw(now: number): void {\n if (this.animation && !this.animation(now)) this.animation = undefined;\n\n const { host, listeners } = this;\n const camera = host.camera();\n const key = `${camera.center}|${camera.zoom}|${camera.pitch}|${camera.bearing}`;\n const moved = key !== this.lastCamera;\n\n if (moved) {\n // The first frame is no move\n if (!this.moving && this.lastCamera !== '') {\n this.moving = true;\n listeners.emit('movestart', camera);\n }\n this.lastCamera = key;\n listeners.emit('move', camera);\n } else if (this.moving && !this.animation && !host.interacting()) {\n this.moving = false;\n listeners.emit('moveend', camera);\n }\n\n // Values for the pointer position are computed before drawing, in the same frame\n const hovered = this.hoverPoint;\n\n if (hovered) {\n this.hoverPoint = undefined;\n // The position under the pointer for its listeners only (a 'hoverfeature' alone does not need it)\n if (listeners.has('hover')) {\n listeners.emit('hover', { point: hovered, lonLat: host.engine().ground(hovered[0], hovered[1]) });\n }\n }\n\n const t0 = performance.now();\n\n // The engine asks for the next frame while it draws (uploads past the frame budget, fades): not ignored\n this.inFrame = false;\n // As the engine has it now (a listener may have moved it), for the custom layers drawn in the frame\n this.#camera = host.customLayers() ? host.engine().camera() : undefined;\n\n const render = (): boolean => {\n try {\n return host.engine().render();\n } finally {\n this.#camera = undefined;\n }\n };\n const loading = render();\n\n this.inFrame = true;\n host.drawn(hovered);\n listeners.emit('render', { ms: performance.now() - t0 });\n this.inFrame = false;\n // A move goes on until a frame finds the camera still: that frame sends 'moveend'\n if (this.animation || (moved && this.moving)) this.request();\n // Idle: nothing loading (the engine with 3D Tiles neither, nor the page's work for what it draws), no animation,\n // no frame asked for (uploads waiting, fades, listeners)\n if (loading || host.upgrading() || host.working() || this.animation || this.requested !== 0) {\n this.#settled = false;\n } else if (!this.#settled) {\n this.#settled = true;\n listeners.emit('idle', {});\n }\n }\n}\n"]}
package/dist/map.js CHANGED
@@ -50,6 +50,8 @@ const hasFeatures = (layer) => 'featureAt' in layer && typeof layer.featureAt ==
50
50
  export class GeoMap {
51
51
  canvas;
52
52
  #engine;
53
+ /** The work the map waits on before it is idle (`track`) */
54
+ #working = 0;
53
55
  /** The engine class (core, or with 3D Tiles) and the engine with 3D Tiles loaded when needed */
54
56
  engines;
55
57
  /** The layers in drawing order and those kept on top */
@@ -135,6 +137,7 @@ export class GeoMap {
135
137
  interacting: () => this.interaction?.active === true,
136
138
  customLayers: () => this.layers.some((layer) => layer instanceof CustomLayer),
137
139
  upgrading: () => this.engines.upgrading !== undefined,
140
+ working: () => this.#working > 0,
138
141
  drawn: (hovered) => {
139
142
  // The picking pass sees the frame just drawn; its answer comes on a later frame
140
143
  if (hovered && this.listeners.has('hoverfeature'))
@@ -689,6 +692,20 @@ export class GeoMap {
689
692
  old.remove();
690
693
  return layer;
691
694
  }
695
+ /**
696
+ * @internal Work the map waits on before it is idle (the vector tiles cut in the workers, uploaded over frames): the
697
+ * engine knows only the tiles it holds, not those coming (an engine made anew holds none until the workers cut them
698
+ * again from their cache, no request on the network meanwhile); a frame once it is done, to tell
699
+ */
700
+ track(work) {
701
+ this.#working += 1;
702
+ const done = () => {
703
+ this.#working -= 1;
704
+ this.requestRender();
705
+ };
706
+ work.then(done, done);
707
+ return work;
708
+ }
692
709
  /** @internal Resolves once the map is idle (everything in view loaded and drawn) */
693
710
  whenIdle() {
694
711
  if (this.gone)