@tileflow/core 0.1.0-alpha.24 → 0.1.0-alpha.26
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 +166 -1228
- package/dist/build.d.ts +9 -4
- package/dist/build.js +831 -619
- package/dist/config-reference.json +1703 -1661
- package/dist/{map-BHL_0Mkx.d.ts → icon-composition-BssSKOKF.d.ts} +83 -3
- package/dist/index.d.ts +141 -3
- package/dist/index.js +1014 -608
- package/dist/native.d.ts +31 -0
- package/dist/native.js +143 -0
- package/docs/authoring-model.md +198 -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/hosted-session-authorization.md +113 -0
- package/docs/map-and-terrain-examples.md +149 -0
- package/docs/native-resource-urls.md +117 -0
- package/docs/public-api-and-browser-subpath.md +86 -0
- package/docs/shared-visual-primitives.md +22 -0
- package/docs/themes-and-module-styles.md +83 -0
- package/package.json +8 -2
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# Data is separate from design
|
|
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
|
+
Team data uses named logical sources and validated MapLibre overlays. The hosted identity never
|
|
6
|
+
contains a CDN URL; `local` is the explicit account-free PMTiles resolution for Dev and Capture:
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import {defineMap, hostedTileset, maplibreOverlay} from '@tileflow/core';
|
|
10
|
+
import {streets} from '@tileflow/maps';
|
|
11
|
+
|
|
12
|
+
const stores = hostedTileset({
|
|
13
|
+
tileset: 'stores',
|
|
14
|
+
local: './data/stores.pmtiles',
|
|
15
|
+
attribution: 'Store data © Example',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export default defineMap({
|
|
19
|
+
id: 'store-locator',
|
|
20
|
+
version: 1,
|
|
21
|
+
extends: streets,
|
|
22
|
+
sources: {stores},
|
|
23
|
+
overlays: {
|
|
24
|
+
stores: maplibreOverlay({
|
|
25
|
+
source: 'stores',
|
|
26
|
+
placement: 'above-roads',
|
|
27
|
+
layers: [
|
|
28
|
+
{
|
|
29
|
+
id: 'stores-points',
|
|
30
|
+
type: 'circle',
|
|
31
|
+
'source-layer': 'store_locations',
|
|
32
|
+
paint: {
|
|
33
|
+
'circle-color': ['match', ['get', 'category'], 'restaurant', '#ef4444', '#64748b'],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Tileflow automatically registers only protocols in namespaces that Tileflow owns.
|
|
43
|
+
`tileflow-pmtiles://` is reserved for PMTiles resources managed by Tileflow. Tileflow never
|
|
44
|
+
automatically registers, removes, or replaces the global `pmtiles://` handler. Explicit PMTiles
|
|
45
|
+
sources supplied by an application remain the application's responsibility.
|
|
46
|
+
|
|
47
|
+
For local resolution, the Style URL is based on the logical tileset ID and remains stable when Dev
|
|
48
|
+
creates a new immutable snapshot. Physical generation IDs and local content hashes never enter the
|
|
49
|
+
Style representation. Each Dev PMTiles request retains the snapshot it resolved until its response
|
|
50
|
+
is prepared, and strong generation ETags prevent one cached PMTiles read from silently combining
|
|
51
|
+
ranges from different generations. Standalone Capture retains one snapshot for its complete render.
|
|
52
|
+
Application Capture does not freeze every local dataset across the complete screenshot window; the
|
|
53
|
+
application's development server remains authoritative for its per-read generation.
|
|
54
|
+
|
|
55
|
+
`local` does not make build output own or publish the archive. Production builds reject unresolved
|
|
56
|
+
local PMTiles. Explicitly publish the tileset through Tileflow's managed upload, validation,
|
|
57
|
+
versioning, and hosting flow, or serve it from infrastructure owned by the application.
|
|
58
|
+
|
|
59
|
+
One overlay owns one ordered group of ordinary MapLibre Style Layers. Public layer IDs are retained
|
|
60
|
+
for `map.on(...)`, feature queries, and application state. Placement is one of `above-water`,
|
|
61
|
+
`below-roads`, `above-roads`, `above-buildings`, `below-labels`, or `above-labels`; overlays sharing
|
|
62
|
+
a placement sort by overlay ID. A map accepts up to 16 Team sources in addition to Tileflow World
|
|
63
|
+
and terrain. These semantic boundaries do not expose generated basemap layer IDs.
|
|
64
|
+
`sources` and `overlays` merge by key through `extends`, declarations replace atomically, and
|
|
65
|
+
`remove()` deletes one inherited key. Removing a still-used source fails validation. There is no
|
|
66
|
+
interaction DSL or parallel expression language.
|
|
67
|
+
|
|
68
|
+
Omitting `data` selects the compiler-owned Tileflow World `v1` compatibility generation:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
import {streets} from '@tileflow/maps';
|
|
72
|
+
|
|
73
|
+
export default streets;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Theme typography can set `fallbacks`, `letterSpacing`, and `transform` globally or per label
|
|
77
|
+
domain, while the font token catalog gives module styles a stable semantic target. Text delivery is
|
|
78
|
+
explicit and atomic: a map may declare either ordered `fonts` directories
|
|
79
|
+
for browser font files or a `glyphs` provider for PBF glyphs, never both. Omitting both inherits the
|
|
80
|
+
parent's provider; declaring either replaces an inherited provider of either kind. After resolution,
|
|
81
|
+
a map that emits text must have exactly one provider. A text-free standalone map may omit both. On a derived
|
|
82
|
+
map, omission inherits the parent provider while `fonts: []` explicitly removes it; that empty array
|
|
83
|
+
is valid only when the resolved map emits no text.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import {defineMap, defineTheme} from '@tileflow/core';
|
|
87
|
+
import {streets, streetsThemes} from '@tileflow/maps';
|
|
88
|
+
|
|
89
|
+
const brandLight = defineTheme(streetsThemes.light, {
|
|
90
|
+
id: 'brand-light',
|
|
91
|
+
version: 1,
|
|
92
|
+
colorScheme: 'light',
|
|
93
|
+
tokens: {font: {default: 'Brand Sans Regular', places: 'Brand Sans Regular'}},
|
|
94
|
+
});
|
|
95
|
+
const brandDark = defineTheme(streetsThemes.dark, {
|
|
96
|
+
id: 'brand-dark',
|
|
97
|
+
version: 1,
|
|
98
|
+
colorScheme: 'dark',
|
|
99
|
+
tokens: {font: {default: 'Brand Sans Regular', places: 'Brand Sans Regular'}},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export default defineMap({
|
|
103
|
+
id: 'brand-map',
|
|
104
|
+
version: 1,
|
|
105
|
+
extends: streets,
|
|
106
|
+
fonts: ['./fonts'],
|
|
107
|
+
themes: {light: brandLight, dark: brandDark},
|
|
108
|
+
defaultTheme: 'light',
|
|
109
|
+
systemThemes: {light: 'light', dark: 'dark'},
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Node preparation reads TTF, OTF, and WOFF2 files, uses each OpenType full name as its exact
|
|
114
|
+
`text-font` ID, and requires `LICENSE.txt` in every contributing directory. Directories apply left
|
|
115
|
+
to right; a later exact name replaces an earlier face and case-only collisions fail. Only primary
|
|
116
|
+
faces used by the final style become content-addressed assets and strict `tileflow:fontFaces`
|
|
117
|
+
metadata. `font` names an exact OpenType full name or glyph face; local `fallbacks` name exact faces
|
|
118
|
+
or explicit CSS generic families. Tileflow never appends a weight suffix or derives one face from
|
|
119
|
+
another. Browser adapters load local faces before MapLibre. Native releases use a locked PBF
|
|
120
|
+
`glyphs` URL provider instead, which enumerates the exact comma-joined MapLibre request keys in
|
|
121
|
+
`fontStacks`.
|
|
122
|
+
|
|
123
|
+
Streets, Ferraris, Härad, Soundings, Verdant, and San Francisto each declare
|
|
124
|
+
`https://api.tileflow.dev/fonts/{fontstack}/{range}.pbf` with the exact `Noto Sans Regular` and
|
|
125
|
+
`Noto Sans Bold` stacks. That compatibility URL is canonical but not content-addressed; responses revalidate and
|
|
126
|
+
do not make a resolved map byte-reproducible. Exact official glyph identity belongs to the
|
|
127
|
+
separately published `/base/<assetSetSha256>/glyphs/...` global base-asset contract. In that URL,
|
|
128
|
+
`assetSetSha256` identifies the standalone glyph collection; it is not the same-domain value as the
|
|
129
|
+
per-map `assetSetSha256` in `build-manifest.json`.
|
|
130
|
+
Cyberpunk and Matrix each replace the URL provider with their own packaged Oxanium directory and
|
|
131
|
+
reference the exact local faces `Oxanium Medium` and `Oxanium SemiBold`. Baedeker and Siegfried
|
|
132
|
+
each own a packaged directory containing Cormorant Garamond Regular, SemiBold, and Italic.
|
|
133
|
+
|
|
134
|
+
Name the official generation deliberately, or use another OpenMapTiles-compatible vector source:
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import {openMapTiles, tileflowWorld, vectorTiles} from '@tileflow/core';
|
|
138
|
+
|
|
139
|
+
const official = tileflowWorld();
|
|
140
|
+
const external = vectorTiles({
|
|
141
|
+
tiles: ['pmtiles://./test/fixtures/world.pmtiles'],
|
|
142
|
+
revision: 'fixture-1',
|
|
143
|
+
attribution: '© Example © OpenStreetMap contributors',
|
|
144
|
+
schema: openMapTiles(),
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The resolved map controls how data is drawn; `data` controls where compatible features come from.
|
|
149
|
+
For Tileflow World, the compiler emits the selector TileJSON URL
|
|
150
|
+
`https://api.tileflow.dev/tiles/world/tiles.json`. Omitted data or `tileflowWorld()` selects
|
|
151
|
+
`world-v1/current`; `tileflowWorld({release: {releaseId, descriptorSha256}})` binds an exact release
|
|
152
|
+
through query parameters. Runtime and capture resolve `current` once per session or job and then
|
|
153
|
+
use the immutable release described by that response. `openMapTiles({layers, fields})` can bind renamed
|
|
154
|
+
source-layers or properties while preserving the versioned semantic contract. External browser
|
|
155
|
+
credentials do not belong in public Style JSON; supply them through the framework's
|
|
156
|
+
`transformRequest` integration.
|
|
157
|
+
|
|
158
|
+
External exact-test sources may use one TileJSON `url` or a bounded direct `tiles` list with optional
|
|
159
|
+
`bounds`, `minzoom`, and `maxzoom`. `pmtiles://` is supported for a checked-in or bring-your-own
|
|
160
|
+
archive. Public vector URLs are HTTPS, root-relative, or HTTP only on loopback development hosts.
|
|
161
|
+
A PMTiles URL names one `.pmtiles` archive through an HTTPS, loopback HTTP, root-relative, or safe
|
|
162
|
+
repository-relative target; credentials, fragments, traversal, and other protocols are rejected at
|
|
163
|
+
both authoring validation and compilation. An external `revision` participates in capture identity,
|
|
164
|
+
so changing fixture bytes cannot silently reuse a baseline.
|
|
165
|
+
|
|
166
|
+
Tileflow's OpenMapTiles contract also recognizes the optional `globallandcover` extension. The
|
|
167
|
+
official Streets map maps its `barren`, `crop`, `grass`, `shrub`, `snow`, `trees`, and
|
|
168
|
+
`urban` classes beneath the OSM land layers. Tileflow World V1 supplies native generalized
|
|
169
|
+
geometry through z10; Streets fades it progressively while detailed `landcover`/`landuse` gains
|
|
170
|
+
opacity and removes it at z11. This is a macro bridge, not local semantic detail. Sources with a
|
|
171
|
+
different layer name can use `openMapTiles({layers: {globalLandcover: 'my_landcover'}})`; archives
|
|
172
|
+
without the extension remain compatible and simply render no features for that style layer. Use
|
|
173
|
+
`land({globalLandcover: {...}})` to customize its fill, opacity, visibility, and zoom range. `crop`
|
|
174
|
+
shares the theme's semantic `landcover.farmland` color with detailed farmland, while `barren` uses
|
|
175
|
+
`landcover.rock`; neither class borrows a road color.
|
|
176
|
+
|
|
177
|
+
The normalized schema also records the meaning of the OpenMapTiles `park` layer. Generic
|
|
178
|
+
`openMapTiles()` defaults to `semantics.parkLayer: 'mixed'` and compiles a private compatibility
|
|
179
|
+
branch for ordinary legacy parks. `tileflowWorldV1Schema()` fixes it to `'protected-only'`: that
|
|
180
|
+
layer is a protection tint, while urban parks and gardens come from
|
|
181
|
+
`landcover.class=grass/subclass=park|garden`. The ambiguous legacy `park` class is therefore not
|
|
182
|
+
a public land-cover target.
|
|
183
|
+
|
|
184
|
+
Tileflow World V1 makes `bathymetry`, `globallandcover`, `circular_feature`, `sidewalk`, and
|
|
185
|
+
`street_furniture` mandatory. Use `tileflowWorldV1Schema()` for that product instead of making raw
|
|
186
|
+
layer names part of a style. Publication tooling calls `validateTileflowWorldV1Tilejson(...)` and
|
|
187
|
+
fails closed unless each layer occurs exactly once, declares its contracted native zooms, and
|
|
188
|
+
advertises the typed fields consumed by the maps. This includes bathymetry z0–z9, global land cover
|
|
189
|
+
z0–z10, the three detailed-city layers at native z15, and numeric
|
|
190
|
+
`transportation.clearance_extra_px_z15` for butt-capping procedural-roundabout approaches. Generic
|
|
191
|
+
OpenMapTiles sources keep the detailed extensions optional and the compiler omits unsupported
|
|
192
|
+
detail. Resolving omitted data or
|
|
193
|
+
`tileflowWorld(...)` uses the strict V1 schema directly, and `water({bathymetry: {...}})` styles the
|
|
194
|
+
emitted depth bands without raw layer IDs. Use `water({bathymetryContours: {...}})` to customize the
|
|
195
|
+
opt-in companion line layer; an empty style selects subtle defaults, and the zero-depth band is
|
|
196
|
+
excluded so the ordinary coastline remains authoritative. The lines follow discrete band polygon
|
|
197
|
+
edges and are therefore approximate rather than surveyed depth contours. Use
|
|
198
|
+
`water({bathymetryLabels: {...}})` to customize the separate opt-in symbol layer; an empty style
|
|
199
|
+
selects the defaults, whose text is the absolute band-minimum number in metres with no unit suffix.
|
|
200
|
+
It remains a band-floor annotation, not a survey sounding.
|
|
201
|
+
|
|
202
|
+
For independent ocean products, `marine` composes stable auxiliary vector sources without changing
|
|
203
|
+
the primary World source:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import {bathymetry, defineMap, fixed, nautical} from '@tileflow/core';
|
|
207
|
+
import {streets} from '@tileflow/maps';
|
|
208
|
+
|
|
209
|
+
export default defineMap({
|
|
210
|
+
id: 'chart',
|
|
211
|
+
version: 1,
|
|
212
|
+
extends: streets,
|
|
213
|
+
marine: 'chart', // 'none' | 'bathymetry' | 'nautical' | 'chart'
|
|
214
|
+
modules: {
|
|
215
|
+
nautical: nautical({
|
|
216
|
+
soundings: {minZoom: 13},
|
|
217
|
+
aids: {minZoom: 11},
|
|
218
|
+
lights: {minZoom: 12},
|
|
219
|
+
hazards: {minZoom: 12},
|
|
220
|
+
hazardAreas: {
|
|
221
|
+
fill: {opacity: fixed(0.1, {reason: 'Chart hazard wash is invariant'})},
|
|
222
|
+
},
|
|
223
|
+
wrecks: {minZoom: 12},
|
|
224
|
+
wreckAreas: {
|
|
225
|
+
outline: {opacity: fixed(0.6, {reason: 'Chart wreck outline is invariant'})},
|
|
226
|
+
},
|
|
227
|
+
reefs: {fill: {opacity: fixed(0.3, {reason: 'Chart reef wash is invariant'})}},
|
|
228
|
+
navigationAreas: {
|
|
229
|
+
fill: {opacity: fixed(0.05, {reason: 'Chart navigation-area wash is invariant'})},
|
|
230
|
+
},
|
|
231
|
+
coverage: {outline: {visible: false}},
|
|
232
|
+
labels: {
|
|
233
|
+
navigationAreas: {minZoom: 11},
|
|
234
|
+
reefs: {minZoom: 13},
|
|
235
|
+
},
|
|
236
|
+
}),
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
`bathymetry` resolves to vector source `tileflow-bathymetry` and supplies the 13-stop
|
|
242
|
+
`bathymetry-v1` band contract to `water(...)`. `nautical` resolves to `tileflow-nautical`; the
|
|
243
|
+
`nautical(...)` module
|
|
244
|
+
styles its `sounding`, `aid`, `light`, `hazard`, `wreck`, `reef`, `navigation_area`, and `coverage`
|
|
245
|
+
layers. The schema also names the product's provider-neutral provenance fields (`provenance`,
|
|
246
|
+
`provider`, `cell`, `edition`, `update`, `scale`, `coverage`, and `licence`). `chart` enables both.
|
|
247
|
+
Point features label depth, name, or light character as applicable. The `lighthouses` symbol style
|
|
248
|
+
targets `aid` points whose canonical `class` is `lighthouse`, leaving other aids under `aids`.
|
|
249
|
+
Polygon reefs, navigation
|
|
250
|
+
areas, hazards, wrecks, and named/provider coverage have separate collision-aware labels under
|
|
251
|
+
`nautical({labels: {...}})`; unnamed coverage does not emit empty text.
|
|
252
|
+
Core does not hard-code a nautical provider or attribution: the selected TileJSON owns its actual
|
|
253
|
+
coverage and attribution, and derived vector tiles are not represented as a certified ENC for
|
|
254
|
+
navigation. The V1 geometry contract preserves `sounding`, `aid`, and `light` as points;
|
|
255
|
+
`reef`, `navigation_area`, and `coverage` as polygons; and both point and polygon geometries for
|
|
256
|
+
`hazard` and `wreck`. Point symbols and polygon areas compile separately, without fabricated
|
|
257
|
+
centroids.
|
|
258
|
+
Advanced callers can override either TileJSON URL, attribution, or source ID and can set the other
|
|
259
|
+
member to `false`:
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
marine: {
|
|
263
|
+
bathymetry: {url: 'https://tiles.example.test/depth/tiles.json'},
|
|
264
|
+
nautical: false,
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Use the `bathymetry(...)` helper when the map needs relief or product-level styling. Bathymetry is
|
|
269
|
+
one logical product with two independently loaded physical sources:
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
marine: {
|
|
273
|
+
bathymetry: bathymetry({
|
|
274
|
+
display: 'hybrid',
|
|
275
|
+
bands: {
|
|
276
|
+
opacity: fixed(0.72, {reason: 'Keep chart symbols above the depth wash'}),
|
|
277
|
+
},
|
|
278
|
+
contours: {visible: true},
|
|
279
|
+
relief: {
|
|
280
|
+
multidirectional: true,
|
|
281
|
+
opacity: fixed(0.18, {reason: 'Relief stays legible beneath bands and labels'}),
|
|
282
|
+
},
|
|
283
|
+
}),
|
|
284
|
+
nautical: false,
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`display: 'bands'` loads only vector TileJSON `/tiles/bathymetry/tiles.json` as
|
|
289
|
+
`tileflow-bathymetry`. `display: 'relief'` loads only `/tiles/bathymetry/dem/tiles.json` as
|
|
290
|
+
`tileflow-bathymetry-dem` and emits continuous `color-relief` plus hillshade. `display: 'hybrid'`
|
|
291
|
+
loads both. The hosted DEM contract is 512 px, Terrarium-encoded, lossless WebP; advanced compatible
|
|
292
|
+
sources may override `url`, `sourceId`, `encoding`, or `tileSize` inside `relief`.
|
|
293
|
+
Multidirectional illumination uses directions 270°, 315°, 0°, and 45°. The simple
|
|
294
|
+
`marine: 'chart'` shorthand deliberately stays vector-only; DEM relief is an explicit advanced
|
|
295
|
+
opt-in.
|
|
296
|
+
|
|
297
|
+
The vector palette uses thresholds −11 000, −8 000, −6 000, −4 000, −2 000, −1 000, −500, −200,
|
|
298
|
+
−100, −50, −20, −10, and 0 metres. `bands`, `contours`, and `labels` remain separate controls.
|
|
299
|
+
Until native contour fields are contracted, contours are a visual fallback drawn from band polygon
|
|
300
|
+
edges, not surveyed isolines. The V1 schema exposes optional source-layer names
|
|
301
|
+
`bathymetry_contour`, `seafloor_landform`, `water_name`, and `bathymetry_coverage`, but promises
|
|
302
|
+
fields only for required `bathymetry.min_depth` and `bathymetry.sort_key`.
|
|
303
|
+
|
|
304
|
+
The DEM enables future client-side sampling and depth profiles, but Core intentionally exposes no
|
|
305
|
+
profile or remote sampling API yet. Rendered relief must not be interpreted as navigation-safe
|
|
306
|
+
depth data.
|
|
307
|
+
|
|
308
|
+
Omitting `marine` preserves the transitional Tileflow World V1 bathymetry fallback. Declaring
|
|
309
|
+
`marine: 'none'` suppresses that fallback, while a selected bathymetry sidecar always takes
|
|
310
|
+
precedence. Compiled styles retain the singular `tileflow:data` metadata for existing consumers and
|
|
311
|
+
add `tileflow:sources` plus `tileflow:sourceRequirements` for independent product validation.
|
|
312
|
+
|
|
313
|
+
Detailed city datasets can bind `sidewalk`, `streetFurniture`, and `circularFeature` source layers
|
|
314
|
+
through `openMapTiles({layers, fields})`. When present, `roads.sidewalks` owns source-backed
|
|
315
|
+
pedestrian polygons, `roads.crossings` owns oriented crossing icons, and `roads.roundabouts` owns
|
|
316
|
+
metric circular road rings. These bindings are optional: a generic OpenMapTiles source remains
|
|
317
|
+
valid and the compiler omits unsupported detail instead of inventing geometry. Crossing icons are
|
|
318
|
+
explicit because the data contract cannot assume a sprite name. They require positive physical
|
|
319
|
+
`markings` evidence and reject `crossing=no`; both fields are semantic bindings, so custom schemas
|
|
320
|
+
can remap them without weakening that rule.
|
|
321
|
+
|
|
322
|
+
The optional `business_corridor` extension contains activity-selected source footprints below
|
|
323
|
+
buildings. Its `activity_score`, `rank`, `min_zoom`, and `confidence` fields control a quiet local
|
|
324
|
+
warm tint without drawing POI-radius circles. Current building footprints may carry sparse
|
|
325
|
+
`building_tone=commercial|destination|active`; absence means the neutral base building. The three
|
|
326
|
+
values preserve why the warm tone was selected even when a theme maps them to the same color.
|
|
327
|
+
Building visibility is fixed by the layer zoom contract and never by optional semantics. Immutable
|
|
328
|
+
V8.9 archives carrying `building_kind` or `has_business` remain readable as a defensive
|
|
329
|
+
compatibility path, but new candidates do not emit those fields. The land module also exposes
|
|
330
|
+
`medical`, `education`, and `government` independently instead of collapsing their already distinct
|
|
331
|
+
source classes into one civic color.
|
|
332
|
+
|
|
333
|
+
World and text assets are independent contracts. `tileflowWorld()` selects `world-v1/current` or an
|
|
334
|
+
exact `releaseId + descriptorSha256`; a `glyphs` declaration contains its own complete URL. Ordinary
|
|
335
|
+
imports of Streets, Ferraris, Härad, Siegfried, Soundings, Cyberpunk, Matrix, Verdant,
|
|
336
|
+
and San Francisto
|
|
337
|
+
remain usable because each official map owns or inherits a URL or packaged-font provider.
|
|
338
|
+
URL-backed maps become exact-byte reproducible
|
|
339
|
+
when the immutable global base-asset set is published and their explicit URL is updated to its
|
|
340
|
+
`assetSetSha256` path; no World or compiler fallback participates in that rollout. The global
|
|
341
|
+
base-asset manifest's hash and a map build manifest's identically named `assetSetSha256` use
|
|
342
|
+
different contracts and must never be substituted for one another.
|
|
343
|
+
|
|
344
|
+
Upstream data attribution remains in the MapLibre source. `Map by Tileflow` is a separate product
|
|
345
|
+
credit/trademark surface and must not replace, obscure, or be presented as upstream attribution.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Hosted session authorization
|
|
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 framework adapters use `createTileflowSessionController` to obtain one short-lived session
|
|
6
|
+
grant before eligible hosted style, tile, font, or sprite requests. Concurrent resources share the
|
|
7
|
+
same preflight, grants are attached only to reviewed HTTP(S) origins, and an expired unused session
|
|
8
|
+
rotates once when the server requests it. A controller also rotates after six hours or 10,000
|
|
9
|
+
eligible requests. The preflight has a 10-second client timeout by default; direct controller users
|
|
10
|
+
can set `grantTimeoutMs` from 1 to 120,000 milliseconds.
|
|
11
|
+
|
|
12
|
+
`analytics.enabled: false` disables only the optional analytics beacon. It does not disable this
|
|
13
|
+
server-owned commercial authorization. User `transformRequest` callbacks still run first; Tileflow
|
|
14
|
+
then decorates only the resulting eligible URL and preserves the other request options.
|
|
15
|
+
|
|
16
|
+
Set `analytics.surfaceId` to a stable product location when the same Map appears in several places:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
const analytics = {surfaceId: 'store-locator'};
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
A Surface ID is 1–64 lowercase ASCII letters, digits, `.`, `_`, or `-`; it starts and ends with a
|
|
23
|
+
letter or digit. Missing or invalid values become `default`. Use a durable integration name such as
|
|
24
|
+
`checkout` or `dealer-search`, not a URL, branch, preview, random component instance, or user ID.
|
|
25
|
+
Surface labels do not change authorization, entitlement, or Session identity.
|
|
26
|
+
|
|
27
|
+
For exact Tileflow World release tiles, framework adapters also use the browser request bridge to
|
|
28
|
+
observe the response's safe fair-use state without adding identity, query parameters, credentials,
|
|
29
|
+
or user headers to the public World URL. The bridge requires the immutable release path and exactly
|
|
30
|
+
one lowercase descriptor digest; the retired mutable World template is never intercepted. Early
|
|
31
|
+
`GRACE` stays silent; signed late `GRACE` creates a compact
|
|
32
|
+
accessible owner-action pill, and `MANAGED_REQUIRED` creates a stronger in-map banner. A missing
|
|
33
|
+
header, absent tile, MapLibre error, or failed response cannot erase an existing manage action; a
|
|
34
|
+
later successful `OPEN` response can clear it. Shaped empty tiles remain render-safe while the owner
|
|
35
|
+
action stays available. Successful World tile bodies are streamed with a 16 MiB maximum: an
|
|
36
|
+
oversized `Content-Length` is rejected before reading, and chunked responses are cancelled as soon
|
|
37
|
+
as their accumulated bytes cross the same bound. Abort signals cancel an active reader; errors do
|
|
38
|
+
not include the remote response body.
|
|
39
|
+
|
|
40
|
+
Other subpaths under `src/`, `themes/`, `templates/`, and `modules/` are internal implementation
|
|
41
|
+
details and can change during alpha releases.
|
|
42
|
+
|
|
43
|
+
Official map definitions and the source assets they own ship together in `@tileflow/maps`, not in
|
|
44
|
+
Core. That package exports the maps and their reusable directory descriptors:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import {
|
|
48
|
+
baedeker,
|
|
49
|
+
baedekerFonts,
|
|
50
|
+
baedekerIcons,
|
|
51
|
+
cyberpunk,
|
|
52
|
+
cyberpunkFonts,
|
|
53
|
+
cyberpunkIcons,
|
|
54
|
+
ferraris,
|
|
55
|
+
ferrarisIcons,
|
|
56
|
+
harad,
|
|
57
|
+
haradIcons,
|
|
58
|
+
matrix,
|
|
59
|
+
matrixFonts,
|
|
60
|
+
matrixIcons,
|
|
61
|
+
sanFrancisto,
|
|
62
|
+
sanFrancistoIcons,
|
|
63
|
+
siegfried,
|
|
64
|
+
siegfriedFonts,
|
|
65
|
+
siegfriedIcons,
|
|
66
|
+
soundings,
|
|
67
|
+
soundingsIcons,
|
|
68
|
+
streets,
|
|
69
|
+
streetsIcons,
|
|
70
|
+
streetsThemes,
|
|
71
|
+
verdant,
|
|
72
|
+
verdantIcons,
|
|
73
|
+
} from '@tileflow/maps';
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
All ten official maps are complete standalone maps. The sole semantic compiler is implicit; none
|
|
77
|
+
imports or extends another official map, and each declares its own icon directory.
|
|
78
|
+
Streets declares `[streetsIcons]` and exposes its complete coordinated appearances as
|
|
79
|
+
`streetsThemes.light` and `streetsThemes.dark`; image tokens select the matching sidewalk pattern
|
|
80
|
+
without changing the asset collection. Baedeker declares `[baedekerIcons]`, whose eight original
|
|
81
|
+
patterns support its travel-atlas design, and `[baedekerFonts]`, its own Cormorant Garamond
|
|
82
|
+
directory; it derives contours in the browser from unpackaged Mapterhorn terrain tiles. Cyberpunk
|
|
83
|
+
declares `[cyberpunkIcons]` and `[cyberpunkFonts]`; Matrix
|
|
84
|
+
independently declares `[matrixIcons]` and `[matrixFonts]`. San Francisto declares
|
|
85
|
+
`[sanFrancistoIcons]` for its four technical hatches and schematic POI node, derives contours from
|
|
86
|
+
unpackaged Mapterhorn tiles, and uses the canonical Noto Sans glyph provider. The same asset
|
|
87
|
+
operation is available to applications:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
export default defineMap({
|
|
91
|
+
id: 'brand-map',
|
|
92
|
+
version: 1,
|
|
93
|
+
extends: streets,
|
|
94
|
+
icons: [...streets.icons, './icons'],
|
|
95
|
+
});
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`icons` is a `readonly TileflowIconSource[]`. Omission inherits the parent's exact array,
|
|
99
|
+
declaration replaces it atomically, and `[]` means no icons. Local and package directories, plus
|
|
100
|
+
explicit locked `iconSet('@team/set')` descriptors, apply left to right.
|
|
101
|
+
`<id>.<ext>` publishes an icon as `<id>`; `<id>.pattern.<ext>` publishes an intrinsic-size pattern
|
|
102
|
+
as `<id>`. The published ID must already be canonical lower-kebab; a later exact ID wins and a
|
|
103
|
+
case-only collision fails. There is no built-in selector, mutable external sprite selector, icon
|
|
104
|
+
mapping, icon-specific inheritance, additive command, or compatibility alias. An Icon Set is an
|
|
105
|
+
explicit locked descriptor, not a generic source registry.
|
|
106
|
+
|
|
107
|
+
`@tileflow/dev` resolves local and package directory descriptors, verifies real-path containment,
|
|
108
|
+
and prepares ordinary public artifacts without serializing installation paths. It compiles one
|
|
109
|
+
deterministic sprite from the final icon composition and validates every literal `icon-image`,
|
|
110
|
+
`fill-pattern`, and `line-pattern` in the final style against it. It also prepares any declared font
|
|
111
|
+
directories generically; `baedekerFonts`, `cyberpunkFonts`, `matrixFonts`, and `siegfriedFonts` are
|
|
112
|
+
ordinary package descriptors rather than pipeline special cases. Calling the pure compiler for a map whose style needs unprepared
|
|
113
|
+
assets fails instead of emitting broken runtime references.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Map and terrain examples
|
|
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
|
+
Typed Tileflow map contracts, semantic modules, and deterministic MapLibre style compilation.
|
|
6
|
+
|
|
7
|
+
Every public `tileflow.config.ts` exports one map. Most maps import an existing map and override only
|
|
8
|
+
the design fields they own:
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import {
|
|
12
|
+
defineMap,
|
|
13
|
+
defineTheme,
|
|
14
|
+
fixed,
|
|
15
|
+
labels,
|
|
16
|
+
poi,
|
|
17
|
+
roads,
|
|
18
|
+
token,
|
|
19
|
+
water,
|
|
20
|
+
zoom,
|
|
21
|
+
} from '@tileflow/core';
|
|
22
|
+
import {streets, streetsThemes} from '@tileflow/maps';
|
|
23
|
+
|
|
24
|
+
const madridDark = defineTheme(streetsThemes.dark, {
|
|
25
|
+
id: 'madrid-dark',
|
|
26
|
+
version: 1,
|
|
27
|
+
colorScheme: 'dark',
|
|
28
|
+
tokens: {color: {'surface.land': '#0d1320', 'surface.water': '#081e2e'}},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default defineMap({
|
|
32
|
+
id: 'madrid',
|
|
33
|
+
name: 'Madrid',
|
|
34
|
+
version: 1,
|
|
35
|
+
extends: streets,
|
|
36
|
+
themes: {light: streetsThemes.light, dark: madridDark},
|
|
37
|
+
defaultTheme: 'light',
|
|
38
|
+
systemThemes: {light: 'light', dark: 'dark'},
|
|
39
|
+
projection: 'globe',
|
|
40
|
+
modules: {
|
|
41
|
+
water: water({
|
|
42
|
+
bodies: {
|
|
43
|
+
fill: {
|
|
44
|
+
opacity: fixed(0.95, {reason: 'Madrid keeps this water density in every theme'}),
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
roads: roads({
|
|
49
|
+
detail: 'streets',
|
|
50
|
+
hierarchy: 'strong',
|
|
51
|
+
classes: {
|
|
52
|
+
primary: {
|
|
53
|
+
surface: {
|
|
54
|
+
fill: {
|
|
55
|
+
color: fixed('#E4A85B', {reason: 'Madrid brand roads stay warm in every theme'}),
|
|
56
|
+
width: zoom.linear([
|
|
57
|
+
[7, fixed(0.6, {reason: 'Madrid keeps its primary-road hierarchy across themes'})],
|
|
58
|
+
[16, fixed(8, {reason: 'Madrid keeps its primary-road hierarchy across themes'})],
|
|
59
|
+
]),
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
secondary: {surface: {fill: {color: token.color('roads.secondary')}}},
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
labels: labels({
|
|
67
|
+
language: 'local',
|
|
68
|
+
places: 'all',
|
|
69
|
+
roads: 'streets',
|
|
70
|
+
styles: {
|
|
71
|
+
places: {
|
|
72
|
+
city: {
|
|
73
|
+
text: {
|
|
74
|
+
size: fixed(18, {reason: 'Madrid keeps its city-label scale across themes'}),
|
|
75
|
+
haloWidth: fixed(1.5, {reason: 'Madrid keeps its city-label halo across themes'}),
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
poi: poi({categories: ['food-drink', 'arts-entertainment', 'transport'], color: 'category'}),
|
|
82
|
+
},
|
|
83
|
+
view: {center: [-3.7038, 40.4168], pitch: 35, zoom: 12},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Map identity and scenes belong to the leaf and do not inherit. Hosted browser policy is configured
|
|
88
|
+
in Tileflow and does not enter cartographic authoring or compilation.
|
|
89
|
+
|
|
90
|
+
Set `projection: 'globe'` for MapLibre's adaptive globe preset. Global zooms render as a sphere,
|
|
91
|
+
then transition to Mercator between zoom 10 and 12 so detailed streets remain planar. Omit the
|
|
92
|
+
property, or set it to `'mercator'`, for a consistently flat map.
|
|
93
|
+
|
|
94
|
+
Terrain keeps the original `'none' | 'hillshade' | '3d'` shorthand. The object form can style every
|
|
95
|
+
MapLibre hillshade paint control, or generate vector contours in the browser from an explicit DEM
|
|
96
|
+
tile template. Contours require `demUrl`, `demMaxZoom`, and zoom-indexed `[minor, index]`
|
|
97
|
+
`thresholds`; the raster `url` remains the separate TileJSON endpoint. Set `mode: 'none'` to compile
|
|
98
|
+
contours without adding a raster source, hillshade, or 3D terrain:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
terrain: {
|
|
102
|
+
mode: 'none',
|
|
103
|
+
encoding: 'terrarium',
|
|
104
|
+
contours: {
|
|
105
|
+
demUrl: 'https://terrain.example.test/{z}/{x}/{y}.webp',
|
|
106
|
+
demMaxZoom: 13,
|
|
107
|
+
maxZoom: 15,
|
|
108
|
+
overzoom: 2,
|
|
109
|
+
thresholds: {
|
|
110
|
+
9: [100, 500],
|
|
111
|
+
11: [50, 250],
|
|
112
|
+
13: [20, 100],
|
|
113
|
+
15: [10, 50],
|
|
114
|
+
},
|
|
115
|
+
minor: {
|
|
116
|
+
color: token.color('terrain.contour.minor'),
|
|
117
|
+
width: token.number('terrain.contour.minor-width'),
|
|
118
|
+
},
|
|
119
|
+
index: {
|
|
120
|
+
color: token.color('terrain.contour.index'),
|
|
121
|
+
width: token.number('terrain.contour.index-width'),
|
|
122
|
+
},
|
|
123
|
+
labels: {
|
|
124
|
+
color: token.color('terrain.contour.label'),
|
|
125
|
+
haloColor: token.color('terrain.contour.halo'),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Declare those `terrain.*` color and number roles with the same keys in every map theme. The
|
|
132
|
+
compiled source is ordinary MapLibre vector data with source layer `contours`; its tile URL
|
|
133
|
+
contains the safely encoded DEM template and complete generation parameters. Register the generic
|
|
134
|
+
protocol before MapLibre reads the style. It lazily initializes the pinned, locally bundled
|
|
135
|
+
`maplibre-contour@0.1.0` browser module on the first contour tile; no public CDN runtime is needed.
|
|
136
|
+
`overzoom` may not exceed the lowest threshold zoom, so generated DEM requests never use a negative
|
|
137
|
+
zoom. Every index interval must be a whole multiple of its minor interval. Contour labels emit the
|
|
138
|
+
scaled numeric elevation without assuming a display unit. Source and layer visibility cannot begin
|
|
139
|
+
before the first configured threshold zoom. To bound main-thread work, effective minor intervals
|
|
140
|
+
must be at least 250 units at z0–4, 100 at z5–7, 50 at z8–10, 20 at z11–12, and 10 from z13 onward.
|
|
141
|
+
Use a trusted, size-bounded DEM tile service; the protocol intentionally accepts author-supplied
|
|
142
|
+
HTTP(S) templates rather than proxying or republishing terrain data.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import maplibregl from 'maplibre-gl';
|
|
146
|
+
import {registerTileflowContourProtocol} from '@tileflow/core/browser';
|
|
147
|
+
|
|
148
|
+
registerTileflowContourProtocol({addProtocol: maplibregl.addProtocol});
|
|
149
|
+
```
|