isard 1.5.0 → 1.6.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 (108) hide show
  1. package/CHANGELOG.md +165 -1
  2. package/README.md +3 -2
  3. package/dist/basemap/furniture.d.ts +8 -0
  4. package/dist/basemap/furniture.js +26 -0
  5. package/dist/basemap/furniture.js.map +1 -0
  6. package/dist/basemap/trees.d.ts +35 -8
  7. package/dist/basemap/trees.js +105 -52
  8. package/dist/basemap/trees.js.map +1 -1
  9. package/dist/basemap/vectorstyle.d.ts +9 -3
  10. package/dist/basemap/vectorstyle.js +5 -6
  11. package/dist/basemap/vectorstyle.js.map +1 -1
  12. package/dist/basemap/vectortiles/context.d.ts +3 -0
  13. package/dist/basemap/vectortiles/context.js.map +1 -1
  14. package/dist/basemap/vectortiles/extrusions.d.ts +4 -0
  15. package/dist/basemap/vectortiles/extrusions.js +17 -2
  16. package/dist/basemap/vectortiles/extrusions.js.map +1 -1
  17. package/dist/basemap/vectortiles/labels.d.ts +3 -0
  18. package/dist/basemap/vectortiles/labels.js +42 -17
  19. package/dist/basemap/vectortiles/labels.js.map +1 -1
  20. package/dist/basemap/vectortiles/lamps.d.ts +23 -0
  21. package/dist/basemap/vectortiles/lamps.js +90 -0
  22. package/dist/basemap/vectortiles/lamps.js.map +1 -0
  23. package/dist/basemap/vectortiles/requests.d.ts +4 -0
  24. package/dist/basemap/vectortiles/requests.js +15 -2
  25. package/dist/basemap/vectortiles/requests.js.map +1 -1
  26. package/dist/basemap/vectortiles/trees.d.ts +9 -0
  27. package/dist/basemap/vectortiles/trees.js +114 -29
  28. package/dist/basemap/vectortiles/trees.js.map +1 -1
  29. package/dist/basemap/vectortiles/vectors.d.ts +17 -0
  30. package/dist/basemap/vectortiles/vectors.js +72 -8
  31. package/dist/basemap/vectortiles/vectors.js.map +1 -1
  32. package/dist/basemap/vectortiles.d.ts +26 -6
  33. package/dist/basemap/vectortiles.js +43 -10
  34. package/dist/basemap/vectortiles.js.map +1 -1
  35. package/dist/basemap.worker.js +2 -2
  36. package/dist/basemap.worker.js.map +12 -10
  37. package/dist/bind/layers.js +1 -0
  38. package/dist/bind/layers.js.map +1 -1
  39. package/dist/data/collada.d.ts +19 -0
  40. package/dist/data/collada.js +319 -0
  41. package/dist/data/collada.js.map +1 -0
  42. package/dist/data/ifc.d.ts +100 -0
  43. package/dist/data/ifc.js +372 -0
  44. package/dist/data/ifc.js.map +1 -0
  45. package/dist/decoder.worker.js +5 -5
  46. package/dist/decoder.worker.js.map +3 -3
  47. package/dist/formats.d.ts +7 -2
  48. package/dist/formats.js +7 -2
  49. package/dist/formats.js.map +1 -1
  50. package/dist/index.d.ts +8 -1
  51. package/dist/index.js +6 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/isard_bg.wasm +0 -0
  54. package/dist/isard_core_bg.wasm +0 -0
  55. package/dist/isard_grid_bg.wasm +0 -0
  56. package/dist/isard_tess_bg.wasm +0 -0
  57. package/dist/layers/extrusions.d.ts +10 -0
  58. package/dist/layers/extrusions.js +28 -2
  59. package/dist/layers/extrusions.js.map +1 -1
  60. package/dist/layers/layers.d.ts +12 -0
  61. package/dist/layers/layers.js +44 -1
  62. package/dist/layers/layers.js.map +1 -1
  63. package/dist/layers/photos.d.ts +48 -0
  64. package/dist/layers/photos.js +140 -0
  65. package/dist/layers/photos.js.map +1 -0
  66. package/dist/layers/symbols.d.ts +5 -1
  67. package/dist/layers/symbols.js +111 -32
  68. package/dist/layers/symbols.js.map +1 -1
  69. package/dist/layers/trees.d.ts +28 -0
  70. package/dist/layers/trees.js +51 -0
  71. package/dist/layers/trees.js.map +1 -0
  72. package/dist/layers/vectors.d.ts +28 -2
  73. package/dist/layers/vectors.js +81 -7
  74. package/dist/layers/vectors.js.map +1 -1
  75. package/dist/map/layer-spec.d.ts +3 -0
  76. package/dist/map/layer-spec.js +2 -0
  77. package/dist/map/layer-spec.js.map +1 -1
  78. package/dist/map.d.ts +8 -0
  79. package/dist/map.js +15 -2
  80. package/dist/map.js.map +1 -1
  81. package/dist/panoramax.d.ts +49 -0
  82. package/dist/panoramax.js +71 -0
  83. package/dist/panoramax.js.map +1 -0
  84. package/dist/react/Layer.d.ts +1 -0
  85. package/dist/react/Layer.js.map +1 -1
  86. package/dist/react/VectorGeometry.d.ts +5 -0
  87. package/dist/react/VectorGeometry.js +12 -4
  88. package/dist/react/VectorGeometry.js.map +1 -1
  89. package/dist/util/gltf.d.ts +9 -0
  90. package/dist/util/gltf.js +27 -0
  91. package/dist/util/gltf.js.map +1 -0
  92. package/dist/util/roofs.d.ts +10 -0
  93. package/dist/util/roofs.js +30 -0
  94. package/dist/util/roofs.js.map +1 -0
  95. package/dist/wasm/isard.d.ts +56 -17
  96. package/dist/wasm/isard.js +128 -18
  97. package/dist/wasm/isard_core.d.ts +44 -17
  98. package/dist/wasm/isard_core.js +104 -18
  99. package/dist/wasm/isard_tess.d.ts +6 -3
  100. package/dist/wasm/isard_tess.js +27 -4
  101. package/dist/workers/basemap/geometry.d.ts +38 -22
  102. package/dist/workers/basemap/labels.d.ts +14 -1
  103. package/dist/workers/basemap/lamps.d.ts +10 -0
  104. package/dist/workers/basemap/trees.d.ts +3 -1
  105. package/dist/workers/basemap.worker.d.ts +43 -3
  106. package/dist/workers/basemap.worker.js +14 -2
  107. package/dist/workers/basemap.worker.js.map +1 -1
  108. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,8 +1,172 @@
1
1
  # Changelog
2
2
 
