@tileflow/core 0.1.0-alpha.35 → 0.1.0-alpha.37
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 +28 -28
- package/dist/browser.d.ts +766 -419
- package/dist/browser.js +3176 -495
- package/dist/build.d.ts +3 -3
- package/dist/build.js +2947 -1618
- package/dist/config-reference.json +7165 -3899
- package/dist/{icon-composition-DSjIKmrK.d.ts → icon-composition-C5X3Q9Ya.d.ts} +622 -98
- package/dist/index.d.ts +322 -129
- package/dist/index.js +4368 -2142
- package/dist/{manifest-types-Bzxon3dx.d.ts → manifest-types-jrBdBrES.d.ts} +29 -8
- package/dist/manifest.d.ts +2 -2
- package/dist/native.js +57 -57
- package/dist/runtime.d.ts +1 -1
- package/dist/worker/buildings-3d-worker.global.js +1 -0
- package/docs/authoring-model.md +8 -3
- package/docs/boundary-areas-and-choropleths.md +217 -0
- package/docs/buildings-3d.md +203 -30
- package/docs/cartographic-defaults.md +142 -0
- package/docs/data-is-separate-from-design.md +66 -33
- package/docs/hosted-session-authorization.md +2 -4
- package/docs/map-and-terrain-examples.md +10 -3
- package/docs/nautical-marks.md +200 -0
- package/docs/public-api-and-browser-subpath.md +4 -1
- package/docs/street-lights.md +22 -6
- package/docs/sun-and-light.md +42 -2
- package/docs/themes-and-module-styles.md +66 -0
- package/docs/trees-3d.md +22 -9
- package/package.json +2 -2
package/docs/buildings-3d.md
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
# 3D buildings
|
|
2
2
|
|
|
3
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
|
|
5
|
-
and
|
|
6
|
-
|
|
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.
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
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.
|
|
10
13
|
|
|
11
14
|
Tileflow landmarks and a map's own building models can stand in the same 3D scene, replacing
|
|
12
15
|
the buildings under their footprints.
|
|
13
16
|
|
|
14
17
|
It is a custom WebGL2 layer for MapLibre GL JS, drawn above the map's buildings and below its
|
|
15
18
|
labels. It needs WebGL2 and the Mercator projection, which MapLibre's globe turns into from about
|
|
16
|
-
zoom 12
|
|
19
|
+
zoom 12. On every map, buildings and placed models appear automatically from zoom 15,
|
|
20
|
+
with opacity rising linearly from 0 at 15 to 1 at 15.25. Zooming back out reverses the fade.
|
|
21
|
+
Their height stays unchanged throughout; shadows and contact shading fade with them.
|
|
22
|
+
`hide()` keeps them hidden at every zoom until `show()` is called.
|
|
17
23
|
|
|
18
24
|
## Show the buildings
|
|
19
25
|
|
|
@@ -31,15 +37,14 @@ import {
|
|
|
31
37
|
|
|
32
38
|
export function raiseTheCity(map: TileflowBuildings3DMap, controller: TileflowThemeController) {
|
|
33
39
|
const buildings = createTileflowBuildings3D({map, controller});
|
|
34
|
-
//
|
|
35
|
-
void buildings.show();
|
|
40
|
+
// Enabled on creation; hide() and show() override visibility.
|
|
36
41
|
return buildings;
|
|
37
42
|
}
|
|
38
43
|
```
|
|
39
44
|
|
|
40
45
|
`show()`, `hide()` and `grow(scale)` change the height immediately by default (duration 0).
|
|
41
46
|
`show({transition: {duration: 800, mode: 'fade'}})` reveals full-height buildings with a
|
|
42
|
-
|
|
47
|
+
smooth opacity fade. The default animation mode is `grow`; both modes respect reduced-motion settings.
|
|
43
48
|
To animate, pass `{transition: {duration: 800}}` explicitly; `grow(scale)` accepts a height share
|
|
44
49
|
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
|
|
45
50
|
motion changes at once. `dispose()` takes the layer off the map.
|
|
@@ -51,7 +56,7 @@ tiles (25 by default), and meshes them a few milliseconds per frame. It reads Wo
|
|
|
51
56
|
`night_use` and the roof fields (see [roofs](#roofs)), and the places in World's `poi` layer (see
|
|
52
57
|
[night light](#night-light)). `stats.declared` tells whether the map's
|
|
53
58
|
style describes its 3D buildings. Recompile existing maps to pick up the new defaults: previously
|
|
54
|
-
compiled metadata
|
|
59
|
+
compiled metadata keeps explicit settings, including `false`, `0` and `lighting: 'style'`.
|
|
55
60
|
|
|
56
61
|
## Design the buildings in the map
|
|
57
62
|
|
|
@@ -108,24 +113,53 @@ export default defineMap({
|
|
|
108
113
|
set of themes changes, and the themes either side of a blend's position mix on the GPU, so a
|
|
109
114
|
moving blend costs nothing per frame.
|
|
110
115
|
- `threeDimensional.shadows` and `occlusion` (the band at the foot of the walls and the darker tight
|
|
111
|
-
corners) default to false. `windows: true` adds invented night windows, lit by rooms; they are
|
|
116
|
+
corners) default to true. Set either to false independently. `windows: true` adds invented night windows, lit by rooms; they are
|
|
112
117
|
decoration, not data, and are off by default. Where World says what a building is used for
|
|
113
118
|
(`class`), sheds, garages, barns, factories, churches and service buildings get no rows of
|
|
114
119
|
windows, homes light more of theirs at night, in a warm light, and offices, schools and hospitals
|
|
115
120
|
fewer, in a cooler one.
|
|
116
121
|
- `threeDimensional.edges` and `edgeColor` set the [edges](#edges), and `nightLight` the
|
|
117
122
|
[night light](#night-light). Numbers and colours may be theme tokens.
|
|
123
|
+
- `threeDimensional.edgeRadius` rounds convex exterior corners in the mesh and softens the
|
|
124
|
+
roof/wall lighting transition. It accepts 0–1 metres and defaults to 0.8; use 0 for sharp corners.
|
|
125
|
+
Courtyard corners and tile cuts stay unchanged. The authored roof shape is preserved; the top
|
|
126
|
+
edge is softened through lighting, not a geometric roof bevel. Rounding adds a bounded number
|
|
127
|
+
of vertices at convex corners, with no extra render pass. Changes to this radius rebuild the
|
|
128
|
+
resident building meshes.
|
|
129
|
+
|
|
130
|
+
In both `style` and `sun` lighting, the vertical gradient darkens the foot of a facade by ten percent, fading smoothly over
|
|
131
|
+
at most three metres (half the height for low buildings). It is evaluated per fragment, so a tall
|
|
132
|
+
building does not acquire a full-height dark ramp. Disabling the authored extrusion's
|
|
133
|
+
`verticalGradient` disables this shading. Ground contact shadow is controlled by `occlusion`, enabled by default. It extends three metres
|
|
134
|
+
from grounded walls, darkening the ground by at most 16%, with a smooth distance falloff and round
|
|
135
|
+
corners. Every map uses these defaults when it enables `occlusion`. The reach is measured in world
|
|
136
|
+
metres, so its screen width scales continuously with zoom and becomes subtle at distant views.
|
|
137
|
+
Overlaps use the strongest
|
|
138
|
+
shade once, so shared walls, courtyards and tile joins do not accumulate dark bands. Raised building
|
|
139
|
+
parts do not cast contact shade. Facade occlusion darkens the base by at most 14% and fades
|
|
140
|
+
exponentially with a three-metre decay radius: about 5% remains one floor up and 2% two floors up.
|
|
141
|
+
It follows height above ground, so raised parts do not restart the contact gradient at their base.
|
|
142
|
+
The facade gradient, contact and inner-corner shade use the strongest contribution once, avoiding
|
|
143
|
+
an extra dark strip where they overlap. Both lighting modes use the same falloff at every zoom.
|
|
144
|
+
The ground mask uses one byte per drawing-buffer pixel while enabled and is released when disabled
|
|
145
|
+
or the renderer is removed; it resizes with the drawing buffer.
|
|
118
146
|
|
|
119
147
|
## Landmarks
|
|
120
148
|
|
|
121
149
|
Maps on Tileflow World show Tileflow's landmarks, such as the Eiffel Tower, as 3D models in place of
|
|
122
150
|
the buildings under them. They come from a landmarks tileset kept apart from the map's data: each of
|
|
123
151
|
its tiles carries the models standing in it, with their levels of detail and footprints, and each
|
|
124
|
-
region's index names its tiles by their hash
|
|
125
|
-
|
|
126
|
-
near it,
|
|
127
|
-
|
|
128
|
-
|
|
152
|
+
region's index names its tiles by their hash, with where each landmark stands and its footprint.
|
|
153
|
+
The renderer reads the tileset's TileJSON, then only the indexes of the regions round the camera
|
|
154
|
+
and the landmark tiles near it, from a zoom before the buildings show, so a landmark in Paris costs
|
|
155
|
+
nothing in Madrid. New and improved landmarks reach every map without a new build of the map.
|
|
156
|
+
|
|
157
|
+
The buildings under a landmark are never raised and then taken away. A tile of buildings is built
|
|
158
|
+
once the indexes round it have arrived, waiting at most a second and a half; while a landmark's
|
|
159
|
+
tile is still on its way, the footprint its index names keeps the buildings under it flat, and the
|
|
160
|
+
landmark fades in when it arrives. A landmark tile that fails, or has not arrived three seconds
|
|
161
|
+
after the buildings round it show, lets those buildings rise until it does. A tileset that cannot be read is tried again
|
|
162
|
+
after five minutes, and the buildings stay drawn meanwhile.
|
|
129
163
|
|
|
130
164
|
A map switches them off, or names the TileJSON of another landmarks tileset:
|
|
131
165
|
|
|
@@ -196,6 +230,10 @@ How to prepare a model:
|
|
|
196
230
|
instead. Each part takes its colour from `modelColors`, or from the buildings' colour when the
|
|
197
231
|
design gives it none, so the model follows the theme from day to night; vertex colours lighter or
|
|
198
232
|
darker than the rest of their part shade it within that colour.
|
|
233
|
+
- **Architectural roles control lighting.** A material may set
|
|
234
|
+
`extras.tileflow.lightingRole` to `wall`, `door`, `roof`, `window`, `lamp`, `logo` or
|
|
235
|
+
`unclassified`. This preserves its palette slot. Without a role, `surface`, `secondary` and
|
|
236
|
+
`accent` are walls, `glass` is a window, and `roof` is a roof.
|
|
199
237
|
- **Metres round an origin on the ground.** The origin is where the model stands, with the model
|
|
200
238
|
above it and its own north along +y once read Z up; nothing may lie more than half a metre below
|
|
201
239
|
it.
|
|
@@ -212,12 +250,49 @@ The build reads each model, reports a problem with the file and what to change,
|
|
|
212
250
|
footprint and writes it beside the style as a prepared model file: a glTF binary whose meshes are
|
|
213
251
|
quantised (`KHR_mesh_quantization`) and compressed by meshoptimizer (`EXT_meshopt_compression`),
|
|
214
252
|
which any glTF viewer opens. The renderer downloads a model only when it stands near the tiles round
|
|
215
|
-
the camera, keeps the nearest dozen, and builds the tiles
|
|
216
|
-
|
|
217
|
-
|
|
253
|
+
the camera, whether or not those tiles hold buildings, keeps the nearest dozen, and builds the tiles
|
|
254
|
+
under it again without the buildings that
|
|
255
|
+
reach more than half a metre into its footprint; a neighbour that only touches it stays. A tile of
|
|
256
|
+
buildings near a model waits for it, at most a second and a half, so its buildings are not raised
|
|
257
|
+
and then taken away, and the model fades in when it comes to stand. A model that cannot be
|
|
258
|
+
downloaded leaves those buildings standing. Reading a prepared model file loads
|
|
218
259
|
meshoptimizer's WebAssembly decoder, so a page with a Content Security Policy allows
|
|
219
260
|
`'wasm-unsafe-eval'` in its `script-src`.
|
|
220
261
|
|
|
262
|
+
Preparation also computes local geometric ambient visibility, independently of the model's
|
|
263
|
+
colour. `modelOcclusion` sets its strength from zero to one (default `0.75`); `occlusion: false`
|
|
264
|
+
disables it. Occluded cavities receive less ambient light while direct sun and dynamic shadows
|
|
265
|
+
remain independent. Existing prepared files with fully exposed vertices retain their appearance.
|
|
266
|
+
Reprepare their original GLBs to gain geometric occlusion. See
|
|
267
|
+
[model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
|
|
268
|
+
for authoring, sampling and costs.
|
|
269
|
+
|
|
270
|
+
## Team 3D models
|
|
271
|
+
|
|
272
|
+
On Tileflow Hosted, a map's building models are its Team's 3D models: each is published once, under
|
|
273
|
+
its name, and every map of the Team that places a model of that name draws it. Locally, the map
|
|
274
|
+
draws its own `.glb` files, so the map is previewed and developed without an account.
|
|
275
|
+
|
|
276
|
+
```sh
|
|
277
|
+
npx tileflow models publish ./models/eiffel-tower.glb --team @acme
|
|
278
|
+
npx tileflow models status eiffel-tower --team @acme
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`tileflow models publish` prepares each building model as a build does and publishes it as the
|
|
282
|
+
model's new current version; with no path, it publishes the building models of every map in the
|
|
283
|
+
project's config. Publishing the bytes of the current version again changes nothing. A hosted map
|
|
284
|
+
follows each model's current version: a new version, or `tileflow models restore`, which makes the
|
|
285
|
+
previous version current again, changes every map that places it the next time the map loads,
|
|
286
|
+
without deploying the maps again. A model keeps its current and previous versions.
|
|
287
|
+
|
|
288
|
+
`tileflow deploy` writes each placement of a building model as the placeholder
|
|
289
|
+
`tileflow://team-models/<name>` (`tileflowTeamModelUrl(name)`), which the deploy replaces with the
|
|
290
|
+
model's address in the map's Team; a model the Team has not published fails the deploy. The deploy
|
|
291
|
+
also reports a model whose published version was prepared from other files than the map's, so an
|
|
292
|
+
outdated model is easy to spot. A model's file, and what Tileflow shows about it, can be read
|
|
293
|
+
without decoding its meshes with `readTileflowModelFileSummary`. Models are served publicly and may
|
|
294
|
+
be cached, so they must not hold secrets or restricted personal data.
|
|
295
|
+
|
|
221
296
|
## Roofs
|
|
222
297
|
|
|
223
298
|
A building whose tiles carry a roof shape (World's `roof_shape`) gets that roof: `gabled`,
|
|
@@ -236,16 +311,49 @@ roof, and so does a building a tile's buffer cuts short.
|
|
|
236
311
|
|
|
237
312
|
## Light and shadows
|
|
238
313
|
|
|
239
|
-
|
|
314
|
+
Sun lighting and cast shadows are SDK defaults, including for Streets. Each map can choose its
|
|
315
|
+
own finish. For native extrusion lighting, sharp corners and no shadows in an extending map:
|
|
316
|
+
|
|
317
|
+
<!-- docs:check -->
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import {defineMap, refine} from '@tileflow/core';
|
|
321
|
+
import {streets} from '@tileflow/maps';
|
|
322
|
+
|
|
323
|
+
export default defineMap({
|
|
324
|
+
id: 'graphic-streets',
|
|
325
|
+
name: 'Graphic streets',
|
|
326
|
+
version: 1,
|
|
327
|
+
extends: streets,
|
|
328
|
+
modules: {
|
|
329
|
+
buildings: refine({
|
|
330
|
+
threeDimensional: {lighting: 'style', edgeRadius: 0, shadows: false, occlusion: false},
|
|
331
|
+
}),
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Select `timeOfDay` through your map adapter to change the authored direction, colour and strength
|
|
337
|
+
of direct and ambient light. Streets includes moonlight and shadows in its night presets. These
|
|
338
|
+
presets are independent of the user's clock and location. In DevTools, use the 3D view and
|
|
339
|
+
**Map → Buildings → Building light / Cast shadows**. The theme's `lighting.sun.shadowIntensity`
|
|
340
|
+
controls shadow strength; `lighting.ambient` fills the shade. Use `controller.setSun({date,
|
|
341
|
+
location})` for an astronomical direction instead, as described in
|
|
342
|
+
[sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md).
|
|
343
|
+
|
|
344
|
+
`threeDimensional.lighting: 'style'` uses MapLibre's `light` position,
|
|
240
345
|
anchor, colour and intensity, together with the authored extrusion's `verticalGradient`.
|
|
241
346
|
Changing the scene sun does not change this base lighting. The style's native lighting defaults
|
|
242
347
|
apply when it declares no light.
|
|
243
348
|
|
|
244
|
-
With `lighting: 'sun'`, the light comes from `controller.getSun()`: the theme's sun, mixed during a blend, or the sun an
|
|
349
|
+
With the default `lighting: 'sun'`, the light comes from `controller.getSun()`: the theme's sun, mixed during a blend, or the sun an
|
|
245
350
|
application places by direction or by date and place (see
|
|
246
351
|
[sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md)).
|
|
352
|
+
Without a controller, the renderer reads the compiled style's sun, falling back to the style's
|
|
353
|
+
light direction and then the SDK daylight. It refreshes this cached light on style changes.
|
|
247
354
|
A sunlit roof keeps the building's colour, a wall turned to a low sun glows, and shade takes the
|
|
248
|
-
sky's colour.
|
|
355
|
+
sky's colour. Shadows lengthen as the sun lowers, with their projected direction limited to 15°
|
|
356
|
+
above the horizon. The actual light retains its authored altitude; a sun below the horizon casts none.
|
|
249
357
|
|
|
250
358
|
A wall sees half the sky, so at night, when the ground gives back little light, walls in shade
|
|
251
359
|
stay darker than roofs. A theme's night `lighting.sun` set high (60° or more) lights roofs more than
|
|
@@ -273,6 +381,42 @@ by day and night, mixed between the themes either side of a blend's position.
|
|
|
273
381
|
|
|
274
382
|
## Night light
|
|
275
383
|
|
|
384
|
+
Architectural illumination has two independent controls:
|
|
385
|
+
|
|
386
|
+
- `floodLight` selects ordinary buildings by height. Ground light starts at `minGroundHeight`
|
|
387
|
+
(30 m by default), with a stable per-building `groundRadius` in metres (default `[30, 100]`).
|
|
388
|
+
Wall light starts above `minWallHeight` (200 m); its reach is building height times
|
|
389
|
+
`wallHeightRatio` (default `1/3`). `intensity` is zero to one and `color` is a theme colour.
|
|
390
|
+
The effect fades with scene brightness; `brightnessScale` compensates an authored light-colour
|
|
391
|
+
transform and defaults to one. Overlapping ground contributions use their strongest value.
|
|
392
|
+
- `modelLighting` assigns physical materials and emission to architectural roles. Each profile accepts
|
|
393
|
+
`emissiveStrength` (zero to five), optional `emissiveColor` (otherwise the surface's own colour), and
|
|
394
|
+
`emissiveHeight`: `[beginHeight, endHeight, beginMultiplier, endMultiplier, curve]`.
|
|
395
|
+
Heights are fractions of the source mesh's vertical bounds (the full model for legacy files),
|
|
396
|
+
multipliers are zero to one, and the exponent is
|
|
397
|
+
`10 ** curve`, with `curve` from -1 to 1. Equal heights give a constant multiplier of one.
|
|
398
|
+
Every gradient component, strength and colour may use theme tokens. Emission is mixed in linear light and is independent
|
|
399
|
+
of ambient occlusion and solar shadows. Values above one saturate the surface emission at one;
|
|
400
|
+
they also control the falloff of explicitly authored area emitters.
|
|
401
|
+
|
|
402
|
+
Physical profiles also accept `color`, `colorMix` (zero to one, default one when a tint exists),
|
|
403
|
+
`roughness` and `metallic` (zero to one). Explicit `modelColors` override role tints. Missing
|
|
404
|
+
physical overrides retain the glTF material. `emissiveVariation` adds a stable, model-seeded amount
|
|
405
|
+
from zero to its value. Optional `colorVariation` supplies independent `[min, max]` ranges for
|
|
406
|
+
`hue` (0–360), `saturation` and `lightness` (0–100), with `space: 'srgb'` (default) or `'linear'`.
|
|
407
|
+
The same model identity keeps its samples across LODs and theme changes.
|
|
408
|
+
|
|
409
|
+
Streets leaves ordinary flood lighting off in its light presets. Its dusk/night wall profile uses a strength
|
|
410
|
+
of `0.1` and `[0.5, 0, 0, 1, 0.8]`, concentrating a warm wash near the foot. Monochrome uses a
|
|
411
|
+
weaker neutral wall profile and no architectural ground flood. Prepared models without role
|
|
412
|
+
metadata use their documented palette mapping. Windows use metallic reflection and varied emission;
|
|
413
|
+
doors and logos have separate profiles, and roofs remain non-emissive. Day/dawn profiles follow
|
|
414
|
+
the scene's brighter material response. A role does not generate missing windows or ground emitters.
|
|
415
|
+
See [building-model preparation](https://github.com/tileflow/tileflow-sdk/blob/main/packages/dev/docs/building-models.md)
|
|
416
|
+
for glTF channels, explicit area emitters, compatibility and resource costs.
|
|
417
|
+
|
|
418
|
+
The separate `nightLight` control retains its decorative building and shop effects:
|
|
419
|
+
|
|
276
420
|
With `nightLight` above 0, the buildings light the street from data at night (as the sun's
|
|
277
421
|
`brightness` falls):
|
|
278
422
|
|
|
@@ -297,15 +441,42 @@ download.
|
|
|
297
441
|
|
|
298
442
|
## Options
|
|
299
443
|
|
|
300
|
-
The renderer's own options say only how it is delivered: `minzoom` (
|
|
444
|
+
The renderer's own options say only how it is delivered: `minzoom` (15 by default, with a 0.25-zoom opacity ramp), `maxTiles`,
|
|
301
445
|
`beforeId` to place the layer before another one, and `transformRequest` to apply the map's request
|
|
302
446
|
rules to the tiles it reads.
|
|
303
447
|
|
|
304
448
|
## Cost
|
|
305
449
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
450
|
+
During partial opacity, a depth-only geometry pass resolves the nearest surfaces before colour
|
|
451
|
+
blending, preventing rear walls from bleeding through. At full opacity only the normal geometry
|
|
452
|
+
pass runs; zoom fading schedules no extra animation frames or per-zoom mesh rebuilds.
|
|
453
|
+
|
|
454
|
+
The browser bundle prepares building meshes in one worker per renderer and transfers the finished
|
|
455
|
+
vertex and index buffers. No worker URL or extra asset hosting is needed. A Content Security Policy
|
|
456
|
+
must allow `worker-src blob:` to use this path; if workers are unavailable or blocked, the renderer
|
|
457
|
+
falls back to local meshing. The worker is terminated on `dispose()`. Pending tile updates retain
|
|
458
|
+
the previous mesh until their replacement is ready; cancelled and obsolete results are discarded.
|
|
459
|
+
|
|
460
|
+
Mercator rendering batches buildings spatially and skips batches outside the camera. Shadow
|
|
461
|
+
casting uses the light's bounds independently, so an offscreen building can still cast a visible
|
|
462
|
+
shadow. Ground contact and flood bands retain their physical reach when testing visibility.
|
|
463
|
+
Rounded corners use fewer segments when their screen radius is small and retain the original
|
|
464
|
+
tessellation up close. Latitude, radius and pitch inform this choice; very oblique views keep full
|
|
465
|
+
detail. Changes are prepared asynchronously; heights, feature colours,
|
|
466
|
+
and the contact-shadow settings stay the same. GPU uploads remain limited to two tiles per frame.
|
|
467
|
+
|
|
468
|
+
The shared shadow map is at most 4096 × 4096. On a planar view it concentrates resolution on visible
|
|
469
|
+
ground and raised surfaces, while every loaded caster can still project into that area. A snapped
|
|
470
|
+
Mercator texel grid stays stable during small pans, including changes of latitude, without fixing
|
|
471
|
+
the meshes' physical metre scale. One filtered depth comparison keeps edges sharp.
|
|
472
|
+
Missing camera capabilities, terrain or a view near the horizon use the full loaded area instead.
|
|
473
|
+
The depth pass runs again when its focused area, light or casting geometry changes and is reused
|
|
474
|
+
when they stay unchanged.
|
|
475
|
+
|
|
476
|
+
Model ambient visibility and lighting roles retain the existing 16-byte vertex stride. They add
|
|
477
|
+
no geometry pass or texture. Architectural ground lighting uses a separate one-byte-per-pixel
|
|
478
|
+
mask and one geometry/composite pair of passes while active; its mask is released when inactive.
|
|
479
|
+
At 1280 × 720 and DPR 1, the mask occupies 921,600 bytes, excluding driver overhead.
|
|
309
480
|
|
|
310
481
|
## Limits
|
|
311
482
|
|
|
@@ -315,7 +486,9 @@ way kept the frame pacing of the same city drawn without shadows.
|
|
|
315
486
|
- Roofs follow the building's smallest enclosing rectangle: a building with courtyards or far from a
|
|
316
487
|
rectangle keeps a flat roof, and `sawtooth` roofs are drawn flat.
|
|
317
488
|
- A colour expression is evaluated at zoom 16.
|
|
318
|
-
- Building models and landmarks take no edges
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
489
|
+
- Building models and landmarks take no illuminated edges and cannot be picked or styled by state.
|
|
490
|
+
- Model materials use semantic colours and role emission; physical roughness, metallic
|
|
491
|
+
reflections and authored textures are not rendered.
|
|
492
|
+
- A hosted map places its building models as its Team's 3D models: `tileflow deploy` uploads no
|
|
493
|
+
model files (see [Team 3D models](#team-3d-models)). A deploy with `--with-native` does not take
|
|
494
|
+
them, since MapLibre Native draws no models. Models named by `url` deploy as they are.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Cartographic defaults
|
|
2
|
+
|
|
3
|
+
Start with the [Core guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md)
|
|
4
|
+
for installation and a complete map. Semantic modules supply a coordinated starting appearance;
|
|
5
|
+
module styles and owner-local render targets can replace those decisions.
|
|
6
|
+
|
|
7
|
+
## Roads and roundabouts
|
|
8
|
+
|
|
9
|
+
`roads({rampOrder: 'below-roads'})` draws ramp fills before receiving carriageways in the same
|
|
10
|
+
surface, bridge or tunnel band. This prevents a rounded ramp endpoint covering the receiving
|
|
11
|
+
road. Choose `'by-class'` to retain class-based fill order instead. Explicit render-stack moves
|
|
12
|
+
remain available; ramp fills have targets `roads.classes.<class>.<structure>.rampFill` when the
|
|
13
|
+
separate ramp order is active. A selector refinement replaces that target's complete filter, so
|
|
14
|
+
retain its ramp eligibility when writing a custom selector. Module class fill styles apply to
|
|
15
|
+
both passes; a refinement of a specific `fill` or `rampFill` target changes only that pass.
|
|
16
|
+
|
|
17
|
+
Core owns circular-road geometry. Physical inner and outer radii take precedence when provided
|
|
18
|
+
by the source. Otherwise the ring follows the enabled class's effective surface width, including
|
|
19
|
+
width treatments. The default border follows the actual casing width and gap. Hidden classes and
|
|
20
|
+
surface fills do not leave orphan rings. Ring colors remain semantic palette choices, independently
|
|
21
|
+
replaceable through `roundabouts.fill.strokeColor` and `roundabouts.casing.strokeColor`; they do
|
|
22
|
+
not copy arbitrary per-class color curves.
|
|
23
|
+
|
|
24
|
+
Metric scaling and base-2 width curves retain their native interpolation. Other width curves are
|
|
25
|
+
sampled into the shared geometry curve: numeric widths target 0.1-pixel error, stopping refinement
|
|
26
|
+
once intervals reach 1/32 zoom; very large widths can exceed that target. Feature-dependent width
|
|
27
|
+
expressions retain authored and integer zoom stops without adaptive refinement, keeping expression
|
|
28
|
+
size bounded. Their interpolation between stops is approximate, with no universal pixel-error bound.
|
|
29
|
+
Step changes complete at the authored zoom, with the transition confined to the preceding 0.000001
|
|
30
|
+
zoom interval. Explicit circle radius and stroke-width expressions bypass this sampling when exact
|
|
31
|
+
independent behavior is required.
|
|
32
|
+
|
|
33
|
+
`roundabouts.fallbackWidth` replaces only the fallback carriageway width in pixels;
|
|
34
|
+
`roundabouts.borderWidth` replaces the total extra width around the ring. Existing circle styles
|
|
35
|
+
can independently replace radius, stroke width, color, opacity and zoom range. For example, this
|
|
36
|
+
module fragment retains geometric defaults but gives rings a deliberate fixed border:
|
|
37
|
+
|
|
38
|
+
<!-- docs:check -->
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import {roads} from '@tileflow/core';
|
|
42
|
+
|
|
43
|
+
export const customRoads = roads({
|
|
44
|
+
rampOrder: 'by-class',
|
|
45
|
+
roundabouts: {borderWidth: 0},
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Ground surfaces
|
|
50
|
+
|
|
51
|
+
The land module draws broad cover and park surfaces before mapped grass, meadow, scrub and
|
|
52
|
+
flowerbed details. Parking, sports pitches, tracks, playgrounds and zoos follow those details so
|
|
53
|
+
they remain visible inside broad green polygons. Both Tileflow World and OpenMapTiles' combined
|
|
54
|
+
park source follow this order. These defaults do not change the map's colors, opacity or textures.
|
|
55
|
+
|
|
56
|
+
A thematic map can give a park tint more prominence through the existing render stack. This
|
|
57
|
+
module fragment is ready to use as `modules.land` in a map:
|
|
58
|
+
|
|
59
|
+
<!-- docs:check -->
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import {land, refineRenderTarget, withRenderStack} from '@tileflow/core';
|
|
63
|
+
|
|
64
|
+
export const thematicLand = withRenderStack(land(), {
|
|
65
|
+
parkTint: refineRenderTarget({
|
|
66
|
+
target: 'land.landcover.urbanPark.fill',
|
|
67
|
+
placement: 'above-land',
|
|
68
|
+
}),
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Refinements target contributions that the map actually draws. Disabling a target and retaining
|
|
73
|
+
a refinement for it is an authoring error. Disabling the whole module removes its contributions
|
|
74
|
+
and its render stack. The built-in surface order needs no target anchors and works when individual
|
|
75
|
+
surfaces are hidden.
|
|
76
|
+
|
|
77
|
+
## Street names on the ground or in the foreground
|
|
78
|
+
|
|
79
|
+
`labels({roadPlacement: 'foreground'})` is the default. Names draw above buildings and vegetation.
|
|
80
|
+
Use `roadPlacement: 'ground'` to draw them below buildings, with text aligned to the map surface.
|
|
81
|
+
Shields and junction numbers remain in the foreground. Ground placement does not require the
|
|
82
|
+
buildings module: street names still draw when buildings are disabled.
|
|
83
|
+
|
|
84
|
+
This module fragment keeps the normal language selection and road-class filters while opting
|
|
85
|
+
into ground names. Individual symbol styles can override the default text alignment:
|
|
86
|
+
|
|
87
|
+
<!-- docs:check -->
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import {labels} from '@tileflow/core';
|
|
91
|
+
|
|
92
|
+
export const groundLabels = labels({
|
|
93
|
+
roadPlacement: 'ground',
|
|
94
|
+
collisionPriority: 'navigation',
|
|
95
|
+
language: 'local',
|
|
96
|
+
roads: 'all',
|
|
97
|
+
styles: {
|
|
98
|
+
roads: {
|
|
99
|
+
primary: {text: {pitchAlignment: 'viewport'}},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
MapLibre offers foreground symbols first when resolving collisions. Ground names therefore yield
|
|
106
|
+
to POIs and other foreground symbols, even with `collisionPriority: 'navigation'`. Navigation
|
|
107
|
+
priority still orders the ground road classes relative to one another; it does not make their
|
|
108
|
+
collision priority independent of drawing order. Foreground placement retains the existing
|
|
109
|
+
navigation priority over ordinary POIs. Neither mode turns collision detection off.
|
|
110
|
+
|
|
111
|
+
Use a refinement on `labels.roads.<class>` to move an individual class after this default has been
|
|
112
|
+
applied. Symbol size, zoom range, spacing, priority, fonts and halos remain available in
|
|
113
|
+
`styles.roads`. The same names, language fallbacks and class selectors are used in both modes.
|
|
114
|
+
|
|
115
|
+
## Discover and override decisions
|
|
116
|
+
|
|
117
|
+
`tileflow describe labels --json` exposes the installed options, and `tileflow inspect --json`
|
|
118
|
+
shows resolved inheritance and merge provenance. `tileflow explain --inspection --json` adds
|
|
119
|
+
compiled semantic targets and physical provenance. These commands describe the authored map;
|
|
120
|
+
they do not rewrite explicit style choices.
|
|
121
|
+
|
|
122
|
+
`tileflow describe themes --json` lists the twelve required base color roles and composition
|
|
123
|
+
rules. A [single independent theme](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/themes-and-module-styles.md)
|
|
124
|
+
needs no time-of-day catalog. Optional color roles derive from that theme and accept explicit
|
|
125
|
+
replacements. Removing inherited lighting uses `reset()` inside `defineTheme`; it restores the
|
|
126
|
+
SDK fallback, while building shadows and occlusion have separate on/off controls.
|
|
127
|
+
|
|
128
|
+
`createStyleResult()` and `tileflow inspect compiled --json` include `report.decisions`: actual
|
|
129
|
+
color roles and selected road, label and 3D-building policies, each with its value, source,
|
|
130
|
+
reason and override guidance. Query `--path /report/decisions --limit 80` to bound this output.
|
|
131
|
+
`default` means an SDK fallback, `map` a value in the resolved map, `theme` a role defined by the
|
|
132
|
+
selected theme and `derived` an optional role computed by Core. Use config inspection's merge
|
|
133
|
+
provenance to distinguish inherited map values from leaf overrides. These decisions describe
|
|
134
|
+
module policies and the theme's color context; later module styles and render-stack refinements
|
|
135
|
+
can override them. They are not a trace of every final paint property or a visual-quality score.
|
|
136
|
+
Simple `createStyle()` and browser drawing do not collect this report.
|
|
137
|
+
|
|
138
|
+
Replace an inherited module with its constructor, patch it with `refine()`, restore an inherited
|
|
139
|
+
value with `reset()`, or remove the whole domain with `disable()`. See the
|
|
140
|
+
[authoring model](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/authoring-model.md)
|
|
141
|
+
for these composition rules. Capture contrasting designs and compare them at matching cameras to
|
|
142
|
+
check both the default result and intentional departures from it.
|
|
@@ -121,14 +121,14 @@ their source faces' shapes. Browser adapters load local faces before MapLibre. N
|
|
|
121
121
|
`glyphs` URL provider instead, which enumerates the exact comma-joined MapLibre request keys in
|
|
122
122
|
`fontStacks`.
|
|
123
123
|
|
|
124
|
-
Streets,
|
|
124
|
+
Streets, Soundings, and Blueprint each declare
|
|
125
125
|
`https://api.tileflow.dev/base/33d4de5e8086d9d629d67d3f39fedb87e23686c4c1ac653c27e2a52aee9d00b3/glyphs/{fontstack}/{range}.pbf`
|
|
126
126
|
with the exact `Noto Sans Regular` and `Noto Sans Bold` stacks. The full hash identifies one
|
|
127
127
|
published, immutable glyph collection independent of World data. The older `/fonts/...` endpoint
|
|
128
128
|
remains a mutable compatibility URL and does not make a resolved map byte-reproducible. The global
|
|
129
129
|
glyph `assetSetSha256` is not the same-domain value as the per-map `assetSetSha256` in
|
|
130
130
|
`build-manifest.json`.
|
|
131
|
-
Baedeker and Siegfried each own a packaged directory containing Cormorant Garamond Regular, SemiBold, and Italic.
|
|
131
|
+
Baedeker, 1777, and Siegfried each own a packaged directory containing Cormorant Garamond Regular, SemiBold, and Italic.
|
|
132
132
|
|
|
133
133
|
Name the official generation deliberately, or use another OpenMapTiles-compatible vector source:
|
|
134
134
|
|
|
@@ -212,48 +212,29 @@ export default defineMap({
|
|
|
212
212
|
marine: 'chart', // 'none' | 'bathymetry' | 'nautical' | 'chart'
|
|
213
213
|
modules: {
|
|
214
214
|
nautical: nautical({
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
lights: {minZoom: 12},
|
|
218
|
-
hazards: {minZoom: 12},
|
|
219
|
-
hazardAreas: {
|
|
220
|
-
fill: {opacity: fixed(0.1, {reason: 'Chart hazard wash is invariant'})},
|
|
221
|
-
},
|
|
222
|
-
wrecks: {minZoom: 12},
|
|
223
|
-
wreckAreas: {
|
|
224
|
-
outline: {opacity: fixed(0.6, {reason: 'Chart wreck outline is invariant'})},
|
|
225
|
-
},
|
|
226
|
-
reefs: {fill: {opacity: fixed(0.3, {reason: 'Chart reef wash is invariant'})}},
|
|
215
|
+
buoys: {minZoom: 12},
|
|
216
|
+
lights: {minZoom: 9},
|
|
227
217
|
navigationAreas: {
|
|
228
218
|
fill: {opacity: fixed(0.05, {reason: 'Chart navigation-area wash is invariant'})},
|
|
229
219
|
},
|
|
230
|
-
|
|
231
|
-
labels: {
|
|
232
|
-
navigationAreas: {minZoom: 11},
|
|
233
|
-
reefs: {minZoom: 13},
|
|
234
|
-
},
|
|
220
|
+
reefs: {fill: {opacity: fixed(0.3, {reason: 'Chart reef wash is invariant'})}},
|
|
221
|
+
labels: {navigationAreas: {minZoom: 12}},
|
|
235
222
|
}),
|
|
236
223
|
},
|
|
237
224
|
});
|
|
238
225
|
```
|
|
239
226
|
|
|
240
227
|
`bathymetry` resolves to vector source `tileflow-bathymetry` and supplies the 13-stop
|
|
241
|
-
`bathymetry-v1` band contract to `water(...)`. `nautical` resolves to `tileflow-nautical
|
|
242
|
-
`nautical(...)` module
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
Polygon reefs, navigation
|
|
249
|
-
areas, hazards, wrecks, and named/provider coverage have separate collision-aware labels under
|
|
250
|
-
`nautical({labels: {...}})`; unnamed coverage does not emit empty text.
|
|
228
|
+
`bathymetry-v1` band contract to `water(...)`. `nautical` resolves to `tileflow-nautical` at
|
|
229
|
+
`/tiles/nautical/tiles.json`; the `nautical(...)` module draws its `nautical-v1` buoys, beacons,
|
|
230
|
+
lighthouses, lights, hazards, wrecks, reefs, navigation areas, routes, cables, harbours, ports, and
|
|
231
|
+
a restrained coverage indication. `chart` enables both. Every map that draws Nautical declares a
|
|
232
|
+
"Not for navigation" notice, which the browser runtimes render on the map. The
|
|
233
|
+
[Nautical marks guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/nautical-marks.md)
|
|
234
|
+
describes the schema, the semantic groups, the theme image tokens, and the notice.
|
|
251
235
|
Core does not hard-code a nautical provider or attribution: the selected TileJSON owns its actual
|
|
252
236
|
coverage and attribution, and derived vector tiles are not represented as a certified ENC for
|
|
253
|
-
navigation.
|
|
254
|
-
`reef`, `navigation_area`, and `coverage` as polygons; and both point and polygon geometries for
|
|
255
|
-
`hazard` and `wreck`. Point symbols and polygon areas compile separately, without fabricated
|
|
256
|
-
centroids.
|
|
237
|
+
navigation. Point symbols and polygon areas compile separately, without fabricated centroids.
|
|
257
238
|
Advanced callers can override either TileJSON URL, attribution, or source ID and can set the other
|
|
258
239
|
member to `false`:
|
|
259
240
|
|
|
@@ -408,6 +389,58 @@ labels default to line placement. Band fills still consume `min_depth` and `sort
|
|
|
408
389
|
`visible: false` to hide either. Without their declaration, those styles emit no name layers.
|
|
409
390
|
Name styles may use `class` in expressions without changing the source classification.
|
|
410
391
|
|
|
392
|
+
Mixed geometry can opt into `placementByGeometry` on either name style. Its resolved policy must
|
|
393
|
+
include `point`, `line`, and `polygon`; `point` must be `'point'`, while the other two accept
|
|
394
|
+
`'point'`, `'line'`, or `'line-center'`. The policy overrides scalar `placement`, including an
|
|
395
|
+
inherited value. Without it, scalar placement and existing generated layers remain unchanged.
|
|
396
|
+
|
|
397
|
+
For example, this map labels point features at their coordinates and retains line placement for
|
|
398
|
+
lines and polygon boundaries:
|
|
399
|
+
|
|
400
|
+
<!-- docs:check -->
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
import {bathymetry, defineMap, labels, water} from '@tileflow/core';
|
|
404
|
+
import {streets} from '@tileflow/maps';
|
|
405
|
+
|
|
406
|
+
export default defineMap({
|
|
407
|
+
id: 'marine-names',
|
|
408
|
+
version: 1,
|
|
409
|
+
extends: streets,
|
|
410
|
+
marine: {
|
|
411
|
+
bathymetry: bathymetry({
|
|
412
|
+
vectorLayers: {landforms: {zoomRanges: [{minZoom: 0, maxZoom: 24}]}},
|
|
413
|
+
}),
|
|
414
|
+
nautical: false,
|
|
415
|
+
},
|
|
416
|
+
modules: {
|
|
417
|
+
labels: labels({language: 'es'}),
|
|
418
|
+
water: water({
|
|
419
|
+
bathymetryLandforms: {
|
|
420
|
+
placementByGeometry: {point: 'point', line: 'line', polygon: 'line'},
|
|
421
|
+
},
|
|
422
|
+
}),
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
This example declares source availability; the selected source must actually contain those
|
|
428
|
+
features. Equal placements share one geometry-filtered group per source range. A policy emits
|
|
429
|
+
at most three groups per range (384 layers for one name family at the 128-range limit). Groups
|
|
430
|
+
compile in `point`, `line`, `line-center` order below ordinary map labels; source priority orders
|
|
431
|
+
features within each group. Module refinements inherit the policy and style every group.
|
|
432
|
+
Render-stack refinements still address exact semantic targets; inspect each placement/range
|
|
433
|
+
target when applying a narrower refinement.
|
|
434
|
+
Normal collision still applies and can hide a name.
|
|
435
|
+
|
|
436
|
+
Placement uses the received geometry: `point` places a line label at its first rendered vertex;
|
|
437
|
+
it does not compute a stable line midpoint. Polygon `point` placement uses an interior position
|
|
438
|
+
of the rendered polygon, while `line` follows its boundary. Core does not generate source anchors,
|
|
439
|
+
merge identities or deduplicate names by text. A source can supply multiple stable point anchors
|
|
440
|
+
with shared identity properties and distinct anchor properties; their extent, visibility and
|
|
441
|
+
editorial policy belong to that source. Keep original source identities as properties rather than
|
|
442
|
+
assuming a universal numeric `feature.id`.
|
|
443
|
+
|
|
411
444
|
Water-module styles override product-level `bands`, `contours`, and `labels`. Existing custom
|
|
412
445
|
`text.field` expressions remain authoritative: when refining a map from legacy polygons to native
|
|
413
446
|
contours, use `expr.get(field('bathymetryContourDepth'))` for its depth. That auxiliary binding
|