@tileflow/core 0.1.0-alpha.30 → 0.1.0-alpha.31
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 +3 -0
- package/dist/browser.d.ts +629 -1
- package/dist/browser.js +559 -16
- package/dist/build.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +24 -0
- package/dist/native.d.ts +134 -1
- package/dist/native.js +691 -1
- package/docs/authoring-model.md +15 -0
- package/docs/map-emphasis.md +89 -0
- package/docs/theme-transitions-and-blends.md +59 -3
- package/package.json +1 -1
package/docs/authoring-model.md
CHANGED
|
@@ -173,6 +173,21 @@ const numericIdentity = expr.abs(expr.toNumber(expr.id(), 0));
|
|
|
173
173
|
const decorativeBucket = expr.modulo(numericIdentity, 7);
|
|
174
174
|
```
|
|
175
175
|
|
|
176
|
+
`expr.featureState(name)` reads a value an application sets on one feature at runtime, such as a
|
|
177
|
+
selection or a hover. `expr.globalState(name)` reads a value an application sets once for the whole
|
|
178
|
+
style with MapLibre's `map.setGlobalStateProperty(name, value)`, such as whether anything is
|
|
179
|
+
selected. Both read `null` until set, so wrap them in a typed assertion with a fallback. MapLibre
|
|
180
|
+
applies a global-state change to paint properties at once, without the style's paint transition.
|
|
181
|
+
|
|
182
|
+
<!-- docs:check -->
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import {expr} from '@tileflow/core';
|
|
186
|
+
|
|
187
|
+
const selecting = expr.toBoolean(expr.globalState('selection'), false);
|
|
188
|
+
const captionInk = expr.case([{when: selecting, value: '#1f2a44'}], '#ffffff');
|
|
189
|
+
```
|
|
190
|
+
|
|
176
191
|
The `land` module exposes stable land-use targets for `cemetery`, `civic`, `commercial`,
|
|
177
192
|
`education`, `government`, `industrial`, `medical`, `military`, `parking`, `railway`,
|
|
178
193
|
`recreation`, and `residential`. Its land-cover taxonomy distinguishes physical cover from authored
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Map emphasis
|
|
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
|
+
An application that draws its own content over a Tileflow map, such as a route, a selected place, or
|
|
6
|
+
search results, often needs the map to step back so that content reads first. Map emphasis does
|
|
7
|
+
that on the map's own MapLibre instance: chosen modules recede towards the map's ground colour, and
|
|
8
|
+
the map's labels and icons fade, except inside an area the application keeps at full strength. The
|
|
9
|
+
change eases in and out, stays when the theme changes, and follows a theme blend.
|
|
10
|
+
|
|
11
|
+
Emphasis needs a browser document and MapLibre GL JS. It changes only the layers a Tileflow map
|
|
12
|
+
compiled, so an application's own layers, such as the route it draws, keep their appearance.
|
|
13
|
+
|
|
14
|
+
## Show an emphasis
|
|
15
|
+
|
|
16
|
+
Create the theme controller for the map, then ask for an emphasis. This example assumes an existing
|
|
17
|
+
MapLibre map named `map` that shows the `dark` theme of a published Tileflow map, and a route the
|
|
18
|
+
application has already drawn:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import type {Map as MapLibreMap} from 'maplibre-gl';
|
|
22
|
+
import {createTileflowThemeController} from '@tileflow/core/browser';
|
|
23
|
+
|
|
24
|
+
export function focusOnRoute(map: MapLibreMap, styleUrl: string) {
|
|
25
|
+
const controller = createTileflowThemeController({
|
|
26
|
+
initial: {style: styleUrl, theme: 'dark'},
|
|
27
|
+
map,
|
|
28
|
+
});
|
|
29
|
+
void controller.setEmphasis(
|
|
30
|
+
{
|
|
31
|
+
labels: 0.6,
|
|
32
|
+
recede: {boundaries: 0.55, roads: 0.5, transit: 0.5},
|
|
33
|
+
},
|
|
34
|
+
{transition: {duration: 560}},
|
|
35
|
+
);
|
|
36
|
+
return () => controller.setEmphasis(undefined, {transition: {duration: 560}});
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Roads, transit, and boundaries move halfway or a little more towards the ground colour, labels and
|
|
41
|
+
icons keep 60 % of their opacity, and the change eases in over 560 milliseconds. Calling
|
|
42
|
+
`setEmphasis(undefined)` eases the map back to its design. Each call supersedes the previous one;
|
|
43
|
+
the promise of a superseded call resolves with `status: 'superseded'`.
|
|
44
|
+
|
|
45
|
+
## Choose what steps back
|
|
46
|
+
|
|
47
|
+
- `recede` maps module names to an amount from 0 (as designed) to 1 (the ground colour itself). The
|
|
48
|
+
modules are the semantic domains a map compiles, such as `roads`, `transit`, `boundaries`,
|
|
49
|
+
`water`, `land`, `buildings`, and `vegetation`. Every colour of a receding layer moves towards the
|
|
50
|
+
ground colour, including the colours inside zoom curves and data cases. Artwork that cannot be
|
|
51
|
+
recoloured, such as a pattern, fades by the same amount instead.
|
|
52
|
+
- `labels` is the share of their opacity that the map's labels and icons keep, from 0 to 1.
|
|
53
|
+
- `keep` is a GeoJSON `Polygon` or `MultiPolygon`. Labels and icons whose features fall inside it
|
|
54
|
+
keep their full strength. It can hold up to 4,096 positions. A corridor round a route is a
|
|
55
|
+
typical keep area.
|
|
56
|
+
|
|
57
|
+
The ground colour is the map's background colour. During a blend it is the blended background, so
|
|
58
|
+
receding colours follow the hour as the blend moves.
|
|
59
|
+
|
|
60
|
+
## How it changes over time
|
|
61
|
+
|
|
62
|
+
Values that depend only on the camera, such as a colour or an opacity with a zoom curve, ease every
|
|
63
|
+
frame. A value that MapLibre evaluates per feature, such as a road colour chosen by class or any
|
|
64
|
+
label opacity once a keep area is given, would make MapLibre lay its source out again on every
|
|
65
|
+
change. Such values change once, at the start of the change, under a snapshot of the map that fades
|
|
66
|
+
out as the transition runs. When the browser asks for reduced motion, every change happens at once.
|
|
67
|
+
|
|
68
|
+
An emphasis stays through `setTheme` and `setBlend`: after a new style is applied, the emphasis is
|
|
69
|
+
written again under the same cross-fade, and each blend position recomputes the receded colours.
|
|
70
|
+
|
|
71
|
+
## How it finds modules
|
|
72
|
+
|
|
73
|
+
Every compiled layer names its semantic module in its metadata:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{"id": "tileflow-road-major-fill", "metadata": {"tileflow:domain": "roads"}}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Layers that a theme blend splits or copies keep the same entry. Applications can read the key from
|
|
80
|
+
`tileflowLayerDomainMetadataKey` in `@tileflow/core/browser`. Physical layer IDs remain diagnostic;
|
|
81
|
+
use the module name instead. Styles compiled before this metadata existed have no module names, so
|
|
82
|
+
an emphasis leaves them unchanged.
|
|
83
|
+
|
|
84
|
+
## Cost
|
|
85
|
+
|
|
86
|
+
Each eased frame writes only the values that changed, with `setPaintProperty` and without
|
|
87
|
+
validation. For a detailed city map with roads, transit, and boundaries receding, that is about 150
|
|
88
|
+
values a frame on a desktop computer. Writing values directly is faster than reading a MapLibre
|
|
89
|
+
global state from every value, because a global state change re-evaluates every layer that reads it.
|
|
@@ -8,8 +8,9 @@ blend when the application controls the appearance continuously, such as a slide
|
|
|
8
8
|
passes from night to day.
|
|
9
9
|
|
|
10
10
|
Both work on the one MapLibre map the application already has. They create no second map, add no
|
|
11
|
-
map views, and request the same tiles as a map that does not change theme.
|
|
12
|
-
|
|
11
|
+
map views, and request the same tiles as a map that does not change theme. The React, Vue, and
|
|
12
|
+
Svelte maps offer them with MapLibre GL JS, and the React Native map with MapLibre Native on iOS
|
|
13
|
+
and Android; see [React Native](#react-native) for what differs there.
|
|
13
14
|
|
|
14
15
|
## Cross-fade a theme change
|
|
15
16
|
|
|
@@ -117,6 +118,48 @@ For a detailed city map this takes a few milliseconds on a mid-range phone. Posi
|
|
|
117
118
|
four-hundredth of a theme are not applied, so a slowly moving position leaves the map at rest
|
|
118
119
|
between changes.
|
|
119
120
|
|
|
121
|
+
## React Native
|
|
122
|
+
|
|
123
|
+
The `Map` of `@tileflow/react-native` accepts the same `themeTransition` and `themeBlend` props:
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import {Map} from '@tileflow/react-native';
|
|
127
|
+
|
|
128
|
+
export function DayMap({position}: {position: number}) {
|
|
129
|
+
return (
|
|
130
|
+
<Map
|
|
131
|
+
source={{map: 'city', manifestUrl: 'https://maps.example.com/tileflow/manifest.json'}}
|
|
132
|
+
themeTransition={{duration: 450}}
|
|
133
|
+
themeBlend={{position, themes: ['night', 'dusk', 'day']}}
|
|
134
|
+
style={{flex: 1}}
|
|
135
|
+
/>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
A transition covers the drawn map with a snapshot of its current frame, below markers and map
|
|
141
|
+
controls. The snapshot follows the camera and fades out once the new style is drawn.
|
|
142
|
+
`onReadinessChange` and `onThemeChange` report `ready` after the fade. The device's reduce-motion
|
|
143
|
+
setting (iOS) or removed animations (Android) change at once.
|
|
144
|
+
|
|
145
|
+
A blend loads every blended theme's style once and replaces the map's style once. Position changes
|
|
146
|
+
then apply in batches on the main thread, at most one at a time, and later positions replace any
|
|
147
|
+
that are still waiting. MapLibre Native's style transition smooths values between batches.
|
|
148
|
+
|
|
149
|
+
What follows the position differs from the browser in a few ways:
|
|
150
|
+
|
|
151
|
+
- Ground colours and numbers, pattern artwork, and light follow the position. Native maps have no
|
|
152
|
+
sky.
|
|
153
|
+
- Labels, icons, and switched values change at the nearest theme under a short cross-fade of the
|
|
154
|
+
whole map. Icon artwork changes by image name instead of mixing pixels, so stretchable images
|
|
155
|
+
keep their stretch and content areas.
|
|
156
|
+
- A value that depends on feature data and cannot be split by class, such as a road casing chosen
|
|
157
|
+
by access and zoom together, is drawn once per theme. Only the nearest theme's copy is shown.
|
|
158
|
+
The map has a few more layers while the blend is shown.
|
|
159
|
+
|
|
160
|
+
If the application's native build predates these props, for example after an over-the-air
|
|
161
|
+
JavaScript update, themes change at once and a blend shows its nearest theme.
|
|
162
|
+
|
|
120
163
|
## Use the core controller
|
|
121
164
|
|
|
122
165
|
Framework adapters use `createTileflowThemeController` from `@tileflow/core/browser`. A direct
|
|
@@ -124,12 +167,12 @@ MapLibre integration can use it too. Resolve runtime styles from the published m
|
|
|
124
167
|
`resolveTileflowRuntimeStyle` and `resolveTileflowRuntimeThemeBlend` from `@tileflow/core/runtime`:
|
|
125
168
|
|
|
126
169
|
```ts
|
|
170
|
+
import type {Map as MapLibreMap} from 'maplibre-gl';
|
|
127
171
|
import {createTileflowThemeController} from '@tileflow/core/browser';
|
|
128
172
|
import {
|
|
129
173
|
resolveTileflowRuntimeStyle,
|
|
130
174
|
resolveTileflowRuntimeThemeBlend,
|
|
131
175
|
} from '@tileflow/core/runtime';
|
|
132
|
-
import type {Map as MapLibreMap} from 'maplibre-gl';
|
|
133
176
|
import type {TileflowRuntimeManifestMap} from '@tileflow/core/runtime';
|
|
134
177
|
|
|
135
178
|
export async function moveThroughTheDay(map: MapLibreMap, manifestMap: TileflowRuntimeManifestMap) {
|
|
@@ -163,3 +206,16 @@ latest wins. A failed change restores the previous appearance and reports `faile
|
|
|
163
206
|
The controller loads blended themes' style documents with the same request policy it uses for
|
|
164
207
|
style font metadata: same-origin credentials, no redirects, and a bounded size. Pass `loadStyle`
|
|
165
208
|
when styles need another loader.
|
|
209
|
+
|
|
210
|
+
The controller also shows [map emphasis](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/map-emphasis.md),
|
|
211
|
+
which stays through theme changes and follows every blend position.
|
|
212
|
+
|
|
213
|
+
## Fast sweeps on slower devices
|
|
214
|
+
|
|
215
|
+
Each position change makes MapLibre evaluate the paint of every layer whose value changed. A clock
|
|
216
|
+
that moves slowly changes few values per frame, and positions closer than a four-hundredth of a
|
|
217
|
+
theme are skipped. A sweep that crosses a whole theme in a few seconds while the camera also moves
|
|
218
|
+
changes most ground layers on every frame. Measured in Chromium on Apple silicon at 1440 × 900 for
|
|
219
|
+
a detailed city map, such a sweep kept 76 frames a second; with the processor slowed four times, as
|
|
220
|
+
on a mid-range phone, it fell to about 11, against 28 for the same camera move without the sweep.
|
|
221
|
+
On slower devices, prefer short sweeps or move the position less often.
|