@tileflow/core 0.1.0-alpha.34 → 0.1.0-alpha.36
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 +24 -23
- package/THIRD_PARTY_NOTICES.md +100 -6
- package/dist/browser.d.ts +758 -388
- package/dist/browser.js +4598 -1169
- package/dist/build.d.ts +9 -5
- package/dist/build.js +2892 -1410
- package/dist/capture.js +1 -0
- package/dist/config-reference.json +3780 -2153
- package/dist/{icon-composition-DqoZWQuz.d.ts → icon-composition-sN3JkXdb.d.ts} +380 -96
- package/dist/index.d.ts +426 -13
- package/dist/index.js +4999 -1841
- package/dist/{manifest-types-hmzy2bBX.d.ts → manifest-types-jrBdBrES.d.ts} +71 -9
- package/dist/manifest.d.ts +2 -2
- package/dist/manifest.js +80 -2
- package/dist/native-profile.js +1 -0
- package/dist/native.d.ts +13 -3
- package/dist/native.js +714 -318
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +124 -6
- package/dist/worker/buildings-3d-worker.global.js +1 -0
- package/docs/authoring-model.md +12 -3
- package/docs/boundary-areas-and-choropleths.md +217 -0
- package/docs/buildings-3d.md +264 -34
- package/docs/cartographic-defaults.md +142 -0
- package/docs/compiled-style-performance.md +14 -3
- package/docs/data-is-separate-from-design.md +191 -7
- package/docs/hosted-session-authorization.md +2 -4
- package/docs/map-and-terrain-examples.md +10 -3
- package/docs/native-artifact-profile.md +5 -0
- package/docs/native-resource-urls.md +5 -3
- package/docs/street-lights.md +22 -6
- package/docs/sun-and-light.md +42 -2
- package/docs/theme-transitions-and-blends.md +50 -0
- package/docs/themes-and-module-styles.md +66 -0
- package/docs/trees-3d.md +85 -13
- package/package.json +5 -4
|
@@ -121,14 +121,14 @@ their source faces' shapes. Browser adapters load local faces before MapLibre. N
|
|
|
121
121
|
`glyphs` URL provider instead, which enumerates the exact comma-joined MapLibre request keys in
|
|
122
122
|
`fontStacks`.
|
|
123
123
|
|
|
124
|
-
Streets,
|
|
124
|
+
Streets, Soundings, and Blueprint each declare
|
|
125
125
|
`https://api.tileflow.dev/base/33d4de5e8086d9d629d67d3f39fedb87e23686c4c1ac653c27e2a52aee9d00b3/glyphs/{fontstack}/{range}.pbf`
|
|
126
126
|
with the exact `Noto Sans Regular` and `Noto Sans Bold` stacks. The full hash identifies one
|
|
127
127
|
published, immutable glyph collection independent of World data. The older `/fonts/...` endpoint
|
|
128
128
|
remains a mutable compatibility URL and does not make a resolved map byte-reproducible. The global
|
|
129
129
|
glyph `assetSetSha256` is not the same-domain value as the per-map `assetSetSha256` in
|
|
130
130
|
`build-manifest.json`.
|
|
131
|
-
Baedeker and Siegfried each own a packaged directory containing Cormorant Garamond Regular, SemiBold, and Italic.
|
|
131
|
+
Baedeker, 1777, and Siegfried each own a packaged directory containing Cormorant Garamond Regular, SemiBold, and Italic.
|
|
132
132
|
|
|
133
133
|
Name the official generation deliberately, or use another OpenMapTiles-compatible vector source:
|
|
134
134
|
|
|
@@ -295,10 +295,181 @@ opt-in.
|
|
|
295
295
|
|
|
296
296
|
The vector palette uses thresholds −11 000, −8 000, −6 000, −4 000, −2 000, −1 000, −500, −200,
|
|
297
297
|
−100, −50, −20, −10, and 0 metres. `bands`, `contours`, and `labels` remain separate controls.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
298
|
+
Without an optional-layer declaration, contours and depth labels retain the legacy polygon-band
|
|
299
|
+
behavior. `marine` shorthands and existing maps keep that behavior.
|
|
300
|
+
|
|
301
|
+
### Declare contour and name layers
|
|
302
|
+
|
|
303
|
+
For a compatible source, `vectorLayers` explicitly declares optional layers. Core never fetches
|
|
304
|
+
TileJSON to detect them and never substitutes polygon edges for declared contour lines. A missing
|
|
305
|
+
declared layer therefore draws no features; validate the selected dataset before rendering.
|
|
306
|
+
`display: 'bands'` or `'hybrid'` loads the vector source.
|
|
307
|
+
|
|
308
|
+
| Declaration | Source layer | Geometry and fields |
|
|
309
|
+
| --------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
|
310
|
+
| `contours: true` | `bathymetry_contour` | Lines with numeric `depth`, negative metres below the source vertical datum. Zero and positive depths are excluded. |
|
|
311
|
+
| `landforms: true` or a range inventory | `seafloor_landform` | Named points, lines, or polygons with stable source `id`, provider `class`, and `name`. |
|
|
312
|
+
| `waterNames: true` or a range inventory | `water_name` | Named points, lines, or polygons with the same name contract. |
|
|
313
|
+
|
|
314
|
+
Name geometry stays in its source representation. `placement: 'point'` (the default) lets the
|
|
315
|
+
renderer place names inside polygons or on points. Use `placement: 'line'` to label source lines.
|
|
316
|
+
Core creates no substitute point, centroid, or line feature.
|
|
317
|
+
|
|
318
|
+
Name layers preserve source properties; `id` is a source identifier, with no guarantee of a numeric
|
|
319
|
+
renderer feature ID. Optional `name:<language>` fields retain the source's language assignments.
|
|
320
|
+
`labels({language: 'es'})` selects a nonempty `name:es`, falling back to `name`; `auto` and `local`
|
|
321
|
+
use `name` directly. Core does not infer the language of an unqualified name or invent translations.
|
|
322
|
+
Optional `min_zoom` is inclusive and `max_zoom` exclusive, defaulting to 0 and 24. These nominal
|
|
323
|
+
ranges intersect the symbol style's range. With `true`, MapLibre evaluates feature zoom filters at
|
|
324
|
+
integer zooms: `min_zoom: 7.6` first becomes eligible at zoom 8. For exact fractional boundaries,
|
|
325
|
+
declare an exhaustive range inventory instead:
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
vectorLayers: {
|
|
329
|
+
contours: true,
|
|
330
|
+
landforms: {zoomRanges: [{minZoom: 0, maxZoom: 24}]},
|
|
331
|
+
waterNames: {
|
|
332
|
+
zoomRanges: [{minZoom: 1, maxZoom: 6.5}, {minZoom: 7.6, maxZoom: 10}],
|
|
333
|
+
},
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Each declared pair becomes a separate symbol layer with fractional renderer bounds and equality
|
|
338
|
+
filters on the source's original range. A `7.6–10` feature appears at 7.6 and disappears at 10.
|
|
339
|
+
Unlisted pairs are omitted. Core never discovers ranges from tiles: validate the source inventory
|
|
340
|
+
against this exhaustive declaration before rendering, and retain that inventory with reproducible
|
|
341
|
+
captures. Numeric pairs must match the source's encoded values exactly.
|
|
342
|
+
|
|
343
|
+
Each name layer accepts 1–128 unique pairs with `0 <= minZoom < maxZoom <= 24`;
|
|
344
|
+
`tileflowBathymetryNameLayerLimits.maxZoomRanges` exposes the limit. Input order does not affect
|
|
345
|
+
output. Different pairs can overlap in zoom because equality filters keep their feature sets
|
|
346
|
+
disjoint. A style may narrow a pair's visibility; an empty intersection emits no layer.
|
|
347
|
+
Missing or null source bounds default to 0/24. Nonnumeric values use the same numeric-conversion
|
|
348
|
+
fallbacks; numeric strings are converted. Such features draw only if their normalized pair is
|
|
349
|
+
declared. Core preserves the original source properties.
|
|
350
|
+
|
|
351
|
+
Optional numeric `sort_key` defaults to 0; smaller values enter collision placement first within
|
|
352
|
+
each cohort. Cohorts follow ascending minimum then maximum zoom and sit below ordinary map labels.
|
|
353
|
+
Names use normal collision placement without overlap by default.
|
|
354
|
+
|
|
355
|
+
This example declares a source whose regional detail supports zoom 14. Its extended ranges are
|
|
356
|
+
explicit design choices, not a promise of worldwide detail:
|
|
357
|
+
|
|
358
|
+
<!-- docs:check -->
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
import {bathymetry, defineMap, expr, field, fixed, labels, water} from '@tileflow/core';
|
|
362
|
+
import {soundings} from '@tileflow/maps';
|
|
363
|
+
|
|
364
|
+
export default defineMap({
|
|
365
|
+
id: 'regional-depth',
|
|
366
|
+
version: 1,
|
|
367
|
+
extends: soundings,
|
|
368
|
+
marine: {
|
|
369
|
+
bathymetry: bathymetry({
|
|
370
|
+
url: 'https://tiles.example.test/regional-depth/tiles.json',
|
|
371
|
+
vectorLayers: {
|
|
372
|
+
contours: true,
|
|
373
|
+
landforms: {zoomRanges: [{minZoom: 0, maxZoom: 24}]},
|
|
374
|
+
waterNames: {zoomRanges: [{minZoom: 7.6, maxZoom: 10}]},
|
|
375
|
+
},
|
|
376
|
+
contours: {},
|
|
377
|
+
labels: {},
|
|
378
|
+
}),
|
|
379
|
+
nautical: false,
|
|
380
|
+
},
|
|
381
|
+
modules: {
|
|
382
|
+
labels: labels({language: 'es'}),
|
|
383
|
+
water: water({
|
|
384
|
+
bathymetry: {
|
|
385
|
+
maxZoom: 15,
|
|
386
|
+
opacity: fixed(0.8, {reason: 'The selected regional source supplies detailed bands'}),
|
|
387
|
+
},
|
|
388
|
+
bathymetryContours: {maxZoom: 15},
|
|
389
|
+
bathymetryLabels: {
|
|
390
|
+
maxZoom: 15,
|
|
391
|
+
text: {
|
|
392
|
+
field: expr.concat(
|
|
393
|
+
expr.toString(expr.abs(expr.get(field('bathymetryContourDepth')))),
|
|
394
|
+
' m',
|
|
395
|
+
),
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
bathymetryLandforms: {maxZoom: 15},
|
|
399
|
+
bathymetryWaterNames: {maxZoom: 15},
|
|
400
|
+
}),
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
`water.bathymetryContours` and `water.bathymetryLabels` now consume the declared lines and `depth`;
|
|
406
|
+
labels default to line placement. Band fills still consume `min_depth` and `sort_key`.
|
|
407
|
+
`water.bathymetryLandforms` and `water.bathymetryWaterNames` style the declared names; set
|
|
408
|
+
`visible: false` to hide either. Without their declaration, those styles emit no name layers.
|
|
409
|
+
Name styles may use `class` in expressions without changing the source classification.
|
|
410
|
+
|
|
411
|
+
Mixed geometry can opt into `placementByGeometry` on either name style. Its resolved policy must
|
|
412
|
+
include `point`, `line`, and `polygon`; `point` must be `'point'`, while the other two accept
|
|
413
|
+
`'point'`, `'line'`, or `'line-center'`. The policy overrides scalar `placement`, including an
|
|
414
|
+
inherited value. Without it, scalar placement and existing generated layers remain unchanged.
|
|
415
|
+
|
|
416
|
+
For example, this map labels point features at their coordinates and retains line placement for
|
|
417
|
+
lines and polygon boundaries:
|
|
418
|
+
|
|
419
|
+
<!-- docs:check -->
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
import {bathymetry, defineMap, labels, water} from '@tileflow/core';
|
|
423
|
+
import {streets} from '@tileflow/maps';
|
|
424
|
+
|
|
425
|
+
export default defineMap({
|
|
426
|
+
id: 'marine-names',
|
|
427
|
+
version: 1,
|
|
428
|
+
extends: streets,
|
|
429
|
+
marine: {
|
|
430
|
+
bathymetry: bathymetry({
|
|
431
|
+
vectorLayers: {landforms: {zoomRanges: [{minZoom: 0, maxZoom: 24}]}},
|
|
432
|
+
}),
|
|
433
|
+
nautical: false,
|
|
434
|
+
},
|
|
435
|
+
modules: {
|
|
436
|
+
labels: labels({language: 'es'}),
|
|
437
|
+
water: water({
|
|
438
|
+
bathymetryLandforms: {
|
|
439
|
+
placementByGeometry: {point: 'point', line: 'line', polygon: 'line'},
|
|
440
|
+
},
|
|
441
|
+
}),
|
|
442
|
+
},
|
|
443
|
+
});
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
This example declares source availability; the selected source must actually contain those
|
|
447
|
+
features. Equal placements share one geometry-filtered group per source range. A policy emits
|
|
448
|
+
at most three groups per range (384 layers for one name family at the 128-range limit). Groups
|
|
449
|
+
compile in `point`, `line`, `line-center` order below ordinary map labels; source priority orders
|
|
450
|
+
features within each group. Module refinements inherit the policy and style every group.
|
|
451
|
+
Render-stack refinements still address exact semantic targets; inspect each placement/range
|
|
452
|
+
target when applying a narrower refinement.
|
|
453
|
+
Normal collision still applies and can hide a name.
|
|
454
|
+
|
|
455
|
+
Placement uses the received geometry: `point` places a line label at its first rendered vertex;
|
|
456
|
+
it does not compute a stable line midpoint. Polygon `point` placement uses an interior position
|
|
457
|
+
of the rendered polygon, while `line` follows its boundary. Core does not generate source anchors,
|
|
458
|
+
merge identities or deduplicate names by text. A source can supply multiple stable point anchors
|
|
459
|
+
with shared identity properties and distinct anchor properties; their extent, visibility and
|
|
460
|
+
editorial policy belong to that source. Keep original source identities as properties rather than
|
|
461
|
+
assuming a universal numeric `feature.id`.
|
|
462
|
+
|
|
463
|
+
Water-module styles override product-level `bands`, `contours`, and `labels`. Existing custom
|
|
464
|
+
`text.field` expressions remain authoritative: when refining a map from legacy polygons to native
|
|
465
|
+
contours, use `expr.get(field('bathymetryContourDepth'))` for its depth. That auxiliary binding
|
|
466
|
+
resolves to `depth` independently of the primary World schema; `bathymetryMinDepth` keeps its
|
|
467
|
+
legacy band meaning. Raw `expr.get('depth')` is rejected by authoring validation.
|
|
468
|
+
Increasing only `maxZoom` does not remove an opacity curve that fades out earlier. Extend ranges
|
|
469
|
+
and opacity only where the selected source supports the intended detail.
|
|
470
|
+
|
|
471
|
+
The V1 schema also reserves `bathymetry_coverage`; Core adds no coverage rendering by default.
|
|
472
|
+
Derived contours and names provide geographic context, not certified navigation data.
|
|
302
473
|
|
|
303
474
|
The DEM enables future client-side sampling and depth profiles, but Core intentionally exposes no
|
|
304
475
|
profile or remote sampling API yet. Rendered relief must not be interpreted as navigation-safe
|
|
@@ -318,6 +489,15 @@ explicit because the data contract cannot assume a sprite name. They require pos
|
|
|
318
489
|
`markings` evidence and reject `crossing=no`; both fields are semantic bindings, so custom schemas
|
|
319
490
|
can remap them without weakening that rule.
|
|
320
491
|
|
|
492
|
+
Metric road rings scale continuously with the map from zoom 15 through 24, including fractional
|
|
493
|
+
zooms. Their casing is below surface road fills so approaching streets cover the outline at each
|
|
494
|
+
junction. The ring fill stays below bridges and road markings.
|
|
495
|
+
|
|
496
|
+
Road treatment `widthScale` scales both the stroke and an authored `gapWidth`, keeping hollow
|
|
497
|
+
casings aligned with the carriageway at fractional zoom levels.
|
|
498
|
+
When every dash treatment is continuous (`[1, 0]`), road layers omit the dash pattern so thin
|
|
499
|
+
casings render as solid strokes. Actual construction and tunnel dashes remain patterned.
|
|
500
|
+
|
|
321
501
|
Datasets may also publish the optional, decorative `canopy` layer: points inside urban parks and
|
|
322
502
|
gardens, at most one per cell of a grid of about 20 m that measured tree canopy covers. Each point
|
|
323
503
|
tells how much room a drawn grove of tree crowns has and how dense the canopy around it is. The
|
|
@@ -421,7 +601,11 @@ buildings. Its `activity_score`, `rank`, `min_zoom`, and `confidence` fields con
|
|
|
421
601
|
warm tint without drawing POI-radius circles. Current building footprints may carry sparse
|
|
422
602
|
`building_tone=commercial|destination|active`; absence means the neutral base building. The three
|
|
423
603
|
values preserve why the warm tone was selected even when a theme maps them to the same color.
|
|
424
|
-
|
|
604
|
+
Buildings may also carry World's `class`, their use in Overture's building subtype values; without
|
|
605
|
+
a tone, the buildings module colours `residential`, `commercial`, public buildings (`civic`,
|
|
606
|
+
`education`, `medical`, `religious` and the like) and works (`industrial`, `agricultural`,
|
|
607
|
+
`service`) with the theme's `residential`, `commercial`, `civic` and `industrial` building colours,
|
|
608
|
+
and every other building with `generic`. Building visibility is fixed by the layer zoom contract and never by optional semantics. The land
|
|
425
609
|
module also exposes
|
|
426
610
|
`medical`, `education`, and `government` independently instead of collapsing their already distinct
|
|
427
611
|
source classes into one civic color.
|
|
@@ -92,9 +92,16 @@ then transition to Mercator between zoom 10 and 12 so detailed streets remain pl
|
|
|
92
92
|
property, or set it to `'mercator'`, for a consistently flat map.
|
|
93
93
|
|
|
94
94
|
Terrain keeps the original `'none' | 'hillshade' | '3d'` shorthand. The object form can style every
|
|
95
|
-
MapLibre hillshade paint control
|
|
96
|
-
|
|
97
|
-
`
|
|
95
|
+
MapLibre hillshade paint control. Raster terrain accepts either a TileJSON `url` or explicit `tiles`
|
|
96
|
+
templates, with optional `tileSize` (256 or 512) and native `minZoom`/`maxZoom` limits. The templates
|
|
97
|
+
must contain `{z}`, `{x}` and `{y}` and use HTTPS (HTTP is allowed for loopback development).
|
|
98
|
+
These source limits control DEM requests independently of `hillshade.minZoom`/`maxZoom` visibility;
|
|
99
|
+
the renderer reuses the highest available native resolution for closer views. Select resolution
|
|
100
|
+
limits supported by the provider and retain its required attribution.
|
|
101
|
+
|
|
102
|
+
Contours generate vector lines in the browser from their own explicit DEM tile template. They
|
|
103
|
+
require `demUrl`, `demMaxZoom`, and zoom-indexed `[minor, index]` `thresholds`; these are separate
|
|
104
|
+
from the raster source's `url` or `tiles`. Set `mode: 'none'` to compile
|
|
98
105
|
contours without adding a raster source, hillshade, or 3D terrain:
|
|
99
106
|
|
|
100
107
|
```ts
|
|
@@ -39,6 +39,11 @@ preparing the referenced resources.
|
|
|
39
39
|
|
|
40
40
|
The exported `tileflowNativeProfile` targets MapLibre React Native 11.3.10 with Native Android 13.2.0
|
|
41
41
|
and iOS 6.26.0. These are static compatibility targets, not a supported application dependency matrix.
|
|
42
|
+
The application integration uses MapLibre React Native 11.5.0 with Android 13.6.1 and iOS 6.31.0.
|
|
43
|
+
The artifact profile retains its earlier validation targets and build-record engine values, keeping
|
|
44
|
+
prepared artifacts compatible with existing Hosted validators. New application dependencies do not
|
|
45
|
+
expand the accepted style vocabulary.
|
|
46
|
+
|
|
42
47
|
The wrapper's Style Spec version is 26.2.1. Tileflow retains its existing compiler and parser at
|
|
43
48
|
24.8.5: `native-v1` checks that compiler's vocabulary and the intersection of its recorded Android
|
|
44
49
|
and iOS support. It does not expose every feature of Style Spec 26.2.1 or upgrade the web compiler.
|
|
@@ -121,7 +121,7 @@ network request. Do not attach a credential merely because this helper accepted
|
|
|
121
121
|
|
|
122
122
|
## Private parser and source builds
|
|
123
123
|
|
|
124
|
-
The native entry embeds the low-level parser from `whatwg-url`
|
|
124
|
+
The native entry embeds the low-level parser from `whatwg-url` 17.2.0, `tr46` 7.0.0 (Unicode 18
|
|
125
125
|
IDNA processing) and Punycode.js 2.3.1. It exposes no URL class or parser configuration. Package
|
|
126
126
|
consumers only install Core; the published `dist/native.js` has no external runtime imports.
|
|
127
127
|
The parser is a development/build dependency, not an additional runtime package to install.
|
|
@@ -136,8 +136,10 @@ policy with its existing host parser; it does not embed the mobile parser in its
|
|
|
136
136
|
|
|
137
137
|
From an SDK source checkout, run the ordinary Core build, not a direct source import in a native
|
|
138
138
|
application. `tsup.native.config.ts` builds only the native entry with `platform: 'browser'`,
|
|
139
|
-
`target: 'es2020'`, ESM and no splitting. Its plugin redirects
|
|
140
|
-
`./encoding` imports
|
|
139
|
+
`target: 'es2020'`, ESM and no splitting. Its plugin redirects the two relative
|
|
140
|
+
`./encoding` imports and the parser's two `@exodus/bytes` encoding imports to Core's private UTF-8
|
|
141
|
+
codec. Query encoding accepts only UTF-8; the legacy encoding bootstrap has no side effects.
|
|
142
|
+
Exodus and its legacy encoding tables are not embedded. The build rejects an
|
|
141
143
|
unexpected dependency version, an unbound codec import or external runtime imports. It does not
|
|
142
144
|
patch packages on disk, configure a global alias or change another entry's build.
|
|
143
145
|
|
package/docs/street-lights.md
CHANGED
|
@@ -6,6 +6,9 @@ glowing head. Streets and pavements the data maps as lit, but whose lamps it doe
|
|
|
6
6
|
soft light along them that claims no lamp's place. As the theme controller's sun darkens the map,
|
|
7
7
|
the lights switch on one by one; they go out as day comes.
|
|
8
8
|
|
|
9
|
+
Street lights are off by default in the SDK and in Streets. A compiled map must explicitly set
|
|
10
|
+
`roads.streetLights.enabled: true` before the renderer or preview will show lamp glows.
|
|
11
|
+
|
|
9
12
|
It is a custom WebGL2 layer for MapLibre GL JS, drawn above the map's buildings and below its
|
|
10
13
|
labels. When `createTileflowBuildings3D` raises the buildings, they hide the lamps behind them. It
|
|
11
14
|
needs WebGL2 and the Mercator projection, and shines from zoom 14 by default.
|
|
@@ -13,7 +16,8 @@ needs WebGL2 and the Mercator projection, and shines from zoom 14 by default.
|
|
|
13
16
|
## Light the streets
|
|
14
17
|
|
|
15
18
|
This example assumes an existing MapLibre map showing a Tileflow map whose vector source is called
|
|
16
|
-
`tileflow`, as Tileflow's maps call World,
|
|
19
|
+
`tileflow`, as Tileflow's maps call World, with `roads.streetLights.enabled: true` in its map
|
|
20
|
+
definition and a theme controller for it:
|
|
17
21
|
|
|
18
22
|
<!-- docs:check -->
|
|
19
23
|
|
|
@@ -56,12 +60,12 @@ lit streets, others almost none. Where the data says nothing, the streets stay d
|
|
|
56
60
|
|
|
57
61
|
The map describes its street lights in its roads module, next to the other details drawn from
|
|
58
62
|
street furniture, and publishes them with its style for every theme; `stats.declared` tells whether
|
|
59
|
-
it does. Without
|
|
63
|
+
it does. Without an enabled description the renderer stays off and mounts no layer:
|
|
60
64
|
|
|
61
65
|
<!-- docs:check -->
|
|
62
66
|
|
|
63
67
|
```ts
|
|
64
|
-
import {defineMap, refine} from '@tileflow/core';
|
|
68
|
+
import {defineMap, fixed, refine} from '@tileflow/core';
|
|
65
69
|
import {streets} from '@tileflow/maps';
|
|
66
70
|
|
|
67
71
|
export default defineMap({
|
|
@@ -71,7 +75,14 @@ export default defineMap({
|
|
|
71
75
|
extends: streets,
|
|
72
76
|
modules: {
|
|
73
77
|
// Sodium-orange light, a little brighter, on every street of tiles that map few lamps.
|
|
74
|
-
roads: refine({
|
|
78
|
+
roads: refine({
|
|
79
|
+
streetLights: {
|
|
80
|
+
enabled: true,
|
|
81
|
+
color: fixed('#ffb35c', {reason: 'Constant sodium-lamp color.'}),
|
|
82
|
+
intensity: 1.3,
|
|
83
|
+
estimate: 'streets',
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
75
86
|
},
|
|
76
87
|
});
|
|
77
88
|
```
|
|
@@ -89,11 +100,16 @@ street rather than as lamps that may not stand there.
|
|
|
89
100
|
|
|
90
101
|
`color` is the light of the lamps whose data gives no colour, a warm white by default, and
|
|
91
102
|
`intensity` scales the light from 0 to 2 (1 by default); both may be theme tokens and mix during a
|
|
92
|
-
blend. `streetLights: {enabled: false}` gives
|
|
103
|
+
blend. These settings apply only after `enabled: true`; `streetLights: {enabled: false}` gives
|
|
104
|
+
the map none.
|
|
93
105
|
|
|
94
106
|
## Options
|
|
95
107
|
|
|
96
|
-
The layer
|
|
108
|
+
The layer follows the compiled map by default. `createTileflowStreetLights({map, controller,
|
|
109
|
+
enabled: true})` explicitly enables the fallback design for a style without street-light metadata;
|
|
110
|
+
`enabled: false` keeps the renderer off even when the map declares lights.
|
|
111
|
+
|
|
112
|
+
The delivery options are `minzoom` (14 by default) and `maxTiles` (25
|
|
97
113
|
by default, at most 64) bound where and how far the lights shine, `beforeId` places the layer before
|
|
98
114
|
another one, and `transformRequest` applies the map's request rules to the tiles it reads.
|
|
99
115
|
|
package/docs/sun-and-light.md
CHANGED
|
@@ -15,8 +15,8 @@ application's own WebGL layers to draw, from the values `getSun()` returns.
|
|
|
15
15
|
## Describe a theme's sun
|
|
16
16
|
|
|
17
17
|
Set `lighting.sun` and `lighting.ambient` in the theme. Every value is optional; unset values take
|
|
18
|
-
the defaults in `tileflowDefaultSunPreset` (azimuth 210°, altitude 60°, white light at 0.
|
|
19
|
-
|
|
18
|
+
the defaults in `tileflowDefaultSunPreset` (azimuth 210°, altitude 60°, white light at 0.2, full
|
|
19
|
+
shadow strength, a white sky at 0.8 for soft daylight shadows):
|
|
20
20
|
|
|
21
21
|
<!-- docs:check -->
|
|
22
22
|
|
|
@@ -43,6 +43,46 @@ others are inherited. The compiled style carries the resolved sun in its metadat
|
|
|
43
43
|
direction in `lighting.position` no longer apply to a theme with a sun. A theme without `sun` or
|
|
44
44
|
`ambient` compiles exactly as before.
|
|
45
45
|
|
|
46
|
+
## Replace inherited lighting
|
|
47
|
+
|
|
48
|
+
`defineTheme(base, definition)` normally merges lighting field by field. An empty `lighting`,
|
|
49
|
+
`sun`, or `ambient` object therefore preserves inherited values. Use the existing `reset()`
|
|
50
|
+
operation to remove a preset explicitly while keeping the base theme's palette and typography:
|
|
51
|
+
|
|
52
|
+
<!-- docs:check -->
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import {defineTheme, reset} from '@tileflow/core';
|
|
56
|
+
import {streetsThemes} from '@tileflow/maps';
|
|
57
|
+
|
|
58
|
+
export const studio = defineTheme(streetsThemes.light, {
|
|
59
|
+
id: 'studio',
|
|
60
|
+
version: 1,
|
|
61
|
+
colorScheme: 'light',
|
|
62
|
+
lighting: {
|
|
63
|
+
sun: reset(),
|
|
64
|
+
ambient: reset(),
|
|
65
|
+
anchor: 'viewport',
|
|
66
|
+
position: [1.15, 180, 40],
|
|
67
|
+
color: '#FFFFFF',
|
|
68
|
+
intensity: 0.15,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Removing both presets lets the native extrusion light use the authored `anchor` and `position`.
|
|
74
|
+
Resetting only `sun` keeps an inherited ambient preset, and resetting only `ambient` keeps the sun;
|
|
75
|
+
the missing counterpart then uses the SDK defaults. `lighting: reset()` clears all inherited
|
|
76
|
+
lighting properties. A direct property such as `position`, `color`, or `intensity` can also be
|
|
77
|
+
reset. The result is a complete theme with those properties absent; it contains no reset nodes.
|
|
78
|
+
To replace a whole sun or ambient object, first derive a theme that resets that object, then derive
|
|
79
|
+
the replacement values from the cleared theme.
|
|
80
|
+
|
|
81
|
+
Removing a preset does not turn off the renderer's lighting, cast shadows, or ambient occlusion.
|
|
82
|
+
The SDK's 3D scene uses its fallback light when no preset is published. For a graphic building
|
|
83
|
+
style, choose the building module's `lighting: 'style'`, `shadows: false`, and `occlusion: false`
|
|
84
|
+
explicitly. A single static theme needs no time-of-day family or running clock.
|
|
85
|
+
|
|
46
86
|
## Read the sun in an application
|
|
47
87
|
|
|
48
88
|
`themeController.getSun()` returns the sun the map shows:
|
|
@@ -258,3 +258,53 @@ export async function duskWhileFlying(
|
|
|
258
258
|
The React, Vue, and Svelte maps take the same flag in `themeBlend`: pass
|
|
259
259
|
`themeBlend={{hold: true, position, themes}}` while the camera moves and drop `hold` when it rests.
|
|
260
260
|
The React Native `Map` blends natively and does not take `hold`.
|
|
261
|
+
|
|
262
|
+
## Time of day
|
|
263
|
+
|
|
264
|
+
`timeOfDay` is an optional map capability. It maps a theme and an artistic preset (`day`,
|
|
265
|
+
`dawn`, `dusk`, `night`) to a complete theme already in `themes`:
|
|
266
|
+
|
|
267
|
+
<!-- docs:check -->
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
import {defineMap} from '@tileflow/core';
|
|
271
|
+
import {streets, streetsThemes} from '@tileflow/maps';
|
|
272
|
+
|
|
273
|
+
export default defineMap({
|
|
274
|
+
id: 'city',
|
|
275
|
+
version: 1,
|
|
276
|
+
extends: streets,
|
|
277
|
+
themes: {
|
|
278
|
+
paper: streetsThemes.light,
|
|
279
|
+
sunrise: streetsThemes.dawn,
|
|
280
|
+
evening: streetsThemes.dusk,
|
|
281
|
+
ink: streetsThemes.dark,
|
|
282
|
+
},
|
|
283
|
+
defaultTheme: 'paper',
|
|
284
|
+
systemThemes: {light: 'paper', dark: 'ink'},
|
|
285
|
+
timeOfDay: {
|
|
286
|
+
paper: {day: 'paper', dawn: 'sunrise', dusk: 'evening', night: 'ink'},
|
|
287
|
+
},
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Applications keep `theme` and optionally add `timeOfDay`: `theme="paper" timeOfDay="dusk"`
|
|
292
|
+
selects `evening`. Compilation supports `createStyle(map, {theme: 'paper', timeOfDay: 'dusk'})`;
|
|
293
|
+
`resolveTileflowRuntimeTheme(map, theme, colorScheme, timeOfDay)` uses the same resolution.
|
|
294
|
+
Omitting the preset preserves the existing selection. `system` resolves its light/dark theme
|
|
295
|
+
first. A concrete variant such as `ink` also identifies its family when a preset is supplied.
|
|
296
|
+
Maps or themes without a family ignore this option. Within a declared family, an unavailable
|
|
297
|
+
preset is an error; controls should offer only declared presets. Families must include their own
|
|
298
|
+
name in their presets, and a concrete theme can belong to only one family.
|
|
299
|
+
|
|
300
|
+
The catalog inherits atomically; `timeOfDay: {}` disables it on an extension. Replacing `themes`
|
|
301
|
+
clears an inherited catalog. `defaultTheme`, `systemThemes`, and `themeTransition` retain their
|
|
302
|
+
behavior. An explicit `themeBlend` owns its concrete theme sequence and takes precedence over
|
|
303
|
+
`timeOfDay`. Changing the preset does not start a clock or call `setSun`; lighting is whatever the
|
|
304
|
+
selected theme already declares, and an application's sun override retains its existing behavior.
|
|
305
|
+
Captures, style URLs, and transition events use the resolved concrete theme name.
|
|
306
|
+
|
|
307
|
+
Maps with a catalog publish runtime manifest version 2. Updated browser and native readers accept
|
|
308
|
+
both versions. Old readers need a version 1 manifest: `createManifest(project, {manifestVersion: 1})`
|
|
309
|
+
omits the catalog and keeps every concrete theme available. Maps without a catalog still emit
|
|
310
|
+
version 1. Update readers before delivering a version 2 manifest.
|
|
@@ -15,6 +15,61 @@ same token keys, so changing a theme can never change map structure or silently
|
|
|
15
15
|
role. `defineTheme(base, definition)` is an authoring convenience that returns a fully materialized
|
|
16
16
|
document; it does not leave an inheritance edge in the resolved config.
|
|
17
17
|
|
|
18
|
+
## Start with one appearance
|
|
19
|
+
|
|
20
|
+
A map can have one appearance for its entire lifetime. Neither `systemThemes` nor `timeOfDay` is
|
|
21
|
+
required. This complete theme supplies the twelve base color roles the semantic compiler needs;
|
|
22
|
+
copy it and change its colors to start an independent design without inheriting an official map:
|
|
23
|
+
|
|
24
|
+
<!-- docs:check -->
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import {defineTheme} from '@tileflow/core';
|
|
28
|
+
|
|
29
|
+
export const paper = defineTheme({
|
|
30
|
+
id: 'paper',
|
|
31
|
+
version: 1,
|
|
32
|
+
colorScheme: 'light',
|
|
33
|
+
tokens: {
|
|
34
|
+
color: {
|
|
35
|
+
'surface.background': '#F6F7F3',
|
|
36
|
+
'surface.land': '#F1F3ED',
|
|
37
|
+
'surface.water': '#A9D3F5',
|
|
38
|
+
'surface.park': '#CDE8B5',
|
|
39
|
+
'surface.building': '#E6E3DA',
|
|
40
|
+
'roads.default': '#FFFFFF',
|
|
41
|
+
'roads.major': '#F4C95D',
|
|
42
|
+
'roads.casing': '#D9DEE2',
|
|
43
|
+
'boundaries.default': '#C9D1D9',
|
|
44
|
+
'labels.primary': '#3C4043',
|
|
45
|
+
'labels.muted': '#727B84',
|
|
46
|
+
'labels.halo': '#FFFFFF',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
An existing map definition can select this appearance with `themes: {paper}` and
|
|
53
|
+
`defaultTheme: 'paper'`. Supply that map's data and font assets separately; the default typography
|
|
54
|
+
uses `Noto Sans Regular`. A theme without these base roles is a valid appearance document but cannot
|
|
55
|
+
compile a semantic map. Colors above are an editable starting point, including for monochrome,
|
|
56
|
+
historical, dark-only, or imagined worlds; `colorScheme` describes the appearance for consumers and
|
|
57
|
+
does not generate a palette or choose a time of day.
|
|
58
|
+
|
|
59
|
+
The compiler derives more specific roles when they are absent. For example, primary and motorway
|
|
60
|
+
roads initially use `roads.major`; declaring `roads.motorway` replaces only that derived color.
|
|
61
|
+
Land-use fills, road casings, and label colors have their own semantic roles, so an explicit role
|
|
62
|
+
always wins over its fallback. Use `color.mix()` and `token.color()` in a theme when the relationship
|
|
63
|
+
itself should be authored and inspectable. Derivations resolve during compilation, without a clock
|
|
64
|
+
or a per-frame palette calculation.
|
|
65
|
+
|
|
66
|
+
Add named appearances when the map needs them. A day/night pair does not need dawn or dusk, and a
|
|
67
|
+
theme named `paper` or `fantasy` does not need a temporal family. Declare `timeOfDay` only when the
|
|
68
|
+
application should offer those artistic moments; see
|
|
69
|
+
[time-of-day selection](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/theme-transitions-and-blends.md#time-of-day).
|
|
70
|
+
|
|
71
|
+
## Compose and replace appearance values
|
|
72
|
+
|
|
18
73
|
Some optional tokens are for the application rather than the map's own layers. Route inks
|
|
19
74
|
(`route.line`, `route.casing`, `route.core`, `route.glow`, `route.alternative`,
|
|
20
75
|
`route.glow.opacity` and `route.core.opacity`) set how an application draws its routes; a theme
|
|
@@ -83,6 +138,17 @@ deterministic OKLCH `color.mix()` keep derived palette logic inspectable. Unknow
|
|
|
83
138
|
cross-category refs, token-schema drift, and unresolved visual nodes fail before Style JSON is
|
|
84
139
|
emitted.
|
|
85
140
|
|
|
141
|
+
Theme lighting also supports explicit removal during `defineTheme` composition. Use
|
|
142
|
+
`lighting: reset()` to remove the entire inherited lighting configuration, or
|
|
143
|
+
`lighting: {sun: reset(), ambient: reset()}` to remove those presets while retaining other light
|
|
144
|
+
properties. The same operation can remove a direct property such as `position` or `color`.
|
|
145
|
+
Omitting a property, or supplying an empty object, continues to inherit it. Reset operations are
|
|
146
|
+
materialized immediately and never enter the resolved theme or Style JSON. Removing a lighting
|
|
147
|
+
preset restores the renderer's fallback; it does not disable 3D shadows or occlusion. Those remain
|
|
148
|
+
explicit building settings. See
|
|
149
|
+
[sun and light](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/docs/sun-and-light.md#replace-inherited-lighting)
|
|
150
|
+
for a complete example.
|
|
151
|
+
|
|
86
152
|
There is no physical-layer override for the Tileflow basemap or separate official-map authoring
|
|
87
153
|
surface. Basemap behavior belongs to a typed control or owner-local render stack. Every
|
|
88
154
|
official and application map therefore follows the same schema, inheritance rules, diagnostics,
|