@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.
- package/README.md +51 -69
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/dist/{3d-CzLTD0RK.d.ts → 3d-CW5Ls0An.d.ts} +32 -2
- package/dist/3d.d.ts +1 -1
- package/dist/3d.js +337 -34
- package/dist/browser.d.ts +119 -7
- package/dist/browser.js +754 -61
- package/dist/build.d.ts +8 -4
- package/dist/build.js +284 -4
- package/dist/config-reference.json +4005 -2284
- package/dist/{icon-composition-HUh_Lj8Z.d.ts → icon-composition-Bbw9umUA.d.ts} +46 -3
- package/dist/index.d.ts +22 -5
- package/dist/index.js +254 -6
- package/dist/{manifest-types-DnhSJjCM.d.ts → manifest-types-CwjHd4qc.d.ts} +10 -0
- package/dist/manifest.d.ts +2 -2
- package/dist/native-profile.d.ts +4 -0
- package/dist/native-profile.js +2 -0
- package/dist/native.d.ts +87 -1
- package/dist/native.js +980 -460
- package/dist/runtime.d.ts +1 -1
- package/dist/worker/camera-tile-worker.global.js +2 -2
- package/package.json +4 -5
- package/docs/application-tile-reading.md +0 -86
- package/docs/authoring-model.md +0 -269
- package/docs/boundary-areas-and-choropleths.md +0 -217
- package/docs/buildings-3d.md +0 -608
- package/docs/camera-moves-and-tile-cache.md +0 -112
- package/docs/capture-scenes.md +0 -40
- package/docs/capture-shots.md +0 -173
- package/docs/cartographic-defaults.md +0 -210
- package/docs/compiled-style-performance.md +0 -286
- package/docs/data-is-separate-from-design.md +0 -610
- package/docs/depth-markers.md +0 -65
- package/docs/dynamic-style-images.md +0 -100
- package/docs/globe-atmosphere.md +0 -113
- package/docs/hosted-session-authorization.md +0 -106
- package/docs/map-and-terrain-examples.md +0 -156
- package/docs/map-emphasis.md +0 -89
- package/docs/native-artifact-profile.md +0 -152
- package/docs/native-resource-urls.md +0 -369
- package/docs/nautical-marks.md +0 -308
- package/docs/public-api-and-browser-subpath.md +0 -101
- package/docs/route-drawing.md +0 -191
- package/docs/shared-visual-primitives.md +0 -101
- package/docs/street-lights.md +0 -127
- package/docs/street-sky.md +0 -156
- package/docs/style-states.md +0 -73
- package/docs/sun-and-light.md +0 -167
- package/docs/theme-transitions-and-blends.md +0 -317
- package/docs/themes-and-module-styles.md +0 -156
- package/docs/trees-3d.md +0 -295
package/docs/buildings-3d.md
DELETED
|
@@ -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.
|