3
- Every notable change of `isard` (published as `@ign-junn/isard` up to 0.1.1). The format follows [Keep a Changelog](https://keepachangelog.com), the
3
+ Every notable change of `isard`. The format follows [Keep a Changelog](https://keepachangelog.com), the
4
4
  versions [Semantic Versioning](https://semver.org) (before 1.0.0, a minor version may change an API).
5
5
 
6
+ ## [1.6.0] - 2026-10-26
7
+
8
+ ### Added
9
+
10
+ - IFC models, further: each storey meshed when asked (`storey.load()`, `storey.elements`): a large model's storeys
11
+ one at a time, those in view only; `model.place`, where the model stands, from its `IfcMapConversion` in a projected
12
+ CRS isard knows (the conversion's rotation and scale, the project's unit, the grid's convergence) or else its site's
13
+ latitude and longitude turned by its true north; `ModelLayer.setHighlight(feature, color)` tints a feature (an IFC
14
+ element picked). The IFC models example meshes its storeys one after the other, places FZK-Haus by its true north and
15
+ highlights the element clicked; the georeferenced BIM scene example (TypeScript and React): buildingSMART's scene, one
16
+ IFC file per discipline, each placed by its own map conversion, fitting together.
17
+ - IFC models (BIM): `loadIfc(url | bytes)` of `isard/formats` reads an IFC file with web-ifc (ThatOpen, MPL-2.0;
18
+ fetched on first use from jsDelivr, or `webIfc`: a folder of yours) in a worker of its own: each storey a glTF binary
19
+ for `addModels`, each element a feature of it (its IFC class, name, storey), its properties on demand
20
+ (`model.properties(element)`: attributes, property and quantity sets), its site (`IfcSite`'s latitude and
21
+ longitude). A model with features (`_BATCHID`, `EXT_mesh_features`) is picked by feature: the hit's `index` is the
22
+ element under the point. The IFC models example (TypeScript and React): the KIT's FZK-Haus at its site in Karlsruhe
23
+ and buildingSMART's Duplex, storey by storey, an element's properties on a click.
24
+ - COLLADA models (BIM and CAD exports): `loadCollada(url)` and `parseCollada(text, images)` of `isard/formats` read a
25
+ `.dae` into a glTF binary for `addModels({ data })`: its meshes where its scene's nodes put them, in metres and turned
26
+ from its up axis to glTF's, its materials' colors, transparencies and textures. The examples' new BIM section: A
27
+ BIM building (iTowns' sample building on the IGN's orthophotos and relief, in TypeScript and React), and Inside a
28
+ shopping mall moved there.
29
+ - Panoramax, the open street-level photos of the IGN and OpenStreetMap France: `panoramaxStyle()` gives its MapLibre
30
+ style (its sequences and a dot at each photo, drawn as any style by `vectorTiles().vectors()`), `panoramaxPicture(id)`
31
+ a photo's data from its STAC API (where and when, its heading, its field of view, its images, its producer and
32
+ licence, the photos before and after it), `panoramaxPhoto` the photo for `addPhoto`. `map.addPhoto` stands any photo
33
+ where it was taken, facing its way (a glTF of the photo made by the page: flat on a frame across its field of view, a
34
+ 360° one on a band round its camera, each pixel where the camera saw it); from its place (`setFirstPerson`) it lies
35
+ over the buildings it shows. The Panoramax example (TypeScript and React): a dot clicked, its photo in 3D, the photos
36
+ before and after it, the eye in its place with the photo left of a line dragged and the 3D map right of it.
37
+ - `labels({ occluded: true })`: the labels hidden by buildings another `vectorTiles()` raises (a `<VectorGeometry>` of
38
+ the same style, as in the React examples).
39
+ - Street lamps: `tiles.lamps()` places them along the style's streets (30 m apart, every other one on the other side,
40
+ as far from the middle as the street's class says), their heads lit at night; `ModelLayer.setGlow(from, color)`
41
+ lights any model from a height up at night (an additive pass of the model layer). The 3D vector examples show them.
42
+ About 0.3 KB gzipped more for the basemap worker.
43
+ - `<VectorGeometry roofs facades>` in React (the roofs make the layer anew, the facades do not); `{ type: 'trees' }`
44
+ for `map.add` and `<Layer>` (`map.addTrees`, its handle a `ModelLayer`).
45
+ - Landmarks: in `vectors()`, a building holding a theatre, a museum, a place of worship, a castle, a monument, an
46
+ attraction or a town hall (OpenMapTiles' points of interest in its tile) has no office windows, and is floodlit at
47
+ night (a warm light from the foot of its walls fading up them, each face as lit as it faces the lights): the Opéra
48
+ Garnier no longer reads as offices in Paris by night. About 0.1 KB gzipped more for the basemap worker.
49
+ - The trees simplified far away instead of thinned: `trees()` draws every tree in full near the camera and, farther,
50
+ a simplified model (a few faces: some 30 triangles instead of 100), thinned four times more gently, up to three times
51
+ as many as the full ones (`treeModel` and `coniferModel` take `far`); the forest stays dense to the horizon.
52
+ - The models' instances placed by the GPU: uploaded once (until they or the relief under them change), each put in its
53
+ east-north-up frame by the vertex shader, instead of each one's matrix made on the main thread at every frame the
54
+ view moved. The trees example: four times as many trees for 3.6 ms a frame (2.7 for a quarter of them before).
55
+ - Trees of two kinds: `trees()` grows conifers among the broadleaved trees by their wood's data (OpenStreetMap's
56
+ `leaf_type`, PLAN IGN's `nature`; OpenMapTiles' forests a third conifers), drawn on a layer that goes with the one
57
+ returned (`coniferModel`, `TreeColors.conifer`); `map.addTrees({ data })` stands trees at the points of a source that
58
+ has them (a city's street trees), conifers by their leaf type or genus, scaled to their height.
59
+ - The buildings' facades: `vectors({ facades: true })`, `addExtrusions({ facades: true })` (`VectorLayer.setFacades`)
60
+ draw the windows on the walls, a grid of bays and floors along each wall and up from its foot, glass by day (some
61
+ panes lighter, some darker), the same windows lit at night; far away, the walls' even tone. The 3D vector examples
62
+ show them. About 0.7 KB gzipped more for the engines, no frame cost measured.
63
+ - The labels hidden by the buildings, as Mapbox GL's Standard style hides them: `labels()` of a `vectorTiles()` that
64
+ raises its buildings (`vectors()` with a `fill-extrusion` layer, or `extrusions()`) hides a label whole when a
65
+ building, a tree or a model stands before its anchor, and lifts a label in a building onto its roof (its style's
66
+ `fill-extrusion-height`). `addSymbols({ occluded: true })` for any symbol layer. The solid scene's depth is read at
67
+ five pixels around each label's anchor (taken from the sun's shadows when they drew it, no cost, else drawn once a
68
+ frame), the label hidden where three stand nearer than it by more than some metres: no wavering on a roof or
69
+ between close walls. About 1.1 KB gzipped more for the engines, 0.5 KB for the basemap worker.
70
+ - `addExtrusions({ roof, roofHeight })`: the extrusion layers' roofs, as `vectors()` builds them: flat by default, one
71
+ of OpenStreetMap's `roof:shape` per building (hipped, gabled, pyramidal, mansard; the others as the nearest of them),
72
+ or `generated` from its outline; their height as their slopes make it unless given. Changing them with `setStyle`
73
+ builds the blocks again. `VectorTiles.extrusions({ roofs })` makes them as `vectors()` does (`'data'`,
74
+ `'generated'`, `'flat'` by default). The buildings example generates the BD TOPO's roofs.
75
+ - `vectors({ roofs })`: the buildings' roofs, flat by default; as their data says when the tiles have it (`roof:shape`:
76
+ hipped, gabled, pyramidal, mansard; `roof:height`), or generated from their outline (`'generated'`: hipped or gabled
77
+ under 450 m², a mansard under 3000 m², flat past that and for courtyards). A roof is the lower envelope of planes
78
+ rising from its outline's edges, built over the whole building and cut to each tile (its ridges go on across the
79
+ tiles), fitted into the building's height. A concave outline (an L, a T, an H) is cut into wings where a wall carried
80
+ on from an inward corner meets it, each wing roofed and closed by a gable against the next, their eaves level (a
81
+ narrower wing's ridge lower); past six wings, flat. The 3D vector examples use the generated roofs. About 8 KB gzipped more
82
+ for the engines and the tessellation module.
83
+ - `vectors()`: the lines' and buildings' patterns (`line-pattern` as tall as the line, repeated along it;
84
+ `fill-extrusion-pattern` on the roofs as the fills' and along the walls), and the fills' patterns over the relief
85
+ (the ground under each pixel from its depth, drawn once a frame when a patterned layer needs it; on the globe, their
86
+ average color still).
87
+ - `vectors()`: the lines' `line-blur` (each feature's, their edges fading over it) and the fills' and lines'
88
+ translations (`fill-translate`, `line-translate`, turning with the map unless `*-translate-anchor` is `viewport`;
89
+ the fills picked where they are drawn).
90
+ - `vectors()`: the style's circle layers, their points drawn as circles facing the screen (`circle-color`,
91
+ `circle-radius`, `circle-stroke-width` and `circle-stroke-color` with their opacities; picked by feature), and the
92
+ fills' patterns (`fill-pattern`, `background-pattern`) from the style's sprite, anchored to the world and fading from
93
+ one zoom's to the next's as MapLibre's (over the relief, a pattern's average color). The sprite's images go to the
94
+ workers only for the raster path.
95
+ - `vectors()`: the lines joined as their `line-join` says (`miter`, MapLibre's default, bevelled past
96
+ `line-miter-limit`; `bevel`; `round`, the only join before) and offset by `line-offset` (each feature's own,
97
+ following the zoom; the ends of the pieces moved along their miters, so that offset lines still meet). Each line
98
+ piece carries the directions of the line around it, 8 bytes more; about 2 KB gzipped more for the engines, 0.6 KB
99
+ for the tessellation module.
100
+
101
+ - Texts along lines follow their bends (`placement: 'line'`, a vector style's `symbol-placement: line`): each glyph
102
+ placed along the line from the anchor and turned with it, read left to right, its own box in the collision grid; not
103
+ shown round a bend sharper than 45° between two glyphs or off the line's ends; on one line of text. A vector
104
+ basemap's texts along lines repeat every `symbol-spacing`. Labels fade in and out (300 ms), keeping their opacity
105
+ as their tiles come again. About 4 KB gzipped more for the engines.
106
+ - The labels' collisions worked out again every 300 ms while the map moves (and at once when it stops), as MapLibre's:
107
+ in between the labels shown follow the map, their boxes as they were. The collision grid in cells of 16 pixels,
108
+ reaching past the screen. A vector basemap's main thread as before its texts along lines (1.7 ms a frame over the
109
+ 3D vector example's drag).
110
+ - GRIB2 packed with CCSDS (data template 5.42), as Météo-France's AROME and ECMWF's open data come: `map.readGrids`
111
+ decodes it in the grid module (CCSDS 121.0-B, as libaec codes it; tested against ecCodes, fuzzed). About 2.4 KB
112
+ gzipped more for the grid module. The example Wind over France (AROME every 2.5 km through the hours of a run), in
113
+ TypeScript and React.
114
+
115
+ ### Changed
116
+
117
+ - The examples regrouped by what they show: Getting started, Data layers, Basemaps and terrain, 3D city (the vector
118
+ maps), 3D data (3D Tiles, point clouds, models, panels, three.js), Street level (panoramas, Panoramax), BIM and
119
+ indoor (IFC, COLLADA, a shopping mall), Flows, Analysis and comparison.
120
+ - Zooming a pitched 3D city with Panoramax over it jerked: a far tile's buildings (10 MB and more) went to the GPU in
121
+ one upload, tens of milliseconds, and Panoramax's sequences (a point every few metres, most segments under a pixel,
122
+ each drawn as wide as the line) loaded the GPU until it stalled the page. A large tile's blocks now go a part at a
123
+ time over several frames (2 MB a part, the tile drawn once they all are there, the one it replaces until then), and
124
+ the lines are simplified in the workers to a quarter of a pixel at the tile's zoom (no change on screen). Zooming
125
+ the Panoramax example: 4 frames over 25 ms a zoom instead of 12, the long tasks' time divided by 2.5; the other 3D
126
+ vector maps as before (vector-3d: no frame over 25 ms). The Panoramax example's line between the photo and the 3D
127
+ map is dragged (it followed the cursor).
128
+ - Lines and circles (of `vectors()`, `addLines`, `addCircles`) drawn after buildings, trees or models are hidden
129
+ behind them on a flat map too, as Mapbox GL's buildings hide its lines and circles (in relief they already were by
130
+ the hills); a second style over a 3D city (Panoramax's) no longer shows through its buildings.
131
+ - The sun's light takes the color of its altitude (nearly white high up, golden under 15°, orange at the horizon) and
132
+ the shadows the sky's bluer light, as dark as their opacity; the shadows soften far from the camera (their samples
133
+ spreading up to three times as wide).
134
+ - `vectors()`: a filter reading the zoom holds as MapLibre evaluates it, at the whole zoom under the camera's, not at
135
+ the level of the tile drawn (drawn from half a level under it, a tile of level 15 showed `['>=', ['zoom'], 15]`
136
+ from 14.5): each feature styled with the zooms its filter holds over, which the engine checks at the camera's zoom.
137
+ - At night, the ground is darker where the buildings around hide the sky: the streets and courtyards read under the
138
+ roofs, which kept the same blue (the sun's map drawn from straight above, its heights giving each pixel's share of
139
+ the sky within 25 metres). Two depth passes more a frame at night, as by day with shadows.
140
+
141
+ ### Fixed
142
+
143
+ - The floodlit landmarks flickered at night as the view moved: the lights were drawn a hair nearer than the walls
144
+ they light, so a wall just behind another (the parts of a monument inside one another) caught light in streaks that
145
+ came and went. The blocks' lights are now drawn only on the surfaces drawn (`invariant gl_Position`, depth `EQUAL`):
146
+ at the Opéra Garnier, a third of the pixels changing as the view moves by a quarter pixel.
147
+ - A paint property set as a `vectors()` style loads (`setPaintProperty` right after `vectors()`, or `paint` of a
148
+ `<VectorGeometry>`) was lost: the tiles cut under the former style before it came were never restyled.
149
+ - Paris by night flickered as the view moved: the parts of a landmark's building (the Opéra Garnier's stage tower,
150
+ the Louvre's wings) were not landmarks unless they held its point of interest, and their dark walls with lit windows
151
+ fought the floodlit walls along them; a building inside a landmark's, or holding its middle, is now one too (a
152
+ neighbour against its wall is not). The lit windows, bright points on dark walls, sparkled far away where they shrank
153
+ to a pixel: they now fade from bays of 6 pixels to 2 (the glass by day as before), a faint even glow in their place.
154
+ - `map.rebuildLayer` (a `<Layer>` or a bound layer made anew) moves the new layer's companions with it (the
155
+ conifers of `addTrees`), and no longer places it under the old one's companions, removed with it.
156
+ - Zooming in a 3D vector map jerked as tiles came in: their meshes were all uploaded as they arrived, several in one
157
+ frame (copying their buffers to the GPU, tens of milliseconds each), and the frames after waited on the GPU.
158
+ `vectors()` now uploads them over several frames, 4 ms a frame (a tile out of view still drawn until its
159
+ replacements are uploaded): zooming in and out of the 3D vector examples, 0 to 3 frames over 25 ms instead of 5 to 27.
160
+ - `vectors()` over the relief with no raster layer under it (`basemap: 'none'`): its fills showed nowhere, the shadow
161
+ volumes they are drawn with finding no ground in the depth buffer. The relief's ground is now drawn (its depth
162
+ alone) when no raster layer draws it first, over the tiles a raster layer would draw (#42).
163
+ - Zooming jerked and tiles blinked out: `vectors()` dropped a tile out of view once the tiles its own update asked for
164
+ were there, not those an earlier update was still waiting for, leaving a hole for a frame or more; the labels of new
165
+ tiles (thousands of features past the source's deepest level, shaped again at each whole zoom) held the main thread
166
+ up to 200 ms. A tile out of view now stays drawn until every tile of the view is there, and the labels are shaped a
167
+ slice at a time, a frame let through every 6 ms: zooming in and out of the 3D vector example, no frame over 50 ms
168
+ (217 before), two long tasks instead of thirteen.
169
+
6
170
  ## [1.5.0] - 2026-10-24
7
171
 
8
172
  ### Added
package/README.md CHANGED
@@ -179,8 +179,9 @@ More in [Develop and release](https://ideal-adventure-zqn6y6p.pages.github.io/gu
179
179
 
180
180
  ## Roadmap
181
181
 
182
- The whole MapLibre style drawn as vectors, a 3D vector map to walk through, fluids on the globe (wakes behind the
183
- buildings, more of the models' files), several projections on the globe, and smaller items: see
182
+ The whole MapLibre style drawn as vectors, a 3D vector map to walk through, Panoramax's street-level photos on the map
183
+ and at street level, fluids on the globe (wakes behind the buildings, more of the models' files), several projections
184
+ on the globe, and smaller items: see
184
185
  [Roadmap](https://ideal-adventure-zqn6y6p.pages.github.io/guide/roadmap).
185
186
 
186
187
  ## Licence
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The street furniture of a vector map (`VectorTiles.lamps`): a street lamp made here as a glTF binary (no file to
3
+ * download), its pole and its head flat-shaded, the head lit at night (`ModelLayer.setGlow` from `LAMP_HEAD` up).
4
+ */
5
+ /** Metres from a lamp's foot to its head: the part lit at night */
6
+ export declare const LAMP_HEAD = 4.6;
7
+ /** A street lamp as a glTF binary, some 5 metres high: a thin pole, a lantern on top */
8
+ export declare const lampModel: () => Uint8Array<ArrayBuffer>;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The street furniture of a vector map (`VectorTiles.lamps`): a street lamp made here as a glTF binary (no file to
3
+ * download), its pole and its head flat-shaded, the head lit at night (`ModelLayer.setGlow` from `LAMP_HEAD` up).
4
+ */
5
+ import { glb, revolve } from "./trees.js";
6
+ /** Colors of a street lamp (linear RGBA, as glTF's `baseColorFactor`): Paris's dark green cast iron */
7
+ const POLE = [0.04, 0.07, 0.05, 1];
8
+ const HEAD = [0.55, 0.52, 0.42, 1];
9
+ /** Metres from a lamp's foot to its head: the part lit at night */
10
+ export const LAMP_HEAD = 4.6;
11
+ /** A street lamp as a glTF binary, some 5 metres high: a thin pole, a lantern on top */
12
+ export const lampModel = () => {
13
+ const pole = revolve([
14
+ [0, 0.14],
15
+ [0.6, 0.1],
16
+ [LAMP_HEAD, 0.06],
17
+ ], 5);
18
+ const head = revolve([
19
+ [LAMP_HEAD, 0.12],
20
+ [4.95, 0.32],
21
+ [5.25, 0.26],
22
+ [5.45, 0],
23
+ ], 6);
24
+ return glb([pole, head], [POLE, HEAD]);
25
+ };
26
+ //# sourceMappingURL=furniture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"furniture.js","sourceRoot":"","sources":["../../src/basemap/furniture.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,uGAAuG;AACvG,MAAM,IAAI,GAAqC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACrE,MAAM,IAAI,GAAqC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAErE,mEAAmE;AACnE,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC;AAE7B,wFAAwF;AACxF,MAAM,CAAC,MAAM,SAAS,GAAG,GAA4B,EAAE;IACrD,MAAM,IAAI,GAAG,OAAO,CAClB;QACE,CAAC,CAAC,EAAE,IAAI,CAAC;QACT,CAAC,GAAG,EAAE,GAAG,CAAC;QACV,CAAC,SAAS,EAAE,IAAI,CAAC;KAClB,EACD,CAAC,CACF,CAAC;IACF,MAAM,IAAI,GAAG,OAAO,CAClB;QACE,CAAC,SAAS,EAAE,IAAI,CAAC;QACjB,CAAC,IAAI,EAAE,IAAI,CAAC;QACZ,CAAC,IAAI,EAAE,IAAI,CAAC;QACZ,CAAC,IAAI,EAAE,CAAC,CAAC;KACV,EACD,CAAC,CACF,CAAC;IAEF,OAAO,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AACzC,CAAC,CAAC","sourcesContent":["/**\n * The street furniture of a vector map (`VectorTiles.lamps`): a street lamp made here as a glTF binary (no file to\n * download), its pole and its head flat-shaded, the head lit at night (`ModelLayer.setGlow` from `LAMP_HEAD` up).\n */\n\nimport { glb, revolve } from './trees.ts';\n\n/** Colors of a street lamp (linear RGBA, as glTF's `baseColorFactor`): Paris's dark green cast iron */\nconst POLE: [number, number, number, number] = [0.04, 0.07, 0.05, 1];\nconst HEAD: [number, number, number, number] = [0.55, 0.52, 0.42, 1];\n\n/** Metres from a lamp's foot to its head: the part lit at night */\nexport const LAMP_HEAD = 4.6;\n\n/** A street lamp as a glTF binary, some 5 metres high: a thin pole, a lantern on top */\nexport const lampModel = (): Uint8Array<ArrayBuffer> => {\n const pole = revolve(\n [\n [0, 0.14],\n [0.6, 0.1],\n [LAMP_HEAD, 0.06],\n ],\n 5,\n );\n const head = revolve(\n [\n [LAMP_HEAD, 0.12],\n [4.95, 0.32],\n [5.25, 0.26],\n [5.45, 0],\n ],\n 6,\n );\n\n return glb([pole, head], [POLE, HEAD]);\n};\n"]}
@@ -1,20 +1,41 @@
1
1
  /**
2
- * The trees of a vector map (`VectorTiles.trees`): a low-poly tree made here as a glTF binary (no file to download),
3
- * its trunk and its crown flat-shaded in their colors, and the style layers they grow on by default.
2
+ * The trees of a vector map (`VectorTiles.trees`): low-poly trees made here as glTF binaries (no file to download), a
3
+ * broadleaved one and a conifer, their trunk and crown flat-shaded in their colors; the style layers they grow on by
4
+ * default, and the share of conifers in a wood from its data.
4
5
  */
6
+ import type { Value } from './mvt.ts';
5
7
  import type { StyleLayer } from './vectorstyle.ts';
6
- /** Numbers of a tree in the workers' answer: longitude, latitude, scale, heading, rank */
7
- export declare const TREE_NUMBERS = 5;
8
- /** Colors of the trunk and of the crown (linear RGBA, as glTF's `baseColorFactor`) */
8
+ /** Numbers of a tree in the workers' answer: longitude, latitude, scale, heading, rank, kind (0 broadleaved, 1
9
+ * conifer) */
10
+ export declare const TREE_NUMBERS = 6;
11
+ /** Colors of the trunk, of the broadleaved crown and of the conifers' (linear RGBA, as glTF's `baseColorFactor`) */
9
12
  export interface TreeColors {
10
13
  trunk: [number, number, number, number];
11
14
  crown: [number, number, number, number];
15
+ conifer: [number, number, number, number];
12
16
  }
17
+ /** A solid of revolution around the vertical (glTF +Y up), flat-shaded: rings of (height, radius), a point at the
18
+ * top when its radius is 0; the triangles' positions and normals, one normal per face */
19
+ export declare const revolve: (rings: [height: number, radius: number][], sides?: number) => {
20
+ positions: number[];
21
+ normals: number[];
22
+ };
23
+ /** Parts flat-shaded in their colors as a glTF binary: one mesh of a primitive each, its front +Z
24
+ * (`ModelInstance.heading` turns it), standing on its origin */
25
+ export declare const glb: (parts: {
26
+ positions: number[];
27
+ normals: number[];
28
+ }[], colors: TreeColors[keyof TreeColors][]) => Uint8Array<ArrayBuffer>;
13
29
  /**
14
- * A tree as a glTF binary, about 10 metres high: a trunk and a rounded crown in `colors`, one mesh of two primitives,
15
- * its front +Z (`ModelInstance.heading` turns it), standing on its origin
30
+ * A broadleaved tree as a glTF binary, about 10 metres high: a trunk and a rounded crown in `colors`; `far`: the
31
+ * same simplified for far away, a few faces (some 30 triangles instead of 100)
16
32
  */
17
- export declare const treeModel: (colors?: Partial<TreeColors>) => Uint8Array<ArrayBuffer>;
33
+ export declare const treeModel: (colors?: Partial<TreeColors>, far?: boolean) => Uint8Array<ArrayBuffer>;
34
+ /**
35
+ * A conifer as a glTF binary, about 11 metres high: a trunk and three cones one over the other in `colors`; `far`: one
36
+ * pyramid on a trunk of three faces for far away (some 17 triangles instead of 63)
37
+ */
38
+ export declare const coniferModel: (colors?: Partial<TreeColors>, far?: boolean) => Uint8Array<ArrayBuffer>;
18
39
  /**
19
40
  * Whether trees grow on a style layer by default: the woods and forests of the style's fills (OpenMapTiles'
20
41
  * `landcover` wood, PLAN IGN's forests) and the grass of its parks and gardens; not the wooded relief of PLAN IGN,
@@ -24,3 +45,9 @@ export declare const treeModel: (colors?: Partial<TreeColors>) => Uint8Array<Arr
24
45
  export declare const isWoodland: (layer: StyleLayer) => boolean;
25
46
  /** Default spacing of the trees of a layer (metres): close in woods and forests, apart on grass */
26
47
  export declare const treeSpacing: (layer: StyleLayer) => number;
48
+ /**
49
+ * The share of conifers in a wood from its properties: OpenStreetMap's `leaf_type` (needleleaved, broadleaved,
50
+ * mixed), PLAN IGN's `nature` (« conifères », « feuillus », « mixte »); without them, OpenMapTiles' forests a third
51
+ * conifers and its other woods some, the grass of parks none
52
+ */
53
+ export declare const coniferShare: (properties: Record<string, Value>) => number;
@@ -1,20 +1,26 @@
1
1
  /**
2
- * The trees of a vector map (`VectorTiles.trees`): a low-poly tree made here as a glTF binary (no file to download),
3
- * its trunk and its crown flat-shaded in their colors, and the style layers they grow on by default.
2
+ * The trees of a vector map (`VectorTiles.trees`): low-poly trees made here as glTF binaries (no file to download), a
3
+ * broadleaved one and a conifer, their trunk and crown flat-shaded in their colors; the style layers they grow on by
4
+ * default, and the share of conifers in a wood from its data.
4
5
  */
5
- /** Numbers of a tree in the workers' answer: longitude, latitude, scale, heading, rank */
6
- export const TREE_NUMBERS = 5;
7
- /** Sides of the trunk and of the crown */
6
+ import { bounds, glbOf } from "../util/gltf.js";
7
+ /** Numbers of a tree in the workers' answer: longitude, latitude, scale, heading, rank, kind (0 broadleaved, 1
8
+ * conifer) */
9
+ export const TREE_NUMBERS = 6;
10
+ /** Sides of the trunk and of the crown near the view; of the crown far away (a pyramid), of the trunk there */
8
11
  const SIDES = 7;
12
+ const FAR_SIDES = 4;
13
+ const FAR_TRUNK_SIDES = 3;
9
14
  const DEFAULT_COLORS = {
10
15
  trunk: [0.2, 0.12, 0.07, 1],
11
16
  crown: [0.12, 0.27, 0.08, 1],
17
+ conifer: [0.05, 0.16, 0.08, 1],
12
18
  };
13
19
  /** A solid of revolution around the vertical (glTF +Y up), flat-shaded: rings of (height, radius), a point at the
14
20
  * top when its radius is 0; the triangles' positions and normals, one normal per face */
15
- const revolve = (rings) => {
21
+ export const revolve = (rings, sides = SIDES) => {
16
22
  const at = (k, [h, r]) => {
17
- const a = (k / SIDES) * Math.PI * 2;
23
+ const a = (k / sides) * Math.PI * 2;
18
24
  return [Math.cos(a) * r, h, Math.sin(a) * r];
19
25
  };
20
26
  const positions = [];
@@ -34,7 +40,7 @@ const revolve = (rings) => {
34
40
  };
35
41
  rings.slice(1).forEach((ring, i) => {
36
42
  const below = rings[i] ?? ring;
37
- Array.from({ length: SIDES }, (_, k) => k).forEach((k) => {
43
+ Array.from({ length: sides }, (_, k) => k).forEach((k) => {
38
44
  const [a, b] = [at(k, below), at(k + 1, below)];
39
45
  const [c, d] = [at(k, ring), at(k + 1, ring)];
40
46
  // Counter-clockwise seen from outside
@@ -45,37 +51,15 @@ const revolve = (rings) => {
45
51
  // The bottom closed: seen from below
46
52
  const base = rings[0];
47
53
  if (base && base[1] > 0) {
48
- Array.from({ length: SIDES }, (_, k) => k).forEach((k) => {
54
+ Array.from({ length: sides }, (_, k) => k).forEach((k) => {
49
55
  face([0, base[0], 0], at(k, base), at(k + 1, base));
50
56
  });
51
57
  }
52
58
  return { positions, normals };
53
59
  };
54
- /** Lowest and highest of interleaved x, y, z (glTF needs them on positions) */
55
- const bounds = (xyz) => {
56
- const axes = [0, 1, 2].map((a) => xyz.filter((_, i) => i % 3 === a));
57
- return { min: axes.map((v) => Math.min(...v)), max: axes.map((v) => Math.max(...v)) };
58
- };
59
- /**
60
- * A tree as a glTF binary, about 10 metres high: a trunk and a rounded crown in `colors`, one mesh of two primitives,
61
- * its front +Z (`ModelInstance.heading` turns it), standing on its origin
62
- */
63
- export const treeModel = (colors = {}) => {
64
- const { trunk, crown } = { ...DEFAULT_COLORS, ...colors };
65
- const parts = [
66
- revolve([
67
- [0, 0.28],
68
- [3.2, 0.2],
69
- ]),
70
- revolve([
71
- [2.4, 0.6],
72
- [3.6, 2.4],
73
- [5.6, 2.9],
74
- [7.8, 2.3],
75
- [9.4, 1.1],
76
- [10.2, 0],
77
- ]),
78
- ];
60
+ /** Parts flat-shaded in their colors as a glTF binary: one mesh of a primitive each, its front +Z
61
+ * (`ModelInstance.heading` turns it), standing on its origin */
62
+ export const glb = (parts, colors) => {
79
63
  // The buffer: each part's positions then normals, 4-byte floats
80
64
  const floats = parts.flatMap((p) => [...p.positions, ...p.normals]);
81
65
  const bin = new Float32Array(floats);
@@ -100,30 +84,81 @@ export const treeModel = (colors = {}) => {
100
84
  primitives: parts.map((_, i) => ({ attributes: { POSITION: i * 2, NORMAL: i * 2 + 1 }, material: i })),
101
85
  },
102
86
  ],
103
- materials: [trunk, crown].map((color) => ({
87
+ materials: colors.map((color) => ({
104
88
  pbrMetallicRoughness: { baseColorFactor: color, metallicFactor: 0, roughnessFactor: 1 },
105
89
  })),
106
90
  accessors,
107
91
  bufferViews: views,
108
92
  buffers: [{ byteLength: bin.byteLength }],
109
93
  };
110
- // The chunks padded to 4 bytes: the JSON with spaces, the binary with zeros
111
- const json = new TextEncoder().encode(JSON.stringify(gltf));
112
- const jsonLength = Math.ceil(json.length / 4) * 4;
113
- const binLength = Math.ceil(bin.byteLength / 4) * 4;
114
- const out = new Uint8Array(12 + 8 + jsonLength + 8 + binLength);
115
- const view = new DataView(out.buffer);
116
- view.setUint32(0, 0x46546c67, true);
117
- view.setUint32(4, 2, true);
118
- view.setUint32(8, out.length, true);
119
- view.setUint32(12, jsonLength, true);
120
- view.setUint32(16, 0x4e4f534a, true);
121
- out.fill(0x20, 20, 20 + jsonLength);
122
- out.set(json, 20);
123
- view.setUint32(20 + jsonLength, binLength, true);
124
- view.setUint32(24 + jsonLength, 0x004e4942, true);
125
- out.set(new Uint8Array(bin.buffer), 28 + jsonLength);
126
- return out;
94
+ return glbOf(gltf, new Uint8Array(bin.buffer));
95
+ };
96
+ /**
97
+ * A broadleaved tree as a glTF binary, about 10 metres high: a trunk and a rounded crown in `colors`; `far`: the
98
+ * same simplified for far away, a few faces (some 30 triangles instead of 100)
99
+ */
100
+ export const treeModel = (colors = {}, far = false) => {
101
+ const { trunk, crown } = { ...DEFAULT_COLORS, ...colors };
102
+ if (far) {
103
+ const stem = revolve([
104
+ [0, 0.3],
105
+ [3, 0.22],
106
+ ], FAR_TRUNK_SIDES);
107
+ const top = revolve([
108
+ [2.4, 0.8],
109
+ [5.6, 2.9],
110
+ [10.2, 0],
111
+ ], FAR_SIDES);
112
+ return glb([stem, top], [trunk, crown]);
113
+ }
114
+ const parts = [
115
+ revolve([
116
+ [0, 0.28],
117
+ [3.2, 0.2],
118
+ ]),
119
+ revolve([
120
+ [2.4, 0.6],
121
+ [3.6, 2.4],
122
+ [5.6, 2.9],
123
+ [7.8, 2.3],
124
+ [9.4, 1.1],
125
+ [10.2, 0],
126
+ ]),
127
+ ];
128
+ return glb(parts, [trunk, crown]);
129
+ };
130
+ /** A cone of the conifers' crown: from `base` metres up, `radius` wide, to its point at `top` */
131
+ const cone = (base, radius, top) => revolve([
132
+ [base, radius],
133
+ [top, 0],
134
+ ]);
135
+ /**
136
+ * A conifer as a glTF binary, about 11 metres high: a trunk and three cones one over the other in `colors`; `far`: one
137
+ * pyramid on a trunk of three faces for far away (some 17 triangles instead of 63)
138
+ */
139
+ export const coniferModel = (colors = {}, far = false) => {
140
+ const { trunk, conifer } = { ...DEFAULT_COLORS, ...colors };
141
+ if (far) {
142
+ const stem = revolve([
143
+ [0, 0.24],
144
+ [2, 0.18],
145
+ ], FAR_TRUNK_SIDES);
146
+ const top = revolve([
147
+ [1.4, 2.3],
148
+ [11, 0],
149
+ ], FAR_SIDES);
150
+ return glb([stem, top], [trunk, conifer]);
151
+ }
152
+ const tiers = [cone(1.4, 2.4, 5.4), cone(3.6, 1.9, 8), cone(6, 1.3, 11)];
153
+ const crown = {
154
+ positions: tiers.flatMap((t) => t.positions),
155
+ normals: tiers.flatMap((t) => t.normals),
156
+ };
157
+ const stem = revolve([
158
+ [0, 0.24],
159
+ [2, 0.18],
160
+ ]);
161
+ return glb([stem, crown], [trunk, conifer]);
127
162
  };
128
163
  /** Whether a style layer is OpenMapTiles' grass (the lawns of parks and gardens among it) */
129
164
  const isGrass = (layer) => layer['source-layer'] === 'landcover' && /grass/i.test(layer.id);
@@ -138,4 +173,22 @@ export const isWoodland = (layer) => layer.type === 'fill' &&
138
173
  (/wood|forest|for[eê]t/i.test(layer.id) || isGrass(layer));
139
174
  /** Default spacing of the trees of a layer (metres): close in woods and forests, apart on grass */
140
175
  export const treeSpacing = (layer) => (isGrass(layer) ? 25 : 9);
176
+ /**
177
+ * The share of conifers in a wood from its properties: OpenStreetMap's `leaf_type` (needleleaved, broadleaved,
178
+ * mixed), PLAN IGN's `nature` (« conifères », « feuillus », « mixte »); without them, OpenMapTiles' forests a third
179
+ * conifers and its other woods some, the grass of parks none
180
+ */
181
+ export const coniferShare = (properties) => {
182
+ const leaf = properties.leaf_type;
183
+ const nature = typeof properties.nature === 'string' ? properties.nature : '';
184
+ if (leaf === 'needleleaved' || /conif/i.test(nature))
185
+ return 1;
186
+ if (leaf === 'broadleaved' || /feuillu/i.test(nature))
187
+ return 0;
188
+ if (leaf === 'mixed' || /mixte/i.test(nature))
189
+ return 0.5;
190
+ if (properties.class === 'wood')
191
+ return properties.subclass === 'forest' ? 0.35 : 0.15;
192
+ return 0;
193
+ };
141
194
  //# sourceMappingURL=trees.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"trees.js","sourceRoot":"","sources":["../../src/basemap/trees.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,0FAA0F;AAC1F,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAK9B,0CAA0C;AAC1C,MAAM,KAAK,GAAG,CAAC,CAAC;AAQhB,MAAM,cAAc,GAAe;IACjC,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;CAC7B,CAAC;AAEF;yFACyF;AACzF,MAAM,OAAO,GAAG,CAAC,KAAyC,EAA8C,EAAE;IACxG,MAAM,EAAE,GAAG,CAAC,CAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAmB,EAAQ,EAAE;QACvD,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;QAEpC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC;IACF,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,IAAI,GAAG,CAAC,CAAO,EAAE,CAAO,EAAE,CAAO,EAAE,EAAE;QACzC,MAAM,CAAC,GAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,GAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,GAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAClG,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAEhC,yDAAyD;QACzD,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO;QACzB,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;YACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QAE/B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACvD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;YAE9C,sCAAsC;YACtC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YACd,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,qCAAqC;IACrC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAEtB,IAAI,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACvD,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AAChC,CAAC,CAAC;AAEF,+EAA+E;AAC/E,MAAM,MAAM,GAAG,CAAC,GAAa,EAAoC,EAAE;IACjE,MAAM,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAErE,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;AACxF,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,SAA8B,EAAE,EAA2B,EAAE;IACrF,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1D,MAAM,KAAK,GAAG;QACZ,OAAO,CAAC;YACN,CAAC,CAAC,EAAE,IAAI,CAAC;YACT,CAAC,GAAG,EAAE,GAAG,CAAC;SACX,CAAC;QACF,OAAO,CAAC;YACN,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,GAAG,EAAE,GAAG,CAAC;YACV,CAAC,IAAI,EAAE,CAAC,CAAC;SACV,CAAC;KACH,CAAC;IACF,gEAAgE;IAChE,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACpE,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAEtG,OAAO;YACL,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE;YACpF,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE;SAC5G,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE;QAC/G,EAAE,UAAU,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;KAC1F,CAAC,CAAC;IACH,MAAM,IAAI,GAAG;QACX,KAAK,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE;QAC7C,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;YACN;gBACE,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;aACvG;SACF;QACD,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACxC,oBAAoB,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE;SACxF,CAAC,CAAC;QACH,SAAS;QACT,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,CAAC;KAC1C,CAAC;IACF,4EAA4E;IAC5E,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,EAAE,GAAG,CAAC,GAAG,UAAU,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAEtC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IACrC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,GAAG,UAAU,CAAC,CAAC;IACpC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAClB,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACjD,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,UAAU,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAClD,GAAG,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,GAAG,UAAU,CAAC,CAAC;IAErD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC;AAEF,6FAA6F;AAC7F,MAAM,OAAO,GAAG,CAAC,KAAiB,EAAW,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,KAAK,WAAW,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAEjH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAiB,EAAW,EAAE,CACvD,KAAK,CAAC,IAAI,KAAK,MAAM;IACrB,KAAK,CAAC,cAAc,CAAC,KAAK,YAAY;IACtC,CAAC,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AAE7D,mGAAmG;AACnG,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAiB,EAAU,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC","sourcesContent":["/**\n * The trees of a vector map (`VectorTiles.trees`): a low-poly tree made here as a glTF binary (no file to download),\n * its trunk and its crown flat-shaded in their colors, and the style layers they grow on by default.\n */\n\nimport type { StyleLayer } from './vectorstyle.ts';\n\n/** Numbers of a tree in the workers' answer: longitude, latitude, scale, heading, rank */\nexport const TREE_NUMBERS = 5;\n\n/** A point or a vector: x, y, z */\ntype Vec3 = [number, number, number];\n\n/** Sides of the trunk and of the crown */\nconst SIDES = 7;\n\n/** Colors of the trunk and of the crown (linear RGBA, as glTF's `baseColorFactor`) */\nexport interface TreeColors {\n trunk: [number, number, number, number];\n crown: [number, number, number, number];\n}\n\nconst DEFAULT_COLORS: TreeColors = {\n trunk: [0.2, 0.12, 0.07, 1],\n crown: [0.12, 0.27, 0.08, 1],\n};\n\n/** A solid of revolution around the vertical (glTF +Y up), flat-shaded: rings of (height, radius), a point at the\n * top when its radius is 0; the triangles' positions and normals, one normal per face */\nconst revolve = (rings: [height: number, radius: number][]): { positions: number[]; normals: number[] } => {\n const at = (k: number, [h, r]: [number, number]): Vec3 => {\n const a = (k / SIDES) * Math.PI * 2;\n\n return [Math.cos(a) * r, h, Math.sin(a) * r];\n };\n const positions: number[] = [];\n const normals: number[] = [];\n const face = (a: Vec3, b: Vec3, c: Vec3) => {\n const u: Vec3 = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];\n const v: Vec3 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];\n const n: Vec3 = [u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]];\n const length = Math.hypot(...n);\n\n // A degenerate face (two points at the apex) is left out\n if (length === 0) return;\n positions.push(...a, ...b, ...c);\n [0, 1, 2].forEach(() => {\n normals.push(n[0] / length, n[1] / length, n[2] / length);\n });\n };\n\n rings.slice(1).forEach((ring, i) => {\n const below = rings[i] ?? ring;\n\n Array.from({ length: SIDES }, (_, k) => k).forEach((k) => {\n const [a, b] = [at(k, below), at(k + 1, below)];\n const [c, d] = [at(k, ring), at(k + 1, ring)];\n\n // Counter-clockwise seen from outside\n face(a, c, b);\n face(b, c, d);\n });\n });\n // The bottom closed: seen from below\n const base = rings[0];\n\n if (base && base[1] > 0) {\n Array.from({ length: SIDES }, (_, k) => k).forEach((k) => {\n face([0, base[0], 0], at(k, base), at(k + 1, base));\n });\n }\n\n return { positions, normals };\n};\n\n/** Lowest and highest of interleaved x, y, z (glTF needs them on positions) */\nconst bounds = (xyz: number[]): { min: number[]; max: number[] } => {\n const axes = [0, 1, 2].map((a) => xyz.filter((_, i) => i % 3 === a));\n\n return { min: axes.map((v) => Math.min(...v)), max: axes.map((v) => Math.max(...v)) };\n};\n\n/**\n * A tree as a glTF binary, about 10 metres high: a trunk and a rounded crown in `colors`, one mesh of two primitives,\n * its front +Z (`ModelInstance.heading` turns it), standing on its origin\n */\nexport const treeModel = (colors: Partial<TreeColors> = {}): Uint8Array<ArrayBuffer> => {\n const { trunk, crown } = { ...DEFAULT_COLORS, ...colors };\n const parts = [\n revolve([\n [0, 0.28],\n [3.2, 0.2],\n ]),\n revolve([\n [2.4, 0.6],\n [3.6, 2.4],\n [5.6, 2.9],\n [7.8, 2.3],\n [9.4, 1.1],\n [10.2, 0],\n ]),\n ];\n // The buffer: each part's positions then normals, 4-byte floats\n const floats = parts.flatMap((p) => [...p.positions, ...p.normals]);\n const bin = new Float32Array(floats);\n const views = parts.flatMap((p, i) => {\n const before = parts.slice(0, i).reduce((n, q) => n + (q.positions.length + q.normals.length) * 4, 0);\n\n return [\n { buffer: 0, byteOffset: before, byteLength: p.positions.length * 4, target: 34962 },\n { buffer: 0, byteOffset: before + p.positions.length * 4, byteLength: p.normals.length * 4, target: 34962 },\n ];\n });\n const accessors = parts.flatMap((p, i) => [\n { bufferView: i * 2, componentType: 5126, count: p.positions.length / 3, type: 'VEC3', ...bounds(p.positions) },\n { bufferView: i * 2 + 1, componentType: 5126, count: p.normals.length / 3, type: 'VEC3' },\n ]);\n const gltf = {\n asset: { version: '2.0', generator: 'isard' },\n scene: 0,\n scenes: [{ nodes: [0] }],\n nodes: [{ mesh: 0 }],\n meshes: [\n {\n primitives: parts.map((_, i) => ({ attributes: { POSITION: i * 2, NORMAL: i * 2 + 1 }, material: i })),\n },\n ],\n materials: [trunk, crown].map((color) => ({\n pbrMetallicRoughness: { baseColorFactor: color, metallicFactor: 0, roughnessFactor: 1 },\n })),\n accessors,\n bufferViews: views,\n buffers: [{ byteLength: bin.byteLength }],\n };\n // The chunks padded to 4 bytes: the JSON with spaces, the binary with zeros\n const json = new TextEncoder().encode(JSON.stringify(gltf));\n const jsonLength = Math.ceil(json.length / 4) * 4;\n const binLength = Math.ceil(bin.byteLength / 4) * 4;\n const out = new Uint8Array(12 + 8 + jsonLength + 8 + binLength);\n const view = new DataView(out.buffer);\n\n view.setUint32(0, 0x46546c67, true);\n view.setUint32(4, 2, true);\n view.setUint32(8, out.length, true);\n view.setUint32(12, jsonLength, true);\n view.setUint32(16, 0x4e4f534a, true);\n out.fill(0x20, 20, 20 + jsonLength);\n out.set(json, 20);\n view.setUint32(20 + jsonLength, binLength, true);\n view.setUint32(24 + jsonLength, 0x004e4942, true);\n out.set(new Uint8Array(bin.buffer), 28 + jsonLength);\n\n return out;\n};\n\n/** Whether a style layer is OpenMapTiles' grass (the lawns of parks and gardens among it) */\nconst isGrass = (layer: StyleLayer): boolean => layer['source-layer'] === 'landcover' && /grass/i.test(layer.id);\n\n/**\n * Whether trees grow on a style layer by default: the woods and forests of the style's fills (OpenMapTiles'\n * `landcover` wood, PLAN IGN's forests) and the grass of its parks and gardens; not the wooded relief of PLAN IGN,\n * which repeats its forests, nor OpenMapTiles' `park` layer (protected areas and historic sites: a river's banks\n * among them)\n */\nexport const isWoodland = (layer: StyleLayer): boolean =>\n layer.type === 'fill' &&\n layer['source-layer'] !== 'oro_relief' &&\n (/wood|forest|for[eê]t/i.test(layer.id) || isGrass(layer));\n\n/** Default spacing of the trees of a layer (metres): close in woods and forests, apart on grass */\nexport const treeSpacing = (layer: StyleLayer): number => (isGrass(layer) ? 25 : 9);\n"]}
1
+ {"version":3,"file":"trees.js","sourceRoot":"","sources":["../../src/basemap/trees.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The trees of a vector map (`VectorTiles.trees`): low-poly trees made here as glTF binaries (no file to download), a\n * broadleaved one and a conifer, their trunk and crown flat-shaded in their colors; the style layers they grow on by\n * default, and the share of conifers in a wood from its data.\n */\n\nimport { bounds, glbOf } from '../util/gltf.ts';\nimport type { Value } from './mvt.ts';\nimport type { StyleLayer } from './vectorstyle.ts';\n\n/** Numbers of a tree in the workers' answer: longitude, latitude, scale, heading, rank, kind (0 broadleaved, 1\n * conifer) */\nexport const TREE_NUMBERS = 6;\n\n/** A point or a vector: x, y, z */\ntype Vec3 = [number, number, number];\n\n/** Sides of the trunk and of the crown near the view; of the crown far away (a pyramid), of the trunk there */\nconst SIDES = 7;\nconst FAR_SIDES = 4;\nconst FAR_TRUNK_SIDES = 3;\n\n/** Colors of the trunk, of the broadleaved crown and of the conifers' (linear RGBA, as glTF's `baseColorFactor`) */\nexport interface TreeColors {\n trunk: [number, number, number, number];\n crown: [number, number, number, number];\n conifer: [number, number, number, number];\n}\n\nconst DEFAULT_COLORS: TreeColors = {\n trunk: [0.2, 0.12, 0.07, 1],\n crown: [0.12, 0.27, 0.08, 1],\n conifer: [0.05, 0.16, 0.08, 1],\n};\n\n/** A solid of revolution around the vertical (glTF +Y up), flat-shaded: rings of (height, radius), a point at the\n * top when its radius is 0; the triangles' positions and normals, one normal per face */\nexport const revolve = (\n rings: [height: number, radius: number][],\n sides = SIDES,\n): { positions: number[]; normals: number[] } => {\n const at = (k: number, [h, r]: [number, number]): Vec3 => {\n const a = (k / sides) * Math.PI * 2;\n\n return [Math.cos(a) * r, h, Math.sin(a) * r];\n };\n const positions: number[] = [];\n const normals: number[] = [];\n const face = (a: Vec3, b: Vec3, c: Vec3) => {\n const u: Vec3 = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];\n const v: Vec3 = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];\n const n: Vec3 = [u[1] * v[2] - u[2] * v[1], u[2] * v[0] - u[0] * v[2], u[0] * v[1] - u[1] * v[0]];\n const length = Math.hypot(...n);\n\n // A degenerate face (two points at the apex) is left out\n if (length === 0) return;\n positions.push(...a, ...b, ...c);\n [0, 1, 2].forEach(() => {\n normals.push(n[0] / length, n[1] / length, n[2] / length);\n });\n };\n\n rings.slice(1).forEach((ring, i) => {\n const below = rings[i] ?? ring;\n\n Array.from({ length: sides }, (_, k) => k).forEach((k) => {\n const [a, b] = [at(k, below), at(k + 1, below)];\n const [c, d] = [at(k, ring), at(k + 1, ring)];\n\n // Counter-clockwise seen from outside\n face(a, c, b);\n face(b, c, d);\n });\n });\n // The bottom closed: seen from below\n const base = rings[0];\n\n if (base && base[1] > 0) {\n Array.from({ length: sides }, (_, k) => k).forEach((k) => {\n face([0, base[0], 0], at(k, base), at(k + 1, base));\n });\n }\n\n return { positions, normals };\n};\n\n/** Parts flat-shaded in their colors as a glTF binary: one mesh of a primitive each, its front +Z\n * (`ModelInstance.heading` turns it), standing on its origin */\nexport const glb = (\n parts: { positions: number[]; normals: number[] }[],\n colors: TreeColors[keyof TreeColors][],\n): Uint8Array<ArrayBuffer> => {\n // The buffer: each part's positions then normals, 4-byte floats\n const floats = parts.flatMap((p) => [...p.positions, ...p.normals]);\n const bin = new Float32Array(floats);\n const views = parts.flatMap((p, i) => {\n const before = parts.slice(0, i).reduce((n, q) => n + (q.positions.length + q.normals.length) * 4, 0);\n\n return [\n { buffer: 0, byteOffset: before, byteLength: p.positions.length * 4, target: 34962 },\n { buffer: 0, byteOffset: before + p.positions.length * 4, byteLength: p.normals.length * 4, target: 34962 },\n ];\n });\n const accessors = parts.flatMap((p, i) => [\n { bufferView: i * 2, componentType: 5126, count: p.positions.length / 3, type: 'VEC3', ...bounds(p.positions) },\n { bufferView: i * 2 + 1, componentType: 5126, count: p.normals.length / 3, type: 'VEC3' },\n ]);\n const gltf = {\n asset: { version: '2.0', generator: 'isard' },\n scene: 0,\n scenes: [{ nodes: [0] }],\n nodes: [{ mesh: 0 }],\n meshes: [\n {\n primitives: parts.map((_, i) => ({ attributes: { POSITION: i * 2, NORMAL: i * 2 + 1 }, material: i })),\n },\n ],\n materials: colors.map((color) => ({\n pbrMetallicRoughness: { baseColorFactor: color, metallicFactor: 0, roughnessFactor: 1 },\n })),\n accessors,\n bufferViews: views,\n buffers: [{ byteLength: bin.byteLength }],\n };\n return glbOf(gltf, new Uint8Array(bin.buffer));\n};\n\n/**\n * A broadleaved tree as a glTF binary, about 10 metres high: a trunk and a rounded crown in `colors`; `far`: the\n * same simplified for far away, a few faces (some 30 triangles instead of 100)\n */\nexport const treeModel = (colors: Partial<TreeColors> = {}, far = false): Uint8Array<ArrayBuffer> => {\n const { trunk, crown } = { ...DEFAULT_COLORS, ...colors };\n\n if (far) {\n const stem = revolve(\n [\n [0, 0.3],\n [3, 0.22],\n ],\n FAR_TRUNK_SIDES,\n );\n const top = revolve(\n [\n [2.4, 0.8],\n [5.6, 2.9],\n [10.2, 0],\n ],\n FAR_SIDES,\n );\n\n return glb([stem, top], [trunk, crown]);\n }\n\n const parts = [\n revolve([\n [0, 0.28],\n [3.2, 0.2],\n ]),\n revolve([\n [2.4, 0.6],\n [3.6, 2.4],\n [5.6, 2.9],\n [7.8, 2.3],\n [9.4, 1.1],\n [10.2, 0],\n ]),\n ];\n\n return glb(parts, [trunk, crown]);\n};\n\n/** A cone of the conifers' crown: from `base` metres up, `radius` wide, to its point at `top` */\nconst cone = (base: number, radius: number, top: number) =>\n revolve([\n [base, radius],\n [top, 0],\n ]);\n\n/**\n * A conifer as a glTF binary, about 11 metres high: a trunk and three cones one over the other in `colors`; `far`: one\n * pyramid on a trunk of three faces for far away (some 17 triangles instead of 63)\n */\nexport const coniferModel = (colors: Partial<TreeColors> = {}, far = false): Uint8Array<ArrayBuffer> => {\n const { trunk, conifer } = { ...DEFAULT_COLORS, ...colors };\n\n if (far) {\n const stem = revolve(\n [\n [0, 0.24],\n [2, 0.18],\n ],\n FAR_TRUNK_SIDES,\n );\n const top = revolve(\n [\n [1.4, 2.3],\n [11, 0],\n ],\n FAR_SIDES,\n );\n\n return glb([stem, top], [trunk, conifer]);\n }\n const tiers = [cone(1.4, 2.4, 5.4), cone(3.6, 1.9, 8), cone(6, 1.3, 11)];\n const crown = {\n positions: tiers.flatMap((t) => t.positions),\n normals: tiers.flatMap((t) => t.normals),\n };\n const stem = revolve([\n [0, 0.24],\n [2, 0.18],\n ]);\n\n return glb([stem, crown], [trunk, conifer]);\n};\n\n/** Whether a style layer is OpenMapTiles' grass (the lawns of parks and gardens among it) */\nconst isGrass = (layer: StyleLayer): boolean => layer['source-layer'] === 'landcover' && /grass/i.test(layer.id);\n\n/**\n * Whether trees grow on a style layer by default: the woods and forests of the style's fills (OpenMapTiles'\n * `landcover` wood, PLAN IGN's forests) and the grass of its parks and gardens; not the wooded relief of PLAN IGN,\n * which repeats its forests, nor OpenMapTiles' `park` layer (protected areas and historic sites: a river's banks\n * among them)\n */\nexport const isWoodland = (layer: StyleLayer): boolean =>\n layer.type === 'fill' &&\n layer['source-layer'] !== 'oro_relief' &&\n (/wood|forest|for[eê]t/i.test(layer.id) || isGrass(layer));\n\n/** Default spacing of the trees of a layer (metres): close in woods and forests, apart on grass */\nexport const treeSpacing = (layer: StyleLayer): number => (isGrass(layer) ? 25 : 9);\n\n/**\n * The share of conifers in a wood from its properties: OpenStreetMap's `leaf_type` (needleleaved, broadleaved,\n * mixed), PLAN IGN's `nature` (« conifères », « feuillus », « mixte »); without them, OpenMapTiles' forests a third\n * conifers and its other woods some, the grass of parks none\n */\nexport const coniferShare = (properties: Record<string, Value>): number => {\n const leaf = properties.leaf_type;\n const nature = typeof properties.nature === 'string' ? properties.nature : '';\n\n if (leaf === 'needleleaved' || /conif/i.test(nature)) return 1;\n if (leaf === 'broadleaved' || /feuillu/i.test(nature)) return 0;\n if (leaf === 'mixed' || /mixte/i.test(nature)) return 0.5;\n if (properties.class === 'wood') return properties.subclass === 'forest' ? 0.35 : 0.15;\n\n return 0;\n};\n"]}
@@ -70,10 +70,15 @@ export interface LabelCandidate {
70
70
  lat: number;
71
71
  /** On a line: the direction there (degrees of longitude and latitude) */
72
72
  along?: [number, number];
73
+ /** A text along a line: the line around it (longitudes and latitudes interleaved), as long before it as after it,
74
+ * for the text to follow its bends */
75
+ path?: number[];
73
76
  /** Its text runs along the line (`symbol-placement: line`, `text-rotation-alignment` not `viewport`) */
74
77
  textAlong?: boolean;
75
78
  textOptional?: boolean;
76
79
  icon?: CandidateIcon;
80
+ /** Metres over the ground: a point in a building of the style's, on its roof */
81
+ height?: number;
77
82
  font: string;
78
83
  size: Json;
79
84
  color: string;
@@ -100,10 +105,11 @@ export declare const css: ([r, g, b, a]: [number, number, number, number]) => st
100
105
  export declare const isExpression: (e: unknown) => e is Json[];
101
106
  export declare const evaluate: (e: Json, ctx: Context) => unknown;
102
107
  /** The kind of geometry a style layer is drawn with as vectors (`vectors()`: backgrounds as fills, extrusions raised
103
- * into blocks); undefined for the others (symbols, circles, rasters) and for the layers drawn with a pattern (their
104
- * images are the raster path's) */
105
- export declare const vectorKind: (layer: StyleLayer) => "fill" | "line" | "extrusion" | undefined;
108
+ * into blocks, circles); undefined for the others (symbols, rasters) */
109
+ export declare const vectorKind: (layer: StyleLayer) => "fill" | "line" | "extrusion" | "circle" | undefined;
106
110
  export declare const filter: (f: Json | undefined, ctx: Context) => boolean;
111
+ /** Whether an expression reads the zoom (a filter that does holds at some zooms only) */
112
+ export declare const usesZoom: (e: Json | undefined) => boolean;
107
113
  /** Whether a value depends on the feature (else only on the zoom, evaluated once per tile) */
108
114
  export declare const isFeatureDependent: (e: Json | undefined) => boolean;
109
115
  /** A `{token}` text field (or an expression) to the text of a feature */