@tileflow/core 0.1.0-alpha.4 → 0.1.0-alpha.41
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 +318 -26
- package/THIRD_PARTY_NOTICES.md +444 -0
- package/dist/3d-CzLTD0RK.d.ts +1092 -0
- package/dist/3d.d.ts +1 -0
- package/dist/3d.js +12660 -0
- package/dist/browser.d.ts +2278 -0
- package/dist/browser.js +21826 -0
- package/dist/build.d.ts +595 -0
- package/dist/build.js +29737 -0
- package/dist/capture-shot-ChPQvPyi.d.ts +291 -0
- package/dist/capture.d.ts +9 -0
- package/dist/capture.js +510 -0
- package/dist/config-reference.json +40527 -0
- package/dist/icon-composition-HUh_Lj8Z.d.ts +2516 -0
- package/dist/index.d.ts +1805 -162
- package/dist/index.js +26400 -3488
- package/dist/manifest-types-DnhSJjCM.d.ts +902 -0
- package/dist/manifest.d.ts +9 -0
- package/dist/manifest.js +449 -0
- package/dist/native-profile.d.ts +206 -0
- package/dist/native-profile.js +9339 -0
- package/dist/native.d.ts +365 -0
- package/dist/native.js +5970 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +1445 -0
- package/dist/worker/camera-tile-worker.global.js +20 -0
- package/docs/application-tile-reading.md +86 -0
- package/docs/authoring-model.md +269 -0
- package/docs/boundary-areas-and-choropleths.md +217 -0
- package/docs/buildings-3d.md +608 -0
- package/docs/camera-moves-and-tile-cache.md +112 -0
- package/docs/capture-scenes.md +40 -0
- package/docs/capture-shots.md +173 -0
- package/docs/cartographic-defaults.md +210 -0
- package/docs/compiled-style-performance.md +286 -0
- package/docs/data-is-separate-from-design.md +610 -0
- package/docs/depth-markers.md +65 -0
- package/docs/dynamic-style-images.md +100 -0
- package/docs/globe-atmosphere.md +113 -0
- package/docs/hosted-session-authorization.md +106 -0
- package/docs/map-and-terrain-examples.md +156 -0
- package/docs/map-emphasis.md +89 -0
- package/docs/native-artifact-profile.md +152 -0
- package/docs/native-resource-urls.md +369 -0
- package/docs/nautical-marks.md +308 -0
- package/docs/public-api-and-browser-subpath.md +101 -0
- package/docs/route-drawing.md +191 -0
- package/docs/shared-visual-primitives.md +101 -0
- package/docs/street-lights.md +127 -0
- package/docs/street-sky.md +156 -0
- package/docs/style-states.md +73 -0
- package/docs/sun-and-light.md +167 -0
- package/docs/theme-transitions-and-blends.md +317 -0
- package/docs/themes-and-module-styles.md +156 -0
- package/docs/trees-3d.md +295 -0
- package/package.json +67 -8
|
@@ -0,0 +1,610 @@
|
|
|
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; the only derived stacks are line-fitted text backgrounds on a `glyphs` provider, which keep
|
|
120
|
+
their source faces' shapes. Browser adapters load local faces before MapLibre. Native releases use a locked PBF
|
|
121
|
+
`glyphs` URL provider instead, which enumerates the exact comma-joined MapLibre request keys in
|
|
122
|
+
`fontStacks`.
|
|
123
|
+
|
|
124
|
+
Streets, Soundings, and Blueprint each declare
|
|
125
|
+
`https://api.tileflow.dev/base/33d4de5e8086d9d629d67d3f39fedb87e23686c4c1ac653c27e2a52aee9d00b3/glyphs/{fontstack}/{range}.pbf`
|
|
126
|
+
with the exact `Noto Sans Regular` and `Noto Sans Bold` stacks. The full hash identifies one
|
|
127
|
+
published, immutable glyph collection independent of World data. The older `/fonts/...` endpoint
|
|
128
|
+
remains a mutable compatibility URL and does not make a resolved map byte-reproducible. The global
|
|
129
|
+
glyph `assetSetSha256` is not the same-domain value as the per-map `assetSetSha256` in
|
|
130
|
+
`build-manifest.json`.
|
|
131
|
+
Baedeker, 1777, and Siegfried each own a packaged directory containing Cormorant Garamond Regular, SemiBold, and Italic.
|
|
132
|
+
|
|
133
|
+
Name the official generation deliberately, or use another OpenMapTiles-compatible vector source:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import {openMapTiles, tileflowWorld, vectorTiles} from '@tileflow/core';
|
|
137
|
+
|
|
138
|
+
const official = tileflowWorld();
|
|
139
|
+
const external = vectorTiles({
|
|
140
|
+
tiles: ['pmtiles://./test/fixtures/world.pmtiles'],
|
|
141
|
+
revision: 'fixture-1',
|
|
142
|
+
attribution: '© Example © OpenStreetMap contributors',
|
|
143
|
+
schema: openMapTiles(),
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The resolved map controls how data is drawn; `data` controls where compatible features come from.
|
|
148
|
+
For Tileflow World, the compiler emits the selector TileJSON URL
|
|
149
|
+
`https://api.tileflow.dev/tiles/world/tiles.json`. Omitted data or `tileflowWorld()` selects
|
|
150
|
+
`world-v1/current`; `tileflowWorld({release: {releaseId, descriptorSha256}})` binds an exact release
|
|
151
|
+
through query parameters. Runtime and capture resolve `current` once per session or job and then
|
|
152
|
+
use the immutable release described by that response. `openMapTiles({layers, fields})` can bind renamed
|
|
153
|
+
source-layers or properties while preserving the versioned semantic contract. External browser
|
|
154
|
+
credentials do not belong in public Style JSON; supply them through the framework's
|
|
155
|
+
`transformRequest` integration.
|
|
156
|
+
|
|
157
|
+
External exact-test sources may use one TileJSON `url` or a bounded direct `tiles` list with optional
|
|
158
|
+
`bounds`, `minzoom`, and `maxzoom`. `pmtiles://` is supported for a checked-in or bring-your-own
|
|
159
|
+
archive. Public vector URLs are HTTPS, root-relative, or HTTP only on loopback development hosts.
|
|
160
|
+
A PMTiles URL names one `.pmtiles` archive through an HTTPS, loopback HTTP, root-relative, or safe
|
|
161
|
+
repository-relative target; credentials, fragments, traversal, and other protocols are rejected at
|
|
162
|
+
both authoring validation and compilation. An external `revision` participates in capture identity,
|
|
163
|
+
so changing fixture bytes cannot silently reuse a baseline.
|
|
164
|
+
|
|
165
|
+
Tileflow's OpenMapTiles contract also recognizes the optional `globallandcover` extension. The
|
|
166
|
+
official Streets map maps its `barren`, `crop`, `grass`, `shrub`, `snow`, `trees`, and
|
|
167
|
+
`urban` classes beneath the OSM land layers. Tileflow World V1 supplies native generalized
|
|
168
|
+
geometry through z10; Streets fades it progressively while detailed `landcover`/`landuse` gains
|
|
169
|
+
opacity and removes it at z11. This is a macro bridge, not local semantic detail. Sources with a
|
|
170
|
+
different layer name can use `openMapTiles({layers: {globalLandcover: 'my_landcover'}})`; archives
|
|
171
|
+
without the extension remain compatible and simply render no features for that style layer. Use
|
|
172
|
+
`land({globalLandcover: {...}})` to customize its fill, opacity, visibility, and zoom range. `crop`
|
|
173
|
+
shares the theme's semantic `landcover.farmland` color with detailed farmland, while `barren` uses
|
|
174
|
+
`landcover.rock`; neither class borrows a road color.
|
|
175
|
+
|
|
176
|
+
The normalized schema also records the meaning of the OpenMapTiles `park` layer. Generic
|
|
177
|
+
`openMapTiles()` defaults to `semantics.parkLayer: 'mixed'` and compiles a private compatibility
|
|
178
|
+
branch for ordinary legacy parks. `tileflowWorldV1Schema()` fixes it to `'protected-only'`: that
|
|
179
|
+
layer is a protection tint, while urban parks and gardens come from
|
|
180
|
+
`landcover.class=grass/subclass=park|garden`. The ambiguous legacy `park` class is therefore not
|
|
181
|
+
a public land-cover target.
|
|
182
|
+
|
|
183
|
+
Tileflow World V1 makes `bathymetry`, `globallandcover`, `circular_feature`, `sidewalk`,
|
|
184
|
+
`street_furniture`, and `transportation_outline` mandatory. Use `tileflowWorldV1Schema()` for that product instead of making raw
|
|
185
|
+
layer names part of a style. Publication tooling calls `validateTileflowWorldV1Tilejson(...)` and
|
|
186
|
+
fails closed unless each layer occurs exactly once, declares its contracted native zooms, and
|
|
187
|
+
advertises the typed fields consumed by the maps. This includes bathymetry z0–z9, global land cover
|
|
188
|
+
z0–z10, the three detailed-city layers at native z15, and numeric
|
|
189
|
+
`transportation.clearance_extra_px_z15` for butt-capping procedural-roundabout approaches. Generic
|
|
190
|
+
OpenMapTiles sources keep the detailed extensions optional and the compiler omits unsupported
|
|
191
|
+
detail. Resolving omitted data or
|
|
192
|
+
`tileflowWorld(...)` uses the strict V1 schema directly, and `water({bathymetry: {...}})` styles the
|
|
193
|
+
emitted depth bands without raw layer IDs. Use `water({bathymetryContours: {...}})` to customize the
|
|
194
|
+
opt-in companion line layer; an empty style selects subtle defaults, and the zero-depth band is
|
|
195
|
+
excluded so the ordinary coastline remains authoritative. The lines follow discrete band polygon
|
|
196
|
+
edges and are therefore approximate rather than surveyed depth contours. Use
|
|
197
|
+
`water({bathymetryLabels: {...}})` to customize the separate opt-in symbol layer; an empty style
|
|
198
|
+
selects the defaults, whose text is the absolute band-minimum number in metres with no unit suffix.
|
|
199
|
+
It remains a band-floor annotation, not a survey sounding.
|
|
200
|
+
|
|
201
|
+
For independent ocean products, `marine` composes stable auxiliary vector sources without changing
|
|
202
|
+
the primary World source:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import {bathymetry, defineMap, fixed, nautical} from '@tileflow/core';
|
|
206
|
+
import {streets} from '@tileflow/maps';
|
|
207
|
+
|
|
208
|
+
export default defineMap({
|
|
209
|
+
id: 'chart',
|
|
210
|
+
version: 1,
|
|
211
|
+
extends: streets,
|
|
212
|
+
marine: 'chart', // 'none' | 'bathymetry' | 'nautical' | 'chart'
|
|
213
|
+
modules: {
|
|
214
|
+
nautical: nautical({
|
|
215
|
+
buoys: {minZoom: 12},
|
|
216
|
+
lights: {minZoom: 9},
|
|
217
|
+
navigationAreas: {
|
|
218
|
+
fill: {opacity: fixed(0.05, {reason: 'Chart navigation-area wash is invariant'})},
|
|
219
|
+
},
|
|
220
|
+
reefs: {fill: {opacity: fixed(0.3, {reason: 'Chart reef wash is invariant'})}},
|
|
221
|
+
labels: {navigationAreas: {minZoom: 12}},
|
|
222
|
+
}),
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
`bathymetry` resolves to vector source `tileflow-bathymetry` and supplies the 13-stop
|
|
228
|
+
`bathymetry-v1` band contract to `water(...)`. `nautical` resolves to `tileflow-nautical` at
|
|
229
|
+
`/tiles/nautical/tiles.json`; the `nautical(...)` module draws its `nautical-v1` buoys, beacons,
|
|
230
|
+
lighthouses, lights, hazards, wrecks, reefs, navigation areas, routes, cables, harbours, ports, and
|
|
231
|
+
a restrained coverage indication. `chart` enables both. Every map that draws Nautical declares a
|
|
232
|
+
"Not for navigation" notice, which the browser runtimes render on the map. The
|
|
233
|
+
[Nautical marks guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/nautical-marks.md)
|
|
234
|
+
describes the schema, the semantic groups, the theme image tokens, and the notice.
|
|
235
|
+
Core does not hard-code a nautical provider or attribution: the selected TileJSON owns its actual
|
|
236
|
+
coverage and attribution, and derived vector tiles are not represented as a certified ENC for
|
|
237
|
+
navigation. Point symbols and polygon areas compile separately, without fabricated centroids.
|
|
238
|
+
Advanced callers can override either TileJSON URL, attribution, or source ID and can set the other
|
|
239
|
+
member to `false`:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
marine: {
|
|
243
|
+
bathymetry: {url: 'https://tiles.example.test/depth/tiles.json'},
|
|
244
|
+
nautical: false,
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Use the `bathymetry(...)` helper when the map needs relief or product-level styling. Bathymetry is
|
|
249
|
+
one logical product with two independently loaded physical sources:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
marine: {
|
|
253
|
+
bathymetry: bathymetry({
|
|
254
|
+
display: 'hybrid',
|
|
255
|
+
bands: {
|
|
256
|
+
opacity: fixed(0.72, {reason: 'Keep chart symbols above the depth wash'}),
|
|
257
|
+
},
|
|
258
|
+
contours: {visible: true},
|
|
259
|
+
relief: {
|
|
260
|
+
multidirectional: true,
|
|
261
|
+
opacity: fixed(0.18, {reason: 'Relief stays legible beneath bands and labels'}),
|
|
262
|
+
},
|
|
263
|
+
}),
|
|
264
|
+
nautical: false,
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
`display: 'bands'` loads only vector TileJSON `/tiles/bathymetry/tiles.json` as
|
|
269
|
+
`tileflow-bathymetry`. `display: 'relief'` loads only `/tiles/bathymetry/dem/tiles.json` as
|
|
270
|
+
`tileflow-bathymetry-dem` and emits continuous `color-relief` plus hillshade. `display: 'hybrid'`
|
|
271
|
+
loads both. The hosted DEM contract is 512 px, Terrarium-encoded, lossless WebP; advanced compatible
|
|
272
|
+
sources may override `url`, `sourceId`, `encoding`, or `tileSize` inside `relief`.
|
|
273
|
+
Multidirectional illumination uses directions 270°, 315°, 0°, and 45°. The simple
|
|
274
|
+
`marine: 'chart'` shorthand deliberately stays vector-only; DEM relief is an explicit advanced
|
|
275
|
+
opt-in.
|
|
276
|
+
|
|
277
|
+
The vector palette uses thresholds −11 000, −8 000, −6 000, −4 000, −2 000, −1 000, −500, −200,
|
|
278
|
+
−100, −50, −20, −10, and 0 metres. `bands`, `contours`, and `labels` remain separate controls.
|
|
279
|
+
Without an optional-layer declaration, contours and depth labels retain the legacy polygon-band
|
|
280
|
+
behavior. `marine` shorthands and existing maps keep that behavior.
|
|
281
|
+
|
|
282
|
+
### Declare contour and name layers
|
|
283
|
+
|
|
284
|
+
For a compatible source, `vectorLayers` explicitly declares optional layers. Core never fetches
|
|
285
|
+
TileJSON to detect them and never substitutes polygon edges for declared contour lines. A missing
|
|
286
|
+
declared layer therefore draws no features; validate the selected dataset before rendering.
|
|
287
|
+
`display: 'bands'` or `'hybrid'` loads the vector source.
|
|
288
|
+
|
|
289
|
+
| Declaration | Source layer | Geometry and fields |
|
|
290
|
+
| --------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
291
|
+
| `contours: true` | `bathymetry_contour` | Lines with numeric `depth`, negative metres below the source vertical datum. Zero and positive depths are excluded. |
|
|
292
|
+
| `landforms: true` or a range inventory | `seafloor_landform` | Named points, lines, or polygons with stable source `id`, provider `class`, and `name`. |
|
|
293
|
+
| `waterNames: true` or a range inventory | `water_name` | Named points, lines, or polygons with the same name contract. |
|
|
294
|
+
|
|
295
|
+
Name geometry stays in its source representation. `placement: 'point'` (the default) lets the
|
|
296
|
+
renderer place names inside polygons or on points. Use `placement: 'line'` to label source lines.
|
|
297
|
+
Core creates no substitute point, centroid, or line feature.
|
|
298
|
+
|
|
299
|
+
Name layers preserve source properties; `id` is a source identifier, with no guarantee of a numeric
|
|
300
|
+
renderer feature ID. Optional `name:<language>` fields retain the source's language assignments.
|
|
301
|
+
`labels({language: 'es'})` selects a nonempty `name:es`, falling back to `name`; `auto` and `local`
|
|
302
|
+
use `name` directly. Core does not infer the language of an unqualified name or invent translations.
|
|
303
|
+
Optional `min_zoom` is inclusive and `max_zoom` exclusive, defaulting to 0 and 24. These nominal
|
|
304
|
+
ranges intersect the symbol style's range. With `true`, MapLibre evaluates feature zoom filters at
|
|
305
|
+
integer zooms: `min_zoom: 7.6` first becomes eligible at zoom 8. For exact fractional boundaries,
|
|
306
|
+
declare an exhaustive range inventory instead:
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
vectorLayers: {
|
|
310
|
+
contours: true,
|
|
311
|
+
landforms: {zoomRanges: [{minZoom: 0, maxZoom: 24}]},
|
|
312
|
+
waterNames: {
|
|
313
|
+
zoomRanges: [{minZoom: 1, maxZoom: 6.5}, {minZoom: 7.6, maxZoom: 10}],
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Each declared pair becomes a separate symbol layer with fractional renderer bounds and equality
|
|
319
|
+
filters on the source's original range. A `7.6–10` feature appears at 7.6 and disappears at 10.
|
|
320
|
+
Unlisted pairs are omitted. Core never discovers ranges from tiles: validate the source inventory
|
|
321
|
+
against this exhaustive declaration before rendering, and retain that inventory with reproducible
|
|
322
|
+
captures. Numeric pairs must match the source's encoded values exactly.
|
|
323
|
+
|
|
324
|
+
Each name layer accepts 1–128 unique pairs with `0 <= minZoom < maxZoom <= 24`;
|
|
325
|
+
`tileflowBathymetryNameLayerLimits.maxZoomRanges` exposes the limit. Input order does not affect
|
|
326
|
+
output. Different pairs can overlap in zoom because equality filters keep their feature sets
|
|
327
|
+
disjoint. A style may narrow a pair's visibility; an empty intersection emits no layer.
|
|
328
|
+
Missing or null source bounds default to 0/24. Nonnumeric values use the same numeric-conversion
|
|
329
|
+
fallbacks; numeric strings are converted. Such features draw only if their normalized pair is
|
|
330
|
+
declared. Core preserves the original source properties.
|
|
331
|
+
|
|
332
|
+
Optional numeric `sort_key` defaults to 0; smaller values enter collision placement first within
|
|
333
|
+
each cohort. Cohorts follow ascending minimum then maximum zoom and sit below ordinary map labels.
|
|
334
|
+
Names use normal collision placement without overlap by default.
|
|
335
|
+
|
|
336
|
+
This example declares a source whose regional detail supports zoom 14. Its extended ranges are
|
|
337
|
+
explicit design choices, not a promise of worldwide detail:
|
|
338
|
+
|
|
339
|
+
<!-- docs:check -->
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
import {bathymetry, defineMap, expr, field, fixed, labels, water} from '@tileflow/core';
|
|
343
|
+
import {soundings} from '@tileflow/maps';
|
|
344
|
+
|
|
345
|
+
export default defineMap({
|
|
346
|
+
id: 'regional-depth',
|
|
347
|
+
version: 1,
|
|
348
|
+
extends: soundings,
|
|
349
|
+
marine: {
|
|
350
|
+
bathymetry: bathymetry({
|
|
351
|
+
url: 'https://tiles.example.test/regional-depth/tiles.json',
|
|
352
|
+
vectorLayers: {
|
|
353
|
+
contours: true,
|
|
354
|
+
landforms: {zoomRanges: [{minZoom: 0, maxZoom: 24}]},
|
|
355
|
+
waterNames: {zoomRanges: [{minZoom: 7.6, maxZoom: 10}]},
|
|
356
|
+
},
|
|
357
|
+
contours: {},
|
|
358
|
+
labels: {},
|
|
359
|
+
}),
|
|
360
|
+
nautical: false,
|
|
361
|
+
},
|
|
362
|
+
modules: {
|
|
363
|
+
labels: labels({language: 'es'}),
|
|
364
|
+
water: water({
|
|
365
|
+
bathymetry: {
|
|
366
|
+
maxZoom: 15,
|
|
367
|
+
opacity: fixed(0.8, {reason: 'The selected regional source supplies detailed bands'}),
|
|
368
|
+
},
|
|
369
|
+
bathymetryContours: {maxZoom: 15},
|
|
370
|
+
bathymetryLabels: {
|
|
371
|
+
maxZoom: 15,
|
|
372
|
+
text: {
|
|
373
|
+
field: expr.concat(
|
|
374
|
+
expr.toString(expr.abs(expr.get(field('bathymetryContourDepth')))),
|
|
375
|
+
' m',
|
|
376
|
+
),
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
bathymetryLandforms: {maxZoom: 15},
|
|
380
|
+
bathymetryWaterNames: {maxZoom: 15},
|
|
381
|
+
}),
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
`water.bathymetryContours` and `water.bathymetryLabels` now consume the declared lines and `depth`;
|
|
387
|
+
labels default to line placement. Band fills still consume `min_depth` and `sort_key`.
|
|
388
|
+
`water.bathymetryLandforms` and `water.bathymetryWaterNames` style the declared names; set
|
|
389
|
+
`visible: false` to hide either. Without their declaration, those styles emit no name layers.
|
|
390
|
+
Name styles may use `class` in expressions without changing the source classification.
|
|
391
|
+
|
|
392
|
+
Mixed geometry can opt into `placementByGeometry` on either name style. Its resolved policy must
|
|
393
|
+
include `point`, `line`, and `polygon`; `point` must be `'point'`, while the other two accept
|
|
394
|
+
`'point'`, `'line'`, or `'line-center'`. The policy overrides scalar `placement`, including an
|
|
395
|
+
inherited value. Without it, scalar placement and existing generated layers remain unchanged.
|
|
396
|
+
|
|
397
|
+
For example, this map labels point features at their coordinates and retains line placement for
|
|
398
|
+
lines and polygon boundaries:
|
|
399
|
+
|
|
400
|
+
<!-- docs:check -->
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
import {bathymetry, defineMap, labels, water} from '@tileflow/core';
|
|
404
|
+
import {streets} from '@tileflow/maps';
|
|
405
|
+
|
|
406
|
+
export default defineMap({
|
|
407
|
+
id: 'marine-names',
|
|
408
|
+
version: 1,
|
|
409
|
+
extends: streets,
|
|
410
|
+
marine: {
|
|
411
|
+
bathymetry: bathymetry({
|
|
412
|
+
vectorLayers: {landforms: {zoomRanges: [{minZoom: 0, maxZoom: 24}]}},
|
|
413
|
+
}),
|
|
414
|
+
nautical: false,
|
|
415
|
+
},
|
|
416
|
+
modules: {
|
|
417
|
+
labels: labels({language: 'es'}),
|
|
418
|
+
water: water({
|
|
419
|
+
bathymetryLandforms: {
|
|
420
|
+
placementByGeometry: {point: 'point', line: 'line', polygon: 'line'},
|
|
421
|
+
},
|
|
422
|
+
}),
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
This example declares source availability; the selected source must actually contain those
|
|
428
|
+
features. Equal placements share one geometry-filtered group per source range. A policy emits
|
|
429
|
+
at most three groups per range (384 layers for one name family at the 128-range limit). Groups
|
|
430
|
+
compile in `point`, `line`, `line-center` order below ordinary map labels; source priority orders
|
|
431
|
+
features within each group. Module refinements inherit the policy and style every group.
|
|
432
|
+
Render-stack refinements still address exact semantic targets; inspect each placement/range
|
|
433
|
+
target when applying a narrower refinement.
|
|
434
|
+
Normal collision still applies and can hide a name.
|
|
435
|
+
|
|
436
|
+
Placement uses the received geometry: `point` places a line label at its first rendered vertex;
|
|
437
|
+
it does not compute a stable line midpoint. Polygon `point` placement uses an interior position
|
|
438
|
+
of the rendered polygon, while `line` follows its boundary. Core does not generate source anchors,
|
|
439
|
+
merge identities or deduplicate names by text. A source can supply multiple stable point anchors
|
|
440
|
+
with shared identity properties and distinct anchor properties; their extent, visibility and
|
|
441
|
+
editorial policy belong to that source. Keep original source identities as properties rather than
|
|
442
|
+
assuming a universal numeric `feature.id`.
|
|
443
|
+
|
|
444
|
+
Water-module styles override product-level `bands`, `contours`, and `labels`. Existing custom
|
|
445
|
+
`text.field` expressions remain authoritative: when refining a map from legacy polygons to native
|
|
446
|
+
contours, use `expr.get(field('bathymetryContourDepth'))` for its depth. That auxiliary binding
|
|
447
|
+
resolves to `depth` independently of the primary World schema; `bathymetryMinDepth` keeps its
|
|
448
|
+
legacy band meaning. Raw `expr.get('depth')` is rejected by authoring validation.
|
|
449
|
+
Increasing only `maxZoom` does not remove an opacity curve that fades out earlier. Extend ranges
|
|
450
|
+
and opacity only where the selected source supports the intended detail.
|
|
451
|
+
|
|
452
|
+
The V1 schema also reserves `bathymetry_coverage`; Core adds no coverage rendering by default.
|
|
453
|
+
Derived contours and names provide geographic context, not certified navigation data.
|
|
454
|
+
|
|
455
|
+
The DEM enables future client-side sampling and depth profiles, but Core intentionally exposes no
|
|
456
|
+
profile or remote sampling API yet. Rendered relief must not be interpreted as navigation-safe
|
|
457
|
+
depth data.
|
|
458
|
+
|
|
459
|
+
Omitting `marine` preserves the transitional Tileflow World V1 bathymetry fallback. Declaring
|
|
460
|
+
`marine: 'none'` suppresses that fallback, while a selected bathymetry sidecar always takes
|
|
461
|
+
precedence. Compiled styles retain the singular `tileflow:data` metadata for existing consumers and
|
|
462
|
+
add `tileflow:sources` plus `tileflow:sourceRequirements` for independent product validation.
|
|
463
|
+
|
|
464
|
+
Detailed city datasets can bind `sidewalk`, `streetFurniture`, and `circularFeature` source layers
|
|
465
|
+
through `openMapTiles({layers, fields})`. When present, `roads.sidewalks` owns source-backed
|
|
466
|
+
pedestrian polygons, `roads.crossings` owns oriented crossing icons, and `roads.roundabouts` owns
|
|
467
|
+
metric circular road rings. A `transportationOutline` binding names lines that trace the outer
|
|
468
|
+
boundary of each road area; when bound, `roads.areas` outlines stroke those lines instead of the
|
|
469
|
+
area polygons, so the holes of a plaza (tree pits, planting beds) are not stroked. These bindings
|
|
470
|
+
are optional: a generic OpenMapTiles source remains
|
|
471
|
+
valid and the compiler omits unsupported detail instead of inventing geometry. Crossing icons are
|
|
472
|
+
explicit because the data contract cannot assume a sprite name. They require positive physical
|
|
473
|
+
`markings` evidence, reject `crossing=no`, and require a numeric `direction` from 0 to 360 degrees
|
|
474
|
+
(numbers and numeric strings are accepted). World derives missing directions only when the
|
|
475
|
+
crossing has an unambiguous road axis; unresolved points on adjoining footways therefore remain
|
|
476
|
+
hidden instead of receiving a default rotation. All three fields are semantic bindings, so custom
|
|
477
|
+
schemas can remap them without weakening that rule.
|
|
478
|
+
|
|
479
|
+
Metric road rings scale continuously with the map from zoom 15 through 24, including fractional
|
|
480
|
+
zooms. Their casing is below surface road fills so approaching streets cover the outline at each
|
|
481
|
+
junction. The ring fill stays below bridges and road markings.
|
|
482
|
+
|
|
483
|
+
Road treatment `widthScale` scales both the stroke and an authored `gapWidth`, keeping hollow
|
|
484
|
+
casings aligned with the carriageway at fractional zoom levels.
|
|
485
|
+
When every dash treatment is continuous (`[1, 0]`), road layers omit the dash pattern so thin
|
|
486
|
+
casings render as solid strokes. Actual construction and tunnel dashes remain patterned.
|
|
487
|
+
|
|
488
|
+
Datasets may also publish the optional, decorative `canopy` layer: points inside urban parks and
|
|
489
|
+
gardens, at most one per cell of a grid of about 20 m that measured tree canopy covers. Each point
|
|
490
|
+
tells how much room a drawn grove of tree crowns has and how dense the canopy around it is. The
|
|
491
|
+
points are not surveyed trees, are unrelated to the optional `tree` layer, and are not search or
|
|
492
|
+
interaction targets. `openMapTiles()` and `tileflowWorldV1Schema()` bind the layer as `canopy` and
|
|
493
|
+
its numeric fields as follows:
|
|
494
|
+
|
|
495
|
+
- `canopyRank` (`rank`): a stable integer from 0 through 15. Lower ranks appear at lower zooms, and
|
|
496
|
+
the rank can vary artwork.
|
|
497
|
+
- `canopyClearanceAtZoom15` (`clearance_px_z15`): 4, 6, 8, 12, 16, or 24. This is the lower bound
|
|
498
|
+
of the distance from the point to the nearest edge a crown must not cover, such as a park edge,
|
|
499
|
+
water, a building, a plaza, a pitch, or a roadway, in pixels of a 512-pixel tile at zoom 15. A
|
|
500
|
+
grove fits at zoom `z` when its drawn reach in screen pixels is at most
|
|
501
|
+
`clearance_px_z15 × 2^(z − 15)`.
|
|
502
|
+
- `canopyCoverPercent` (`cover_pct`): 3, 30, or 60, the lower bound of the share, in percent, of
|
|
503
|
+
canopy taller than 3 m within 10 m of the point. The field is absent where no measurement exists;
|
|
504
|
+
treat absence as a middle density.
|
|
505
|
+
|
|
506
|
+
Features are MultiPoints, one per identical set of attributes. Native zoom 15 holds every point,
|
|
507
|
+
zoom 14 holds the points whose rank is below 8 at the same positions, and clients overzoom zoom 15.
|
|
508
|
+
`validateTileflowWorldV1Tilejson(...)` accepts a TileJSON without `canopy`; once declared, the layer
|
|
509
|
+
must occur exactly once, declare native z14–z15, and advertise Number `rank`, `clearance_px_z15`, and
|
|
510
|
+
`cover_pct`. No module draws the layer. A source without it remains valid, and render passes bound
|
|
511
|
+
to it draw nothing. `capabilities: {canopy: false}` removes the binding, after which a pass with
|
|
512
|
+
`feature: 'canopy'` fails with a missing-binding error.
|
|
513
|
+
|
|
514
|
+
Draw groves with symbol render passes, one grove of whole crowns per point, sized to its room:
|
|
515
|
+
|
|
516
|
+
<!-- docs:check -->
|
|
517
|
+
|
|
518
|
+
```ts
|
|
519
|
+
import {expr, field, fixed, renderPass, token, vegetation, withRenderStack} from '@tileflow/core';
|
|
520
|
+
|
|
521
|
+
// The grove artwork reaches 32 pixels from its center at icon size 1.
|
|
522
|
+
const clearance = expr.get(field('canopyClearanceAtZoom15'));
|
|
523
|
+
// The size that fills the room at one zoom, never larger than the artwork itself.
|
|
524
|
+
const fit = (divisor: number) => expr.min(expr.divide(clearance, divisor), 1);
|
|
525
|
+
|
|
526
|
+
const groves = (minZoom: number, fromRank: number, toRank: number) =>
|
|
527
|
+
renderPass({
|
|
528
|
+
placement: 'above-vegetation',
|
|
529
|
+
feature: 'canopy',
|
|
530
|
+
renderer: 'symbol',
|
|
531
|
+
selector: {
|
|
532
|
+
kind: 'all',
|
|
533
|
+
selectors: [
|
|
534
|
+
{field: 'canopyRank', kind: 'compare', operator: 'gte', value: fromRank},
|
|
535
|
+
{field: 'canopyRank', kind: 'compare', operator: 'lt', value: toRank},
|
|
536
|
+
],
|
|
537
|
+
},
|
|
538
|
+
style: {
|
|
539
|
+
minZoom,
|
|
540
|
+
icon: {
|
|
541
|
+
ignorePlacement: true,
|
|
542
|
+
image: token.image('vegetation.canopy.grove'),
|
|
543
|
+
overlap: 'always',
|
|
544
|
+
size: expr.interpolate({base: 2, kind: 'exponential'}, expr.zoom(), [
|
|
545
|
+
[14, fit(64)],
|
|
546
|
+
[15, fit(32)],
|
|
547
|
+
[16, fit(16)],
|
|
548
|
+
[17, fit(8)],
|
|
549
|
+
[18, fit(4)],
|
|
550
|
+
]),
|
|
551
|
+
},
|
|
552
|
+
text: {
|
|
553
|
+
field: 'o',
|
|
554
|
+
font: token.font('default'),
|
|
555
|
+
opacity: fixed(0, {reason: 'The core text only decides placement'}),
|
|
556
|
+
overlap: 'cooperative',
|
|
557
|
+
size: fixed(8, {reason: 'Matches the solid core of the grove artwork'}),
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
export const canopyGroves = withRenderStack(vegetation(), {
|
|
563
|
+
groves14: groves(14, 0, 4),
|
|
564
|
+
groves15: groves(15, 4, 8),
|
|
565
|
+
groves16: groves(16, 8, 16),
|
|
566
|
+
});
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
- Give each rank range its own pass with `minZoom`, so its points appear whole at their entry zoom.
|
|
570
|
+
MapLibre evaluates expressions that combine zoom and feature data at whole zoom levels and
|
|
571
|
+
interpolates between them, so a fade per feature would leave symbols half transparent. Zoom 14
|
|
572
|
+
tiles carry the ranks below 8, so the first pass may cover ranks 0–7.
|
|
573
|
+
- Size each grove from `canopyClearanceAtZoom15`. Here the size is `clearance / 32` at zoom 15 and
|
|
574
|
+
doubles with each zoom until the grove reaches the artwork's size, which keeps the reach within
|
|
575
|
+
the room.
|
|
576
|
+
- Collision tests an icon's whole image, including the transparent margin around the crowns. A small
|
|
577
|
+
invisible text the size of the grove's core therefore decides placement with
|
|
578
|
+
`overlap: 'cooperative'`, while the icon draws with `overlap: 'always'` and `ignorePlacement: true`.
|
|
579
|
+
Groves may overlap each other but give way to labels, which draw above the vegetation band and are
|
|
580
|
+
therefore placed first, with the default `never`.
|
|
581
|
+
|
|
582
|
+
Every theme of the map must define the image token, and the map's icon directories must provide the
|
|
583
|
+
artwork. Native artifacts replace `cooperative` with `allowOverlap: true`, so there groves can cover
|
|
584
|
+
labels; see [symbol overlap modes](shared-visual-primitives.md#symbol-overlap-modes).
|
|
585
|
+
|
|
586
|
+
The optional `business_corridor` extension contains activity-selected source footprints below
|
|
587
|
+
buildings. Its `activity_score`, `rank`, `min_zoom`, and `confidence` fields control a quiet local
|
|
588
|
+
warm tint without drawing POI-radius circles. Current building footprints may carry sparse
|
|
589
|
+
`building_tone=commercial|destination|active`; absence means the neutral base building. The three
|
|
590
|
+
values preserve why the warm tone was selected even when a theme maps them to the same color.
|
|
591
|
+
Buildings may also carry World's `class`, their use in Overture's building subtype values; without
|
|
592
|
+
a tone, the buildings module colours `residential`, `commercial`, public buildings (`civic`,
|
|
593
|
+
`education`, `medical`, `religious` and the like) and works (`industrial`, `agricultural`,
|
|
594
|
+
`service`) with the theme's `residential`, `commercial`, `civic` and `industrial` building colours,
|
|
595
|
+
and every other building with `generic`. Building visibility is fixed by the layer zoom contract and never by optional semantics. The land
|
|
596
|
+
module also exposes
|
|
597
|
+
`medical`, `education`, and `government` independently instead of collapsing their already distinct
|
|
598
|
+
source classes into one civic color.
|
|
599
|
+
|
|
600
|
+
World and text assets are independent contracts. `tileflowWorld()` selects `world-v1/current` or an
|
|
601
|
+
exact `releaseId + descriptorSha256`; a `glyphs` declaration contains its own complete URL. Every
|
|
602
|
+
official map compiles as imported because it owns or inherits a URL or packaged-font provider.
|
|
603
|
+
URL-backed maps become exact-byte reproducible
|
|
604
|
+
when the immutable global base-asset set is published and their explicit URL is updated to its
|
|
605
|
+
`assetSetSha256` path; no World or compiler fallback participates in that rollout. The global
|
|
606
|
+
base-asset manifest's hash and a map build manifest's identically named `assetSetSha256` use
|
|
607
|
+
different contracts and must never be substituted for one another.
|
|
608
|
+
|
|
609
|
+
Upstream data attribution remains in the MapLibre source. `Map by Tileflow` is a separate product
|
|
610
|
+
credit/trademark surface and must not replace, obscure, or be presented as upstream attribution.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Depth markers
|
|
2
|
+
|
|
3
|
+
DOM markers float above the map: in a 3D scene a pin behind a tower still shows in front of it.
|
|
4
|
+
`createTileflowDepthMarkers` draws markers in the map's 3D scene instead. A MapLibre custom layer
|
|
5
|
+
draws them against the depth the map's extrusions write, so nearer buildings hide them as the camera
|
|
6
|
+
moves.
|
|
7
|
+
|
|
8
|
+
## Add markers
|
|
9
|
+
|
|
10
|
+
This example assumes an existing MapLibre map named `map` with 3D buildings, and two pictures drawn
|
|
11
|
+
by the application:
|
|
12
|
+
|
|
13
|
+
<!-- docs:check -->
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import {createTileflowDepthMarkers, type TileflowDepthMarkerMap} from '@tileflow/core/browser';
|
|
17
|
+
|
|
18
|
+
export function pinDestination(
|
|
19
|
+
map: TileflowDepthMarkerMap,
|
|
20
|
+
at: [number, number],
|
|
21
|
+
pin: HTMLCanvasElement,
|
|
22
|
+
ring: HTMLCanvasElement,
|
|
23
|
+
) {
|
|
24
|
+
const markers = createTileflowDepthMarkers(map);
|
|
25
|
+
// A pin facing the viewer, 28 × 40 CSS pixels, standing on the position.
|
|
26
|
+
markers.add({lngLat: at, image: pin, size: [28, 40], anchor: 'bottom'});
|
|
27
|
+
// A ring lying on the ground, 60 metres across.
|
|
28
|
+
const halo = markers.add({lngLat: at, image: ring, size: [60, 60], alignment: 'ground'});
|
|
29
|
+
return {
|
|
30
|
+
pulse: (opacity: number) => halo.update({opacity}),
|
|
31
|
+
dispose: () => markers.dispose(),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A marker has:
|
|
37
|
+
|
|
38
|
+
- **Position.** `lngLat`, and `altitude` in metres above the ground.
|
|
39
|
+
- **Picture.** `image`: an image, an image bitmap, a canvas or image data. After drawing into a
|
|
40
|
+
canvas again, pass it to `update({image})` so the marker uploads it once more. Markers that share
|
|
41
|
+
a picture share its upload.
|
|
42
|
+
- **Alignment.** `screen` (the default) faces the viewer at `size` in CSS pixels, like a pin.
|
|
43
|
+
`ground` lies flat on the ground at `size` in metres, in the camera's perspective, like a ring or
|
|
44
|
+
an arrow painted on the street.
|
|
45
|
+
- **Anchor and placement.** `anchor` (`center` or `bottom`), `offset` in CSS pixels for screen
|
|
46
|
+
markers, and `rotation` in degrees clockwise: from the screen's up for screen markers, from north
|
|
47
|
+
for ground markers.
|
|
48
|
+
- **Opacity.** From 0 to 1.
|
|
49
|
+
- **`occlude`.** Defaults to true: nearer 3D content hides the marker. Set it to false for a marker
|
|
50
|
+
that must always show.
|
|
51
|
+
|
|
52
|
+
Markers test against the scene's depth without writing to it, so they never hide one another. A
|
|
53
|
+
screen marker takes the depth of its position: a tower in front of the position hides the whole
|
|
54
|
+
pin, a tower in front of part of it hides that part.
|
|
55
|
+
|
|
56
|
+
## Layer and lifecycle
|
|
57
|
+
|
|
58
|
+
The markers share one custom layer, `tileflow-depth-markers` unless `id` names another, added with
|
|
59
|
+
the first marker. `beforeId` places it under a layer, given as an id or as a function that finds the
|
|
60
|
+
layer again after a style change. After a style change the layer puts itself back, and pictures
|
|
61
|
+
are uploaded again. `dispose()` removes the layer and releases its GPU resources.
|
|
62
|
+
|
|
63
|
+
Depth markers need MapLibre GL JS on WebGL 2 (GL JS 5 or later). They work on globe and Mercator
|
|
64
|
+
projections. They draw pictures only: draw text and effects into the picture, or keep DOM markers
|
|
65
|
+
for interactive content.
|