@tileflow/core 0.1.0-alpha.42 → 0.1.0-alpha.44

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 (50) hide show
  1. package/README.md +56 -69
  2. package/THIRD_PARTY_NOTICES.md +3 -3
  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 +425 -50
  6. package/dist/browser.d.ts +95 -5
  7. package/dist/browser.js +866 -101
  8. package/dist/build.d.ts +8 -4
  9. package/dist/build.js +250 -3
  10. package/dist/config-reference.json +3395 -2140
  11. package/dist/{icon-composition-B1A2udKb.d.ts → icon-composition-Bbw9umUA.d.ts} +37 -2
  12. package/dist/index.d.ts +13 -4
  13. package/dist/index.js +193 -3
  14. package/dist/{manifest-types-B0lQjabX.d.ts → manifest-types-CwjHd4qc.d.ts} +8 -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.js +789 -462
  19. package/dist/runtime.d.ts +1 -1
  20. package/dist/worker/camera-tile-worker.global.js +2 -2
  21. package/package.json +5 -6
  22. package/docs/application-tile-reading.md +0 -86
  23. package/docs/authoring-model.md +0 -269
  24. package/docs/boundary-areas-and-choropleths.md +0 -230
  25. package/docs/buildings-3d.md +0 -624
  26. package/docs/camera-moves-and-tile-cache.md +0 -112
  27. package/docs/capture-scenes.md +0 -40
  28. package/docs/capture-shots.md +0 -173
  29. package/docs/cartographic-defaults.md +0 -214
  30. package/docs/compiled-style-performance.md +0 -286
  31. package/docs/data-is-separate-from-design.md +0 -610
  32. package/docs/depth-markers.md +0 -65
  33. package/docs/dynamic-style-images.md +0 -100
  34. package/docs/globe-atmosphere.md +0 -113
  35. package/docs/hosted-session-authorization.md +0 -106
  36. package/docs/map-and-terrain-examples.md +0 -156
  37. package/docs/map-emphasis.md +0 -89
  38. package/docs/native-artifact-profile.md +0 -152
  39. package/docs/native-resource-urls.md +0 -369
  40. package/docs/nautical-marks.md +0 -308
  41. package/docs/public-api-and-browser-subpath.md +0 -101
  42. package/docs/route-drawing.md +0 -191
  43. package/docs/shared-visual-primitives.md +0 -101
  44. package/docs/street-lights.md +0 -127
  45. package/docs/street-sky.md +0 -156
  46. package/docs/style-states.md +0 -73
  47. package/docs/sun-and-light.md +0 -167
  48. package/docs/theme-transitions-and-blends.md +0 -317
  49. package/docs/themes-and-module-styles.md +0 -156
  50. package/docs/trees-3d.md +0 -295
