@tileflow/core 0.1.0-alpha.3 → 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/LICENSE +201 -0
- package/README.md +273 -26
- package/THIRD_PARTY_NOTICES.md +327 -0
- package/dist/browser.d.ts +962 -0
- package/dist/browser.js +6237 -0
- package/dist/build.d.ts +574 -0
- package/dist/build.js +25423 -0
- package/dist/capture-scene-D6soWztE.d.ts +161 -0
- package/dist/capture.d.ts +9 -0
- package/dist/capture.js +276 -0
- package/dist/config-reference.json +32261 -0
- package/dist/icon-composition-BiOTwrvD.d.ts +1331 -0
- package/dist/index.d.ts +1114 -162
- package/dist/index.js +18714 -3479
- package/dist/manifest-types-CTT2eLbp.d.ts +790 -0
- package/dist/manifest.d.ts +9 -0
- package/dist/manifest.js +368 -0
- package/dist/native-profile.d.ts +188 -0
- package/dist/native-profile.js +9316 -0
- package/dist/native.d.ts +355 -0
- package/dist/native.js +5552 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +1317 -0
- package/docs/authoring-model.md +241 -0
- package/docs/capture-scenes.md +36 -0
- package/docs/compiled-style-performance.md +263 -0
- package/docs/data-is-separate-from-design.md +345 -0
- package/docs/globe-atmosphere.md +83 -0
- package/docs/hosted-session-authorization.md +108 -0
- package/docs/map-and-terrain-examples.md +149 -0
- package/docs/map-emphasis.md +89 -0
- package/docs/native-artifact-profile.md +145 -0
- package/docs/native-resource-urls.md +367 -0
- package/docs/public-api-and-browser-subpath.md +93 -0
- package/docs/shared-visual-primitives.md +81 -0
- package/docs/theme-transitions-and-blends.md +221 -0
- package/docs/themes-and-module-styles.md +83 -0
- package/package.json +58 -8
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Authoring model
|
|
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
|
+
Tileflow exposes one authoring concept and one constructor: `defineMap()`. A complete map omits
|
|
6
|
+
`extends`; an inherited map sets `extends` to another imported map object. All first-party
|
|
7
|
+
maps are independent standalone maps. They use the sole semantic compiler while defining
|
|
8
|
+
their complete designs and asset providers directly; no official map imports or extends another
|
|
9
|
+
official map. Applications can extend any of those maps through the same public API. Streets
|
|
10
|
+
itself owns coordinated light and dark themes.
|
|
11
|
+
There is no public compiler selector or alternate compiler. The authoring contract has no recipe or
|
|
12
|
+
cartographic compatibility alias.
|
|
13
|
+
|
|
14
|
+
### Machine-readable language surface
|
|
15
|
+
|
|
16
|
+
`tileflowAuthoringManifest` is the deterministic, deeply frozen description of the public semantic
|
|
17
|
+
language. Its schema version is `tileflowAuthoringManifestSchemaVersion`. The manifest's `domains`
|
|
18
|
+
array is generated from the compiler's closed registry and records exact compile order, module and
|
|
19
|
+
service dependencies, and provided services. `operations` lists only integrated public operations:
|
|
20
|
+
defining/extending a map, directly replacing, refining, disabling, or resetting a keyed domain,
|
|
21
|
+
and adding owner-local render passes or target refinements. Direct declaration is the sole
|
|
22
|
+
replacement syntax; there is no redundant `set()` helper.
|
|
23
|
+
|
|
24
|
+
Agents and other tools can discover this contract without importing executable configuration:
|
|
25
|
+
`tileflow language manifest --json` emits the manifest and `tileflow language schema --json` emits
|
|
26
|
+
the packaged generated authoring/resolved JSON Schema. Both commands are deterministic, read-only,
|
|
27
|
+
and network-free. The manifest links every domain to its authoring, options, patch, and resolved
|
|
28
|
+
schema definitions, and enumerates the closed expression and render-selector vocabulary with its
|
|
29
|
+
finite limits.
|
|
30
|
+
|
|
31
|
+
The manifest also describes the exact structured compiler contract. A successful
|
|
32
|
+
`createStyleResult()` has `{ok: true, diagnostics, report, style}`; a failure has
|
|
33
|
+
`{ok: false, diagnostics, report}` and forbids `style`. Report schema version 1 requires
|
|
34
|
+
`domains`, `map`, `planner`, `schemaVersion`, and sorted semantic `targets`; `theme`, source
|
|
35
|
+
`requirements`, and physical `provenance` are optional. Pass `{inspection: true}` only when the
|
|
36
|
+
read-only physical-output provenance sidecar is needed. Its IDs and indexes are diagnostic
|
|
37
|
+
observations, never stable or addressable authoring targets.
|
|
38
|
+
|
|
39
|
+
`diffTileflowMaps(before, after)` resolves both inputs before comparing them and returns semantic
|
|
40
|
+
diff schema version 1:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
{
|
|
44
|
+
schemaVersion: 1,
|
|
45
|
+
from: {id, version},
|
|
46
|
+
to: {id, version},
|
|
47
|
+
summary: {add, remove, change, total},
|
|
48
|
+
changes: [
|
|
49
|
+
{kind: 'add', path: '/projection', after: 'globe'},
|
|
50
|
+
{kind: 'remove', path: '/modules/water', before: {/* ... */}},
|
|
51
|
+
{kind: 'change', path: '/view/zoom', before: 12, after: 13},
|
|
52
|
+
],
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Paths are RFC 6901 JSON Pointers. Object keys compare recursively in code-unit order; arrays compare
|
|
57
|
+
atomically because map inheritance replaces arrays. Leaf identity (`id`, `name`, `version`) and
|
|
58
|
+
delivery metadata identify the endpoints but are not cartographic changes.
|
|
59
|
+
|
|
60
|
+
Custom build tooling can opt into a separate, read-only diagnostic sidecar from the explicit build
|
|
61
|
+
entrypoint:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import {createStyleWithInspection} from '@tileflow/core/build';
|
|
65
|
+
|
|
66
|
+
const {style, inspection} = createStyleWithInspection(map);
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`inspection.layers` stays aligned with the final Style layer order and records the semantic
|
|
70
|
+
`owner`, `slot`, `target`, and ordered render passes/refinements represented by each layer, including
|
|
71
|
+
layers merged by the physical planner. `createStyleFromCatalogWithInspection` and
|
|
72
|
+
`createStylesFromCatalogWithInspection` provide the corresponding catalog-oriented forms. The
|
|
73
|
+
ordinary Style bytes are identical to compilation without inspection; private compiler metadata is
|
|
74
|
+
stripped before finalization, and the sidecar never enters a Style, runtime manifest, or production
|
|
75
|
+
artifact unless a caller deliberately stores it.
|
|
76
|
+
|
|
77
|
+
Resolution happens before validation, asset preparation, compilation, capture, build, or deploy.
|
|
78
|
+
Only `view` deep-merges (its nested arrays and expressions still replace). The `themes` collection
|
|
79
|
+
replaces atomically when declared and clears an inherited `systemThemes` mapping; omission inherits
|
|
80
|
+
the collection. `defaultTheme` may independently choose one inherited concrete theme, while an
|
|
81
|
+
explicit `systemThemes` replaces the complete light/dark mapping. Every resolved selector must name
|
|
82
|
+
a theme in the final collection.
|
|
83
|
+
`modules` merges by domain name: an omitted domain remains inherited, while declaring `roads(...)`
|
|
84
|
+
or another domain replaces that inherited module request and every compiler-owned contribution
|
|
85
|
+
attached to it as a unit. `data`, `projection`, `terrain`, `marine`, `icons`, and the text provider
|
|
86
|
+
are atomic; identity and tooling metadata are leaf-owned. `disable()` removes the domain and
|
|
87
|
+
its complete inherited render stack. The resolved result is a standalone map with no runtime dependency on
|
|
88
|
+
TypeScript imports.
|
|
89
|
+
|
|
90
|
+
`icons` is an intentional exception to implicit array composition: omission inherits the exact
|
|
91
|
+
parent array, any declaration replaces it atomically, and `[]` disables map icons. Compose with a
|
|
92
|
+
spread when the child should keep a parent's directories. `fonts` and `glyphs` are mutually
|
|
93
|
+
exclusive text providers; declaring either atomically replaces an inherited provider of either
|
|
94
|
+
kind. Unknown keys and former compatibility shapes are rejected.
|
|
95
|
+
|
|
96
|
+
Most authors should extend an existing official or application map. A complete standalone map uses
|
|
97
|
+
the same `defineMap()` call without `extends`:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import {defineMap} from '@tileflow/core';
|
|
101
|
+
import {streetsIcons, streetsThemes} from '@tileflow/maps';
|
|
102
|
+
|
|
103
|
+
const companyBase = defineMap({
|
|
104
|
+
id: 'company-base',
|
|
105
|
+
name: 'Company base',
|
|
106
|
+
version: 1,
|
|
107
|
+
glyphs: {
|
|
108
|
+
kind: 'url',
|
|
109
|
+
url: 'https://api.tileflow.dev/base/33d4de5e8086d9d629d67d3f39fedb87e23686c4c1ac653c27e2a52aee9d00b3/glyphs/{fontstack}/{range}.pbf',
|
|
110
|
+
fontStacks: ['Noto Sans Regular', 'Noto Sans Bold'],
|
|
111
|
+
},
|
|
112
|
+
icons: [streetsIcons],
|
|
113
|
+
themes: {light: streetsThemes.light, dark: streetsThemes.dark},
|
|
114
|
+
defaultTheme: 'light',
|
|
115
|
+
systemThemes: {light: 'light', dark: 'dark'},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
export default defineMap({
|
|
119
|
+
id: 'company-navigation',
|
|
120
|
+
version: 1,
|
|
121
|
+
extends: companyBase,
|
|
122
|
+
projection: 'globe',
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The standalone map above is complete as written. A map always owns its complete text provider; Core does not
|
|
127
|
+
obtain fonts or sprites from World and never invents a fallback URL. The URL-backed first-party
|
|
128
|
+
`streets`, `ferraris`, `harad`, `soundings`, and `blueprint` maps declare their glyph
|
|
129
|
+
providers directly. `baedeker`, `civica`, and `siegfried`
|
|
130
|
+
declare packaged fonts, so ordinary imports and derived maps compile without out-of-band release
|
|
131
|
+
metadata.
|
|
132
|
+
|
|
133
|
+
```mermaid
|
|
134
|
+
flowchart LR
|
|
135
|
+
A["tileflow.config.ts exports one map"] --> B["Resolve imported map lineage"]
|
|
136
|
+
B --> C["Complete semantic design + closed domain registry"]
|
|
137
|
+
C --> D["Domain IR + assembly + render stacks"]
|
|
138
|
+
D --> E["Physical planner"]
|
|
139
|
+
E --> F["Single MapLibre lowering"]
|
|
140
|
+
F --> G["Validated Style JSON + compilation report"]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Modules are keyed by domain, so object order never controls rendering and a domain can appear only
|
|
144
|
+
once. A map extending Streets inherits its complete module set. Use `disable()` to remove a domain
|
|
145
|
+
deliberately. The public domains are `land`, `water`, `nautical`, `roads`, `buildings`, `boundaries`,
|
|
146
|
+
`labels`, `poi`, `aeroways`, `transit`, `vegetation`, `addresses`, and `landforms`.
|
|
147
|
+
The closed domain registry is exhaustive over that domain set and owns defaults, dependency order,
|
|
148
|
+
services, and compiler orchestration. Contract tests keep its keys and type tags in lockstep with
|
|
149
|
+
the strict resolved schema, generated authoring/options/patch/resolved schema aliases, and public
|
|
150
|
+
exports. Disabling `roads` also removes dependent road names, shields, and junction references;
|
|
151
|
+
independent place, water, aerodrome, and POI labels remain under the labels module.
|
|
152
|
+
|
|
153
|
+
Every styling module supports semantic shortcuts and exact semantic targets. Visual style values
|
|
154
|
+
accept typed token refs, documented `fixed(value, {reason})` values, closed data expressions through
|
|
155
|
+
`expr.*` plus `field(...)`, and zoom functions through `zoom.step(...)`, `zoom.linear(...)`, or
|
|
156
|
+
`zoom.exponential(...)`. Refs and fixed leaves also work inside typed expressions and zoom stops.
|
|
157
|
+
Structural controls such as presets, visibility, and zoom gates remain ordinary literals. Exact
|
|
158
|
+
controls address stable concepts such as `roads.classes.primary.surface.fill`, not renderer layer
|
|
159
|
+
IDs.
|
|
160
|
+
|
|
161
|
+
`expr.id()` reads the source-owned feature identifier: a string, number, or `null` when absent.
|
|
162
|
+
It performs no I/O and does not derive an identifier from geometry. `expr.modulo(dividend, divisor)`
|
|
163
|
+
uses native numeric remainder, including its sign behavior. Normalize input and provide a deliberate
|
|
164
|
+
fallback before using numeric IDs for decorative variation; missing or non-numeric IDs must not
|
|
165
|
+
silently imply distinct features. Identity stability across data releases belongs to the producer.
|
|
166
|
+
|
|
167
|
+
<!-- docs:check -->
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
import {expr} from '@tileflow/core';
|
|
171
|
+
|
|
172
|
+
const numericIdentity = expr.abs(expr.toNumber(expr.id(), 0));
|
|
173
|
+
const decorativeBucket = expr.modulo(numericIdentity, 7);
|
|
174
|
+
```
|
|
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
|
+
|
|
191
|
+
The `land` module exposes stable land-use targets for `cemetery`, `civic`, `commercial`,
|
|
192
|
+
`education`, `government`, `industrial`, `medical`, `military`, `parking`, `railway`,
|
|
193
|
+
`recreation`, and `residential`. Its land-cover taxonomy distinguishes physical cover from authored
|
|
194
|
+
urban green: `farmland`, `flowerbed`, `grass`, `ice`, `meadow`, `protected`,
|
|
195
|
+
`recreationGround`, `rock`, `sand`, `scrub`, `urbanPark`, `villageGreen`, `wetland`, and
|
|
196
|
+
`wood`. The plain `grass` branch excludes every typed grass subclass, so one source feature cannot
|
|
197
|
+
receive two opaque green fills. Parking areas are polygons from the configured land-use source;
|
|
198
|
+
parking access aisles remain road features under `roads.serviceTypes.parkingAisle`. The
|
|
199
|
+
`commercial` target recognizes ordinary `commercial` and `retail` values plus Tileflow's derived
|
|
200
|
+
`business_area` ground class. Its `globalLandcover` fill styles the optional low-zoom global
|
|
201
|
+
land-cover extension without a raw layer patch. The `water.bathymetry` fill does the same for the
|
|
202
|
+
typed Tileflow World V1 depth bands. An explicit `water({bathymetryContours: {}})` traces the
|
|
203
|
+
submerged edges between those discrete polygon bands; it is an approximate visual contour, not a
|
|
204
|
+
surveyed isoline. `water({bathymetryLabels: {}})` separately opts into numeric metre values derived
|
|
205
|
+
from each polygon's absolute minimum band depth. Existing maps gain neither detail by default, and
|
|
206
|
+
sources without bathymetry bindings omit both requested layers. These labels describe coarse band
|
|
207
|
+
floors, not measured survey soundings, and must not be presented as hydrographic sounding data.
|
|
208
|
+
|
|
209
|
+
`addresses` renders the standard OpenMapTiles `housenumber` points at detailed zooms and exposes a
|
|
210
|
+
single semantic `labels` style. `landforms` renders the standard `mountain_peak` classes (`peak`,
|
|
211
|
+
`volcano`, `saddle`, `ridge`, `cliff`, and `arete`) with rank-aware collision priority and optional
|
|
212
|
+
metric elevation. Both source-layer and field names remain remappable through `openMapTiles(...)`;
|
|
213
|
+
landform names share the language selected by `labels({language: ...})`.
|
|
214
|
+
|
|
215
|
+
`aeroways.runwayRef` owns high-zoom runway designators from the remappable OpenMapTiles `ref`
|
|
216
|
+
field. Aerodrome names stay under `labels.styles.aerodrome`; `labels.aerodromeCodes` controls whether
|
|
217
|
+
they append no code, IATA only, or IATA with an ICAO fallback (`'none' | 'iata' | 'all'`).
|
|
218
|
+
|
|
219
|
+
The `vegetation` module binds individual-tree points from the optional OpenMapTiles `tree`
|
|
220
|
+
extension. Its default `mode: '3d'` emits a portable circle fallback plus metadata for runtimes
|
|
221
|
+
that can upgrade the points to instanced 3D trees. `mode: 'flat'` keeps the MapLibre circles, and
|
|
222
|
+
`flat` exposes the complete `CircleStyle` fallback. `threeDimensional` controls bark color,
|
|
223
|
+
broadleaf and conifer palettes, and independent height and crown scales. The legacy `minZoom`
|
|
224
|
+
shortcut remains available; `flat.minZoom` takes precedence when both are present. The binding
|
|
225
|
+
includes height, crown diameter, genus, leaf type, and species fields so compatible runtimes can
|
|
226
|
+
preserve source measurements and botanical form without hard-coding raw property names. The
|
|
227
|
+
pitched-scene stack follows physical height: pedestrian and transport surfaces, transport markings,
|
|
228
|
+
buildings, then vegetation, so street paint never floats over 3D geometry. Text draws last: road
|
|
229
|
+
names, route shields, junction numbers, and place, water, aerodrome, and POI annotations stay above
|
|
230
|
+
every ground layer, so building fills, boundaries, and trees cannot cover them.
|
|
231
|
+
`labels.collisionPriority` only orders labels within that final stack.
|
|
232
|
+
|
|
233
|
+
Render passes, and refinements that move a target their module owns, share one `placement`
|
|
234
|
+
vocabulary. A band edge is `below-` or `above-` one of `land`, `relief`, `water`, `roads`,
|
|
235
|
+
`boundaries`, `buildings`, `vegetation`, and `labels`; it needs no anchor, so a vegetation pass can
|
|
236
|
+
draw `'above-relief'` without depending on another module. `{above: target}` and `{below: target}`
|
|
237
|
+
draw beside one semantic target and inherit its band and, without their own `feature`, its vector
|
|
238
|
+
data; passes placed beside a moved target follow it. Operations sharing a placement paint in
|
|
239
|
+
declaration order, the first lowest. For symbols, a higher layer is also placed first in MapLibre
|
|
240
|
+
collisions, so placement decides both drawing and precedence. An owner whose layers are all
|
|
241
|
+
render passes still satisfies other operations' `requirements`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Capture scenes
|
|
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
|
+
Commit named `scenes` on the singular exported map. A scene implicitly targets that map, so it does
|
|
6
|
+
not repeat a `map` selector. Scene metadata does not inherit and is removed before cartographic
|
|
7
|
+
compilation; it affects visual evidence, not style or manifest identity.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {defineMap} from '@tileflow/core';
|
|
11
|
+
import {streets} from '@tileflow/maps';
|
|
12
|
+
|
|
13
|
+
export default defineMap({
|
|
14
|
+
id: 'madrid',
|
|
15
|
+
name: 'Madrid',
|
|
16
|
+
version: 1,
|
|
17
|
+
extends: streets,
|
|
18
|
+
scenes: {
|
|
19
|
+
'madrid-desktop': {
|
|
20
|
+
theme: 'dark',
|
|
21
|
+
camera: {type: 'center', center: [-3.7038, 40.4168], zoom: 12},
|
|
22
|
+
viewport: {width: 1280, height: 800, dpr: 1},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use `tileflow preview --scene madrid-desktop` (`tileflow dev` remains an alias) for live review and
|
|
29
|
+
`tileflow capture madrid-desktop` for exact pixels and a schema-version-4 receipt containing the
|
|
30
|
+
concrete theme plus Streets, data, style, renderer, and image identities. World capture resolves the selected TileJSON
|
|
31
|
+
once and records the exact `world-v1` release plus descriptor/archive/data-contract hashes;
|
|
32
|
+
external fixtures may identify their explicit revision.
|
|
33
|
+
|
|
34
|
+
Every exact World V1 release ID is already canonical at the producer boundary: 12–128 characters
|
|
35
|
+
matching `^world-v1-[a-z0-9][a-z0-9._-]*[a-z0-9]$`. Core validates the literal value and never trims,
|
|
36
|
+
lowercases, or upgrades it to another World generation.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Compiled-style performance
|
|
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 semantic compiler resolves semantic modules and their owned contributions first, then compacts equivalent
|
|
6
|
+
physical MapLibre layers. At high-detail zooms, road classes become a small set of data-driven
|
|
7
|
+
cohorts; equivalent road labels and tunnel hatches share compatible buckets; and land-cover,
|
|
8
|
+
land-use, and waterway classes share compatible buckets. Compaction selects
|
|
9
|
+
cohorts from compiler-owned semantic targets, not by parsing physical IDs; its temporary
|
|
10
|
+
owner/slot/target provenance is stripped from the public Style JSON.
|
|
11
|
+
|
|
12
|
+
After physical planning, Core embeds a bounded private `tileflow:interaction-manifest` lookup for the
|
|
13
|
+
final POI representations. `@tileflow/interactions/maplibre` validates and consumes that metadata
|
|
14
|
+
so applications can bind to `domain: 'poi'` without depending on physical layer IDs. The lookup is
|
|
15
|
+
paired atomically with the exact style it describes; it is not a public style-authoring API.
|
|
16
|
+
|
|
17
|
+
Use the exported structural sweep to enforce budgets without loading tiles or a browser:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {analyzeTileflowStylePerformance, createStyle} from '@tileflow/core';
|
|
21
|
+
import map from './tileflow.config';
|
|
22
|
+
|
|
23
|
+
const report = analyzeTileflowStylePerformance(createStyle(map));
|
|
24
|
+
console.log(report.zooms[16]);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The report covers z0–z22 by default and includes active layers, conservative bucket estimates,
|
|
28
|
+
symbols, and active layers per source-layer. It is intended for regression gates; browser traces
|
|
29
|
+
remain the source of truth for decode, upload, placement, and frame time.
|
|
30
|
+
|
|
31
|
+
A `SymbolStyle` root zoom range governs its text/icon layer and is inherited by its marker layer.
|
|
32
|
+
Because a marker is materialized as a separate circle layer, an explicit marker range may refine
|
|
33
|
+
that inherited default. Text and icon ranges must agree because MapLibre renders them together.
|
|
34
|
+
|
|
35
|
+
The road module distinguishes the path family semantically:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
roads({
|
|
39
|
+
extras: {paths: true},
|
|
40
|
+
crossings: {image: token.image('roads.crosswalk')},
|
|
41
|
+
sidewalks: {
|
|
42
|
+
surface: {color: token.color('roads.city.casing'), minZoom: 17},
|
|
43
|
+
pattern: {
|
|
44
|
+
pattern: token.image('roads.sidewalkPattern'),
|
|
45
|
+
minZoom: 17,
|
|
46
|
+
opacity: fixed(0.6, {reason: 'Sidewalk texture strength is invariant'}),
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
roundabouts: {
|
|
50
|
+
casing: {strokeColor: token.color('roads.default.casing')},
|
|
51
|
+
fill: {strokeColor: token.color('roads.default')},
|
|
52
|
+
},
|
|
53
|
+
areas: {
|
|
54
|
+
pedestrian: {
|
|
55
|
+
fill: {color: token.color('surface.land')},
|
|
56
|
+
outline: {
|
|
57
|
+
color: token.color('roads.default.casing'),
|
|
58
|
+
width: fixed(1, {reason: 'Pedestrian-area outline weight is invariant'}),
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
classes: {
|
|
63
|
+
primary: {
|
|
64
|
+
tunnel: {
|
|
65
|
+
casing: {
|
|
66
|
+
color: token.color('roads.city.tunnelCasing'),
|
|
67
|
+
width: fixed(10, {reason: 'Primary-tunnel hierarchy is invariant'}),
|
|
68
|
+
},
|
|
69
|
+
fill: {
|
|
70
|
+
color: token.color('roads.city.tunnel'),
|
|
71
|
+
width: fixed(8, {reason: 'Primary-tunnel hierarchy is invariant'}),
|
|
72
|
+
},
|
|
73
|
+
hatch: {
|
|
74
|
+
color: token.color('roads.city.tunnelCasing'),
|
|
75
|
+
opacity: fixed(0.25, {reason: 'Tunnel hatch strength is invariant'}),
|
|
76
|
+
spacing: fixed(10, {reason: 'Tunnel hatch rhythm is invariant'}),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
pedestrian: {
|
|
81
|
+
surface: {
|
|
82
|
+
fill: {
|
|
83
|
+
color: token.color('roads.path.transition'),
|
|
84
|
+
width: fixed(6, {reason: 'Pedestrian-road hierarchy is invariant'}),
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
footway: {
|
|
89
|
+
surface: {
|
|
90
|
+
fill: {
|
|
91
|
+
color: token.color('roads.path'),
|
|
92
|
+
width: fixed(1.2, {reason: 'Path hierarchy is invariant'}),
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
cycleway: {
|
|
97
|
+
surface: {
|
|
98
|
+
fill: {
|
|
99
|
+
color: token.color('roads.cycleway'),
|
|
100
|
+
width: fixed(1.5, {reason: 'Cycleway hierarchy is invariant'}),
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
steps: {
|
|
105
|
+
surface: {
|
|
106
|
+
fill: {
|
|
107
|
+
color: token.color('roads.path.casing'),
|
|
108
|
+
width: fixed(1.4, {reason: 'Step hierarchy is invariant'}),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
pathway: {
|
|
113
|
+
surface: {
|
|
114
|
+
fill: {
|
|
115
|
+
color: token.color('roads.path'),
|
|
116
|
+
width: fixed(1.2, {reason: 'Path hierarchy is invariant'}),
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
modifiers: {
|
|
122
|
+
expressway: {widthScale: 1.06},
|
|
123
|
+
ramp: {widthScale: 0.7},
|
|
124
|
+
unpaved: {surface: {fill: {color: token.color('roads.path')}}},
|
|
125
|
+
construction: {
|
|
126
|
+
surface: {
|
|
127
|
+
fill: {opacity: fixed(0.7, {reason: 'Construction emphasis is invariant'})},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
indoor: {
|
|
131
|
+
surface: {fill: {opacity: fixed(0.4, {reason: 'Indoor-road emphasis is invariant'})}},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
restrictions: {
|
|
135
|
+
access: {
|
|
136
|
+
surface: {fill: {opacity: fixed(0.55, {reason: 'Restricted-road emphasis is invariant'})}},
|
|
137
|
+
},
|
|
138
|
+
toll: {
|
|
139
|
+
surface: {
|
|
140
|
+
casing: {color: fixed('#C5B7D8', {reason: 'Toll ink is regulatory and invariant'})},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
serviceTypes: {
|
|
145
|
+
driveway: {widthScale: 0.75},
|
|
146
|
+
parkingAisle: {widthScale: 0.6},
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
These targets are non-overlapping translations of the OpenMapTiles road class and subclass. The
|
|
152
|
+
same names are available under `labels().roadClasses` and `labels().styles.roads`. Surface, tunnel,
|
|
153
|
+
and bridge phases can be controlled independently for each target; each structure has
|
|
154
|
+
`shadow`/`casing`/`fill` and an optional `hatch`. Glyph hatch marks inherit the resolved fill width
|
|
155
|
+
when `size` is omitted and never participate in label collision. Setting `hatch.pattern` instead
|
|
156
|
+
emits a repeated sprite texture clipped to the resolved fill width. No raw source filter or generated
|
|
157
|
+
layer ID is needed. `hatch.patternWidths` can list intrinsic sprite heights named
|
|
158
|
+
`${pattern}-${width}`; the compiler selects the nearest height from the resolved road width so the
|
|
159
|
+
texture's marks remain approximately constant in screen pixels across classes, ramps, and zooms.
|
|
160
|
+
|
|
161
|
+
`classes.pedestrian` styles line-like pedestrian ways. Polygon pedestrian plazas are a distinct
|
|
162
|
+
geometry and use `areas.pedestrian`, including optional fill opacity, outline color, or sprite
|
|
163
|
+
pattern. This prevents plazas from degrading into a thin polygon outline.
|
|
164
|
+
|
|
165
|
+
Road treatments refine a class without creating another class or exposing source fields.
|
|
166
|
+
`construction`, `expressway`, `indoor`, `official`, `ramp`, and `unpaved` live under `modifiers`;
|
|
167
|
+
general, bicycle, foot, horse, and toll restrictions live under `restrictions`; `alley`,
|
|
168
|
+
`crossover`, `driveway`, `parkingAisle`, and `yard` live under `serviceTypes`; and exact
|
|
169
|
+
mountain-bike difficulty values live under `mountainBike`. Treatments reuse
|
|
170
|
+
surface/tunnel/bridge and casing/fill/shadow paint controls, plus a relative `widthScale`. The
|
|
171
|
+
compiler emits feature-driven paint expressions inside the existing stable class layers rather
|
|
172
|
+
than multiplying every possible combination.
|
|
173
|
+
|
|
174
|
+
Road names, shields, and motorway junctions remain label concerns:
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
labels({
|
|
178
|
+
roads: 'all',
|
|
179
|
+
shields: 'major',
|
|
180
|
+
junctions: true,
|
|
181
|
+
styles: {
|
|
182
|
+
shields: {
|
|
183
|
+
default: {
|
|
184
|
+
icon: {
|
|
185
|
+
image: token.image('roads.shield.rectangleNeutral'),
|
|
186
|
+
optional: false,
|
|
187
|
+
pitchAlignment: 'viewport',
|
|
188
|
+
rotationAlignment: 'viewport',
|
|
189
|
+
textFit: 'width',
|
|
190
|
+
textFitPadding: [0, 4, 0, 4],
|
|
191
|
+
},
|
|
192
|
+
text: {
|
|
193
|
+
color: token.color('labels.shieldDark'),
|
|
194
|
+
font: token.font('places'),
|
|
195
|
+
optional: false,
|
|
196
|
+
pitchAlignment: 'viewport',
|
|
197
|
+
rotationAlignment: 'viewport',
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
overview: {minZoom: 6, maxZoom: 11, placement: 'point'},
|
|
201
|
+
detail: {minZoom: 11, placement: 'line', spacing: 400},
|
|
202
|
+
kinds: {
|
|
203
|
+
'rectangle-neutral': {image: token.image('roads.shield.rectangleNeutral')},
|
|
204
|
+
'rectangle-blue': {image: token.image('roads.shield.rectangleBlue')},
|
|
205
|
+
},
|
|
206
|
+
textColors: {
|
|
207
|
+
dark: {color: token.color('labels.shieldDark')},
|
|
208
|
+
light: {color: token.color('labels.shieldLight')},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
junctions: {
|
|
212
|
+
text: {
|
|
213
|
+
color: token.color('labels.strong'),
|
|
214
|
+
haloColor: token.color('labels.halo'),
|
|
215
|
+
haloWidth: fixed(2, {reason: 'Junction halo weight is invariant'}),
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`icon.textFit` and `icon.textFitPadding` let a symbol background grow with its text. Setting both
|
|
223
|
+
icon and text `optional: false` keeps a route shield atomic during collision placement: neither an
|
|
224
|
+
empty badge nor a detached reference can render alone. `overview` renders producer-selected points
|
|
225
|
+
at low zoom and `detail` switches to line placement at street zooms; exclusive `maxZoom: 11` and
|
|
226
|
+
inclusive `minZoom: 11` make that handoff atomic. Keeping both icon and text aligned to `viewport`
|
|
227
|
+
makes the badge horizontal regardless of road bearing.
|
|
228
|
+
|
|
229
|
+
`kinds` and `textColors` are closed presentation tables. The vector producer supplies semantic
|
|
230
|
+
`shield_kind`, `shield_text_color`, `shield_text`, and `shield_rank` values; Core only matches them
|
|
231
|
+
against the authored tables and falls back to `default`. Country, route-prefix, and network-shape
|
|
232
|
+
rules therefore stay in the data authority instead of leaking into a map theme or multiplying
|
|
233
|
+
runtime layers.
|
|
234
|
+
|
|
235
|
+
Tileflow World owns POI classification, editorial zoom eligibility, and cross-source ranking. Its
|
|
236
|
+
canonical `poi` layer exposes `category`, `type`, `icon`, `min_zoom`, `filter_rank`, and `size_rank`;
|
|
237
|
+
Core validates that complete contract and never reconstructs those decisions from OpenMapTiles
|
|
238
|
+
`class`, `subclass`, or `rank`. `filter_rank` is an
|
|
239
|
+
integer from 0 through 5, with lower values reserved for the strongest candidates. The numeric
|
|
240
|
+
`density` option is an inclusive threshold from 1 through 5 and defaults to 3. `size_rank` is an
|
|
241
|
+
integer from 0 through 16 used to order eligible candidates before MapLibre performs final
|
|
242
|
+
collision placement.
|
|
243
|
+
|
|
244
|
+
The closed category vocabulary is `arts-entertainment`, `education`, `food-drink`, `landmark`,
|
|
245
|
+
`lodging`, `medical`, `park-nature`, `public-services`, `religion`, `retail`, `sport-leisure`,
|
|
246
|
+
`transport`, and `visitor-amenity`. Producer `type` and `icon` values use stable snake_case names.
|
|
247
|
+
When an `icon` sprite exists it is used directly; an unknown or absent icon safely falls back to the
|
|
248
|
+
category's themed image role. `labels` and `icons` are booleans. Category styles control only
|
|
249
|
+
presentation and may hide or delay candidates, never promote them above the producer's selection.
|
|
250
|
+
`placement.coupleIconAndLabel` keeps a POI's icon and label atomic during collision placement.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
poi({
|
|
254
|
+
categories: ['food-drink', 'landmark', 'transport'],
|
|
255
|
+
density: 3,
|
|
256
|
+
icons: true,
|
|
257
|
+
labels: true,
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The same contract drives regular POI layers, custom HUD treatments, semantic interactions, and
|
|
262
|
+
feature inspection. There is intentionally no compatibility path for the former class mapping,
|
|
263
|
+
rank ceilings, or named density/detail presets.
|