@tileflow/core 0.1.0-alpha.41 → 0.1.0-alpha.43

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 (51) hide show
  1. package/README.md +51 -69
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/dist/{3d-CzLTD0RK.d.ts → 3d-CW5Ls0An.d.ts} +32 -2
  4. package/dist/3d.d.ts +1 -1
  5. package/dist/3d.js +337 -34
  6. package/dist/browser.d.ts +119 -7
  7. package/dist/browser.js +754 -61
  8. package/dist/build.d.ts +8 -4
  9. package/dist/build.js +284 -4
  10. package/dist/config-reference.json +4005 -2284
  11. package/dist/{icon-composition-HUh_Lj8Z.d.ts → icon-composition-Bbw9umUA.d.ts} +46 -3
  12. package/dist/index.d.ts +22 -5
  13. package/dist/index.js +254 -6
  14. package/dist/{manifest-types-DnhSJjCM.d.ts → manifest-types-CwjHd4qc.d.ts} +10 -0
  15. package/dist/manifest.d.ts +2 -2
  16. package/dist/native-profile.d.ts +4 -0
  17. package/dist/native-profile.js +2 -0
  18. package/dist/native.d.ts +87 -1
  19. package/dist/native.js +980 -460
  20. package/dist/runtime.d.ts +1 -1
  21. package/dist/worker/camera-tile-worker.global.js +2 -2
  22. package/package.json +4 -5
  23. package/docs/application-tile-reading.md +0 -86
  24. package/docs/authoring-model.md +0 -269
  25. package/docs/boundary-areas-and-choropleths.md +0 -217
  26. package/docs/buildings-3d.md +0 -608
  27. package/docs/camera-moves-and-tile-cache.md +0 -112
  28. package/docs/capture-scenes.md +0 -40
  29. package/docs/capture-shots.md +0 -173
  30. package/docs/cartographic-defaults.md +0 -210
  31. package/docs/compiled-style-performance.md +0 -286
  32. package/docs/data-is-separate-from-design.md +0 -610
  33. package/docs/depth-markers.md +0 -65
  34. package/docs/dynamic-style-images.md +0 -100
  35. package/docs/globe-atmosphere.md +0 -113
  36. package/docs/hosted-session-authorization.md +0 -106
  37. package/docs/map-and-terrain-examples.md +0 -156
  38. package/docs/map-emphasis.md +0 -89
  39. package/docs/native-artifact-profile.md +0 -152
  40. package/docs/native-resource-urls.md +0 -369
  41. package/docs/nautical-marks.md +0 -308
  42. package/docs/public-api-and-browser-subpath.md +0 -101
  43. package/docs/route-drawing.md +0 -191
  44. package/docs/shared-visual-primitives.md +0 -101
  45. package/docs/street-lights.md +0 -127
  46. package/docs/street-sky.md +0 -156
  47. package/docs/style-states.md +0 -73
  48. package/docs/sun-and-light.md +0 -167
  49. package/docs/theme-transitions-and-blends.md +0 -317
  50. package/docs/themes-and-module-styles.md +0 -156
  51. package/docs/trees-3d.md +0 -295