@@ -1,101 +0,0 @@
1
- # Shared visual primitives
2
-
3
- Start with the [@tileflow/core guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md) for installation and a complete first example.
4
-
5
- The same small visual language is reused across every geographic domain. This keeps an agent from
6
- having to learn different spellings for the same MapLibre behavior:
7
-
8
- - `BackgroundStyle`: color, opacity, pattern, visibility, and zoom range.
9
- - `FillStyle`: color, opacity, pattern, antialiasing, visibility, and zoom range.
10
- - `LineStyle`: color, width, opacity, dash, blur, gap, offset, pattern, caps, joins, and zoom range.
11
- - `LineHatchStyle`: repeated diagonal detail with color, opacity, spacing, size, angle, and zoom
12
- range for a road structure.
13
- - `TextStyle`: field, exact font face, exact fallback faces, size, color, halo, line-fitted
14
- background, spacing metrics, transform, collision policy, rotation, fixed/variable anchoring, line
15
- constraints, and zoom range.
16
- - `IconStyle`: image, size, color, halo, rotation/alignment, collision policy, and zoom range.
17
- - `CircleStyle`: radius, fill/stroke appearance, blur, opacity, pitch behavior, and zoom range.
18
- - `ExtrusionStyle`: color, height, base, opacity, pattern, vertical gradient, and zoom range.
19
-
20
- Compounds express common cartographic structures: `AreaStyle` has `fill` and `outline`,
21
- `LineStackStyle` has `shadow`, `casing`, and `fill`, and `SymbolStyle` combines placement with
22
- optional `text`, `icon`, and `marker`. Geographic selection remains owned by semantic modules;
23
- ordinary visual styles intentionally do not accept raw source filters.
24
-
25
- ## Symbol overlap modes
26
-
27
- `text.allowOverlap` and `icon.allowOverlap` either hide a part that collides with a symbol placed
28
- before it or always show it. `overlap` selects one of three modes instead:
29
-
30
- - `never`: hide the part when it collides.
31
- - `always`: show the part even when it collides.
32
- - `cooperative`: show the part unless it collides with a symbol placed with `never`. It may overlap
33
- symbols placed with `always` or `cooperative`.
34
-
35
- MapLibre places symbols from the top layer down, so a symbol placed before another is usually higher
36
- in the stack. `overlap` takes precedence over `allowOverlap` on the same part, including an
37
- `allowOverlap` default supplied by a module or already present on a refined target, so the compiled
38
- layer carries only `text-overlap` or `icon-overlap`. MapLibre Native has no overlap modes, so native
39
- artifact preparation replaces the mode with the matching `text-allow-overlap` or
40
- `icon-allow-overlap`: `false` for `never` and `true` for `always` and `cooperative`. There a
41
- cooperative part no longer gives way to symbols placed with `never`. The
42
- [canopy grove recipe](data-is-separate-from-design.md) uses a cooperative text to decide where
43
- icons draw.
44
-
45
- ## Line-fitted text backgrounds
46
-
47
- `text.background` draws one opaque strip behind each shaped line of a label, fitted to that line's
48
- own width. Use it for captions whose lines differ in length, where a stretched icon plate
49
- (`icon.textFit`) would cover the whole block.
50
-
51
- <!-- docs:check -->
52
-
53
- ```ts
54
- import {fixed, poi, zoom} from '@tileflow/core';
55
-
56
- export const captions = poi({
57
- categories: ['arts-entertainment'],
58
- styles: {
59
- 'arts-entertainment': {
60
- text: {
61
- background: {
62
- color: fixed('#080E11', {reason: 'Caption strip'}),
63
- fit: 'lines',
64
- padding: 0.33,
65
- },
66
- color: fixed('#ECEEE9', {reason: 'Caption text'}),
67
- font: fixed('Noto Sans Bold', {reason: 'Caption face'}),
68
- letterSpacing: fixed(0.08, {reason: 'Caption tracking'}),
69
- lineHeight: fixed(1.2, {reason: 'Caption leading'}),
70
- size: zoom.linear([
71
- [14, 11],
72
- [18, 14],
73
- ]),
74
- },
75
- },
76
- },
77
- });
78
- ```
79
-
80
- Tileflow lowers the background to MapLibre properties only. `text-font` becomes a derived stack,
81
- such as `Noto Sans Bold lines-v1-t80-l1200-p330`. Its glyphs keep the source advances and shapes and
82
- add a rectangular cell, which the text halo paints. The halo width tracks `text-size / 6`, including
83
- zoom curves. Shaping, wrapping, collision, variable anchors, fading and right-to-left text therefore
84
- stay native, and the compiled style needs no runtime extension.
85
-
86
- - The colour must be opaque. Neighbouring cells overlap where they join.
87
- - `font` must name one stack served by the map's `glyphs` provider, without `fallbacks`. Maps that
88
- use browser-rendered `fonts` reject backgrounds.
89
- - `letterSpacing` and `lineHeight` must be constants. The derived stack encodes them in thousandths
90
- of an em, and the layer uses those rounded values. Tracking runs from 0 to 0.5em, leading from 0.8
91
- to 3 and padding from 0 to 1em.
92
- - The background replaces the text halo. While a background is active, `haloWidth` and `haloBlur`
93
- are derived, and `haloColor` is the strip colour. Later refinements of size, tracking, leading or
94
- font re-derive the stack and halo.
95
- - Collision still reserves the shaped block. Padding beyond it is drawn outside that box, so keep
96
- `padding` (text padding) at least as large as the strip padding in pixels.
97
-
98
- The map's `glyphs` provider must serve derived stacks. `deriveTileflowLineBackgroundGlyphs`
99
- computes any range from the source stack's range, and `parseTileflowLineBackgroundFontStack`
100
- recognises derived requests. Local previews and captures derive ranges themselves; hosted and
101
- self-hosted providers must do the same.
@@ -1,127 +0,0 @@
1
- # Street lights
2
-
3
- `createTileflowStreetLights` lights a Tileflow map's streets at night from its own vector tiles. The
4
- lamps the map's data holds shine where they stand, each with a pool of light on the ground and a
5
- glowing head. Streets and pavements the data maps as lit, but whose lamps it does not hold, take a
6
- soft light along them that claims no lamp's place. As the theme controller's sun darkens the map,
7
- the lights switch on one by one; they go out as day comes.
8
-
9
- Street lights are off by default in the SDK and in Streets. A compiled map must explicitly set
10
- `roads.streetLights.enabled: true` before the renderer or preview will show lamp glows.
11
-
12
- React, Vue and Svelte maps light a map that enables street lights automatically, as part of its
13
- [3D scene](buildings-3d.md#the-maps-3d-scene); the 2D view has no street lights.
14
-
15
- It is a custom WebGL2 layer for MapLibre GL JS, drawn above the map's buildings and below its
16
- labels. When `createTileflowBuildings3D` raises the buildings, they hide the lamps behind them. It
17
- needs WebGL2 and the Mercator projection, and shines from zoom 14 by default.
18
-
19
- ## Light the streets
20
-
21
- This example assumes an existing MapLibre map showing a Tileflow map whose vector source is called
22
- `tileflow`, as Tileflow's maps call World, with `roads.streetLights.enabled: true` in its map
23
- definition and a theme controller for it:
24
-
25
- <!-- docs:check -->
26
-
27
- ```ts
28
- import {
29
- createTileflowStreetLights,
30
- type TileflowStreetLightsMap,
31
- type TileflowThemeController,
32
- } from '@tileflow/core/browser';
33
-
34
- export function lightTheStreets(map: TileflowStreetLightsMap, controller: TileflowThemeController) {
35
- // The lights switch on as the theme's or the application's sun darkens the map.
36
- return createTileflowStreetLights({map, controller});
37
- }
38
- ```
39
-
40
- The lights follow `controller.getSun()`: they switch on as its `brightness` falls through dusk, each
41
- lamp at its own moment, and are all on at night. A theme whose night has no `lighting.sun` keeps
42
- the default daylight sun, so its lights stay off; see
43
- [sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md).
44
- `on` reports how far they have switched on, from 0 to 1, `stats` the lamps on screen, and
45
- `dispose()` takes the layer off the map.
46
-
47
- ## What the light comes from
48
-
49
- The layer reads the source's z15 tiles round the camera, the zoom at which World carries its street
50
- detail:
51
-
52
- - Lamps from World's `street_furniture` points whose `subclass` is `street_lamp`. Where the data
53
- gives them, a lamp's `height`, `lamp_mount`, `light_count`, `lamp_type` and `light_colour` set how
54
- high it stands, how far its light reaches and its colour: sodium lamps shine orange, LED and
55
- metal-halide lamps white. Other lamps take the map's light colour.
56
- - Streets in World's `transportation` layer, and pavements in its `sidewalk` layer, that the data
57
- maps as lit (`lit`). A street mapped as unlit never takes light.
58
-
59
- How much of this a city's data holds varies widely: some map most of their lamps or most of their
60
- lit streets, others almost none. Where the data says nothing, the streets stay dark.
61
-
62
- ## Design the lights in the map
63
-
64
- The map describes its street lights in its roads module, next to the other details drawn from
65
- street furniture, and publishes them with its style for every theme; `stats.declared` tells whether
66
- it does. Without an enabled description the renderer stays off and mounts no layer:
67
-
68
- <!-- docs:check -->
69
-
70
- ```ts
71
- import {defineMap, fixed, refine} from '@tileflow/core';
72
- import {streets} from '@tileflow/maps';
73
-
74
- export default defineMap({
75
- id: 'night-city',
76
- name: 'Night city',
77
- version: 1,
78
- extends: streets,
79
- modules: {
80
- // Sodium-orange light, a little brighter, on every street of tiles that map few lamps.
81
- roads: refine({
82
- streetLights: {
83
- enabled: true,
84
- color: fixed('#ffb35c', {reason: 'Constant sodium-lamp color.'}),
85
- intensity: 1.3,
86
- estimate: 'streets',
87
- },
88
- }),
89
- },
90
- });
91
- ```
92
-
93
- `estimate` chooses where the layer adds light the data does not map as lamps:
94
-
95
- - `lit` (the default): along the streets and pavements mapped as lit.
96
- - `streets`: also along streets the data says nothing about, in tiles where the data maps few
97
- lamps. This invents light, as night windows do, for maps that want every street lit; a tile whose
98
- data maps its lamps keeps to them.
99
- - `none`: only mapped lamps shine.
100
-
101
- Estimated light keeps to the middle of its street and shows no lamp head, so it reads as a lit
102
- street rather than as lamps that may not stand there.
103
-
104
- `color` is the light of the lamps whose data gives no colour, a warm white by default, and
105
- `intensity` scales the light from 0 to 2 (1 by default); both may be theme tokens and mix during a
106
- blend. These settings apply only after `enabled: true`; `streetLights: {enabled: false}` gives
107
- the map none.
108
-
109
- ## Options
110
-
111
- The layer follows the compiled map by default. `createTileflowStreetLights({map, controller,
112
- enabled: true})` explicitly enables the fallback design for a style without street-light metadata;
113
- `enabled: false` keeps the renderer off even when the map declares lights.
114
-
115
- The delivery options are `minzoom` (14 by default) and `maxTiles` (25
116
- by default, at most 64) bound where and how far the lights shine, `beforeId` places the layer before
117
- another one, and `transformRequest` applies the map's request rules to the tiles it reads.
118
-
119
- Layers that read the same source share each tile's download, so the street lights and the 3D
120
- buildings fetch a tile once between them. The lamps are placed in the worker the page's 3D layers
121
- share, which decodes each tile once for all of them.
122
-
123
- ## Limits
124
-
125
- - Mercator only; the layer draws nothing below its `minzoom`.
126
- - The light falls on the ground; walls the lamps face are not lit by them.
127
- - No terrain: lamps stand on flat ground.
@@ -1,156 +0,0 @@
1
- # Street sky
2
-
3
- A map's `sky` paints the sky above the horizon when the camera tilts up the street, in either
4
- projection: blue by day, warm at dusk, dark with stars at night. Install `@tileflow/core` and
5
- `@tileflow/maps` and use a browser adapter from the same compatible SDK release. Streets declares a
6
- sky in every theme; the example extends it and keeps its sky.
7
-
8
- <!-- docs:check -->
9
-
10
- ```ts
11
- import {defineMap} from '@tileflow/core';
12
- import {streets} from '@tileflow/maps';
13
-
14
- export default defineMap({
15
- id: 'street-level',
16
- version: 1,
17
- extends: streets,
18
- view: {center: [-3.6904, 40.4493], zoom: 17, bearing: 8, pitch: 80},
19
- });
20
- ```
21
-
22
- The view looks north along Paseo de la Castellana in Madrid, with the sky above the skyline.
23
- Coordinates are longitude, then latitude.
24
-
25
- ## Camera pitch
26
-
27
- React, Vue, Svelte, the Dev preview, and browser capture create maps with a maximum pitch of 85
28
- degrees, `tileflowMaxPitch`, instead of MapLibre's default of 60. Every portable view already admits
29
- pitch 0–85. Pass `mapOptions: {maxPitch}` to lower the limit for one map. A map constructed directly
30
- with MapLibre keeps MapLibre's default unless it sets `maxPitch: tileflowMaxPitch` itself.
31
-
32
- The React Native surface still uses the native engines' 60-degree limit.
33
-
34
- ## Configure the sky
35
-
36
- `sky: true` uses the defaults. An options object replaces an inherited sky atomically, and
37
- `sky: false` removes it.
38
-
39
- | Option | Default | Meaning |
40
- | --------------- | --------- | ----------------------------------------------------------------- |
41
- | `color` | `#5c9ce6` | The sky overhead |
42
- | `horizonColor` | `#dcebf7` | The pale band along the horizon, also 3D terrain's distance fog |
43
- | `horizonBlend` | `0.6` | How far up the band reaches, as a share of half the view's height |
44
- | `glowColor` | `#ffc68a` | The light round a low sun, which glows on the horizon towards it |
45
- | `starIntensity` | `0.6` | The brightest the stars shine, from 0 (none) to 1 |
46
-
47
- Colors take `token.color(...)`, color operations, or `fixed(color, {reason})`, and must resolve to
48
- opaque colors. Numbers take `token.number(...)`, `fixed(number, {reason})`, or a number, and must
49
- resolve to 0–1. Unknown properties are rejected. Because tokens resolve per theme, one declaration
50
- gives each theme its own sky. Streets uses its themes' `sky.color`, `sky.horizon`,
51
- `sky.horizonBlend`, and `sky.glow` tokens:
52
-
53
- ```ts
54
- sky: {
55
- color: token.color('sky.color'),
56
- horizonColor: token.color('sky.horizon'),
57
- horizonBlend: token.number('sky.horizonBlend'),
58
- glowColor: token.color('sky.glow'),
59
- starIntensity: 0.6,
60
- },
61
- ```
62
-
63
- A map that replaces Streets' themes supplies those tokens or declares its own `sky`.
64
-
65
- ## A sky that follows the sun
66
-
67
- When an application places the sun with the theme controller's `setSun`, by direction or by the
68
- real sun of a date and place, the sky follows its height continuously instead of staying on the
69
- theme's preset. Above about 14 degrees the sky is the day theme's; it warms into the dawn or dusk
70
- theme's sky as the sun nears the horizon, using dawn while the sun stands in the eastern half of
71
- the sky and dusk in the western half; it holds that sky within two degrees of sunrise or sunset,
72
- and it darkens into the night theme's sky as twilight ends, about ten degrees below the horizon.
73
- The themes come from the map's `timeOfDay` family of the theme on screen, and every colour is
74
- resolved from that theme's own tokens, so a Monochrome family stays grey and a client's own
75
- family keeps its palette. A family with only one twilight uses it at both ends of the day; a theme
76
- outside a family, or a family without a day and a night theme, keeps its own sky.
77
- `setSun(undefined)` returns the sky to the themes. The map's colours on the ground stay those of
78
- the theme or blend on screen; pair `setSun` with `setBlend` to move them too. See
79
- [sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md).
80
-
81
- <!-- docs:check -->
82
-
83
- ```ts
84
- import type {TileflowThemeController} from '@tileflow/core/browser';
85
-
86
- export function sunsetOverMadrid(controller: TileflowThemeController) {
87
- // 19:15 in Madrid on 21 October 2026: the sun stands 1.5 degrees above the western horizon.
88
- return controller.setSun({date: Date.UTC(2026, 9, 21, 17, 15), location: [-3.69, 40.45]});
89
- }
90
- ```
91
-
92
- ## Glow, sun and stars
93
-
94
- A low sun glows on the horizon towards it, in the theme's `glowColor` or, for a sun an application
95
- placed, in the glow of its hour. The glow is strongest as the sun touches the horizon, fades by
96
- mid-morning, and is gone once the sun is about nine degrees below. While the sun is low and above
97
- the horizon, its disc shows too. The sun is the one the theme controller shows, so its
98
- disc stands where the 3D scene's shadows say it is; without a controller, the theme's own sun.
99
-
100
- Stars come out as the sky darkens: a night sky shows them, a day or dusk sky does not, and a
101
- time-of-day blend or a placed sun moving into night brings them in with the dark. They sit on a
102
- fixed dome, so they turn with the camera's bearing and pitch but not with its position.
103
-
104
- The glow, the sun, and the stars draw beneath every map layer and only where nothing else has
105
- drawn, so buildings and trees stand in front of them. With 3D terrain enabled, they are not
106
- drawn.
107
-
108
- ## Distance haze
109
-
110
- Above 45 degrees of pitch, the far city fades into the sky's horizon color, so streets meet the sky
111
- instead of ending in a sharp edge. The haze starts at the camera's distance to the map's center and
112
- is complete where MapLibre stops drawing the ground, about 6.7 times farther, where the sky starts in
113
- the same color. It comes in between 45 and 60 degrees, follows the current sky (theme blends
114
- and a sky that follows the sun included), and leaves the globe alone. Place and POI labels above the
115
- 3D layers stay clear.
116
-
117
- Tileflow's 3D buildings stand on the tiles round the camera, up to a limit of 25 by default, which
118
- end well before the horizon. Where that ring ends nearer than the horizon, the haze fits itself to
119
- it: it is nine-tenths complete at the nearest point where the ring's far side shows across the view,
120
- and complete soon after, never nearer than three camera distances for the nine-tenths, so a renderer
121
- held to few tiles does not pull the haze in. Buildings on the ring's far side sink into the ground
122
- over its last fifth, and nothing past the ring or past the complete haze is drawn. Buildings on the
123
- near side of a ring held short by the tile limit keep standing.
124
-
125
- The haze is drawn twice, from one model: Tileflow's 3D buildings, trees, and placed models haze
126
- each pixel by its depth, and a band over the flat map, just under the first 3D layer, hazes the
127
- flat ground by the depth its screen row has. Road names and one-way arrows that a style draws on the
128
- ground before its 3D layers fade with the ground, and so do distant 3D shadows, contact shade, and
129
- night floodlight. A style without a sky, or whose sky has no opaque horizon color, has no haze.
130
-
131
- ## Browser integration
132
-
133
- The compiler emits MapLibre `sky` in every concrete style and versioned `metadata['tileflow:sky']`
134
- with the star brightness, the glow, and, for a theme in a time-of-day family, the family's skies;
135
- `readTileflowSky` reads it and `tileflowSkyAtSun` mixes the sky of a sun's height. MapLibre draws the sky itself, so raw MapLibre consumers see it. On a
136
- globe, the sky blends into MapLibre's atmosphere at world zooms; when a map also declares
137
- `atmosphere`, the sky's colors replace the atmosphere's sky colors while its space background and
138
- globe stars stay.
139
-
140
- React, Vue, Svelte, and standalone browser capture draw the glow, the sun, the stars, and the flat
141
- map's haze through `attachTileflowAtmosphere(map)`. A direct MapLibre integration calls that
142
- function, or `attachTileflowSky(map)` or `attachTileflowSkyHaze(map)` from `@tileflow/core/browser`
143
- for one of them alone, after constructing the map; each returns a cleanup function. The sky is the
144
- custom layer `tileflow-sky` at the bottom of the layer stack, and the haze the custom layer
145
- `tileflow-sky-haze` under the first 3D layer, or the first label layer without one. Tileflow's 3D
146
- layers haze themselves. A sky that follows the sun needs the theme controller, which writes the
147
- sky's colours; raw MapLibre consumers see each theme's own sky.
148
-
149
- These helpers can attach before the first style is set. They wait for style events, follow style
150
- replacements, and tolerate a cleared style. Dispose them when the integration releases the map.
151
-
152
- MapLibre applies its own fog only to 3D terrain; with 3D terrain enabled, the flat haze band is not
153
- drawn and the terrain's fog takes its place. Other layers drawn by an application, such as
154
- `fill-extrusion` layers, stay sharp. Native artifacts remove the sky during preparation and record
155
- the removal; see the
156
- [native artifact profile](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/native-artifact-profile.md).
@@ -1,73 +0,0 @@
1
- # Style states
2
-
3
- A theme can read a value that the application sets once for the whole style, such as whether
4
- anything is selected. The theme reads it with `expr.globalState(name)`; the application sets it with
5
- the theme controller's `setState(name, value)`. MapLibre calls these values global state (GL JS 5.6
6
- or later).
7
-
8
- ## Read a state in a theme
9
-
10
- A state reads `null` until the application sets it, so wrap it in a typed assertion with a fallback.
11
- To let a value move smoothly, make the state a number and interpolate on it:
12
-
13
- <!-- docs:check -->
14
-
15
- ```ts
16
- import {expr} from '@tileflow/core';
17
-
18
- // 0 shows the standing highlight, 1 the quiet plate; values between mix the two.
19
- const selection = expr.toNumber(expr.globalState('selection'), 0);
20
- export const captionPlate = expr.interpolate({kind: 'linear'}, selection, [
21
- [0, '#ef5f57'],
22
- [1, '#fff9ee'],
23
- ]);
24
- ```
25
-
26
- A boolean works too, through `expr.case`, but it can only switch.
27
-
28
- ## Set a state from the application
29
-
30
- This example assumes an existing MapLibre map named `map` that shows the `light` theme of a
31
- published Tileflow map:
32
-
33
- <!-- docs:check -->
34
-
35
- ```ts
36
- import {createTileflowThemeController, type TileflowStyleSwitchMap} from '@tileflow/core/browser';
37
-
38
- // A MapLibre map satisfies TileflowStyleSwitchMap.
39
- export function selectionStates(map: TileflowStyleSwitchMap, styleUrl: string) {
40
- const controller = createTileflowThemeController({
41
- initial: {style: styleUrl, theme: 'light'},
42
- map,
43
- });
44
- void controller.setState('selection', 0);
45
- return {
46
- select: () => controller.setState('selection', 1, {transition: {duration: 300}}),
47
- clear: () => controller.setState('selection', 0, {transition: {duration: 300}}),
48
- };
49
- }
50
- ```
51
-
52
- A number eases from the value the map shows to the new one over the transition, so the plate mixes
53
- smoothly from one colour to the other. Booleans, strings, `null`, and a number set for the first
54
- time change at once, so set a starting value before easing it. A browser preference for
55
- reduced motion changes at once. Each call supersedes an easing of the same state still in progress,
56
- which resolves with `status: 'superseded'`; the new easing starts from where the old one stood.
57
-
58
- States stay through `setTheme` and every `setBlend`: the controller writes them again after each
59
- new style. `getState(name)` returns the value set last.
60
-
61
- ## Keep eased values free of feature data
62
-
63
- Keep a value that eases on a state free of feature data and feature state. MapLibre evaluates such
64
- a value for the whole layer at each frame. A value that also reads a feature's data or state is
65
- evaluated per feature when its tiles are laid out, so each change of the state lays them out
66
- again: the value follows late, in steps, and each frame costs far more. Put the per-feature choice
67
- in another layer, or let the application draw the feature it marks.
68
-
69
- ## Cost
70
-
71
- Each frame of an easing sets the state once, and MapLibre then evaluates again every paint value
72
- that reads it. A state read by a few layers costs little; a state read by most layers of a map can
73
- cost more per frame than the blend itself on slower devices.
@@ -1,167 +0,0 @@
1
- # Sun and light
2
-
3
- A theme can describe its sun and the light from its sky, as a light preset for that theme: a high
4
- sun in white light for a day theme, a low amber sun in the west for an evening theme, a cold moon
5
- with faint shadows for a night theme. In a browser, the theme controller returns the sun the map
6
- shows, mixed between neighbouring themes during a blend, so an application's own 3D layers can light
7
- their models and cast their shadows from it. An application can also place the sun itself, by
8
- direction or by the real position of the sun for a date and place.
9
-
10
- MapLibre lights its `fill-extrusion` layers with one directional light and casts no shadows. When a
11
- theme describes a sun, the map's light is anchored to the map and points from the sun, so Tileflow's
12
- 3D buildings are shaded from where it stands. Shadows, ambient occlusion and lit windows are for the
13
- application's own WebGL layers to draw, from the values `getSun()` returns.
14
-
15
- ## Describe a theme's sun
16
-
17
- Set `lighting.sun` and `lighting.ambient` in the theme. Every value is optional; unset values take
18
- the defaults in `tileflowDefaultSunPreset` (azimuth 210°, altitude 60°, white light at 0.2, full
19
- shadow strength, a white sky at 0.8 for soft daylight shadows):
20
-
21
- <!-- docs:check -->
22
-
23
- ```ts
24
- import {defineTheme} from '@tileflow/core';
25
- import {streetsThemes} from '@tileflow/maps';
26
-
27
- export const evening = defineTheme(streetsThemes.dark, {
28
- id: 'city-evening',
29
- version: 1,
30
- colorScheme: 'dark',
31
- lighting: {
32
- // Degrees clockwise from north, and above the horizon.
33
- sun: {azimuth: 285, altitude: 9, color: '#ff9a50', intensity: 1, shadowIntensity: 0.9},
34
- ambient: {color: '#5a4290', intensity: 0.3},
35
- },
36
- });
37
- ```
38
-
39
- `intensity` values run from 0 to 1. `shadowIntensity` says how dark the shadows the sun casts fall,
40
- from 0 (none) to 1. A theme derived from another changes single values of its sun and sky light; the
41
- others are inherited. The compiled style carries the resolved sun in its metadata
42
- (`tileflow:sun`) and a map-anchored `light` that points from it; MapLibre's `anchor` and the
43
- direction in `lighting.position` no longer apply to a theme with a sun. A theme without `sun` or
44
- `ambient` compiles exactly as before.
45
-
46
- ## Replace inherited lighting
47
-
48
- `defineTheme(base, definition)` normally merges lighting field by field. An empty `lighting`,
49
- `sun`, or `ambient` object therefore preserves inherited values. Use the existing `reset()`
50
- operation to remove a preset explicitly while keeping the base theme's palette and typography:
51
-
52
- <!-- docs:check -->
53
-
54
- ```ts
55
- import {defineTheme, reset} from '@tileflow/core';
56
- import {streetsThemes} from '@tileflow/maps';
57
-
58
- export const studio = defineTheme(streetsThemes.light, {
59
- id: 'studio',
60
- version: 1,
61
- colorScheme: 'light',
62
- lighting: {
63
- sun: reset(),
64
- ambient: reset(),
65
- anchor: 'viewport',
66
- position: [1.15, 180, 40],
67
- color: '#FFFFFF',
68
- intensity: 0.15,
69
- },
70
- });
71
- ```
72
-
73
- Removing both presets lets the native extrusion light use the authored `anchor` and `position`.
74
- Resetting only `sun` keeps an inherited ambient preset, and resetting only `ambient` keeps the sun;
75
- the missing counterpart then uses the SDK defaults. `lighting: reset()` clears all inherited
76
- lighting properties. A direct property such as `position`, `color`, or `intensity` can also be
77
- reset. The result is a complete theme with those properties absent; it contains no reset nodes.
78
- To replace a whole sun or ambient object, first derive a theme that resets that object, then derive
79
- the replacement values from the cleared theme.
80
-
81
- Removing a preset does not turn off the renderer's lighting, cast shadows, or ambient occlusion.
82
- The SDK's 3D scene uses its fallback light when no preset is published. For a graphic building
83
- style, choose the building module's `lighting: 'style'`, `shadows: false`, and `occlusion: false`
84
- explicitly. A single static theme needs no time-of-day family or running clock.
85
-
86
- ## Read the sun in an application
87
-
88
- `themeController.getSun()` returns the sun the map shows:
89
-
90
- - `azimuth` and `altitude`, in degrees, and `direction`, a unit vector towards the sun with x east,
91
- y north and z up.
92
- - `color` and `rgb` (sRGB channels from 0 to 1) of the direct light, and its `intensity`, which fades
93
- to 0 over the sun's last degrees above the horizon: a sun below it lights nothing.
94
- - `ambient`, the light from the sky, with its own `color`, `rgb` and `intensity`.
95
- - `shadowIntensity`, how dark the sun's shadows fall.
96
- - `brightness`, how bright flat, open ground is under this light, from 0 to 1: the sky's light plus
97
- the sun's at its height, each weighted by its colour's luminance. Use it to switch lamps or windows
98
- on as a scene darkens, whatever theme or sun lights it.
99
- - `source`: `theme`, `direction`, or `date`.
100
-
101
- During a blend the sun is mixed between the neighbouring themes' suns: the azimuth turns the short
102
- way round, colours mix in OKLab as the map's colours do, and numbers mix linearly. Read it when a
103
- custom layer draws; the map redraws whenever the sun changes.
104
-
105
- ## Place the sun
106
-
107
- `setSun` places the sun by direction, or by the real sun of a date over a place given as
108
- `[longitude, latitude]`, the map's centre when it is left out. The themes keep the sun's colours,
109
- strengths and shadow strength; only its direction changes. A map with a
110
- [street sky](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/street-sky.md)
111
- also takes the sky of the sun's hour from its time-of-day themes. `setSun(undefined)` returns both
112
- to the themes. This example assumes an existing MapLibre map and a controller for it:
113
-
114
- <!-- docs:check -->
115
-
116
- ```ts
117
- import type {TileflowThemeController} from '@tileflow/core/browser';
118
-
119
- export function followTheRealSun(controller: TileflowThemeController) {
120
- // Where the sun stands over Miami Beach now; a later call moves it on.
121
- void controller.setSun({date: Date.now(), location: [-80.13, 25.79]});
122
- }
123
-
124
- export function lowWesternSun(controller: TileflowThemeController) {
125
- // Turns the short way round to the west over a second.
126
- return controller.setSun({azimuth: 270, altitude: 8}, {transition: {duration: 1000}});
127
- }
128
- ```
129
-
130
- A request that follows an animation, such as a clock that moves every frame, passes no transition.
131
- A request with a transition supersedes one still in progress, which resolves with
132
- `status: 'superseded'`; a browser preference for reduced motion changes at once.
133
-
134
- `tileflowSunPosition(date, [longitude, latitude])` returns the sun's azimuth and apparent altitude,
135
- with atmospheric refraction, from NOAA's solar equations (accurate to about a hundredth of a degree
136
- for years 1800 to 2200). It runs anywhere, without a map:
137
-
138
- <!-- docs:check -->
139
-
140
- ```ts
141
- import {tileflowSunPosition} from '@tileflow/core/browser';
142
-
143
- // Solar noon in Miami at the June solstice, 17:22 UTC: the sun stands about 87.7° high, due south.
144
- export const noon = tileflowSunPosition(Date.UTC(2026, 5, 21, 17, 22), [-80.19, 25.77]);
145
- ```
146
-
147
- ## Light a custom layer
148
-
149
- For a custom WebGL layer that draws 3D content, light each surface with the sky's light, plus the
150
- sun's where the surface faces it and nothing stands in the way:
151
-
152
- light = ambient.rgb × ambient.intensity + rgb × intensity × max(normal · direction, 0) × (1 − shadowIntensity × shadowed)
153
-
154
- Convert the colours to linear light before adding them and back to sRGB after. MapLibre's own 2D
155
- layers take no shadows, so a layer that casts shadows on the ground darkens the map it is drawn over:
156
- draw the ground under the scene, after the scene's own geometry, with multiplying blending and a
157
- depth test against it.
158
-
159
- ## Limits
160
-
161
- - MapLibre shades extrusions with its one light; it has no shadow maps, ambient occlusion or
162
- emissive light. The sun gives custom layers what they need to draw them.
163
- - Themes interpolate the sun linearly between presets; a request by date follows the real sun, not
164
- the themes' presets.
165
- - Style expressions cannot read the sun yet. An application that wants a theme to react to it sets a
166
- [style state](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/style-states.md)
167
- from `getSun().brightness`.