@tileflow/core 0.1.0-alpha.37 → 0.1.0-alpha.39
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.
- package/README.md +10 -10
- package/dist/3d-BkaJjwfk.d.ts +1083 -0
- package/dist/3d.d.ts +1 -0
- package/dist/3d.js +12331 -0
- package/dist/browser.d.ts +788 -1409
- package/dist/browser.js +11457 -9407
- package/dist/build.d.ts +4 -3
- package/dist/build.js +1135 -203
- package/dist/capture.js +4 -1
- package/dist/config-reference.json +927 -548
- package/dist/{icon-composition-C5X3Q9Ya.d.ts → icon-composition-Ivgoxz8d.d.ts} +172 -5
- package/dist/index.d.ts +174 -5
- package/dist/index.js +1797 -330
- package/dist/{manifest-types-jrBdBrES.d.ts → manifest-types-DRy2980g.d.ts} +5 -2
- package/dist/manifest.d.ts +2 -2
- package/dist/manifest.js +4 -1
- package/dist/native-profile.d.ts +4 -0
- package/dist/native-profile.js +2 -0
- package/dist/native.js +11 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +11 -1
- package/dist/worker/camera-tile-worker.global.js +20 -0
- package/docs/application-tile-reading.md +5 -2
- package/docs/authoring-model.md +8 -0
- package/docs/buildings-3d.md +80 -12
- package/docs/compiled-style-performance.md +11 -2
- package/docs/data-is-separate-from-design.md +6 -3
- package/docs/globe-atmosphere.md +3 -0
- package/docs/native-artifact-profile.md +2 -2
- package/docs/nautical-marks.md +147 -39
- package/docs/public-api-and-browser-subpath.md +7 -2
- package/docs/street-lights.md +5 -1
- package/docs/street-sky.md +153 -0
- package/docs/sun-and-light.md +4 -2
- package/docs/trees-3d.md +11 -6
- package/package.json +6 -1
- package/dist/worker/buildings-3d-worker.global.js +0 -1
package/docs/nautical-marks.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# Nautical marks
|
|
2
2
|
|
|
3
3
|
The `nautical` module draws navigation marks from a `nautical-v1` vector source: buoys, beacons,
|
|
4
|
-
lighthouses, lights, rocks and other hazards, wrecks, reefs, navigation
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
lighthouses, lights that flash in their rhythm, rocks and other hazards, wrecks, reefs, navigation
|
|
5
|
+
areas, routes, submarine and overhead cables, bridges with their clearances, harbours, marinas,
|
|
6
|
+
berths, small-craft facilities, inland notice boards, waterway distance marks, ports, and a
|
|
7
|
+
restrained indication of where a provider publishes marks. It is reference cartography for
|
|
8
|
+
general-purpose maps. It is not a nautical chart, and a map that draws it always shows a "Not for
|
|
9
|
+
navigation" notice.
|
|
8
10
|
|
|
9
11
|
## Enable Nautical on a map
|
|
10
12
|
|
|
@@ -100,19 +102,25 @@ displayed.
|
|
|
100
102
|
Every feature carries `provider`, `provenance`, `edition`, `coverage`, `licence`, and `min_zoom`,
|
|
101
103
|
the first zoom at which the feature appears. `rank` (1 is the most important feature of its layer)
|
|
102
104
|
and `name` are optional. Colours are S-57 colour names, and multi-valued colours and categories are
|
|
103
|
-
comma-separated in source order, for example `black,yellow`.
|
|
104
|
-
field names, layers,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
| `
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
105
|
+
comma-separated in source order, for example `black,yellow`. Clearances, depths, and heights are in
|
|
106
|
+
metres. `tileflowNauticalV1Schema` lists the field names, layers, geometry types, and known
|
|
107
|
+
providers: `openstreetmap` (coverage class `community`), `nga-list-of-lights` (`list`), and the
|
|
108
|
+
`chart` providers `noaa-office-of-coast-survey` and `usace-ienc` (U.S. Army Corps of Engineers
|
|
109
|
+
Inland ENCs). The source's TileJSON owns every provider credit.
|
|
110
|
+
|
|
111
|
+
| Source layer | Geometry | Fields the module reads |
|
|
112
|
+
| ----------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
113
|
+
| `aid` | Point | `class`, `subclass`, `category`, `color`, `rank`, `name`, `information`, `distance`, `distance_unit` |
|
|
114
|
+
| `light` | Point | `class` (`major` or `minor`), `color`, `label`, `rank`, `character`, `group`, `period_s`, `sequence` |
|
|
115
|
+
| `hazard` | Point, LineString, Polygon | `class`, `water_level`, `name` |
|
|
116
|
+
| `wreck` | Point, Polygon | `class`, `name` |
|
|
117
|
+
| `reef` | Polygon | `name` |
|
|
118
|
+
| `navigation_area` | Point, LineString, Polygon | `class`, `subclass`, `size`, `rank`, `name`, `vertical_clearance` and its `_closed`, `_open`, and `_safe` variants |
|
|
119
|
+
| `coverage` | Polygon | `families` |
|
|
120
|
+
| `sounding` | Point | `depth` |
|
|
121
|
+
|
|
122
|
+
The Nautical source promotes each light's `provenance` as its feature ID
|
|
123
|
+
(`promoteId: {light: 'provenance'}`), so lights can be animated with MapLibre feature state.
|
|
116
124
|
|
|
117
125
|
Every layer filters on `min_zoom`, so a compatible source that keeps features in lower-zoom tiles
|
|
118
126
|
still shows each one from its own first zoom. Point groups place a lower `rank` first.
|
|
@@ -122,26 +130,42 @@ still shows each one from its own first zoom. Point groups place a lower `rank`
|
|
|
122
130
|
Each group is a style option of `nautical(...)`, with the same marker, symbol, area, and line style
|
|
123
131
|
objects as other modules.
|
|
124
132
|
|
|
125
|
-
| Option | Draws
|
|
126
|
-
| ----------------- |
|
|
127
|
-
| `buoys` | `aid` buoys, light floats, and light vessels
|
|
128
|
-
| `beacons` | `aid` beacons and daymarks
|
|
129
|
-
| `lighthouses` | `aid` lighthouses
|
|
130
|
-
| `aids` | Other `aid` classes, such as moorings, fog signals, and racons
|
|
131
|
-
| `lights` | `light` points, one per sector
|
|
132
|
-
| `hazards` | `hazard` points
|
|
133
|
-
| `hazardAreas` | `hazard` polygons; the outline also strokes linear hazards
|
|
134
|
-
| `wrecks` | `wreck` points
|
|
135
|
-
| `wreckAreas` | `wreck` polygons
|
|
136
|
-
| `reefs` | `reef` polygons
|
|
137
|
-
| `navigationAreas` | `navigation_area` polygons
|
|
138
|
-
| `routes` | `navigation_area` lines of class `route` and `traffic-separation`
|
|
139
|
-
| `cables` | `navigation_area` lines of class `cable`, `pipeline`, and their area classes
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
133
|
+
| Option | Draws | Default symbology |
|
|
134
|
+
| ----------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------- |
|
|
135
|
+
| `buoys` | `aid` buoys, light floats, and light vessels | Circle in the first colour, ringed with the second |
|
|
136
|
+
| `beacons` | `aid` beacons and daymarks | Small circle in the first colour |
|
|
137
|
+
| `lighthouses` | `aid` lighthouses | Ink circle and name |
|
|
138
|
+
| `aids` | Other `aid` classes, such as moorings, fog signals, and racons | Small hollow circle |
|
|
139
|
+
| `lights` | `light` points, one per sector | Flare in the light's first colour and its chart `label` |
|
|
140
|
+
| `hazards` | `hazard` points | `+` for rocks, `*` for rocks that show; circled obstacles |
|
|
141
|
+
| `hazardAreas` | `hazard` polygons; the outline also strokes linear hazards | Accent wash with a dotted outline |
|
|
142
|
+
| `wrecks` | `wreck` points | `Wk`, in the accent colour when dangerous |
|
|
143
|
+
| `wreckAreas` | `wreck` polygons | Ink wash with a dashed outline |
|
|
144
|
+
| `reefs` | `reef` polygons | Sand wash with a dotted outline |
|
|
145
|
+
| `navigationAreas` | `navigation_area` polygons | Dashed outlines; accent for operational areas |
|
|
146
|
+
| `routes` | `navigation_area` lines of class `route` and `traffic-separation` | Dashed line; accent for traffic separation |
|
|
147
|
+
| `cables` | `navigation_area` lines of class `cable`, `pipeline`, and their area classes | Fine dashed accent line |
|
|
148
|
+
| `overheadCables` | `navigation_area` lines of class `overhead-cable` | Dash-dot accent line |
|
|
149
|
+
| `bridges` | `navigation_area` bridge polygons; the outline also strokes bridge lines | Land-coloured deck with an ink outline |
|
|
150
|
+
| `berths` | `navigation_area` berth polygons; the outline also strokes berth lines | Thin ink outline, wider and darker for `guest` berths |
|
|
151
|
+
| `harbourAreas` | `navigation_area` harbour and marina polygons | Faint ink wash with a solid outline |
|
|
152
|
+
| `harbours` | `navigation_area` harbour points, and a symbol inside harbour polygons | Ink circle and name; the marina token for marinas |
|
|
153
|
+
| `facilities` | `navigation_area` facility points, polygons, and line centres, from zoom 13 | Hollow circle; facility icons from the theme |
|
|
154
|
+
| `notices` | `aid` notice boards, from zoom 12 | Circle in the board's function colour and `information` |
|
|
155
|
+
| `distanceMarks` | `aid` distance marks, from zoom 11 | Ink dot and distance with its unit, fainter if not posted |
|
|
156
|
+
| `ports` | `navigation_area` points of class `port` | Ink circle and name sized by `size`, from low zooms |
|
|
157
|
+
| `coverage` | `coverage` polygons that cover every family | Faint tint at low zooms only |
|
|
158
|
+
| `soundings` | `sounding` points | Depth figure |
|
|
159
|
+
| `labels` | Names inside polygons, bridge and overhead-cable clearances; coverage opt-in | Collision-aware text |
|
|
160
|
+
|
|
161
|
+
Bridge, berth, facility, and harbour polygons are drawn by their own groups rather than as
|
|
162
|
+
navigation areas. `labels.bridges` shows `clearance 4.5 m`, or `closed 3.6 m · open 12 m` for an
|
|
163
|
+
opening bridge, at bridge points, inside bridge polygons, and at the centre of bridge lines, from
|
|
164
|
+
zoom 13. `labels.overheadCables` shows `safe clearance 28.5 m`, or the plain clearance, at cable
|
|
165
|
+
points and along cable lines. Distance marks read `12 km`, `5 hm`, `300 m`, `12 mi`, or `4.5 NM`
|
|
166
|
+
from zoom 13. Notice boards follow the CEVNI colours: red for prohibitions, regulations, and
|
|
167
|
+
restrictions, green for recommendations, and blue for information; their `information`, such as a
|
|
168
|
+
speed limit, appears from zoom 14. Board orientation and impact are not drawn.
|
|
145
169
|
|
|
146
170
|
Operational areas are restricted, military, anchorage, anchor-berth, dumping-ground, and
|
|
147
171
|
traffic-separation areas. Marks keep their conventional colours in every theme; a white light is
|
|
@@ -173,6 +197,77 @@ export default defineMap({
|
|
|
173
197
|
});
|
|
174
198
|
```
|
|
175
199
|
|
|
200
|
+
## Flashing lights
|
|
201
|
+
|
|
202
|
+
In React, Vue, Svelte, and the development preview, every light with a rhythm flashes: its flare or
|
|
203
|
+
circle draws at its styled opacity while the light shines and at 15 % of it during an eclipse.
|
|
204
|
+
Fixed lights, lights without a usable characteristic, reduced motion, capture, native renderers, and
|
|
205
|
+
static images show every light steadily, exactly as it is styled. Set `animateLights: false` to keep
|
|
206
|
+
a map's lights steady everywhere:
|
|
207
|
+
|
|
208
|
+
<!-- docs:check -->
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import {defineMap, nautical} from '@tileflow/core';
|
|
212
|
+
import {streets} from '@tileflow/maps';
|
|
213
|
+
|
|
214
|
+
export default defineMap({
|
|
215
|
+
id: 'harbour-guide',
|
|
216
|
+
version: 1,
|
|
217
|
+
extends: streets,
|
|
218
|
+
marine: 'nautical',
|
|
219
|
+
modules: {nautical: nautical({animateLights: false})},
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
A light flashes by its published `sequence` when it has one: light and eclipse durations in
|
|
224
|
+
seconds, eclipses in parentheses, for example `0.5+(1.5)+0.5+(4.5)`. Otherwise
|
|
225
|
+
`tileflowLightRhythm(properties)` derives a conventional rhythm from `character`, `group`, and
|
|
226
|
+
`period_s`; `tileflowLightRhythmConventions` holds the values below.
|
|
227
|
+
|
|
228
|
+
| Character | Rhythm |
|
|
229
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
230
|
+
| `Fl` | Flashes of `min(1 s, 0.3 × period ÷ flashes)`; gaps within a group at most twice the flash |
|
|
231
|
+
| `Fl` with a group such as `2+1` | Groups separated by twice the inner gap; the rest of the period is the final eclipse |
|
|
232
|
+
| `LFl` | The same layout with 2 s flashes, shorter only when the period cannot hold them |
|
|
233
|
+
| `Q`, `VQ`, `UQ` | 60, 120, and 240 flashes a minute, lit for 30 % of each; a group of `n` flashes then eclipses |
|
|
234
|
+
| `IQ`, `IVQ`, `IUQ` | Quick flashes for 60 % of the period, then an eclipse |
|
|
235
|
+
| `Iso` | Equal light and eclipse |
|
|
236
|
+
| `Oc` | Eclipses totalling 25 % of the period, laid out like `Fl`, so the light is always longer |
|
|
237
|
+
| `Mo` | The Morse letter in `group`: 0.5 s dots, three-unit dashes, one-unit gaps, then the rest of the period |
|
|
238
|
+
| `FFl`, `F+LFl` | A fixed light at half intensity with full flashes, or long flashes |
|
|
239
|
+
| `Q+LFl`, `VQ+LFl`, `UQ+LFl`, `Fl+LFl` | The group (six quick flashes by default), one 2 s long flash, then the eclipse |
|
|
240
|
+
| `Oc+Fl` | Its occulting part |
|
|
241
|
+
| `Al.<character>`, `Al` | Each lit phase shows the next colour of `color`; `Al` alone shows each colour for an equal share |
|
|
242
|
+
| `F`, unknown, or missing period | Steady; grouped `Q` and `VQ` cardinal marks without a period use the IALA periods |
|
|
243
|
+
|
|
244
|
+
`tileflowLightStateAt(rhythm, seconds)` returns the intensity, colour index, and time to the next
|
|
245
|
+
change at any moment. Lights with the same rhythm flash together.
|
|
246
|
+
|
|
247
|
+
React, Vue, Svelte, the development preview, and standalone capture call
|
|
248
|
+
`attachTileflowNauticalLights(map)` from `@tileflow/core/browser`. It reads the light layers the
|
|
249
|
+
style declares in `metadata['tileflow:nautical-lights']`, and writes each rendered light's
|
|
250
|
+
`tileflowLightIntensity` and `tileflowLightColour` feature state only when its phase changes. It runs
|
|
251
|
+
one timer for the next change, and none while the page is hidden, while no light layer is visible at
|
|
252
|
+
the current zoom, or under reduced motion. A direct MapLibre integration attaches it after
|
|
253
|
+
constructing the map:
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
import maplibregl from 'maplibre-gl';
|
|
257
|
+
import {attachTileflowNauticalLights, attachTileflowNotices} from '@tileflow/core/browser';
|
|
258
|
+
|
|
259
|
+
const map = new maplibregl.Map({
|
|
260
|
+
container: 'map',
|
|
261
|
+
style: '/tileflow/styles/harbour-guide/light.json',
|
|
262
|
+
});
|
|
263
|
+
attachTileflowNotices(map);
|
|
264
|
+
const detach = attachTileflowNauticalLights(map);
|
|
265
|
+
// Call detach() to return every light to steady; removing the map also detaches it.
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
`steadyTileflowNauticalLights(style)` returns a copy that draws every light steadily without feature
|
|
269
|
+
state; native artifact preparation applies it automatically.
|
|
270
|
+
|
|
176
271
|
## Chart symbols from the theme
|
|
177
272
|
|
|
178
273
|
A theme that declares the image tokens below replaces a point group's circle marker with that icon.
|
|
@@ -190,11 +285,24 @@ Buoy variants fall back to `nautical.buoy`. `tileflowNauticalImageRoles` lists t
|
|
|
190
285
|
| `nautical.aid` | `aids` |
|
|
191
286
|
| `nautical.wreck` | `wrecks` |
|
|
192
287
|
| `nautical.harbour` | `harbours` |
|
|
288
|
+
| `nautical.marina` | Harbours whose `subclass` lists a marina |
|
|
193
289
|
| `nautical.port` | `ports` |
|
|
290
|
+
| `nautical.notice` | `notices` |
|
|
291
|
+
| `nautical.notice.<role>` | Notices of that function |
|
|
292
|
+
| `nautical.distance-mark` | `distanceMarks` |
|
|
293
|
+
| `nautical.facility` | `facilities` |
|
|
294
|
+
| `nautical.facility.<cat>` | Facilities listing that category |
|
|
295
|
+
|
|
296
|
+
Notice roles are `prohibition`, `regulation`, `restriction`, `recommendation`, and `information`.
|
|
297
|
+
Facility categories are `fuel-station`, `slipway`, `boat-hoist`, `pump-out`, `water-tap`,
|
|
298
|
+
`visitor-berth` (also used for `visitors-mooring`), and `boatyard`: a facility listing several shows
|
|
299
|
+
the first in that order (`tileflowNauticalFacilityIcons`), and the others show `nautical.facility`.
|
|
300
|
+
Every icon expression selects among the declared image names only.
|
|
194
301
|
|
|
195
302
|
Buoys, beacons, other aids, and lights colour their icon with the mark's own colour, so declare
|
|
196
303
|
them as [SDF icons](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/native-icons.md)
|
|
197
304
|
that share one representation within each group. The defaults size icons for the SDF profile,
|
|
198
|
-
whose drawing occupies the central 12 of 24 pixels; set `icon.size` for other artwork.
|
|
199
|
-
declares these tokens with its own buoy, light,
|
|
305
|
+
whose drawing occupies the central 12 of 24 pixels; set `icon.size` for other artwork. Notice
|
|
306
|
+
boards take their function colour. Soundings declares these tokens with its own buoy, light,
|
|
307
|
+
lighthouse, wreck, harbour, marina, notice-board, distance-mark, and facility symbols and keeps
|
|
200
308
|
beacons as coloured dots.
|
|
@@ -79,6 +79,7 @@ import {
|
|
|
79
79
|
attachTileflowAtmosphere,
|
|
80
80
|
attachTileflowFairUseNotice,
|
|
81
81
|
attachTileflowMapLifecycle,
|
|
82
|
+
attachTileflowNauticalLights,
|
|
82
83
|
attachTileflowNotices,
|
|
83
84
|
createTileflowSessionStarter,
|
|
84
85
|
createTileflowTransformRequest,
|
|
@@ -86,9 +87,13 @@ import {
|
|
|
86
87
|
} from '@tileflow/core/browser';
|
|
87
88
|
```
|
|
88
89
|
|
|
90
|
+
`@tileflow/core/3d` holds only `createTileflowMap3D` and the 3D renderers it mounts, so an
|
|
91
|
+
application can load them with a dynamic import when a style declares a 3D scene; see
|
|
92
|
+
[3D buildings](buildings-3d.md#the-maps-3d-scene). The browser entry exports the same function.
|
|
93
|
+
|
|
89
94
|
`attachTileflowNotices(map)` renders the notices a compiled style declares, such as Nautical's
|
|
90
|
-
"Not for navigation", as a compact map control
|
|
91
|
-
[Nautical marks](nautical-marks.md). The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
|
|
95
|
+
"Not for navigation", as a compact map control, and `attachTileflowNauticalLights(map)` flashes
|
|
96
|
+
Nautical lights in their rhythm; see [Nautical marks](nautical-marks.md). The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
|
|
92
97
|
runtime dependency, and reads no browser global during module evaluation. See the
|
|
93
98
|
[framework browser runtime contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/framework-browser-runtime.md) and the
|
|
94
99
|
[cartographic authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/cartographic-authoring.md). Exact field
|
package/docs/street-lights.md
CHANGED
|
@@ -9,6 +9,9 @@ the lights switch on one by one; they go out as day comes.
|
|
|
9
9
|
Street lights are off by default in the SDK and in Streets. A compiled map must explicitly set
|
|
10
10
|
`roads.streetLights.enabled: true` before the renderer or preview will show lamp glows.
|
|
11
11
|
|
|
12
|
+
React, Vue and Svelte maps light a map that enables street lights automatically, as part of its
|
|
13
|
+
[3D scene](buildings-3d.md#the-maps-3d-scene); the 2D view has no street lights.
|
|
14
|
+
|
|
12
15
|
It is a custom WebGL2 layer for MapLibre GL JS, drawn above the map's buildings and below its
|
|
13
16
|
labels. When `createTileflowBuildings3D` raises the buildings, they hide the lamps behind them. It
|
|
14
17
|
needs WebGL2 and the Mercator projection, and shines from zoom 14 by default.
|
|
@@ -114,7 +117,8 @@ by default, at most 64) bound where and how far the lights shine, `beforeId` pla
|
|
|
114
117
|
another one, and `transformRequest` applies the map's request rules to the tiles it reads.
|
|
115
118
|
|
|
116
119
|
Layers that read the same source share each tile's download, so the street lights and the 3D
|
|
117
|
-
buildings fetch a tile once between them.
|
|
120
|
+
buildings fetch a tile once between them. The lamps are placed in the worker the page's 3D layers
|
|
121
|
+
share, which decodes each tile once for all of them.
|
|
118
122
|
|
|
119
123
|
## Limits
|
|
120
124
|
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Street sky
|
|
2
|
+
|
|
3
|
+
A map's `sky` paints the sky above the horizon when the camera tilts up the street, in either
|
|
4
|
+
projection: blue by day, warm at dusk, dark with stars at night. Install `@tileflow/core` and
|
|
5
|
+
`@tileflow/maps` and use a browser adapter from the same compatible SDK release. Streets declares a
|
|
6
|
+
sky in every theme; the example extends it and keeps its sky.
|
|
7
|
+
|
|
8
|
+
<!-- docs:check -->
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import {defineMap} from '@tileflow/core';
|
|
12
|
+
import {streets} from '@tileflow/maps';
|
|
13
|
+
|
|
14
|
+
export default defineMap({
|
|
15
|
+
id: 'street-level',
|
|
16
|
+
version: 1,
|
|
17
|
+
extends: streets,
|
|
18
|
+
view: {center: [-3.6904, 40.4493], zoom: 17, bearing: 8, pitch: 80},
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The view looks north along Paseo de la Castellana in Madrid, with the sky above the skyline.
|
|
23
|
+
Coordinates are longitude, then latitude.
|
|
24
|
+
|
|
25
|
+
## Camera pitch
|
|
26
|
+
|
|
27
|
+
React, Vue, Svelte, the Dev preview, and browser capture create maps with a maximum pitch of 85
|
|
28
|
+
degrees, `tileflowMaxPitch`, instead of MapLibre's default of 60. Every portable view already admits
|
|
29
|
+
pitch 0–85. Pass `mapOptions: {maxPitch}` to lower the limit for one map. A map constructed directly
|
|
30
|
+
with MapLibre keeps MapLibre's default unless it sets `maxPitch: tileflowMaxPitch` itself.
|
|
31
|
+
|
|
32
|
+
The React Native surface still uses the native engines' 60-degree limit.
|
|
33
|
+
|
|
34
|
+
## Configure the sky
|
|
35
|
+
|
|
36
|
+
`sky: true` uses the defaults. An options object replaces an inherited sky atomically, and
|
|
37
|
+
`sky: false` removes it.
|
|
38
|
+
|
|
39
|
+
| Option | Default | Meaning |
|
|
40
|
+
| --------------- | --------- | ----------------------------------------------------------------- |
|
|
41
|
+
| `color` | `#5c9ce6` | The sky overhead |
|
|
42
|
+
| `horizonColor` | `#dcebf7` | The pale band along the horizon, also 3D terrain's distance fog |
|
|
43
|
+
| `horizonBlend` | `0.6` | How far up the band reaches, as a share of half the view's height |
|
|
44
|
+
| `glowColor` | `#ffc68a` | The light round a low sun, which glows on the horizon towards it |
|
|
45
|
+
| `starIntensity` | `0.6` | The brightest the stars shine, from 0 (none) to 1 |
|
|
46
|
+
|
|
47
|
+
Colors take `token.color(...)`, color operations, or `fixed(color, {reason})`, and must resolve to
|
|
48
|
+
opaque colors. Numbers take `token.number(...)`, `fixed(number, {reason})`, or a number, and must
|
|
49
|
+
resolve to 0–1. Unknown properties are rejected. Because tokens resolve per theme, one declaration
|
|
50
|
+
gives each theme its own sky. Streets uses its themes' `sky.color`, `sky.horizon`,
|
|
51
|
+
`sky.horizonBlend`, and `sky.glow` tokens:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
sky: {
|
|
55
|
+
color: token.color('sky.color'),
|
|
56
|
+
horizonColor: token.color('sky.horizon'),
|
|
57
|
+
horizonBlend: token.number('sky.horizonBlend'),
|
|
58
|
+
glowColor: token.color('sky.glow'),
|
|
59
|
+
starIntensity: 0.6,
|
|
60
|
+
},
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
A map that replaces Streets' themes supplies those tokens or declares its own `sky`.
|
|
64
|
+
|
|
65
|
+
## A sky that follows the sun
|
|
66
|
+
|
|
67
|
+
When an application places the sun with the theme controller's `setSun`, by direction or by the
|
|
68
|
+
real sun of a date and place, the sky follows its height continuously instead of staying on the
|
|
69
|
+
theme's preset. Above about 14 degrees the sky is the day theme's; it warms into the dawn or dusk
|
|
70
|
+
theme's sky as the sun nears the horizon, using dawn while the sun stands in the eastern half of
|
|
71
|
+
the sky and dusk in the western half; it holds that sky within two degrees of sunrise or sunset,
|
|
72
|
+
and it darkens into the night theme's sky as twilight ends, about ten degrees below the horizon.
|
|
73
|
+
The themes come from the map's `timeOfDay` family of the theme on screen, and every colour is
|
|
74
|
+
resolved from that theme's own tokens, so a Monochrome family stays grey and a client's own
|
|
75
|
+
family keeps its palette. A family with only one twilight uses it at both ends of the day; a theme
|
|
76
|
+
outside a family, or a family without a day and a night theme, keeps its own sky.
|
|
77
|
+
`setSun(undefined)` returns the sky to the themes. The map's colours on the ground stay those of
|
|
78
|
+
the theme or blend on screen; pair `setSun` with `setBlend` to move them too. See
|
|
79
|
+
[sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md).
|
|
80
|
+
|
|
81
|
+
<!-- docs:check -->
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import type {TileflowThemeController} from '@tileflow/core/browser';
|
|
85
|
+
|
|
86
|
+
export function sunsetOverMadrid(controller: TileflowThemeController) {
|
|
87
|
+
// 19:15 in Madrid on 21 October 2026: the sun stands 1.5 degrees above the western horizon.
|
|
88
|
+
return controller.setSun({date: Date.UTC(2026, 9, 21, 17, 15), location: [-3.69, 40.45]});
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Glow, sun and stars
|
|
93
|
+
|
|
94
|
+
A low sun glows on the horizon towards it, in the theme's `glowColor` or, for a sun an application
|
|
95
|
+
placed, in the glow of its hour. The glow is strongest as the sun touches the horizon, fades by
|
|
96
|
+
mid-morning, and is gone once the sun is about nine degrees below. While the sun is low and above
|
|
97
|
+
the horizon, its disc shows too. The sun is the one the theme controller shows, so its
|
|
98
|
+
disc stands where the 3D scene's shadows say it is; without a controller, the theme's own sun.
|
|
99
|
+
|
|
100
|
+
Stars come out as the sky darkens: a night sky shows them, a day or dusk sky does not, and a
|
|
101
|
+
time-of-day blend or a placed sun moving into night brings them in with the dark. They sit on a
|
|
102
|
+
fixed dome, so they turn with the camera's bearing and pitch but not with its position.
|
|
103
|
+
|
|
104
|
+
The glow, the sun, and the stars draw beneath every map layer and only where nothing else has
|
|
105
|
+
drawn, so buildings and trees stand in front of them. With 3D terrain enabled, they are not
|
|
106
|
+
drawn.
|
|
107
|
+
|
|
108
|
+
## Distance haze
|
|
109
|
+
|
|
110
|
+
Above 45 degrees of pitch, the far city fades into the sky's horizon color, so streets meet the sky
|
|
111
|
+
instead of ending in a sharp edge. The haze starts at the camera's distance to the map's center and
|
|
112
|
+
is complete where MapLibre stops drawing the ground, about 6.7 times farther, where the sky starts in
|
|
113
|
+
the same color. It comes in between 45 and 60 degrees, follows the current sky (theme blends
|
|
114
|
+
and a sky that follows the sun included), and leaves the globe alone. Place and POI labels above the
|
|
115
|
+
3D layers stay clear.
|
|
116
|
+
|
|
117
|
+
Tileflow's 3D buildings stand on the tiles round the camera, up to a limit of 25 by default, which
|
|
118
|
+
end well before the horizon. Where that ring ends nearer than the horizon, the haze fits itself to
|
|
119
|
+
it: it is nine-tenths complete at the nearest point where the ring's far side shows across the view,
|
|
120
|
+
and complete soon after, never nearer than three camera distances for the nine-tenths, so a renderer
|
|
121
|
+
held to few tiles does not pull the haze in. Buildings on the ring's far side sink into the ground
|
|
122
|
+
over its last fifth, and nothing past the ring or past the complete haze is drawn. Buildings on the
|
|
123
|
+
near side of a ring held short by the tile limit keep standing.
|
|
124
|
+
|
|
125
|
+
The haze is drawn twice, from one model: Tileflow's 3D buildings, trees, and placed models haze
|
|
126
|
+
each pixel by its depth, and a band over the flat map, just under the first 3D layer, hazes the
|
|
127
|
+
flat ground by the depth its screen row has. Road names and one-way arrows that a style draws on the
|
|
128
|
+
ground before its 3D layers fade with the ground, and so do distant 3D shadows, contact shade, and
|
|
129
|
+
night floodlight. A style without a sky, or whose sky has no opaque horizon color, has no haze.
|
|
130
|
+
|
|
131
|
+
## Browser integration
|
|
132
|
+
|
|
133
|
+
The compiler emits MapLibre `sky` in every concrete style and versioned `metadata['tileflow:sky']`
|
|
134
|
+
with the star brightness, the glow, and, for a theme in a time-of-day family, the family's skies;
|
|
135
|
+
`readTileflowSky` reads it and `tileflowSkyAtSun` mixes the sky of a sun's height. MapLibre draws the sky itself, so raw MapLibre consumers see it. On a
|
|
136
|
+
globe, the sky blends into MapLibre's atmosphere at world zooms; when a map also declares
|
|
137
|
+
`atmosphere`, the sky's colors replace the atmosphere's sky colors while its space background and
|
|
138
|
+
globe stars stay.
|
|
139
|
+
|
|
140
|
+
React, Vue, Svelte, and standalone browser capture draw the glow, the sun, the stars, and the flat
|
|
141
|
+
map's haze through `attachTileflowAtmosphere(map)`. A direct MapLibre integration calls that
|
|
142
|
+
function, or `attachTileflowSky(map)` or `attachTileflowSkyHaze(map)` from `@tileflow/core/browser`
|
|
143
|
+
for one of them alone, after constructing the map; each returns a cleanup function. The sky is the
|
|
144
|
+
custom layer `tileflow-sky` at the bottom of the layer stack, and the haze the custom layer
|
|
145
|
+
`tileflow-sky-haze` under the first 3D layer, or the first label layer without one. Tileflow's 3D
|
|
146
|
+
layers haze themselves. A sky that follows the sun needs the theme controller, which writes the
|
|
147
|
+
sky's colours; raw MapLibre consumers see each theme's own sky.
|
|
148
|
+
|
|
149
|
+
MapLibre applies its own fog only to 3D terrain; with 3D terrain enabled, the flat haze band is not
|
|
150
|
+
drawn and the terrain's fog takes its place. Other layers drawn by an application, such as
|
|
151
|
+
`fill-extrusion` layers, stay sharp. Native artifacts remove the sky during preparation and record
|
|
152
|
+
the removal; see the
|
|
153
|
+
[native artifact profile](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/native-artifact-profile.md).
|
package/docs/sun-and-light.md
CHANGED
|
@@ -106,8 +106,10 @@ custom layer draws; the map redraws whenever the sun changes.
|
|
|
106
106
|
|
|
107
107
|
`setSun` places the sun by direction, or by the real sun of a date over a place given as
|
|
108
108
|
`[longitude, latitude]`, the map's centre when it is left out. The themes keep the sun's colours,
|
|
109
|
-
strengths and shadow strength; only its direction changes.
|
|
110
|
-
|
|
109
|
+
strengths and shadow strength; only its direction changes. A map with a
|
|
110
|
+
[street sky](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/street-sky.md)
|
|
111
|
+
also takes the sky of the sun's hour from its time-of-day themes. `setSun(undefined)` returns both
|
|
112
|
+
to the themes. This example assumes an existing MapLibre map and a controller for it:
|
|
111
113
|
|
|
112
114
|
<!-- docs:check -->
|
|
113
115
|
|
package/docs/trees-3d.md
CHANGED
|
@@ -20,7 +20,8 @@ for its models unless an extension explicitly chooses `flat` or `auto`.
|
|
|
20
20
|
## Visibility and representation
|
|
21
21
|
|
|
22
22
|
Use `createTileflowVegetation` for application controls. React, Vue and Svelte mount it automatically
|
|
23
|
-
in interactive maps
|
|
23
|
+
in interactive maps, as part of the map's [3D scene](buildings-3d.md#the-maps-3d-scene). The map
|
|
24
|
+
chooses `vegetation.mode` independently of visibility:
|
|
24
25
|
|
|
25
26
|
| Mode | 2D view | 3D view |
|
|
26
27
|
| -------------- | ------------------- | ------------------- |
|
|
@@ -28,7 +29,8 @@ in interactive maps. The map chooses `vegetation.mode` independently of visibili
|
|
|
28
29
|
| `flat` | Flat crowns/circles | Flat crowns/circles |
|
|
29
30
|
| `auto` | Flat crowns/circles | Model trees |
|
|
30
31
|
|
|
31
|
-
`viewMode` is explicit application state
|
|
32
|
+
`viewMode` is explicit application state, not a pitch threshold: `2d` by default for
|
|
33
|
+
`createTileflowVegetation`, and `3d` by default in the framework components and `createTileflowMap3D`. Tilting the camera
|
|
32
34
|
does not change representation. Visibility defaults to `vegetation.visible ?? true`; OFF hides every
|
|
33
35
|
representation. Turning trees off releases model buffers and tile readers. Controls apply immediately,
|
|
34
36
|
without a grow/sink animation, and do not change building appearance or lighting.
|
|
@@ -70,7 +72,7 @@ export default defineMap({
|
|
|
70
72
|
Use `refine({visible: false})` for initially hidden vegetation that can be enabled at runtime.
|
|
71
73
|
Use `vegetation: disable()` to remove the module entirely; a runtime toggle cannot restore a module
|
|
72
74
|
that is absent from the compiled map. Browser framework components accept `vegetation={false}` and
|
|
73
|
-
`viewMode="
|
|
75
|
+
`viewMode="2d"` (Vue: `:vegetation="false"`, `view-mode="2d"`). Image mode uses the compiled static
|
|
74
76
|
fallback and does not apply these interactive controls.
|
|
75
77
|
|
|
76
78
|
## Animate trees with the low-level renderer
|
|
@@ -260,7 +262,7 @@ Shadow direction stops lowering at 15° above the horizon; the actual scene ligh
|
|
|
260
262
|
|
|
261
263
|
## Options
|
|
262
264
|
|
|
263
|
-
`minzoom` (15 by default, fading to full coverage at 15.25
|
|
265
|
+
`minzoom` (15 by default, fading to full coverage at 15.25) and `maxTiles` (25 by default, at most 64) bound where and how far trees
|
|
264
266
|
stand; trees thin out and fade with distance from the map's centre, and near trees take more detail
|
|
265
267
|
than far ones. `canopySource` names a vector source with canopy points when they are not in the
|
|
266
268
|
map's World source. `beforeId` places the layer before another one; `transformRequest` applies the
|
|
@@ -270,9 +272,12 @@ tile's download.
|
|
|
270
272
|
## Cost
|
|
271
273
|
|
|
272
274
|
A near tree has 60 to 120 triangles and a far one about 20, and each form is one instanced draw per
|
|
273
|
-
tile.
|
|
275
|
+
tile. Trees are placed in the worker the page's 3D layers share, which decodes each tile once for
|
|
276
|
+
all of them, nearest tiles first. The shared shadow map concentrates resolution on the view when its public camera methods
|
|
274
277
|
provide a safe planar fit. It is drawn again when that focused area, the light, visible tree
|
|
275
|
-
selection, fading or geometry changes. Unchanged views reuse it.
|
|
278
|
+
selection, fading or geometry changes. Unchanged views reuse it. While the camera moves, the trees'
|
|
279
|
+
shadows follow their selection and fading in steps of a sixteenth of a tile, and exactly again once
|
|
280
|
+
it stops. Over the Champ de Mars in Paris,
|
|
276
281
|
where OpenStreetMap maps every tree, about 13,000 trees stand in a pitched view at zoom 16.6.
|
|
277
282
|
|
|
278
283
|
## Limits
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/core",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.39",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Typed Tileflow config and MapLibre style compiler.",
|
|
6
6
|
"type": "module",
|
|
@@ -35,6 +35,11 @@
|
|
|
35
35
|
"import": "./dist/index.js",
|
|
36
36
|
"default": "./dist/index.js"
|
|
37
37
|
},
|
|
38
|
+
"./3d": {
|
|
39
|
+
"types": "./dist/3d.d.ts",
|
|
40
|
+
"import": "./dist/3d.js",
|
|
41
|
+
"default": "./dist/3d.js"
|
|
42
|
+
},
|
|
38
43
|
"./browser": {
|
|
39
44
|
"types": "./dist/browser.d.ts",
|
|
40
45
|
"import": "./dist/browser.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";(()=>{var Re=new Set,De=!1;function Ue(e,n,t=2){let s=n&&n.length,o=s?n[0]*t:e.length;Re.size&&Re.clear();let r=bn(e,0,o,t,!0),u=[];if(!r||r.next===r.prev)return u;let i=0,l=0,c=0;if(s&&(r=Pn(e,n,r,t)),e.length>80*t){i=e[0],l=e[1];let a=i,f=l;for(let h=t;h<o;h+=t){let m=e[h],b=e[h+1];m<i&&(i=m),b<l&&(l=b),m>a&&(a=m),b>f&&(f=b)}c=Math.max(a-i,f-l),c=c!==0?32767/c:0}return We(r,u,i,l,c),u}function bn(e,n,t,s,o){let r=null;if(o===Xn(e,n,t,s)>0)for(let u=n;u<t;u+=s)r=mn(u/s|0,e[u],e[u+1],r);else for(let u=t-s;u>=n;u-=s)r=mn(u/s|0,e[u],e[u+1],r);return r&&Ae(r,r.next)&&(Te(r),r=r.next),r}function ae(e,n=e){let t=n===e,s=e,o;do o=!1,s!==s.next&&(Re.size===0||!Re.has(s))&&(Ae(s,s.next)||P(s.prev,s,s.next)===0)?((t||s===n)&&(n=s.prev),De=!0,Te(s),s=s.prev,o=!0):(t||s!==n)&&(s=s.next,o=!t);while(o||s!==n);return n}function We(e,n,t,s,o){o&&jn(e,t,s,o);let r=e,u=!1;for(;e.prev!==e.next;){let i=e.prev,l=e.next;if(P(i,e,l)<0&&(o?Bn(e,t,s,o):kn(e))){n.push(i.i,e.i,l.i),Te(e),e=l,r=l;continue}if(e=l,e===r){if(De=!1,e=ae(e),De){r=e;continue}if(!u){e=Fn(e,n),r=e,u=!0;continue}_n(e,n,t,s,o);break}}}function kn(e){let n=e.prev,t=e,s=e.next,o=n.x,r=t.x,u=s.x,i=n.y,l=t.y,c=s.y,a=Math.min(o,r,u),f=Math.min(i,l,c),h=Math.max(o,r,u),m=Math.max(i,l,c),b=s.next;for(;b!==n;){if(b.x>=a&&b.x<=h&&b.y>=f&&b.y<=m&&!(o===b.x&&i===b.y)&&Ne(o,i,r,l,u,c,b.x,b.y)&&P(b.prev,b,b.next)>=0)return!1;b=b.next}return!0}function Bn(e,n,t,s){let o=e.prev,r=e,u=e.next,i=o.x,l=r.x,c=u.x,a=o.y,f=r.y,h=u.y,m=Math.min(i,l,c),b=Math.min(a,f,h),d=Math.max(i,l,c),x=Math.max(a,f,h),U=$e(m,b,n,t,s),k=$e(d,x,n,t,s),M=e.prevZ;for(;M&&M.z>=U;){if(M.x>=m&&M.x<=d&&M.y>=b&&M.y<=x&&M!==u&&!(i===M.x&&a===M.y)&&Ne(i,a,l,f,c,h,M.x,M.y)&&P(M.prev,M,M.next)>=0)return!1;M=M.prevZ}let y=e.nextZ;for(;y&&y.z<=k;){if(y.x>=m&&y.x<=d&&y.y>=b&&y.y<=x&&y!==u&&!(i===y.x&&a===y.y)&&Ne(i,a,l,f,c,h,y.x,y.y)&&P(y.prev,y,y.next)>=0)return!1;y=y.nextZ}return!0}function Fn(e,n){let t=e,s=!1;do{let o=t.prev,r=t.next.next;yn(o,t,t.next,r,!1)&&Ie(o,r)&&Ie(r,o)&&(n.push(o.i,t.i,r.i),Te(t),Te(t.next),t=e=r,s=!0),t=t.next}while(t!==e);return s?ae(t):t}function _n(e,n,t,s,o){let r=e;do{let u=r.next.next;for(;u!==r.prev;){if(r.i!==u.i&&Nn(r,u)){let i=pn(r,u);r=ae(r,r.next),i=ae(i,i.next),We(r,n,t,s,o),We(i,n,t,s,o);return}u=u.next}r=r.next}while(r!==e)}var Ke=!1;function Pn(e,n,t,s){let o=[];for(let r=0,u=n.length;r<u;r++){let i=n[r]*s,l=r<u-1?n[r+1]*s:e.length,c=bn(e,i,l,s,!1);c===c.next&&Re.add(c),o.push(Hn(c))}o.sort(On),zn(e.length/s,n.length),dn(t,t),Ke=!0;for(let r=0;r<o.length;r++)t=Ln(o[r],t);return Ke=!1,ae(t)}function On(e,n){return e.x-n.x||e.y-n.y||(e.next.y-e.y)/(e.next.x-e.x)-(n.next.y-n.y)/(n.next.x-n.x)}function Ln(e,n){let t=Sn(e,n);if(!t)return n;let s=pn(t,e),o=s.next;return dn(t,o.next),ae(s,s.next),ae(t,t.next)}var xn=16,_=new Float64Array(0),He=0,Je=[],Qe=[];function zn(e,n){let t=Math.ceil((e+2*n)/xn)+n+2;_.length<t*4&&(_=new Float64Array(t*4)),He=0}function dn(e,n){let t=e;do{let s=He++;Je[s]=t;let o=t.x,r=t.y,u=t.x,i=t.y,l=0;do{let a=t.next;t.z=s,a.x<o&&(o=a.x),a.x>u&&(u=a.x),a.y<r&&(r=a.y),a.y>i&&(i=a.y),t=a}while(++l<xn&&t!==n);Qe[s]=t;let c=s*4;_[c]=o,_[c+1]=r,_[c+2]=u,_[c+3]=i}while(t!==n)}function Cn(e,n){let t=e.z*4;n.x<_[t]&&(_[t]=n.x),n.y<_[t+1]&&(_[t+1]=n.y),n.x>_[t+2]&&(_[t+2]=n.x),n.y>_[t+3]&&(_[t+3]=n.y)}function fn(e){let n=Qe[e];for(;n.prev.next!==n;)n=n.next;return Qe[e]=n,n}function hn(e){let n=Je[e];for(;n.prev.next!==n;)n=n.next;return Je[e]=n,n}function Sn(e,n){let t=n,s=e.x,o=e.y,r=-1/0,u;if(Ae(e,t))return t;for(let h=0,m=0;h<He;h++,m+=4){if(o<_[m+1]||o>_[m+3]||_[m]>s||_[m+2]<=r)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t){if(Ae(e,t.next))return t.next;if(o<=t.y&&o>=t.next.y&&t.next.y!==t.y){let d=t.x+(o-t.y)*(t.next.x-t.x)/(t.next.y-t.y);if(d<=s&&d>r&&(r=d,u=t.x<t.next.x?t:t.next,d===s))return u}}t=t.next}while(t!==b)}if(!u)return null;let i=u.x,l=u.y,c=Math.min(o,l),a=Math.max(o,l),f=1/0;for(let h=0,m=0;h<He;h++,m+=4){if(_[m+2]<i||_[m]>s||_[m+3]<c||_[m+1]>a)continue;let b=fn(h);t=hn(h);do{if(t.prev.next===t&&s>=t.x&&t.x>=i&&s!==t.x&&Ne(o<l?s:r,o,i,l,o<l?r:s,o,t.x,t.y)){let d=Math.abs(o-t.y)/(s-t.x);(Ie(t,e)||t.y===o&&t.next.y===o&&t.next.x>s)&&(d<f||d===f&&(t.x>u.x||t.x===u.x&&Zn(u,t)))&&(u=t,f=d)}t=t.next}while(t!==b)}return u}function Zn(e,n){return P(e.prev,e,n.prev)<0&&P(n.next,e,e.next)<0}var J=[],de=[],ce=new Uint32Array(0),we=new Uint32Array(0),ve=new Uint32Array(256);function jn(e,n,t,s){let o=e,r=0;do o.z=$e(o.x,o.y,n,t,s),J[r++]=o,o=o.next;while(o!==e);En(r);let u=null;for(let i=0;i<r;i++){let l=J[i];l.prevZ=u,u&&(u.nextZ=l),u=l}u.nextZ=null,J.fill(null,0,r),de.fill(null,0,r)}function En(e){if(e<=32){for(let n=1;n<e;n++){let t=J[n],s=t.z,o=n-1;for(;o>=0&&J[o].z>s;)J[o+1]=J[o],o--;J[o+1]=t}return}ce.length<e&&(ce=new Uint32Array(e),we=new Uint32Array(e),de=new Array(e));for(let n=0;n<e;n++)ce[n]=J[n].z;je(e,J,ce,de,we,0),je(e,de,we,J,ce,8),je(e,J,ce,de,we,16),je(e,de,we,J,ce,24)}function je(e,n,t,s,o,r){ve.fill(0);for(let i=0;i<e;i++)ve[t[i]>>>r&255]++;let u=0;for(let i=0;i<256;i++){let l=ve[i];ve[i]=u,u+=l}for(let i=0;i<e;i++){let l=t[i],c=ve[l>>>r&255]++;s[c]=n[i],o[c]=l}}function $e(e,n,t,s,o){return e=(e-t)*o|0,n=(n-s)*o|0,e=(e|e<<8)&16711935,e=(e|e<<4)&252645135,e=(e|e<<2)&858993459,e=(e|e<<1)&1431655765,n=(n|n<<8)&16711935,n=(n|n<<4)&252645135,n=(n|n<<2)&858993459,n=(n|n<<1)&1431655765,e|n<<1}function Hn(e){let n=e,t=e;do(n.x<t.x||n.x===t.x&&n.y<t.y)&&(t=n),n=n.next;while(n!==e);return t}function Ne(e,n,t,s,o,r,u,i){return(o-u)*(n-i)>=(e-u)*(r-i)&&(e-u)*(s-i)>=(t-u)*(n-i)&&(t-u)*(r-i)>=(o-u)*(s-i)}function Nn(e,n){let t=Ae(e,n)&&P(e.prev,e,e.next)>0&&P(n.prev,n,n.next)>0;return e.next.i!==n.i&&(t||Ie(e,n)&&Ie(n,e)&&(P(e.prev,e,n.prev)!==0||P(e,n.prev,n)!==0))&&!Vn(e,n)&&(t||Yn(e,n))}function P(e,n,t){return(n.y-e.y)*(t.x-n.x)-(n.x-e.x)*(t.y-n.y)}function Ae(e,n){return e.x===n.x&&e.y===n.y}function yn(e,n,t,s,o=!0){let r=P(e,n,t),u=P(e,n,s),i=P(t,s,e),l=P(t,s,n);return(r>0&&u<0||r<0&&u>0)&&(i>0&&l<0||i<0&&l>0)?!0:o?!!(r===0&&Ee(e,t,n)||u===0&&Ee(e,s,n)||i===0&&Ee(t,e,s)||l===0&&Ee(t,n,s)):!1}function Ee(e,n,t){return n.x<=Math.max(e.x,t.x)&&n.x>=Math.min(e.x,t.x)&&n.y<=Math.max(e.y,t.y)&&n.y>=Math.min(e.y,t.y)}function Vn(e,n){let t=Math.min(e.x,n.x),s=Math.max(e.x,n.x),o=Math.min(e.y,n.y),r=Math.max(e.y,n.y),u=e;do{let i=u.next;if(u.x>s&&i.x>s||u.x<t&&i.x<t||u.y>r&&i.y>r||u.y<o&&i.y<o){u=i;continue}if(u.i!==e.i&&i.i!==e.i&&u.i!==n.i&&i.i!==n.i&&yn(u,i,e,n))return!0;u=i}while(u!==e);return!1}function Ie(e,n){return P(e.prev,e,e.next)<0?P(e,n,e.next)>=0&&P(e,e.prev,n)>=0:P(e,n,e.prev)<0||P(e,e.next,n)<0}function Yn(e,n){let t=e,s=!1,o=(e.x+n.x)/2,r=(e.y+n.y)/2;do{let u=t.next;t.y>r!=u.y>r&&o<(u.x-t.x)*(r-t.y)/(u.y-t.y)+t.x&&(s=!s),t=u}while(t!==e);return s}function pn(e,n){let t=qe(e.i,e.x,e.y),s=qe(n.i,n.x,n.y),o=e.next,r=n.prev;return e.next=n,n.prev=e,t.next=o,o.prev=t,s.next=t,t.prev=s,r.next=s,s.prev=r,s}function mn(e,n,t,s){let o=qe(e,n,t);return s?(o.next=s.next,o.prev=s,s.next.prev=o,s.next=o):(o.prev=o,o.next=o),o}function Te(e){e.next.prev=e.prev,e.prev.next=e.next,e.prevZ&&(e.prevZ.nextZ=e.nextZ),e.nextZ&&(e.nextZ.prevZ=e.prevZ),Ke&&Cn(e.prev,e.next)}function qe(e,n,t){return{i:e,x:n,y:t,prev:null,next:null,z:0,prevZ:null,nextZ:null}}function Xn(e,n,t,s){let o=0;for(let r=n,u=t-s;r<t;r+=s)o+=(e[u]-e[r])*(e[r+1]+e[u+1]),u=r;return o}var Gn=.7,Dn=Math.tan(Math.PI/6),Wn=Math.tan(35*Math.PI/180),Kn=Math.tan(Math.PI/18),ke=.65,Be=.4;function vn(e,n,t,s,o,r,u){let i=String(t.roof_shape??"");if(!i||i==="flat"||i==="sawtooth"||et(e,u))return;let l=$n(e);if(!l||Math.abs(en(e))<Gn*4*l.halfU*l.halfV)return;let c=l.u,a=l.v,f=l.halfU*r,h=l.halfV*r;f<h&&([c,a,f,h]=[a,c,h,f]);let m=wn(t.roof_direction),b=m===void 0?void 0:[Math.sin(m*Math.PI/180),-Math.cos(m*Math.PI/180)];(b!==void 0?Math.abs(b[0]*c[0]+b[1]*c[1])>Math.abs(b[0]*a[0]+b[1]*a[1]):t.roof_orientation==="across")&&i!=="skillion"&&([c,a,f,h]=[a,c,h,f]);let x=Qn(i,f,h,b&&[Mn(b,c),Mn(b,a)]);if(!x)return;let U=Math.max(0,o-s),k=wn(t.roof_height),M,y;if(k!==void 0&&k>0)y=o,M=Math.max(s+Math.min(2,U/2),o-k);else{let p=Math.min(Jn(i,h),U*.6,30);t.height_source==="height"?(y=o,M=o-p):(M=o,y=o+p)}let I=y-M;if(!(I>=.3))return;let[R,V]=l.centre,B=(p,A)=>{let w=(p-R)*r,g=(A-V)*r;return[w*c[0]+g*c[1],w*a[0]+g*a[1]]},j=(p,A)=>[R+(p*c[0]+A*a[0])/r,V+(p*c[1]+A*a[1])/r],Q=(p,A)=>{let w=1/0;for(let[g,N,E]of x)w=Math.min(w,g*p+N*A+E);return Math.max(0,w)},$=(p,A)=>M+I*Q(...B(p,A)),L=[];for(let p=0;p+1<n.length;p+=2)L.push(...B(n[p],n[p+1]));en(L)<0&&nt(L);let z=1.01,q=[-f*z,-h*z,f*z,-h*z,f*z,h*z,-f*z,h*z],Y=[];for(let[p,A]of x.entries()){let w=q;for(let[F,H]of x.entries()){if(F===p)continue;let C=A[0]-H[0],X=A[1]-H[1],ne=A[2]-H[2];if(Math.abs(C)+Math.abs(X)+Math.abs(ne)<1e-9){F<p&&(w=[]);continue}if(w=gn(w,C,X,ne),w.length<6)break}if(w.length<6)continue;let g=L,N=w.length/2;for(let F=0;F<N&&g.length>=6;F+=1){let[H,C]=[w[F*2],w[F*2+1]],[X,ne]=[w[(F+1)%N*2],w[(F+1)%N*2+1]];g=gn(g,ne-C,H-X,-(ne-C)*H-(H-X)*C)}if(g.length<6||Math.abs(en(g))<1e-6)continue;let E=[];for(let F=0;F<g.length;F+=2)E.push(...j(g[F],g[F+1]));let se=Ue(E);if(se.length===0)continue;let ee=I*A[0],oe=I*A[1],T=ee*c[0]+oe*a[0],v=ee*c[1]+oe*a[1],O=Math.hypot(T,v,1);Y.push({ring:E,triangles:se,normal:[-T/O,-v/O]})}if(Y.length!==0)return{eaves:M,ridge:y,faces:Y,heightAt:$,breaks(p,A,w,g){let[N,E]=B(p,A),[se,ee]=B(w,g),oe=[];for(let T=0;T<x.length;T+=1)for(let v=T+1;v<x.length;v+=1){let O=(he,S)=>(x[T][0]-x[v][0])*he+(x[T][1]-x[v][1])*S+(x[T][2]-x[v][2]),F=O(N,E),H=O(se,ee);if(F===H||F*H>0)continue;let C=F/(F-H);if(C<=1e-4||C>=1-1e-4)continue;let X=N+(se-N)*C,ne=E+(ee-E)*C;x[T][0]*X+x[T][1]*ne+x[T][2]<=Q(X,ne)+1e-6&&oe.push(C)}return[...new Set(oe.map(T=>Math.round(T*1e6)/1e6))].sort((T,v)=>T-v)}}}function Jn(e,n){switch(e){case"skillion":return 2*n*Kn;case"pyramidal":return n*Wn;case"gambrel":case"mansard":return n*.8;case"dome":case"onion":case"round":return n;default:return n*Dn}}function Qn(e,n,t,s){let o=n,r=t,u=[[0,-1/r,1],[0,1/r,1]],i=c=>c.flatMap(a=>[1,-1].flatMap(f=>{let h=a==="v"?r:o,m=ke/(Be*r)*f,b=(1-ke)/((1-Be)*r)*f,d=x=>a==="v"?[0,-x]:[-x,0];return[[...d(m),ke/(Be*r)*h],[...d(b),ke+(1-ke)/((1-Be)*r)*(h-Be*r)]]})),l=[.99,.8,.5,.2];switch(e){case"gabled":return u;case"saltbox":{let c=r/3;return[[0,-1/(r-c),1+c/(r-c)],[0,1/(r+c),1-c/(r+c)]]}case"hipped":return[...u,[-1/r,0,o/r],[1/r,0,o/r]];case"half_hipped":return[...u,[-1/r,0,.5+o/r],[1/r,0,.5+o/r]];case"pyramidal":return[...u,[-1/o,0,1],[1/o,0,1]];case"gambrel":return i(["v"]);case"mansard":return i(["v","u"]);case"skillion":{let[c,a]=s??[0,1],f=Math.abs(c)*o+Math.abs(a)*r;return f>0?[[-c/(2*f),-a/(2*f),.5]]:void 0}case"round":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return[1,-1].map(f=>[0,-f*c/(r*a),1/a])})];case"dome":case"onion":return[[0,0,1],...l.flatMap(c=>{let a=Math.sqrt(1-c*c);return Array.from({length:12},(f,h)=>{let m=h*Math.PI/6;return[-c*Math.cos(m)/(o*a),-c*Math.sin(m)/(r*a),1/a]})})];default:return}}function gn(e,n,t,s){let o=e.length/2,r=[];for(let u=0;u<o;u+=1){let i=e[u*2],l=e[u*2+1],c=e[(u+1)%o*2],a=e[(u+1)%o*2+1],f=n*i+t*l+s,h=n*c+t*a+s;if(f<=0&&r.push(i,l),f<0&&h>0||f>0&&h<0){let m=f/(f-h);r.push(i+(c-i)*m,l+(a-l)*m)}}return r}function $n(e){let n=qn(e);if(n.length<3)return;let t,s=1/0;for(let o=0;o<n.length;o+=1){let[r,u]=n[o],[i,l]=n[(o+1)%n.length],c=Math.hypot(i-r,l-u);if(c===0)continue;let a=[(i-r)/c,(l-u)/c],f=[-a[1],a[0]],[h,m,b,d]=[1/0,-1/0,1/0,-1/0];for(let[U,k]of n){let M=U*a[0]+k*a[1],y=U*f[0]+k*f[1];h=Math.min(h,M),m=Math.max(m,M),b=Math.min(b,y),d=Math.max(d,y)}let x=(m-h)*(d-b);if(x<s){s=x;let U=(h+m)/2,k=(b+d)/2;t={centre:[U*a[0]+k*f[0],U*a[1]+k*f[1]],u:a,v:f,halfU:(m-h)/2,halfV:(d-b)/2}}}return t&&t.halfU>0&&t.halfV>0?t:void 0}function qn(e){let n=[];for(let r=0;r+1<e.length;r+=2)n.push([e[r],e[r+1]]);n.sort((r,u)=>r[0]-u[0]||r[1]-u[1]);let t=(r,u,i)=>(u[0]-r[0])*(i[1]-r[1])-(u[1]-r[1])*(i[0]-r[0]),s=[];for(let r of n){for(;s.length>=2&&t(s.at(-2),s.at(-1),r)<=0;)s.pop();s.push(r)}let o=[];for(let r of n.reverse()){for(;o.length>=2&&t(o.at(-2),o.at(-1),r)<=0;)o.pop();o.push(r)}return[...s.slice(0,-1),...o.slice(0,-1)]}function et(e,n){let[t,s,o,r]=[1/0,-1/0,1/0,-1/0];for(let i=0;i+1<e.length;i+=2)t=Math.min(t,e[i]),s=Math.max(s,e[i]),o=Math.min(o,e[i+1]),r=Math.max(r,e[i+1]);let u=e.length/2;for(let i=0;i<u;i+=1){let l=e[i*2],c=e[i*2+1],a=e[(i+1)%u*2],f=e[(i+1)%u*2+1];if(t<0&&l===t&&a===t||s>n&&l===s&&a===s||o<0&&c===o&&f===o||r>n&&c===r&&f===r)return!0}return!1}function en(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n/2}function nt(e){let n=[];for(let t=0;t<e.length;t+=2)n.push([e[t],e[t+1]]);n.reverse(),e.splice(0,e.length,...n.flat())}function Mn(e,n){return e[0]*n[0]+e[1]*n[1]}function wn(e){let n=typeof e=="string"?Number(e):e;return typeof n=="number"&&Number.isFinite(n)?n:void 0}var Mt=Object.freeze({surface:0,secondary:1,glass:2,accent:3,roof:4}),wt=Object.freeze({unclassified:0,wall:1,door:2,roof:3,window:4,lamp:5,logo:6}),vt=Object.freeze([1,1,4,1,3]),Rt=Object.freeze({levels:3,vertices:2e5,triangles:2e5,footprintPoints:4096,radius:2e3,height:1e3,models:512,bytes:16*1024*1024});function nn(e,n,t){for(let s of e){let o=!1;for(let r=0,u=s.length-2;r<s.length;u=r,r+=2){let i=s[r],l=s[r+1],c=s[u],a=s[u+1];l>t!=a>t&&n<(c-i)*(t-l)/(a-l)+i&&(o=!o)}if(o)return!0}return!1}function In(e,n,t){for(let s of n){let[o,r,u,i]=An(s);for(let l of e){let[c,a,f,h]=An(l);if(!(c>u-t||f<o+t||a>i-t||h<r+t)&&(Rn(l,s,t)||Rn(s,l,t)))return!0}}return!1}function Rn(e,n,t){let s=(o,r)=>nn([n],o,r)&&ot(n,o,r)>t;for(let o=0;o+1<e.length;o+=2)if(s(e[o],e[o+1]))return!0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=[0,1];for(let f=0,h=n.length-2;f<n.length;h=f,f+=2){let m=tt(u,i,l,c,n[h],n[h+1],n[f],n[f+1]);m!==void 0&&a.push(m)}if(a.length!==2){a.sort((f,h)=>f-h);for(let f=1;f<a.length;f+=1){let h=(a[f-1]+a[f])/2;if(s(u+(l-u)*h,i+(c-i)*h))return!0}}}return!1}function tt(e,n,t,s,o,r,u,i){let l=t-e,c=s-n,a=u-o,f=i-r,h=l*f-c*a;if(h===0)return;let m=((o-e)*f-(r-n)*a)/h,b=((o-e)*c-(r-n)*l)/h;return m>0&&m<1&&b>=0&&b<=1?m:void 0}function ot(e,n,t){let s=1/0;for(let o=0,r=e.length-2;o<e.length;r=o,o+=2){let[u,i,l,c]=[e[r],e[r+1],e[o],e[o+1]],a=(l-u)**2+(c-i)**2,f=a>0?Math.max(0,Math.min(1,((n-u)*(l-u)+(t-i)*(c-i))/a)):0;s=Math.min(s,Math.hypot(n-(u+(l-u)*f),t-(i+(c-i)*f)))}return s}function An(e){let[n,t,s,o]=[1/0,1/0,-1/0,-1/0];for(let r=0;r+1<e.length;r+=2)n=Math.min(n,e[r]),s=Math.max(s,e[r]),t=Math.min(t,e[r+1]),o=Math.max(o,e[r+1]);return[n,t,s,o]}var te=8192,tn=28,rt={commercial:1,active:2,destination:3},st={residential:1,...Object.fromEntries(["civic","commercial","education","entertainment","medical","transportation"].map(e=>[e,2])),...Object.fromEntries(["agricultural","industrial","military","outbuilding","religious","service"].map(e=>[e,3]))},it={dining:1,entertainment:2,nightlife:3},on={...Object.fromEntries(["alcoholic_beverage_venue","bar","beer","biergarten","brewery","casino","cocktail_bar","dance_club","karaoke","lounge","music_venue","night_club","nightclub","pub","wine_bar"].map(e=>[e,3])),...Object.fromEntries(["arts_centre","bowling_alley","cinema","comedy_club","concert_hall","movie_theater","opera","performing_arts_venue","theater","theatre","theatre_venue"].map(e=>[e,2])),...Object.fromEntries(["casual_eatery","fast_food","fast_food_restaurant","food_court","restaurant"].map(e=>[e,1]))},ut=6,lt=.5,ct=.5;function Un(e,n){let t=te/e.extent,s=n.tileMetres/te,o=n.defaultHeight??5,r=Number.isFinite(n.edgeRadius)?Math.max(0,Math.min(1,n.edgeRadius))/s:0,u=new sn,i=new Ve,l=new Ve,c=[],a=[],f=0,h=mt(e,n);for(let[y,I]of e.features.entries()){if(I.type!=="Polygon"||c.length>=65535)continue;let R=I.properties,V=R.hide_3d;if(V===!0||V===1||V==="1"||V==="true")continue;let B=Math.min(6e3,Tn(R.render_height)??o),j=Math.min(B,Tn(R.render_min_height)??0),Q=rt[String(R.building_tone??"")]??0,$=st[String(R.class??"")]??0,L=h.get(y)??0,z=c.length,q=!1;for(let Y of bt(I.geometry,t)){let p=Y.map(dt).filter(v=>v.length>=6);if(p.length===0||Ye(p[0])===0)continue;let[A,w]=xt(p[0]);if(n.masks?.length&&(nn(n.masks,A,w)||In(p,n.masks,ct/s)))continue;let g={cell:Math.min(7,Math.max(0,Math.floor(w/1024)))*8+Math.min(7,Math.max(0,Math.floor(A/1024))),bounds:[1/0,1/0,-1/0,-1/0,B+.1],roofsFrom:i.length,roofsCount:0,wallsFrom:l.length,wallsCount:0};for(let v of p)for(let O=0;O<v.length;O+=2)g.bounds[0]=Math.min(g.bounds[0],v[O]-1),g.bounds[1]=Math.min(g.bounds[1],v[O+1]-1),g.bounds[2]=Math.max(g.bounds[2],v[O]+1),g.bounds[3]=Math.max(g.bounds[3],v[O+1]+1);q=!0,f=Math.max(f,B);let N=Number.isSafeInteger(I.id),E=rn(N?I.id*.0731:A*.0731+w*.19137+z*1.618),se=!N&&p[0].some(v=>v<=0||v>=te)?rn(B*7.31+j*3.17+Q):E,ee={base:j,top:B,jitter:se,seed:E,tone:Q,group:$,use:L,cx:A,cy:w,index:z},oe=p.map((v,O)=>ht(v,O,r,n.cornerAngle));p=oe.map(({ring:v})=>v);let T=Y.length===1&&p.length===1?vn(Y[0],p[0],R,j,B,s,te):void 0;T?(f=Math.max(f,T.ridge),g.bounds[4]=Math.max(g.bounds[4],T.ridge+.1),ft(T,ee,u,i)):at(p,ee,u,i);for(let[v,O]of p.entries()){let F=Ye(O)>0,H=v===0?F?-1:1:F?1:-1,C=O.length/2,X=S=>{let Z=(S%C+C)%C;return[O[Z*2],O[Z*2+1]]},ne=S=>{let[Z,W]=X(S-1),[G,D]=X(S),[ie,me]=X(S+1);if(fe(Z,W,G,D)||fe(G,D,ie,me))return!1;let be=Math.hypot(G-Z,D-W),ue=Math.hypot(ie-G,me-D);if(!be||!ue)return!1;let Fe=-(D-W)/be*H,_e=(G-Z)/be*H;return(Fe*(ie-G)+_e*(me-D))/ue>lt},Xe=(S,Z)=>T?T.heightAt(S,Z):B,he=0;for(let S=0;S<C;S+=1){let[Z,W]=X(S),[G,D]=X(S+1),ie=Math.hypot(G-Z,D-W);if(ie===0||fe(Z,W,G,D))continue;let me=-(D-W)/ie*H,be=(G-Z)/ie*H,ue=oe[v].normals[S]??[me,be],Fe=oe[v].normals[(S+1)%C]??[me,be],_e=K=>{let pe=ue[0]+(Fe[0]-ue[0])*K,ge=ue[1]+(Fe[1]-ue[1])*K,le=Math.hypot(pe,ge)||1;return[pe/le,ge/le]},re=[0,...T?T.breaks(Z,W,G,D):[],1];for(let K=0;K+1<re.length;K+=1){let pe=K===0,ge=K+2===re.length,le=Z+(G-Z)*re[K],Pe=W+(D-W)*re[K],Oe=Z+(G-Z)*re[K+1],Le=W+(D-W)*re[K+1],Me=Math.hypot(Oe-le,Le-Pe)/te*n.tileMetres,ze=(pe&&ne(S)?1:0)|(ge&&ne(S+1)?2:0)|(v>0?4:0),Ce=(pe&&un(Z,W)?1:0)|(ge&&un(G,D)?2:0),Ge=he+Me,Se=_e(re[K]),Ze=_e(re[K+1]),cn=Xe(le,Pe),an=Xe(Oe,Le),xe=u.write(ee,le,Pe,0,Se[0],Se[1],1,he,ze,Me,0,Ce,cn);u.write(ee,Oe,Le,0,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),u.write(ee,le,Pe,1,Se[0],Se[1],1,he,ze,Me,0,Ce,cn),u.write(ee,Oe,Le,1,Ze[0],Ze[1],1,Ge,ze,Me,1,Ce,an),l.push(xe,xe+1,xe+2,xe+1,xe+3,xe+2),he=Ge}}}g.roofsCount=i.length-g.roofsFrom,g.wallsCount=l.length-g.wallsFrom,a.push(g)}q&&c.push(R)}if(u.count===0)return;let m=new Uint32Array(i.length+l.length),b=[],d=0,x=i.length,U=i.values(),k=l.values();a.sort((y,I)=>y.cell-I.cell);let M=-1;for(let y of a){y.cell!==M&&(M=y.cell,b.push({bounds:[...y.bounds],roofsFrom:d,roofsCount:0,wallsFrom:x,wallsCount:0}));let I=b.at(-1);for(let R=0;R<5;R+=1)I.bounds[R]=R<2?Math.min(I.bounds[R],y.bounds[R]):Math.max(I.bounds[R],y.bounds[R]);m.set(U.subarray(y.roofsFrom,y.roofsFrom+y.roofsCount),d),m.set(k.subarray(y.wallsFrom,y.wallsFrom+y.wallsCount),x),d+=y.roofsCount,x+=y.wallsCount,I.roofsCount+=y.roofsCount,I.wallsCount+=y.wallsCount}return{groups:b,vertices:u.bytes(),indices:m,wallsFrom:i.length,top:f,features:c}}function at(e,n,t,s){let o=[],r=[];for(let l of e)r.push(o.length/2),o.push(...l);r.push(o.length/2);let u=(l,c)=>{let a=r.findIndex((d,x)=>l>=d&&l<r[x+1]),[f,h]=[r[a],r[a+1]-1];if(c<f||c>h)return!1;let[m,b]=l<c?[l,c]:[c,l];return b-m!==1&&!(m===f&&b===h)?!1:!fe(o[l*2],o[l*2+1],o[c*2],o[c*2+1])},i=Ue(o,r.slice(1,-1),2);for(let l=0;l+2<i.length;l+=3){let c=[i[l],i[l+1],i[l+2]],a=0;for(let f=0;f<3;f+=1)u(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries())s.push(t.write(n,o[h*2],o[h*2+1],1,0,0,0,0,0,0,f,a))}}function ft(e,n,t,s){for(let o of e.faces){let r=o.ring,u=r.length/2,i=(l,c)=>{let[a,f]=l<c?[l,c]:[c,l];return f-a!==1&&!(a===0&&f===u-1)?!1:!fe(r[l*2],r[l*2+1],r[c*2],r[c*2+1])};for(let l=0;l+2<o.triangles.length;l+=3){let c=[o.triangles[l],o.triangles[l+1],o.triangles[l+2]],a=0;for(let f=0;f<3;f+=1)i(c[(f+1)%3],c[(f+2)%3])&&(a|=1<<f);for(let[f,h]of c.entries()){let m=r[h*2],b=r[h*2+1];s.push(t.write(n,m,b,1,o.normal[0],o.normal[1],0,0,0,0,f,a,e.heightAt(m,b)))}}}}function ht(e,n,t,s=Math.PI/6){if(t<1)return{ring:e,normals:[]};let o=e.length/2,r=Ye(e)>0,u=n===0?r?-1:1:r?1:-1,i=[],l=[],c=(a,f,h)=>{a=ye(a),f=ye(f),!(i.at(-2)===a&&i.at(-1)===f)&&(i.push(a,f),l.push(h))};for(let a=0;a<o;a+=1){let f=(a+o-1)%o,h=(a+1)%o,[m,b]=[e[f*2],e[f*2+1]],[d,x]=[e[a*2],e[a*2+1]],[U,k]=[e[h*2],e[h*2+1]],M=Math.hypot(d-m,x-b),y=Math.hypot(U-d,k-x);if(!M||!y||un(d,x)||fe(m,b,d,x)||fe(d,x,U,k)){c(d,x);continue}let[I,R]=[(d-m)/M,(x-b)/M],[V,B]=[(U-d)/y,(k-x)/y],j=Math.atan2(I*B-R*V,I*V+R*B);if(j*u>=-.08||Math.abs(j)>Math.PI*.85){c(d,x);continue}let Q=Math.tan(Math.abs(j)/2),$=Math.min(t*Q,M*.2,y*.2),L=$/Q;if(L<1){c(d,x);continue}let[z,q]=[-R*u,I*u],[Y,p]=[d-I*$-z*L,x-R*$-q*L],A=Math.atan2(q,z),w=Math.max(s<=Math.PI/6?2:1,Math.ceil(Math.abs(j)/Math.max(Math.PI/6,Math.min(Math.PI/3,s))));for(let g=0;g<=w;g+=1){let N=A+j*g/w,E=[Math.cos(N),Math.sin(N)];c(Y+E[0]*L,p+E[1]*L,E)}}return i[0]===i.at(-2)&&i[1]===i.at(-1)&&(i.splice(-2),l.pop()),i.length>=6?{ring:i,normals:l}:{ring:e,normals:[]}}var sn=class{#e=new ArrayBuffer(4096*tn);#n=new DataView(this.#e);count=0;write(n,t,s,o,r,u,i,l,c,a,f=0,h=0,m=n.top){let b=tn;if((this.count+1)*b>this.#e.byteLength){let U=new ArrayBuffer(this.#e.byteLength*2);new Uint8Array(U).set(new Uint8Array(this.#e)),this.#e=U,this.#n=new DataView(U)}let d=this.#n,x=this.count*b;return d.setUint16(x,ye(t),!0),d.setUint16(x+2,ye(s),!0),d.setUint16(x+4,Math.min(65535,Math.round(n.base*10)),!0),d.setUint16(x+6,Math.min(65535,Math.max(0,Math.round(m*10))),!0),d.setInt8(x+8,Math.round(r*127)),d.setInt8(x+9,Math.round(u*127)),d.setUint8(x+10,o),d.setUint8(x+11,i),d.setUint16(x+12,Math.round(l*10)%65536,!0),d.setUint8(x+14,Math.round(n.jitter*255)),d.setUint8(x+15,Math.round(n.seed*255)),d.setUint16(x+16,ye(n.cx),!0),d.setUint16(x+18,ye(n.cy),!0),d.setUint8(x+20,n.tone+4*n.group),d.setUint8(x+21,n.use),d.setUint8(x+22,c),d.setUint8(x+23,Math.min(255,Math.round(a))),d.setUint16(x+24,n.index,!0),d.setUint8(x+26,f),d.setUint8(x+27,h),this.count+=1,this.count-1}bytes(){return new Uint8Array(this.#e.slice(0,this.count*tn))}},Ve=class{#e=new Uint32Array(8192);length=0;push(...n){if(this.length+n.length>this.#e.length){let t=new Uint32Array(Math.max(this.length+n.length,this.#e.length*2));t.set(this.#e),this.#e=t}for(let t of n)this.#e[this.length++]=t}values(){return this.#e.subarray(0,this.length)}};function mt(e,n){let t=new Map;if(e.features.some(i=>i.properties.night_use!==void 0)){for(let[i,l]of e.features.entries()){let c=it[String(l.properties.night_use??"")];c&&t.set(i,c)}return t}let s=n.places;if(!s||s.features.length===0)return t;let o=ut/n.tileMetres*e.extent,r=[];for(let[i,l]of e.features.entries()){if(l.type!=="Polygon")continue;let c=l.properties.hide_3d;if(c===!0||c===1||c==="1"||c==="true")continue;let a=[1/0,1/0,-1/0,-1/0];for(let f of l.geometry)for(let h=0;h+1<f.length;h+=2)a[0]=Math.min(a[0],f[h]),a[1]=Math.min(a[1],f[h+1]),a[2]=Math.max(a[2],f[h]),a[3]=Math.max(a[3],f[h+1]);r.push({index:i,rings:l.geometry,box:a})}let u=e.extent/s.extent;for(let i of s.features){if(i.type!=="Point")continue;let l=i.properties,c=on[String(l.type??"")]??on[String(l.subclass??"")]??on[String(l.class??"")];if(c)for(let a of i.geometry)for(let f=0;f+1<a.length;f+=2){let h=a[f]*u,m=a[f+1]*u,b,d,x=o;for(let k of r){let[M,y,I,R]=k.box;if(h<M-o||h>I+o||m<y-o||m>R+o)continue;let V=!1;for(let B of k.rings)for(let j=0,Q=B.length-2;j+1<B.length;Q=j,j+=2){let $=B[Q],L=B[Q+1],z=B[j],q=B[j+1];q>m!=L>m&&h<($-z)*(m-q)/(L-q)+z&&(V=!V);let Y=z-$,p=q-L,A=Y*Y+p*p,w=A?Math.min(1,Math.max(0,((h-$)*Y+(m-L)*p)/A)):0,g=Math.hypot(h-$-w*Y,m-L-w*p);g<x&&(x=g,d=k.index)}if(V){b=k.index;break}}let U=b??d;U!==void 0&&t.set(U,Math.max(t.get(U)??0,c))}}return t}function ye(e){return Math.min(te,Math.max(0,Math.round(e)))}function Tn(e){let n=Number(e);return Number.isFinite(n)&&n>0?n:void 0}function bt(e,n){let t=[],s;for(let o of e){let r=[];for(let i=0;i+1<o.length;i+=2){let l=o[i]*n,c=o[i+1]*n;r.length&&l===r.at(-2)&&c===r.at(-1)||r.push(l,c)}if(r.length>=6&&r[0]===r.at(-2)&&r[1]===r.at(-1)&&(r.length-=2),r.length<6)continue;let u=Math.sign(Ye(r));u&&(s??=u,u===s||t.length===0?t.push([r]):t.at(-1).push(r))}return t}function Ye(e){let n=0,t=e.length/2;for(let s=0;s<t;s+=1){let o=(s+1)%t;n+=e[s*2]*e[o*2+1]-e[o*2]*e[s*2+1]}return n}function xt(e){let n=0,t=0,s=e.length/2;for(let o=0;o<s;o+=1)n+=e[o*2],t+=e[o*2+1];return[n/s,t/s]}function un(e,n){return e<=0||n<=0||e>=te||n>=te}function fe(e,n,t,s){let o=te;return e<=0&&t<=0||e>=o&&t>=o||n<=0&&s<=0||n>=o&&s>=o}function dt(e){let n=te,t=[...e],s=[[0,0,o=>o>=0],[0,n,o=>o<=n],[1,0,o=>o>=0],[1,n,o=>o<=n]];for(let[o,r,u]of s){let i=[],l=t.length/2;for(let c=0;c<l;c+=1){let a=[t[c*2],t[c*2+1]],f=[t[(c+1)%l*2],t[(c+1)%l*2+1]],h=u(a[o]),m=u(f[o]);if(h&&i.push(a[0],a[1]),h!==m){let b=(r-a[o])/(f[o]-a[o]);i.push(a[0]+(f[0]-a[0])*b,a[1]+(f[1]-a[1])*b)}}if(t=i,t.length<6)return[]}return t}function rn(e){let n=Math.sin(e*12.9898+78.233)*43758.5453;return n-Math.floor(n)}var ln=globalThis;ln.onmessage=({data:e})=>{try{let n=Un(e.layer,e.options);ln.postMessage({mesh:n},n?[n.vertices.buffer,n.indices.buffer]:[])}catch(n){ln.postMessage({error:String(n)})}};})();
|