@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.
- package/README.md +56 -69
- package/THIRD_PARTY_NOTICES.md +3 -3
- package/dist/{3d-CzLTD0RK.d.ts → 3d-CW5Ls0An.d.ts} +32 -2
- package/dist/3d.d.ts +1 -1
- package/dist/3d.js +425 -50
- package/dist/browser.d.ts +95 -5
- package/dist/browser.js +866 -101
- package/dist/build.d.ts +8 -4
- package/dist/build.js +250 -3
- package/dist/config-reference.json +3395 -2140
- package/dist/{icon-composition-B1A2udKb.d.ts → icon-composition-Bbw9umUA.d.ts} +37 -2
- package/dist/index.d.ts +13 -4
- package/dist/index.js +193 -3
- package/dist/{manifest-types-B0lQjabX.d.ts → manifest-types-CwjHd4qc.d.ts} +8 -0
- package/dist/manifest.d.ts +2 -2
- package/dist/native-profile.d.ts +4 -0
- package/dist/native-profile.js +2 -0
- package/dist/native.js +789 -462
- package/dist/runtime.d.ts +1 -1
- package/dist/worker/camera-tile-worker.global.js +2 -2
- package/package.json +5 -6
- package/docs/application-tile-reading.md +0 -86
- package/docs/authoring-model.md +0 -269
- package/docs/boundary-areas-and-choropleths.md +0 -230
- package/docs/buildings-3d.md +0 -624
- package/docs/camera-moves-and-tile-cache.md +0 -112
- package/docs/capture-scenes.md +0 -40
- package/docs/capture-shots.md +0 -173
- package/docs/cartographic-defaults.md +0 -214
- package/docs/compiled-style-performance.md +0 -286
- package/docs/data-is-separate-from-design.md +0 -610
- package/docs/depth-markers.md +0 -65
- package/docs/dynamic-style-images.md +0 -100
- package/docs/globe-atmosphere.md +0 -113
- package/docs/hosted-session-authorization.md +0 -106
- package/docs/map-and-terrain-examples.md +0 -156
- package/docs/map-emphasis.md +0 -89
- package/docs/native-artifact-profile.md +0 -152
- package/docs/native-resource-urls.md +0 -369
- package/docs/nautical-marks.md +0 -308
- package/docs/public-api-and-browser-subpath.md +0 -101
- package/docs/route-drawing.md +0 -191
- package/docs/shared-visual-primitives.md +0 -101
- package/docs/street-lights.md +0 -127
- package/docs/street-sky.md +0 -156
- package/docs/style-states.md +0 -73
- package/docs/sun-and-light.md +0 -167
- package/docs/theme-transitions-and-blends.md +0 -317
- package/docs/themes-and-module-styles.md +0 -156
- package/docs/trees-3d.md +0 -295
package/docs/authoring-model.md
DELETED
|
@@ -1,269 +0,0 @@
|
|
|
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. `models`, the directories of a map's own
|
|
93
|
-
3D models, follows the same rule. `fonts` and `glyphs` are mutually
|
|
94
|
-
exclusive text providers; declaring either atomically replaces an inherited provider of either
|
|
95
|
-
kind. Unknown keys and former compatibility shapes are rejected.
|
|
96
|
-
|
|
97
|
-
Most authors should extend an existing official or application map. A complete standalone map uses
|
|
98
|
-
the same `defineMap()` call without `extends`:
|
|
99
|
-
|
|
100
|
-
```ts
|
|
101
|
-
import {defineMap} from '@tileflow/core';
|
|
102
|
-
import {streetsIcons, streetsThemes} from '@tileflow/maps';
|
|
103
|
-
|
|
104
|
-
const companyBase = defineMap({
|
|
105
|
-
id: 'company-base',
|
|
106
|
-
name: 'Company base',
|
|
107
|
-
version: 1,
|
|
108
|
-
glyphs: {
|
|
109
|
-
kind: 'url',
|
|
110
|
-
url: 'https://api.tileflow.dev/base/33d4de5e8086d9d629d67d3f39fedb87e23686c4c1ac653c27e2a52aee9d00b3/glyphs/{fontstack}/{range}.pbf',
|
|
111
|
-
fontStacks: ['Noto Sans Regular', 'Noto Sans Bold'],
|
|
112
|
-
},
|
|
113
|
-
icons: [streetsIcons],
|
|
114
|
-
themes: {light: streetsThemes.light, dark: streetsThemes.dark},
|
|
115
|
-
defaultTheme: 'light',
|
|
116
|
-
systemThemes: {light: 'light', dark: 'dark'},
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
export default defineMap({
|
|
120
|
-
id: 'company-navigation',
|
|
121
|
-
version: 1,
|
|
122
|
-
extends: companyBase,
|
|
123
|
-
projection: 'globe',
|
|
124
|
-
});
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
The standalone map above is complete as written. A map always owns its complete text provider; Core does not
|
|
128
|
-
obtain fonts or sprites from World and never invents a fallback URL. The URL-backed first-party
|
|
129
|
-
`streets`, `soundings`, and `blueprint` maps declare their glyph
|
|
130
|
-
providers directly. `baedeker`, `civica`, `map1777`, and `siegfried`
|
|
131
|
-
declare packaged fonts, so ordinary imports and derived maps compile without out-of-band release
|
|
132
|
-
metadata.
|
|
133
|
-
|
|
134
|
-
```mermaid
|
|
135
|
-
flowchart LR
|
|
136
|
-
A["tileflow.config.ts exports one map"] --> B["Resolve imported map lineage"]
|
|
137
|
-
B --> C["Complete semantic design + closed domain registry"]
|
|
138
|
-
C --> D["Domain IR + assembly + render stacks"]
|
|
139
|
-
D --> E["Physical planner"]
|
|
140
|
-
E --> F["Single MapLibre lowering"]
|
|
141
|
-
F --> G["Validated Style JSON + compilation report"]
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Modules are keyed by domain, so object order never controls rendering and a domain can appear only
|
|
145
|
-
once. A map extending Streets inherits its complete module set. Use `disable()` to remove a domain
|
|
146
|
-
deliberately. The public domains are `land`, `water`, `nautical`, `roads`, `buildings`, `boundaries`,
|
|
147
|
-
`labels`, `poi`, `aeroways`, `transit`, `vegetation`, `addresses`, and `landforms`.
|
|
148
|
-
The closed domain registry is exhaustive over that domain set and owns defaults, dependency order,
|
|
149
|
-
services, and compiler orchestration. Contract tests keep its keys and type tags in lockstep with
|
|
150
|
-
the strict resolved schema, generated authoring/options/patch/resolved schema aliases, and public
|
|
151
|
-
exports. Disabling `roads` also removes dependent road names, shields, and junction references;
|
|
152
|
-
independent place, water, aerodrome, and POI labels remain under the labels module.
|
|
153
|
-
|
|
154
|
-
Every styling module supports semantic shortcuts and exact semantic targets. Visual style values
|
|
155
|
-
accept typed token refs, documented `fixed(value, {reason})` values, closed data expressions through
|
|
156
|
-
`expr.*` plus `field(...)`, and zoom functions through `zoom.step(...)`, `zoom.linear(...)`, or
|
|
157
|
-
`zoom.exponential(...)`. Refs and fixed leaves also work as the results of typed expressions and
|
|
158
|
-
as zoom-stop values; conditions and other operands take plain values, such as `3` rather than
|
|
159
|
-
`fixed(3)` in a rank test inside a colour expression. Structural controls such as presets,
|
|
160
|
-
visibility, text fields, and zoom gates remain ordinary literals. Expression builders take at most
|
|
161
|
-
16 branches, stops, or operands; an error names the received count and the config line of the
|
|
162
|
-
call, and a larger lookup composes nested groups.
|
|
163
|
-
|
|
164
|
-
Layout properties, such as text fields, text and icon sizes, and symbol placement, are evaluated at
|
|
165
|
-
each tile's integer zoom: a step at zoom 17.5 takes effect at zoom 18. Paint properties follow the
|
|
166
|
-
camera's exact zoom. `createStyleResult()` reports a `TF_LAYOUT_FRACTIONAL_ZOOM_STEP` warning with
|
|
167
|
-
the semantic target for every layout step at a fractional zoom. Exact
|
|
168
|
-
controls address stable concepts such as `roads.classes.primary.surface.fill`, not renderer layer
|
|
169
|
-
IDs.
|
|
170
|
-
|
|
171
|
-
`expr.id()` reads the source-owned feature identifier: a string, number, or `null` when absent.
|
|
172
|
-
It performs no I/O and does not derive an identifier from geometry. `expr.modulo(dividend, divisor)`
|
|
173
|
-
uses native numeric remainder, including its sign behavior. Normalize input and provide a deliberate
|
|
174
|
-
fallback before using numeric IDs for decorative variation; missing or non-numeric IDs must not
|
|
175
|
-
silently imply distinct features. Identity stability across data releases belongs to the producer.
|
|
176
|
-
|
|
177
|
-
<!-- docs:check -->
|
|
178
|
-
|
|
179
|
-
```ts
|
|
180
|
-
import {expr} from '@tileflow/core';
|
|
181
|
-
|
|
182
|
-
const numericIdentity = expr.abs(expr.toNumber(expr.id(), 0));
|
|
183
|
-
const decorativeBucket = expr.modulo(numericIdentity, 7);
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
`expr.featureState(name)` reads a value an application sets on one feature at runtime, such as a
|
|
187
|
-
selection or a hover. `expr.globalState(name)` reads a value an application sets once for the whole
|
|
188
|
-
style with MapLibre's `map.setGlobalStateProperty(name, value)`, such as whether anything is
|
|
189
|
-
selected. Both read `null` until set, so wrap them in a typed assertion with a fallback. MapLibre
|
|
190
|
-
applies a global-state change to paint properties at once, without the style's paint transition.
|
|
191
|
-
|
|
192
|
-
<!-- docs:check -->
|
|
193
|
-
|
|
194
|
-
```ts
|
|
195
|
-
import {expr} from '@tileflow/core';
|
|
196
|
-
|
|
197
|
-
const selecting = expr.toBoolean(expr.globalState('selection'), false);
|
|
198
|
-
const captionInk = expr.case([{when: selecting, value: '#1f2a44'}], '#ffffff');
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
The `land` module exposes stable land-use targets for `cemetery`, `civic`, `commercial`,
|
|
202
|
-
`education`, `government`, `industrial`, `medical`, `military`, `parking`, `railway`,
|
|
203
|
-
`recreation`, and `residential`. Its land-cover taxonomy distinguishes physical cover from authored
|
|
204
|
-
urban green: `farmland`, `flowerbed`, `grass`, `ice`, `meadow`, `protected`,
|
|
205
|
-
`recreationGround`, `rock`, `sand`, `scrub`, `urbanPark`, `villageGreen`, `wetland`, and
|
|
206
|
-
`wood`. The plain `grass` branch excludes every typed grass subclass, so one source feature cannot
|
|
207
|
-
receive two opaque green fills. Parking areas are polygons from the configured land-use source;
|
|
208
|
-
parking access aisles remain road features under `roads.serviceTypes.parkingAisle`. The
|
|
209
|
-
`commercial` target recognizes ordinary `commercial` and `retail` values plus Tileflow's derived
|
|
210
|
-
`business_area` ground class. Its `globalLandcover` fill styles the optional low-zoom global
|
|
211
|
-
land-cover extension without a raw layer patch. Its `barriers` draw `hedge`, `fence` and `wall`
|
|
212
|
-
from the data's barrier layer (Tileflow World V1's `traffic_control`) at the top of the land band:
|
|
213
|
-
a `line`, a `shadow` line under it, and an `area` fill for the hedges the data maps as areas.
|
|
214
|
-
Nothing is drawn for a barrier without a style, nor for data without the layer. The `water.bathymetry` fill does the same for the
|
|
215
|
-
typed Tileflow World V1 depth bands. An explicit `water({bathymetryContours: {}})` traces the
|
|
216
|
-
submerged edges between those discrete polygon bands; it is an approximate visual contour, not a
|
|
217
|
-
surveyed isoline. `water({bathymetryLabels: {}})` separately opts into numeric metre values derived
|
|
218
|
-
from each polygon's absolute minimum band depth. Existing maps gain neither detail by default, and
|
|
219
|
-
sources without bathymetry bindings omit both requested layers. These labels describe coarse band
|
|
220
|
-
floors, not measured survey soundings, and must not be presented as hydrographic sounding data.
|
|
221
|
-
An explicitly selected Bathymetry source can instead declare `vectorLayers.contours: true` to use
|
|
222
|
-
real contour lines and their `depth` values. Its optional named seafloor and water features have
|
|
223
|
-
separate `water.bathymetryLandforms` and `water.bathymetryWaterNames` styles, with the language
|
|
224
|
-
selected by `labels(...)`. See [the marine source contract](data-is-separate-from-design.md#declare-contour-and-name-layers).
|
|
225
|
-
Those name styles can explicitly select placement for each geometry family with
|
|
226
|
-
`placementByGeometry`; omitting it preserves scalar placement.
|
|
227
|
-
|
|
228
|
-
`addresses` renders the standard OpenMapTiles `housenumber` points at detailed zooms and exposes a
|
|
229
|
-
single semantic `labels` style. `landforms` renders the standard `mountain_peak` classes (`peak`,
|
|
230
|
-
`volcano`, `saddle`, `ridge`, `cliff`, and `arete`) with rank-aware collision priority and optional
|
|
231
|
-
metric elevation. Both source-layer and field names remain remappable through `openMapTiles(...)`;
|
|
232
|
-
landform names share the language selected by `labels({language: ...})`.
|
|
233
|
-
|
|
234
|
-
`aeroways.runwayRef` owns high-zoom runway designators from the remappable OpenMapTiles `ref`
|
|
235
|
-
field. Aerodrome names stay under `labels.styles.aerodrome`; `labels.aerodromeCodes` controls whether
|
|
236
|
-
they append no code, IATA only, or IATA with an ICAO fallback (`'none' | 'iata' | 'all'`).
|
|
237
|
-
|
|
238
|
-
The `vegetation` module binds individual-tree points from the optional OpenMapTiles `tree`
|
|
239
|
-
extension. Its default `mode: '3d'` emits a portable circle fallback plus metadata for runtimes
|
|
240
|
-
that can upgrade the points to instanced 3D trees. `mode: 'flat'` keeps the MapLibre circles, and
|
|
241
|
-
`flat` exposes the complete `CircleStyle` fallback. `threeDimensional` controls bark color,
|
|
242
|
-
broadleaf and conifer palettes, and independent height and crown scales. `flat.minZoom` sets the
|
|
243
|
-
first zoom with trees. The binding includes height, crown diameter, genus, leaf type, and species
|
|
244
|
-
fields so compatible runtimes can preserve source measurements and botanical form without
|
|
245
|
-
hard-coding raw property names. The module does not draw the unrelated, decorative `canopy` layer;
|
|
246
|
-
render passes can, as in the [canopy grove recipe](data-is-separate-from-design.md). The
|
|
247
|
-
pitched-scene stack follows physical height: pedestrian and transport surfaces, transport markings,
|
|
248
|
-
buildings, then vegetation, so street paint never floats over 3D geometry. Text draws last: road
|
|
249
|
-
names, route shields, junction numbers, and place, water, aerodrome, and POI annotations stay above
|
|
250
|
-
every ground layer, so building fills, boundaries, and trees cannot cover them.
|
|
251
|
-
`labels.collisionPriority` only orders labels within that final stack.
|
|
252
|
-
|
|
253
|
-
Render passes, and refinements that move a target their module owns, share one `placement`
|
|
254
|
-
vocabulary. A band edge is `below-` or `above-` one of `land`, `relief`, `water`, `roads`,
|
|
255
|
-
`boundaries`, `buildings`, `vegetation`, and `labels`; it needs no anchor, so a vegetation pass can
|
|
256
|
-
draw `'above-relief'` without depending on another module. `{above: target}` and `{below: target}`
|
|
257
|
-
draw beside one semantic target and inherit its band and, without their own `feature`, its vector
|
|
258
|
-
data; passes placed beside a moved target follow it. Operations sharing a placement paint in
|
|
259
|
-
declaration order, the first lowest. For symbols, a higher layer is also placed first in MapLibre
|
|
260
|
-
collisions, so placement decides both drawing and precedence. An owner whose layers are all
|
|
261
|
-
render passes still satisfies other operations' `requirements`.
|
|
262
|
-
|
|
263
|
-
A target exists only in the mode that draws it: buildings draw `buildings.flat.fill` and
|
|
264
|
-
`buildings.flat.outline` in mode `'flat'`, and `buildings.extrusion` in mode `'3d'`. `refine()`
|
|
265
|
-
keeps the inherited render stack, so refining an inherited module's `mode` fails when a pass is
|
|
266
|
-
placed beside, or a refinement targets, the other mode's target; the error names both modes.
|
|
267
|
-
Streets' shadows and its extrusion pass stand beside its flat buildings. To draw native extrusions
|
|
268
|
-
in a map that extends Streets, remove that stack with `refine({mode: '3d', renderStack: reset()})`,
|
|
269
|
-
or declare `buildings({mode: '3d'})` to replace the module.
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
# Boundary areas and choropleths
|
|
2
|
-
|
|
3
|
-
Tileflow's Boundaries tileset holds administrative and neighbourhood areas worldwide, from
|
|
4
|
-
countries down to districts and neighbourhoods. A map declares it with `boundaryAreas`, and
|
|
5
|
-
`boundaryChoropleth()` colors its areas from an application's values: unemployment by
|
|
6
|
-
municipality, sales by county, election results by district.
|
|
7
|
-
|
|
8
|
-
Boundaries is a separate product from World. World's border lines stay in the `boundaries` module;
|
|
9
|
-
`boundaryAreas` adds the areas themselves, each with a stable ID and official codes to join data
|
|
10
|
-
to.
|
|
11
|
-
|
|
12
|
-
## What the tileset contains
|
|
13
|
-
|
|
14
|
-
The `boundaries` family comes from OpenStreetMap under the
|
|
15
|
-
[Open Database License](https://opendatacommons.org/licenses/odbl/1-0/). Its source carries the
|
|
16
|
-
attribution `© OpenStreetMap contributors`, which MapLibre shows whenever a layer drawn from it is
|
|
17
|
-
on screen. The attribution is fixed by the family and cannot be replaced.
|
|
18
|
-
|
|
19
|
-
It has two layers, from zoom 0 to 12:
|
|
20
|
-
|
|
21
|
-
- `boundary_area`: one polygon per area and class. Its outline is the boundary line.
|
|
22
|
-
- `boundary_label`: one point per area and class, with `name` and `name:<language>`.
|
|
23
|
-
|
|
24
|
-
Polygons carry the properties needed to filter and join them:
|
|
25
|
-
|
|
26
|
-
| Property | Content |
|
|
27
|
-
| ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
-
| feature ID | The area's registry ID. It survives releases while the area exists. |
|
|
29
|
-
| `class` | `country`, `dependency`, `region`, `province_or_county`, `municipality`, `district` or `neighbourhood`. |
|
|
30
|
-
| `class_tier` | `primary` for the level that covers a country with that class, otherwise `secondary`. |
|
|
31
|
-
| `admin_level` | OpenStreetMap's `admin_level`. |
|
|
32
|
-
| `admin_depth` | Depth in the country's administrative chain, from 0 for the country. Neighbourhoods have none. |
|
|
33
|
-
| `ref` | The official code with its scheme, such as `ES:INE:28079` or `US:GEOID:36061`; otherwise ISO 3166-2, or ISO 3166-1 for countries. |
|
|
34
|
-
| `iso_3166_1` | The country's ISO 3166-1 alpha-2 code. |
|
|
35
|
-
|
|
36
|
-
Labels and the release's lookup table also carry `iso_3166_2`, `nuts`, `wikidata` and names.
|
|
37
|
-
|
|
38
|
-
One area can serve several classes. Berlin, for example, is a region, a district and a
|
|
39
|
-
municipality: the tileset draws it once per class, with the same ID and the `ref` of each class.
|
|
40
|
-
Filter a choropleth by class so that only one of those features is visible.
|
|
41
|
-
|
|
42
|
-
Each class appears from a fixed zoom: countries and dependencies from 0, regions from 3, provinces
|
|
43
|
-
and counties from 6 (secondary ones from 7), municipalities from 8, districts from 10 and
|
|
44
|
-
neighbourhoods from 11. `tileflowBoundaryAreasV1Schema.classMinzoom` lists them.
|
|
45
|
-
|
|
46
|
-
## Declare the source
|
|
47
|
-
|
|
48
|
-
<!-- docs:check -->
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
import {defineMap} from '@tileflow/core';
|
|
52
|
-
import {streets} from '@tileflow/maps';
|
|
53
|
-
|
|
54
|
-
export default defineMap({
|
|
55
|
-
id: 'municipal-indicators',
|
|
56
|
-
version: 1,
|
|
57
|
-
extends: streets,
|
|
58
|
-
boundaryAreas: {family: 'boundaries'},
|
|
59
|
-
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
The compiled style gains the vector source `tileflow-boundaries`, which reads
|
|
63
|
-
`https://api.tileflow.dev/tiles/boundaries/tiles.json` (or the same path under the API base URL
|
|
64
|
-
passed to the compiler). `url` replaces it with a compatible TileJSON or a `pmtiles://` archive,
|
|
65
|
-
such as a local copy for offline development; the attribution stays the same. `false` removes
|
|
66
|
-
Boundaries inherited from a parent map.
|
|
67
|
-
|
|
68
|
-
## Color areas from values in the style
|
|
69
|
-
|
|
70
|
-
`values` compiles rows into the style. Rows join by `id` (the registry ID), `ref` or `iso_3166_1`,
|
|
71
|
-
the keys the polygons carry. This example colors municipalities in the Community of Madrid by
|
|
72
|
-
their INE code, the code Spanish statistics publish:
|
|
73
|
-
|
|
74
|
-
<!-- docs:check -->
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
import {boundaryChoropleth, choroplethScale, defineMap} from '@tileflow/core';
|
|
78
|
-
import {streets} from '@tileflow/maps';
|
|
79
|
-
|
|
80
|
-
// Rows as a statistics office publishes them: an INE municipality code and a rate.
|
|
81
|
-
const unemployment = [
|
|
82
|
-
{ine: '28079', rate: 9.8},
|
|
83
|
-
{ine: '28005', rate: 12.4},
|
|
84
|
-
{ine: '28006', rate: 6.1},
|
|
85
|
-
{ine: '28058', rate: 13.7},
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
export default defineMap({
|
|
89
|
-
id: 'madrid-unemployment',
|
|
90
|
-
version: 1,
|
|
91
|
-
extends: streets,
|
|
92
|
-
boundaryAreas: {family: 'boundaries'},
|
|
93
|
-
overlays: {
|
|
94
|
-
unemployment: boundaryChoropleth({
|
|
95
|
-
id: 'unemployment',
|
|
96
|
-
classes: ['municipality'],
|
|
97
|
-
countries: ['ES'],
|
|
98
|
-
scale: choroplethScale.step('#fef0d9', [
|
|
99
|
-
[8, '#fdcc8a'],
|
|
100
|
-
[10, '#fc8d59'],
|
|
101
|
-
[12, '#d7301f'],
|
|
102
|
-
]),
|
|
103
|
-
missingColor: '#d9d9d9',
|
|
104
|
-
outline: {color: '#ffffff', width: 0.75},
|
|
105
|
-
values: {
|
|
106
|
-
joinBy: 'ref',
|
|
107
|
-
rows: unemployment.map(({ine, rate}) => ({key: `ES:INE:${ine}`, value: rate})),
|
|
108
|
-
},
|
|
109
|
-
}),
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Municipalities under 8 % are pale, those from 12 % dark red, and municipalities without a row
|
|
115
|
-
gray. The overlay adds the layers `unemployment-fill` and `unemployment-outline` below the
|
|
116
|
-
basemap's labels. Their minimum zoom defaults to the first zoom of the selected classes, here 8.
|
|
117
|
-
|
|
118
|
-
A choropleth accepts these options:
|
|
119
|
-
|
|
120
|
-
- `classes` (required), `tiers` and `countries` filter the areas;
|
|
121
|
-
- `minzoom` and `maxzoom` limit the zooms;
|
|
122
|
-
- `scale` sets the colors, `missingColor` the color of areas without a value (transparent by
|
|
123
|
-
default) and `opacity` the fill opacity (0.8 by default);
|
|
124
|
-
- `outline` draws the boundary lines;
|
|
125
|
-
- `placement` positions the layers among the basemap's like any overlay (`below-labels` by
|
|
126
|
-
default).
|
|
127
|
-
|
|
128
|
-
## Choose a color scale
|
|
129
|
-
|
|
130
|
-
`choroplethScale` builds validated scales:
|
|
131
|
-
|
|
132
|
-
- `step(base, stops)`: `base` below the first threshold, then one color from each threshold on;
|
|
133
|
-
- `linear(stops)`: colors interpolated between stops and clamped beyond them;
|
|
134
|
-
- `categories({value: color})`: one color per string value.
|
|
135
|
-
|
|
136
|
-
Stops are `[value, color]` pairs in increasing order, at most 64. A numeric scale gives
|
|
137
|
-
`missingColor` to any area whose value is not a number; a category scale gives it to unknown
|
|
138
|
-
categories. `choroplethColorExpression(scale, input, missingColor)` returns the same MapLibre
|
|
139
|
-
expression for any input, for use in layers of your own.
|
|
140
|
-
|
|
141
|
-
## Update values at run time with feature-state
|
|
142
|
-
|
|
143
|
-
Omit `values` and the choropleth reads the feature-state key `value` (or `state`). Set it from the
|
|
144
|
-
application with `createBoundaryFeatureState` from `@tileflow/core/browser` and the map instance:
|
|
145
|
-
|
|
146
|
-
<!-- docs:check -->
|
|
147
|
-
|
|
148
|
-
```ts
|
|
149
|
-
import {createBoundaryFeatureState, type TileflowFeatureStateMap} from '@tileflow/core/browser';
|
|
150
|
-
|
|
151
|
-
// A MapLibre map, such as the one `<Map onLoad>` passes in React.
|
|
152
|
-
export function showRates(map: TileflowFeatureStateMap, rates: ReadonlyMap<number, number>) {
|
|
153
|
-
const state = createBoundaryFeatureState(map);
|
|
154
|
-
state.set({joinBy: 'id', rows: [...rates].map(([key, value]) => ({key, value}))});
|
|
155
|
-
return state;
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
`set` replaces the previous values and removes those no longer present. Values survive a style
|
|
160
|
-
reload, such as a theme change: the controller sets them again once the source is back. `clear`
|
|
161
|
-
removes them, and `dispose` also stops following the map. `labels: true` also sets the values on
|
|
162
|
-
`boundary_label`, for labels styled by value.
|
|
163
|
-
|
|
164
|
-
Feature-state is keyed by the source's feature ID, the registry ID by default. To key it by
|
|
165
|
-
official code instead, declare `boundaryAreas: {family: 'boundaries', promoteId: 'ref'}` and set
|
|
166
|
-
rows with `joinBy: 'ref'`. A map that promotes `ref` cannot also join values by `id`; compilation
|
|
167
|
-
fails with `TF_BOUNDARY_CHOROPLETH_JOIN`.
|
|
168
|
-
|
|
169
|
-
In React, `useBoundaryFeatureState(map, values, options)` from `@tileflow/react` keeps
|
|
170
|
-
feature-state equal to `values`; see the
|
|
171
|
-
[React README](https://github.com/tileflow/tileflow-sdk/blob/main/packages/react/README.md). In
|
|
172
|
-
React Native, the `Map` prop `boundaryFeatureState={{values, labels, state}}` does the same with
|
|
173
|
-
the same rows and options; see the
|
|
174
|
-
[React Native README](https://github.com/tileflow/tileflow-sdk/blob/main/packages/react-native/README.md).
|
|
175
|
-
|
|
176
|
-
## Join by codes the polygons do not carry
|
|
177
|
-
|
|
178
|
-
Rows keyed by `iso_3166_1` or `ref` need no table: the polygons carry both codes, and a map that
|
|
179
|
-
promotes `ref` can set feature-state by official code directly. Rows keyed by `iso_3166_2`, `nuts`
|
|
180
|
-
or `wikidata`, or keyed by `ref` while feature-state uses registry IDs, go through the release's
|
|
181
|
-
lookup table.
|
|
182
|
-
|
|
183
|
-
The table is split by class, one gzip CSV per class with one row per area of that class:
|
|
184
|
-
`boundaries-lookup-country.csv.gz`, `boundaries-lookup-region.csv.gz`, and so on for every class.
|
|
185
|
-
The Boundaries TileJSON lists their URLs under `tileflow.boundaries.lookup.classes`, by class, and
|
|
186
|
-
the description of the table under `tileflow.boundaries.lookup.manifest`. Fine classes cover many
|
|
187
|
-
areas worldwide, so join where the data is prepared, on a server or at build time, and send the
|
|
188
|
-
application the joined rows, keyed by `id` or `ref`. Parse a file with `parseBoundaryLookupCsv`;
|
|
189
|
-
`joinBoundaryRows` then re-keys the rows:
|
|
190
|
-
|
|
191
|
-
<!-- docs:check -->
|
|
192
|
-
|
|
193
|
-
```ts
|
|
194
|
-
import {readFile} from 'node:fs/promises';
|
|
195
|
-
import {gunzipSync} from 'node:zlib';
|
|
196
|
-
import {createBoundaryLookup, joinBoundaryRows, parseBoundaryLookupCsv} from '@tileflow/core';
|
|
197
|
-
|
|
198
|
-
// A downloaded `boundaries-lookup-region.csv.gz`, read where the data is prepared.
|
|
199
|
-
export async function regionsByNuts(lookupFile: string, rows: {nuts: string; value: number}[]) {
|
|
200
|
-
const text = gunzipSync(await readFile(lookupFile)).toString('utf8');
|
|
201
|
-
const lookup = createBoundaryLookup(parseBoundaryLookupCsv(text));
|
|
202
|
-
const joined = joinBoundaryRows(
|
|
203
|
-
rows.map(({nuts, value}) => ({key: nuts, value})),
|
|
204
|
-
{joinBy: 'nuts', lookup, classes: ['region']},
|
|
205
|
-
);
|
|
206
|
-
if (joined.unmatched.length > 0) console.warn('Unmatched NUTS codes', joined.unmatched);
|
|
207
|
-
return joined;
|
|
208
|
-
}
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Rows from several classes join against one lookup built from the rows of each class file. Lookup
|
|
212
|
-
downloads are web delivery, counted like stored tiles; a native app does not download them.
|
|
213
|
-
|
|
214
|
-
The result has the same `{joinBy, rows}` shape that `values` and `set` accept, keyed by `id` or,
|
|
215
|
-
with `to: 'ref'`, by `ref`. It also lists the keys it could not use:
|
|
216
|
-
|
|
217
|
-
- `unmatched`: keys no area has;
|
|
218
|
-
- `ambiguous`: keys shared by more than one area, which are left out;
|
|
219
|
-
- `duplicates`: keys repeated in the rows, where the last row wins.
|
|
220
|
-
|
|
221
|
-
ISO 3166, NUTS and Wikidata codes are compared upper-cased; `ref` is compared as written, so a
|
|
222
|
-
customer's bare INE code needs its `ES:INE:` prefix.
|
|
223
|
-
|
|
224
|
-
## Limits
|
|
225
|
-
|
|
226
|
-
- Values compiled into the style grow it with every row. Use feature-state for large or changing
|
|
227
|
-
data.
|
|
228
|
-
- The `boundaries-official` family, with one official source per country and level, is not yet
|
|
229
|
-
available.
|
|
230
|
-
- Tile URLs and the lookup table do not establish redistribution rights beyond the ODbL.
|