@@ -1,608 +0,0 @@
1
- # 3D buildings
2
-
3
- `createTileflowBuildings3D` raises the buildings of a Tileflow map's vector tiles round the camera.
4
- By default it preserves the map's building colours, uses the scene sun, casts shadows, adds soft
5
- contact shading and rounds exterior corners by 0.8 metres. An unspecified sun uses soft daylight
6
- with directional intensity 0.2 and ambient intensity 0.8; authored theme lighting stays in control.
7
- Showing, hiding and changing height are immediate. Illuminated outlines, random colour variation,
8
- invented windows and night lights remain off. Every effect is independently overridable and
9
- independent of the map's trees.
10
-
11
- Streets supplies architectural flood lighting and subtle model wall emission for its dark
12
- presets. These controls are independent of invented windows and the optional `nightLight` effects.
13
-
14
- Tileflow landmarks and a map's own building models can stand in the same 3D scene, replacing
15
- the buildings under their footprints.
16
-
17
- It is a custom WebGL2 layer for MapLibre GL JS, drawn above the map's buildings and below its
18
- labels. It needs WebGL2 and the Mercator projection, which MapLibre's globe turns into from about
19
- zoom 12. On every map the city comes in as the camera zooms in: placed models and landmarks first,
20
- fading in and rising from the ground between zooms 14.2 and 14.6, then the buildings between 15 and
21
- 15.3. Their shadows and contact shading follow their height. Zooming back out lowers and fades them
22
- again. With reduced motion (`prefers-reduced-motion`) they keep their height and only fade.
23
- `hide()` keeps them hidden at every zoom until `show()` is called.
24
-
25
- A map sets these bands with the buildings module's `threeDimensional.entrance`, each a
26
- `[from, to]` pair of zooms; a band left out keeps its default:
27
-
28
- ```ts
29
- buildings({threeDimensional: {entrance: {buildings: [15.5, 16], models: [14.5, 15]}}});
30
- ```
31
-
32
- The renderer reads its tiles from a zoom below its `minzoom`, so a band that starts earlier than
33
- that comes in from there.
34
-
35
- ## The map's 3D scene
36
-
37
- React, Vue and Svelte `Map` components show the 3D scene of every map whose style declares one, by
38
- default: buildings with Tileflow landmarks and the map's Team building models, 3D trees, and street
39
- lights where the map enables them. They load the 3D renderers from `@tileflow/core/3d` only when
40
- the style declares a scene, so a 2D map does not download them. Set `viewMode="2d"` (Vue:
41
- `view-mode="2d"`) to opt out: no 3D buildings or street lights are mounted, and vegetation follows
42
- its [display policy](trees-3d.md#visibility-and-representation) for the 2D view. Changing it back to
43
- `3d` shows the scene on the same map. React Native, image mode, Capture and Static Maps do not draw
44
- the scene.
45
-
46
- Applications that build their own MapLibre map mount the same scene with `createTileflowMap3D`.
47
- It mounts each renderer the style declares, follows theme and style changes, and releases a
48
- renderer when the style no longer declares it:
49
-
50
- <!-- docs:check -->
51
-
52
- ```ts
53
- import {
54
- createTileflowMap3D,
55
- type TileflowMap3DMap,
56
- type TileflowThemeController,
57
- } from '@tileflow/core/browser';
58
-
59
- export function showTheScene(map: TileflowMap3DMap, controller: TileflowThemeController) {
60
- const scene = createTileflowMap3D({map, controller});
61
- // Shown on creation; hide() and show() switch the whole scene, dispose() removes it.
62
- return scene;
63
- }
64
- ```
65
-
66
- `hide()` takes the buildings down and releases the street lights; `show()` brings them back. Both
67
- take the same transition as the buildings' `show()` and `hide()`. `setVegetation(false)` hides
68
- every tree and `setVegetation(undefined)` restores the map's default. To load the renderers only
69
- for maps that need them, call `attachTileflowMap3D({map, controller, load: () =>
70
- import('@tileflow/core/3d')})` from `@tileflow/core/browser`, as the framework components do; its
71
- `setViewMode()` and `setVegetation()` apply once the scene mounts.
72
-
73
- A MapLibre application that loads a compiled `style.json` directly gets no 3D scene: buildings,
74
- landmarks, models, trees and lights are custom WebGL layers added only by this runtime. Where a map
75
- draws a plain `fill-extrusion` building pass, the style ships it hidden and grouped under
76
- `metadata['tileflow:3d-toggle'] = 'building'`; showing those layers gives extruded footprints only.
77
-
78
- ## Decorative building surfaces
79
-
80
- `threeDimensional.surfaces` adds optional brick joints, roof tiles and a painted cornice to
81
- ordinary extruded buildings. These are artistic decoration, not surveyed materials. They keep
82
- the building's authored colour and use its existing lighting, shadows and footprint. Custom GLB
83
- models and landmarks retain their own materials. Leaving `surfaces` out preserves a smooth finish.
84
-
85
- ```ts
86
- buildings({
87
- threeDimensional: {
88
- surfaces: {
89
- walls: {kind: 'brick', size: [6, 3], jointWidth: 0.16, contrast: 0.28},
90
- roof: {kind: 'tiles', size: 10, jointWidth: 0.12, contrast: 0.12},
91
- cornice: {height: 0.7, contrast: 0.3},
92
- },
93
- },
94
- });
95
- ```
96
-
97
- Wall `size` is `[width, courseHeight]` in metres. Roof size is a square in projected Mercator
98
- metres: its ground size shrinks with latitude, while its grid remains fixed through camera moves
99
- and tile boundaries. Sizes accept 0.5–100; joints accept 0.01–2 and are limited to a quarter of the
100
- smallest cell dimension. `contrast` accepts 0–1, including theme number tokens; zero hides that
101
- finish. The defaults are `[6, 3]`, 0.12 and 0.2 for walls, and 8, 0.12 and 0.1 for roofs.
102
-
103
- The cornice is a light band with a subtle seam, not an overhanging mesh. Its `height` accepts
104
- 0.05–5 metres, defaults to 0.6 and is limited to one fifth of a wall's height; its contrast defaults
105
- to 0.2. The roof border uses projected metres and follows exterior and courtyard edges without
106
- drawing internal triangle edges. The shader filters joints and fades details smaller than a few
107
- pixels. No texture downloads, extra draw calls or per-brick geometry are required. These finishes
108
- belong to the WebGL 3D scene; portable 2D styles retain their ordinary building fill.
109
-
110
- ## Show the buildings
111
-
112
- This example assumes an existing MapLibre map showing a Tileflow map whose vector source is called
113
- `tileflow`, as Tileflow's maps call World, and a theme controller for it:
114
-
115
- <!-- docs:check -->
116
-
117
- ```ts
118
- import {
119
- createTileflowBuildings3D,
120
- type TileflowBuildings3DMap,
121
- type TileflowThemeController,
122
- } from '@tileflow/core/browser';
123
-
124
- export function raiseTheCity(map: TileflowBuildings3DMap, controller: TileflowThemeController) {
125
- const buildings = createTileflowBuildings3D({map, controller});
126
- // Enabled on creation; hide() and show() override visibility.
127
- return buildings;
128
- }
129
- ```
130
-
131
- `show()`, `hide()` and `grow(scale)` change the height immediately by default (duration 0).
132
- `show({transition: {duration: 800, mode: 'fade'}})` reveals full-height buildings with a
133
- smooth opacity fade. The default animation mode is `grow`; both modes respect reduced-motion settings.
134
- To animate, pass `{transition: {duration: 800}}` explicitly; `grow(scale)` accepts a height share
135
- from 0 to 1. Each returns a promise that resolves once the change ends; a newer call takes over from where the buildings stand. A browser preference for reduced
136
- motion changes at once. `dispose()` takes the layer off the map.
137
-
138
- The renderer reads the source's z15 tiles round the camera (z15 is where World's buildings carry
139
- their heights; a source that stops earlier is read at its own maximum zoom), up to `maxTiles`
140
- tiles (25 by default), and meshes them a few milliseconds per frame. It reads World's
141
- `render_height`, `render_min_height`, `hide_3d`, `height_source`, `building_tone`, `class`,
142
- `night_use` and the roof fields (see [roofs](#roofs)), and the places in World's `poi` layer (see
143
- [night light](#night-light)). `stats.declared` tells whether the map's
144
- style describes its 3D buildings. Recompile existing maps to pick up the new defaults: previously
145
- compiled metadata keeps explicit settings, including `false`, `0` and `lighting: 'style'`.
146
-
147
- ## Design the buildings in the map
148
-
149
- The map describes how its 3D buildings look, in its buildings module, and publishes it with its
150
- style for every theme; the renderer follows theme changes and blends. The design is visible in
151
- `tileflow preview` and validated with the rest of the map:
152
-
153
- <!-- docs:check -->
154
-
155
- ```ts
156
- import {defineMap, expr, field, refine, refineRenderTarget} from '@tileflow/core';
157
- import {streets} from '@tileflow/maps';
158
-
159
- const floors = expr.divide(expr.toNumber(expr.get(field('renderHeight')), 0), 3.2);
160
-
161
- export default defineMap({
162
- id: 'lit-city',
163
- name: 'Lit city',
164
- version: 1,
165
- extends: streets,
166
- modules: {
167
- buildings: refine({
168
- threeDimensional: {
169
- lighting: 'sun',
170
- shadows: true,
171
- occlusion: true,
172
- edges: 1,
173
- colorVariation: 0.06,
174
- windows: true,
175
- nightLight: 0.6,
176
- edgeColor: '#2b2a33',
177
- },
178
- renderStack: {
179
- // Core's 3D buildings take the colour of the map's 3D building pass.
180
- tints: refineRenderTarget({
181
- target: 'buildings.render.extrusion',
182
- renderer: 'extrusion',
183
- style: {
184
- color: expr.step(expr.modulo(floors, 3), '#e8d9c4', [
185
- [1, '#d6e2d8'],
186
- [2, '#e5d4e6'],
187
- ]),
188
- },
189
- }),
190
- },
191
- }),
192
- },
193
- });
194
- ```
195
-
196
- - Colour: each building takes the colour the map gives its 3D buildings: Streets' 3D building pass
197
- (the theme's `buildings.extrusion`), else the buildings module's extrusion, else its roofs. A
198
- colour that depends on the building's data is evaluated per building and theme when a tile or the
199
- set of themes changes, and the themes either side of a blend's position mix on the GPU, so a
200
- moving blend costs nothing per frame.
201
- - `threeDimensional.shadows` and `occlusion` (the band at the foot of the walls and the darker tight
202
- corners) default to true. Set either to false independently. `windows: true` adds invented night windows, lit by rooms; they are
203
- decoration, not data, and are off by default. Where World says what a building is used for
204
- (`class`), sheds, garages, barns, factories, churches and service buildings get no rows of
205
- windows, homes light more of theirs at night, in a warm light, and offices, schools and hospitals
206
- fewer, in a cooler one.
207
- - `threeDimensional.edges` and `edgeColor` set the [edges](#edges), and `nightLight` the
208
- [night light](#night-light). Numbers and colours may be theme tokens.
209
- - `threeDimensional.edgeRadius` rounds convex exterior corners in the mesh and softens the
210
- roof/wall lighting transition. It accepts 0–1 metres and defaults to 0.8; use 0 for sharp corners.
211
- Courtyard corners and tile cuts stay unchanged. The authored roof shape is preserved; the top
212
- edge is softened through lighting, not a geometric roof bevel. Rounding adds a bounded number
213
- of vertices at convex corners, with no extra render pass. Changes to this radius rebuild the
214
- resident building meshes.
215
-
216
- In both `style` and `sun` lighting, the vertical gradient darkens the foot of a facade by ten percent, fading smoothly over
217
- at most three metres (half the height for low buildings). It is evaluated per fragment, so a tall
218
- building does not acquire a full-height dark ramp. Disabling the authored extrusion's
219
- `verticalGradient` disables this shading. Ground contact shadow is controlled by `occlusion`, enabled by default. It extends three metres
220
- from grounded walls, darkening the ground by at most 16%, with a smooth distance falloff and round
221
- corners. Every map uses these defaults when it enables `occlusion`. The reach is measured in world
222
- metres, so its screen width scales continuously with zoom and becomes subtle at distant views.
223
- Overlaps use the strongest
224
- shade once, so shared walls, courtyards and tile joins do not accumulate dark bands. Raised building
225
- parts do not cast contact shade. Facade occlusion darkens the base by at most 14% and fades
226
- exponentially with a three-metre decay radius: about 5% remains one floor up and 2% two floors up.
227
- It follows height above ground, so raised parts do not restart the contact gradient at their base.
228
- The facade gradient, contact and inner-corner shade use the strongest contribution once, avoiding
229
- an extra dark strip where they overlap. Both lighting modes use the same falloff at every zoom.
230
- The ground mask uses one byte per drawing-buffer pixel while enabled and is released when disabled
231
- or the renderer is removed; it resizes with the drawing buffer.
232
-
233
- ## Landmarks
234
-
235
- Maps on Tileflow World show Tileflow's landmarks, such as the Eiffel Tower, as 3D models in place of
236
- the buildings under them. They come from a landmarks tileset kept apart from the map's data: each of
237
- its tiles carries the models standing in it, with their levels of detail and footprints, and each
238
- region's index names its tiles by their hash, with where each landmark stands and its footprint.
239
- Core reads only indexes in the `tileflow-landmark-region-v2` format; an index that names no
240
- footprints is not read, and its region shows no landmarks.
241
- The renderer reads the tileset's TileJSON, then only the indexes of the regions round the camera
242
- and the landmark tiles near it, from a zoom before the buildings show, so a landmark in Paris costs
243
- nothing in Madrid. New and improved landmarks reach every map without a new build of the map.
244
-
245
- The buildings under a landmark are never raised and then taken away. A tile of buildings is built
246
- once the indexes round it have arrived, waiting at most a second and a half; while a landmark's
247
- tile is still on its way, the footprint its index names keeps the buildings under it flat, and the
248
- landmark fades in when it arrives. A landmark tile that fails, or has not arrived three seconds
249
- after the buildings round it show, lets those buildings rise until it does. A tileset that cannot be read is tried again
250
- after five minutes, and the buildings stay drawn meanwhile.
251
-
252
- A map switches them off, or names the TileJSON of another landmarks tileset:
253
-
254
- <!-- docs:check -->
255
-
256
- ```ts
257
- import {defineMap, refine} from '@tileflow/core';
258
- import {streets} from '@tileflow/maps';
259
-
260
- export default defineMap({
261
- id: 'no-landmarks',
262
- name: 'Streets without landmarks',
263
- version: 1,
264
- extends: streets,
265
- modules: {
266
- buildings: refine({threeDimensional: {landmarks: false}}),
267
- },
268
- });
269
- ```
270
-
271
- The map's own building models come first: a landmark with the same id as one of them, or whose
272
- footprint meets one's, is not drawn.
273
-
274
- ## Building models
275
-
276
- A map can place its own 3D models of buildings in the city: a landmark the extruded footprints draw
277
- badly, or the buildings an application presents. Each model stands where the map says, in the same
278
- light, shadows and theme as the buildings round it, and the buildings under its footprint are no
279
- longer raised while it stands. Put glTF binaries (`.glb`) in a directory, declare it in the map's
280
- `models`, and say where each model stands:
281
-
282
- <!-- docs:check -->
283
-
284
- ```ts
285
- import {defineMap, fixed, refine} from '@tileflow/core';
286
- import {streets, streetsModels} from '@tileflow/maps';
287
-
288
- export default defineMap({
289
- id: 'paris-landmarks',
290
- name: 'Paris landmarks',
291
- version: 1,
292
- extends: streets,
293
- // ./models holds eiffel-tower.glb, and eiffel-tower.mid.glb and eiffel-tower.far.glb for
294
- // farther views.
295
- // Keep Streets' tree models alongside the building models.
296
- models: [streetsModels, './models'],
297
- modules: {
298
- buildings: refine({
299
- threeDimensional: {
300
- models: [{model: 'eiffel-tower', at: [2.294481, 48.858258], heading: 0}],
301
- modelColors: {
302
- surface: fixed('#7a5c45', {reason: "The tower's bronze paint"}),
303
- accent: fixed('#5b4332', {reason: "The tower's bronze paint, in shadow"}),
304
- },
305
- },
306
- }),
307
- },
308
- });
309
- ```
310
-
311
- `at` is the longitude and latitude of the model's origin and `heading` the degrees clockwise from
312
- north its own north is turned to. A map places up to 256 models; one model may stand in several
313
- places. A placement may instead name a prepared model file served elsewhere with `url`, its HTTPS
314
- address; the map then needs no `.glb` of its own for it.
315
-
316
- How to prepare a model:
317
-
318
- - **Materials name the parts.** Name each material after the part it is: an architectural part,
319
- `surface`, `secondary`, `glass`, `accent` or `roof`, or a part of the model's own, such as
320
- `water`, `vegetation`, `wood` or `stone`. A name is lower-case words joined by hyphens or
321
- underscores, at most 32 characters; an exporter's `.001` suffix is ignored, and a level names at
322
- most 16 parts. A model whose materials name only a tree's parts (`bark`, `leaves`, `fronds`,
323
- `stem`) is a
324
- [tree model](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/trees-3d.md)
325
- instead.
326
- - **The map colours parts by name.** A part takes the colour `modelColors` gives its name, so the
327
- model follows the theme from day to night; a part the map does not colour keeps its material's
328
- own colour. Like every visual colour, a `modelColors` entry is a theme token such as
329
- `token.color('buildings.modelRoof')`, or a literal wrapped in `fixed(value, {reason})`; vertex
330
- colours lighter or darker than the rest of their part shade it within that colour. Name pools
331
- `water`, planting `vegetation` and timber `wood` where they fit: Streets colours these from its
332
- water and tree colours, so a model that uses them follows the map without a colour of its own.
333
- - **Architectural roles control lighting.** A material may set
334
- `extras.tileflow.lightingRole` to `wall`, `door`, `roof`, `window`, `lamp`, `logo` or
335
- `unclassified`, whatever part it names. Without a role, `surface`, `secondary` and `accent` are
336
- walls, `glass` is a window, and `roof` is a roof; every other part is `unclassified`, so a pool
337
- or a planted bed never takes a window's light.
338
- - **Metres round an origin on the ground.** The origin is where the model stands, with the model
339
- above it and its own north along +y once read Z up; nothing may lie more than half a metre below
340
- it.
341
- - **What it stands in for.** A mesh whose material is named `footprint` is never drawn: it lays
342
- out on the ground what the model stands in for, such as a landmark's whole base. Without one, the
343
- model's own outline on the ground stands in, courtyards included.
344
- - **Levels of detail.** `<id>.mid.glb` and `<id>.far.glb`, when present, are drawn farther from the
345
- camera, measured to the centre of the model's bounds: a model 30 m or taller from 2 km and 5 km,
346
- one 80 m or more across from 1.5 km and 3.5 km, and any other from 1 km and 2.5 km. A model beside
347
- a low camera at a steep pitch keeps its near level however far the map's centre is, and a model
348
- near a threshold keeps its level until it is about 10% past it. A model casts its shadow with its
349
- next lighter level, and a model outside the view or the light's reach is not drawn.
350
- - **Budgets.** A level has at most 200,000 triangles and 200,000 vertices, and a map at most 64
351
- building models. Triangles of the default scene are read with their node transforms; textures,
352
- animations and compressed geometry (Draco or meshopt) are not.
353
-
354
- The build reads each model, reports a problem with the file and what to change, works out its
355
- footprint and writes it beside the style as a prepared model file: a glTF binary whose meshes are
356
- quantised (`KHR_mesh_quantization`) and compressed by meshoptimizer (`EXT_meshopt_compression`),
357
- which any glTF viewer opens. The renderer downloads a model only when it stands near the tiles round
358
- the camera, whether or not those tiles hold buildings, keeps the nearest dozen, and builds the tiles
359
- under it again without the buildings that
360
- reach more than half a metre into its footprint; a neighbour that only touches it stays. A tile of
361
- buildings near a model waits for it, at most a second and a half, so its buildings are not raised
362
- and then taken away, and the model fades in when it comes to stand. A model that cannot be
363
- downloaded leaves those buildings standing. Reading a prepared model file loads
364
- meshoptimizer's WebAssembly decoder, so a page with a Content Security Policy allows
365
- `'wasm-unsafe-eval'` in its `script-src`.
366
-
367
- Preparation also computes local geometric ambient visibility, independently of the model's
368
- colour. `modelOcclusion` sets its strength from zero to one (default `0.75`); `occlusion: false`
369
- disables it. Occluded cavities receive less ambient light while direct sun and dynamic shadows
370
- remain independent. Existing prepared files with fully exposed vertices retain their appearance.
371
- Reprepare their original GLBs to gain geometric occlusion. See
372
- [model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
373
- for authoring, sampling and costs.
374
-
375
- ## Team 3D models
376
-
377
- On Tileflow Hosted, a map's building models are its Team's 3D models: each is published once, under
378
- its name, and every map of the Team that places a model of that name draws it. Locally, the map
379
- draws its own `.glb` files, so the map is previewed and developed without an account.
380
-
381
- ```sh
382
- npx tileflow models publish ./models/eiffel-tower.glb --team @acme
383
- npx tileflow models status eiffel-tower --team @acme
384
- ```
385
-
386
- `tileflow models publish` prepares each building model as a build does and publishes it as the
387
- model's new current version; with no path, it publishes the building models of every map in the
388
- project's config. Publishing the bytes of the current version again changes nothing. A hosted map
389
- follows each model's current version: a new version, or `tileflow models restore`, which makes the
390
- previous version current again, changes every map that places it the next time the map loads,
391
- without deploying the maps again. A model keeps its current and previous versions.
392
-
393
- `tileflow deploy` writes each placement of a building model as the placeholder
394
- `tileflow://team-models/<name>` (`tileflowTeamModelUrl(name)`), which the deploy replaces with the
395
- model's address in the map's Team; a model the Team has not published fails the deploy. The deploy
396
- also reports a model whose published version was prepared from other files than the map's, so an
397
- outdated model is easy to spot. A model's file, and what Tileflow shows about it, can be read
398
- without decoding its meshes with `readTileflowModelFileSummary`. Models are served publicly and may
399
- be cached, so they must not hold secrets or restricted personal data.
400
-
401
- ## Roofs
402
-
403
- A building whose tiles carry a roof shape (World's `roof_shape`) gets that roof: `gabled`,
404
- `hipped`, `half_hipped`, `pyramidal`, `skillion`, `gambrel`, `mansard` and `saltbox` roofs, and
405
- faceted `dome`, `onion` and `round` ones. The ridge follows the building's long side, or
406
- `roof_orientation` and `roof_direction` when the tiles state them, and the walls rise to the roof,
407
- so gable ends are walls. Each slope takes the sun, shadows and edges like any face: the ridge and
408
- the hips are drawn as edges too.
409
-
410
- The roof's height is `roof_height` where the tiles state it, taken from the building's height.
411
- Otherwise the roof keeps the pitch of its shape (30° for a gabled or hipped roof, a half sphere for a
412
- dome); it stands on the walls when the height came from levels or a fallback, and reaches the
413
- building's height when that was measured (`height_source` `height`). A building without a roof
414
- shape, a flat roof, a building with courtyards or one far from a rectangle (an L, a U) keeps a flat
415
- roof, and so does a building a tile's buffer cuts short.
416
-
417
- ## Light and shadows
418
-
419
- Sun lighting and cast shadows are SDK defaults, including for Streets. Each map can choose its
420
- own finish. For native extrusion lighting, sharp corners and no shadows in an extending map:
421
-
422
- <!-- docs:check -->
423
-
424
- ```ts
425
- import {defineMap, refine} from '@tileflow/core';
426
- import {streets} from '@tileflow/maps';
427
-
428
- export default defineMap({
429
- id: 'graphic-streets',
430
- name: 'Graphic streets',
431
- version: 1,
432
- extends: streets,
433
- modules: {
434
- buildings: refine({
435
- threeDimensional: {lighting: 'style', edgeRadius: 0, shadows: false, occlusion: false},
436
- }),
437
- },
438
- });
439
- ```
440
-
441
- Select `timeOfDay` through your map adapter to change the authored direction, colour and strength
442
- of direct and ambient light. Streets includes moonlight and shadows in its night presets. These
443
- presets are independent of the user's clock and location. In Inspector, use the 3D view and
444
- **Map → Buildings → Building light / Cast shadows**. The theme's `lighting.sun.shadowIntensity`
445
- controls shadow strength; `lighting.ambient` fills the shade. Use `controller.setSun({date,
446
- location})` for an astronomical direction instead, as described in
447
- [sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md).
448
-
449
- `threeDimensional.lighting: 'style'` uses MapLibre's `light` position,
450
- anchor, colour and intensity, together with the authored extrusion's `verticalGradient`.
451
- Changing the scene sun does not change this base lighting. The style's native lighting defaults
452
- apply when it declares no light.
453
-
454
- With the default `lighting: 'sun'`, the light comes from `controller.getSun()`: the theme's sun, mixed during a blend, or the sun an
455
- application places by direction or by date and place (see
456
- [sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md)).
457
- Without a controller, the renderer reads the compiled style's sun, falling back to the style's
458
- light direction and then the SDK daylight. It refreshes this cached light on style changes.
459
- A sunlit roof keeps the building's colour, a wall turned to a low sun glows, and shade takes the
460
- sky's colour. Shadows lengthen as the sun lowers, with their projected direction limited to 15°
461
- above the horizon. The actual light retains its authored altitude; a sun below the horizon casts none.
462
-
463
- A wall sees half the sky, so at night, when the ground gives back little light, walls in shade
464
- stay darker than roofs. A theme's night `lighting.sun` set high (60° or more) lights roofs more than
465
- any wall, so roofs and walls read apart under the moon.
466
-
467
- MapLibre's own layers take no shadows, so the renderer darkens the map it is drawn over: after its
468
- buildings, it multiplies the ground by the shadows and by a band at the foot of the walls. Labels
469
- and icons drawn above it keep their colours. Layers kept under 3D content with
470
- `createTileflowGroundLayers` are darkened with the ground. Other Core 3D layers on the map, such as
471
- [3D trees](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/trees-3d.md), share
472
- the buildings' shadows when `shadows: true`. With it off, buildings neither cast nor receive
473
- scene shadows, even when another 3D layer uses them.
474
-
475
- ## Edges
476
-
477
- Where one building ends and the next starts stays legible at any distance: each roof's outline and
478
- each building's outer corners catch the light in a line one CSS pixel wide, whatever the zoom, when enabled. `colorVariation` independently varies building brightness by up to the specified
479
- fraction (0–1); it defaults to 0, so authored colours remain unchanged. Use 0.06 for the earlier
480
- 6% variation. The lines are strongest at night; diagonals inside a roof, inner corners and the cuts at
481
- a tile's edge draw none. `edges` sets their strength from 0 (the default, none) to 1.
482
-
483
- `edgeColor` gives the lines a colour of the map's own, per theme through a token: a dark outline as
484
- on a printed map, or a neon one at night. It is drawn as it is, unlit, at the full `edges` strength
485
- by day and night, mixed between the themes either side of a blend's position.
486
-
487
- ## Night light
488
-
489
- Architectural illumination has two independent controls:
490
-
491
- - `floodLight` selects ordinary buildings by height. Ground light starts at `minGroundHeight`
492
- (30 m by default), with a stable per-building `groundRadius` in metres (default `[30, 100]`).
493
- Wall light starts above `minWallHeight` (200 m); its reach is building height times
494
- `wallHeightRatio` (default `1/3`). `intensity` is zero to one and `color` is a theme colour.
495
- The effect fades with scene brightness; `brightnessScale` compensates an authored light-colour
496
- transform and defaults to one. Overlapping ground contributions use their strongest value.
497
- - `modelLighting` assigns physical materials and emission to architectural roles. Each profile accepts
498
- `emissiveStrength` (zero to five), optional `emissiveColor` (otherwise the surface's own colour), and
499
- `emissiveHeight`: `[beginHeight, endHeight, beginMultiplier, endMultiplier, curve]`.
500
- Heights are fractions of the source mesh's vertical bounds (the full model for legacy files),
501
- multipliers are zero to one, and the exponent is
502
- `10 ** curve`, with `curve` from -1 to 1. Equal heights give a constant multiplier of one.
503
- Every gradient component, strength and colour may use theme tokens. Emission is mixed in linear light and is independent
504
- of ambient occlusion and solar shadows. Values above one saturate the surface emission at one;
505
- they also control the falloff of explicitly authored area emitters.
506
-
507
- Physical profiles also accept `color`, `colorMix` (zero to one, default one when a tint exists),
508
- `roughness` and `metallic` (zero to one). Explicit `modelColors` override role tints. Missing
509
- physical overrides retain the glTF material. `emissiveVariation` adds a stable, model-seeded amount
510
- from zero to its value. Optional `colorVariation` supplies independent `[min, max]` ranges for
511
- `hue` (0–360), `saturation` and `lightness` (0–100), with `space: 'srgb'` (default) or `'linear'`.
512
- The same model identity keeps its samples across LODs and theme changes.
513
-
514
- Streets leaves ordinary flood lighting off in its light presets. Its dusk/night wall profile uses a strength
515
- of `0.1` and `[0.5, 0, 0, 1, 0.8]`, concentrating a warm wash near the foot. Monochrome uses a
516
- weaker neutral wall profile and no architectural ground flood. Prepared models without role
517
- metadata take each part's default role. Windows use metallic reflection and varied emission;
518
- doors and logos have separate profiles, and roofs remain non-emissive. Day/dawn profiles follow
519
- the scene's brighter material response. A role does not generate missing windows or ground emitters.
520
- See [building-model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
521
- for glTF channels, explicit area emitters, compatibility and resource costs.
522
-
523
- The separate `nightLight` control retains its decorative building and shop effects:
524
-
525
- With `nightLight` above 0, the buildings light the street from data at night (as the sun's
526
- `brightness` falls):
527
-
528
- - Landmarks (World's `building_tone` `destination`) and towers of 40 m or more, one in two, are
529
- flood-lit at their foot, on the wall and the ground around it.
530
- - Buildings that hold places busy at night light the windows of their ground floor and spill their
531
- light a few metres onto the pavement: bars and clubs in a neon of their own or a warm light,
532
- theatres and cinemas in a bright white, places to eat in amber. A building takes the strongest
533
- use of its places, nightlife before entertainment before dining. Walls round a courtyard stay
534
- dark.
535
-
536
- The night use comes from World's `night_use` on each building. In tiles whose buildings do not
537
- carry it, the renderer finds it in the tile's places: a place inside a building, or within a few
538
- metres of its facade, gives the building its use.
539
-
540
- `nightLight` sets how bright all of this shines, from 0 (none) to 2; it defaults to 0.
541
-
542
- For the streets themselves, see
543
- [street lights](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/street-lights.md),
544
- which draws over the buildings so they hide the lamps behind them; both layers share each tile's
545
- download.
546
-
547
- ## Options
548
-
549
- The renderer's own options say only how it is delivered: `minzoom` (15 by default; the default
550
- entrance bands start from it), `maxTiles`,
551
- `beforeId` to place the layer before another one, and `transformRequest` to apply the map's request
552
- rules to the tiles it reads.
553
-
554
- ## Cost
555
-
556
- During partial opacity, a depth-only geometry pass resolves the nearest surfaces before colour
557
- blending, preventing rear walls from bleeding through. At full opacity only the normal geometry
558
- pass runs; zoom fading schedules no extra animation frames or per-zoom mesh rebuilds.
559
-
560
- The browser bundle builds from the tiles' bytes in one worker that the page's 3D buildings, trees
561
- and street lights share, nearest tiles first: it decodes each tile once for all of them, meshes its
562
- buildings, keeps their indices to 16 bits when the vertices allow, lays out the walls' contact-band
563
- segments and transfers the finished arrays. The renderer uploads them to the GPU and keeps no copy;
564
- a style reloaded in full keeps the uploaded buffers. No worker URL or extra asset hosting is needed.
565
- A Content Security Policy must allow `worker-src blob:` to use this path; if workers are
566
- unavailable or blocked, the layers build in the page instead. The worker is terminated when the
567
- last of those layers is disposed. Tiles the camera has left are not downloaded or built. Pending tile updates retain
568
- the previous mesh until their replacement is ready; cancelled and obsolete results are discarded.
569
-
570
- Mercator rendering batches buildings spatially and skips batches outside the camera. Shadow
571
- casting uses the light's bounds independently, so an offscreen building can still cast a visible
572
- shadow. Ground contact and flood bands retain their physical reach when testing visibility.
573
- Rounded corners use fewer segments when their screen radius is small and retain the original
574
- tessellation up close. Latitude, radius and pitch inform this choice; very oblique views keep full
575
- detail. Changes are prepared asynchronously; heights, feature colours,
576
- and the contact-shadow settings stay the same. GPU uploads remain limited to two tiles per frame.
577
-
578
- The shared shadow map is at most 4096 × 4096. On a planar view it concentrates resolution on visible
579
- ground and raised surfaces, while every loaded caster can still project into that area. A snapped
580
- Mercator texel grid stays stable during small pans, including changes of latitude, without fixing
581
- the meshes' physical metre scale. One filtered depth comparison keeps edges sharp.
582
- Missing camera capabilities, terrain or a view near the horizon use the full loaded area instead.
583
- The depth pass runs again when its focused area, light or casting geometry changes and is reused
584
- when they stay unchanged. While the camera moves, the focused area takes a quarter of its span as a
585
- margin on each side and is kept while it still covers the view at a similar resolution, so the
586
- depth pass is not drawn on every frame; once the camera stops, it is fitted to the view again.
587
-
588
- Model ambient visibility and lighting roles retain the existing 16-byte vertex stride. They add
589
- no geometry pass or texture. Architectural ground lighting uses a separate one-byte-per-pixel
590
- mask and one geometry/composite pair of passes while active; its mask is released when inactive.
591
- The contact band and the architectural light draw their masks in the layer's prerender, before
592
- the map's main pass, so the map's framebuffer is never switched while its layers draw.
593
- At 1280 × 720 and DPR 1, the mask occupies 921,600 bytes, excluding driver overhead.
594
-
595
- ## Limits
596
-
597
- - Mercator only; the layer draws nothing below its `minzoom`.
598
- - No terrain: buildings stand on flat ground.
599
- - One building colour per feature and theme, on its roof and walls alike; facades are not drawn.
600
- - Roofs follow the building's smallest enclosing rectangle: a building with courtyards or far from a
601
- rectangle keeps a flat roof, and `sawtooth` roofs are drawn flat.
602
- - A colour expression is evaluated at zoom 16.
603
- - Building models and landmarks take no illuminated edges and cannot be picked or styled by state.
604
- - Model materials use semantic colours and role emission; physical roughness, metallic
605
- reflections and authored textures are not rendered.
606
- - A hosted map places its building models as its Team's 3D models: `tileflow deploy` uploads no
607
- model files (see [Team 3D models](#team-3d-models)). A deploy with `--with-native` does not take
608
- them, since MapLibre Native draws no models. Models named by `url` deploy as they are.