@react-x11/components 0.4.0 → 0.6.0
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 +143 -8
- package/dist/charts/components.d.ts +3 -1
- package/dist/charts/components.d.ts.map +1 -1
- package/dist/charts/components.js +8 -4
- package/dist/charts/components.js.map +1 -1
- package/dist/charts/node.d.ts +13 -6
- package/dist/charts/node.d.ts.map +1 -1
- package/dist/charts/node.js +75 -24
- package/dist/charts/node.js.map +1 -1
- package/dist/charts/render.d.ts +4 -0
- package/dist/charts/render.d.ts.map +1 -1
- package/dist/charts/render.js +11 -6
- package/dist/charts/render.js.map +1 -1
- package/dist/code-editor/node.d.ts +47 -16
- package/dist/code-editor/node.d.ts.map +1 -1
- package/dist/code-editor/node.js +121 -51
- package/dist/code-editor/node.js.map +1 -1
- package/dist/flow/draw.d.ts +19 -1
- package/dist/flow/draw.d.ts.map +1 -1
- package/dist/flow/draw.js +64 -35
- package/dist/flow/draw.js.map +1 -1
- package/dist/flow/index.d.ts.map +1 -1
- package/dist/flow/index.js +24 -8
- package/dist/flow/index.js.map +1 -1
- package/dist/flow/model.d.ts +4 -2
- package/dist/flow/model.d.ts.map +1 -1
- package/dist/flow/model.js +4 -2
- package/dist/flow/model.js.map +1 -1
- package/dist/flow/node.d.ts +40 -9
- package/dist/flow/node.d.ts.map +1 -1
- package/dist/flow/node.js +142 -53
- package/dist/flow/node.js.map +1 -1
- package/dist/flow/types.d.ts +46 -21
- package/dist/flow/types.d.ts.map +1 -1
- package/dist/formula/index.d.ts +4 -2
- package/dist/formula/index.d.ts.map +1 -1
- package/dist/formula/index.js.map +1 -1
- package/dist/formula/node.d.ts +5 -1
- package/dist/formula/node.d.ts.map +1 -1
- package/dist/formula/node.js +19 -3
- package/dist/formula/node.js.map +1 -1
- package/dist/html/controls.d.ts +4 -1
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +11 -5
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +18 -8
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/parse.d.ts +15 -10
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +41 -16
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +3 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +3 -1
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/values.d.ts +14 -1
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js +11 -8
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/index.d.ts.map +1 -1
- package/dist/html/index.js +1 -0
- package/dist/html/index.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +12 -5
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +7 -4
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +22 -1
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/node.d.ts +22 -8
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +97 -15
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +3 -0
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +2 -2
- package/dist/html/paint.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/maps/clip.d.ts +43 -0
- package/dist/maps/clip.d.ts.map +1 -0
- package/dist/maps/clip.js +171 -0
- package/dist/maps/clip.js.map +1 -0
- package/dist/maps/gzip.d.ts +15 -0
- package/dist/maps/gzip.d.ts.map +1 -0
- package/dist/maps/gzip.js +56 -0
- package/dist/maps/gzip.js.map +1 -0
- package/dist/maps/index.d.ts +55 -0
- package/dist/maps/index.d.ts.map +1 -0
- package/dist/maps/index.js +141 -0
- package/dist/maps/index.js.map +1 -0
- package/dist/maps/labels.d.ts +123 -0
- package/dist/maps/labels.d.ts.map +1 -0
- package/dist/maps/labels.js +323 -0
- package/dist/maps/labels.js.map +1 -0
- package/dist/maps/mvt.d.ts +244 -0
- package/dist/maps/mvt.d.ts.map +1 -0
- package/dist/maps/mvt.js +820 -0
- package/dist/maps/mvt.js.map +1 -0
- package/dist/maps/node.d.ts +278 -0
- package/dist/maps/node.d.ts.map +1 -0
- package/dist/maps/node.js +1459 -0
- package/dist/maps/node.js.map +1 -0
- package/dist/maps/overlay.d.ts +157 -0
- package/dist/maps/overlay.d.ts.map +1 -0
- package/dist/maps/overlay.js +472 -0
- package/dist/maps/overlay.js.map +1 -0
- package/dist/maps/paint.d.ts +226 -0
- package/dist/maps/paint.d.ts.map +1 -0
- package/dist/maps/paint.js +705 -0
- package/dist/maps/paint.js.map +1 -0
- package/dist/maps/proj.d.ts +281 -0
- package/dist/maps/proj.d.ts.map +1 -0
- package/dist/maps/proj.js +451 -0
- package/dist/maps/proj.js.map +1 -0
- package/dist/maps/sources.d.ts +256 -0
- package/dist/maps/sources.d.ts.map +1 -0
- package/dist/maps/sources.js +210 -0
- package/dist/maps/sources.js.map +1 -0
- package/dist/maps/style.d.ts +141 -0
- package/dist/maps/style.d.ts.map +1 -0
- package/dist/maps/style.js +147 -0
- package/dist/maps/style.js.map +1 -0
- package/dist/maps/styles.d.ts +82 -0
- package/dist/maps/styles.d.ts.map +1 -0
- package/dist/maps/styles.js +898 -0
- package/dist/maps/styles.js.map +1 -0
- package/dist/maps/tiles.d.ts +283 -0
- package/dist/maps/tiles.d.ts.map +1 -0
- package/dist/maps/tiles.js +469 -0
- package/dist/maps/tiles.js.map +1 -0
- package/dist/maps/types.d.ts +244 -0
- package/dist/maps/types.d.ts.map +1 -0
- package/dist/maps/types.js +2 -0
- package/dist/maps/types.js.map +1 -0
- package/dist/markdown/ast.d.ts +80 -6
- package/dist/markdown/ast.d.ts.map +1 -1
- package/dist/markdown/ast.js +13 -4
- package/dist/markdown/ast.js.map +1 -1
- package/dist/markdown/expressions.d.ts +27 -0
- package/dist/markdown/expressions.d.ts.map +1 -0
- package/dist/markdown/expressions.js +203 -0
- package/dist/markdown/expressions.js.map +1 -0
- package/dist/markdown/index.d.ts +61 -2
- package/dist/markdown/index.d.ts.map +1 -1
- package/dist/markdown/index.js +101 -2
- package/dist/markdown/index.js.map +1 -1
- package/dist/markdown/parse.d.ts +1 -1
- package/dist/markdown/parse.d.ts.map +1 -1
- package/dist/markdown/parse.js +163 -15
- package/dist/markdown/parse.js.map +1 -1
- package/dist/markdown/spans.d.ts.map +1 -1
- package/dist/markdown/spans.js +4 -0
- package/dist/markdown/spans.js.map +1 -1
- package/dist/markdown/tags.d.ts +35 -0
- package/dist/markdown/tags.d.ts.map +1 -0
- package/dist/markdown/tags.js +213 -0
- package/dist/markdown/tags.js.map +1 -0
- package/dist/reorder/hx.d.ts +18 -0
- package/dist/reorder/hx.d.ts.map +1 -0
- package/dist/reorder/hx.js +10 -0
- package/dist/reorder/hx.js.map +1 -0
- package/dist/reorder/index.d.ts +314 -0
- package/dist/reorder/index.d.ts.map +1 -0
- package/dist/reorder/index.js +1539 -0
- package/dist/reorder/index.js.map +1 -0
- package/dist/reorder/model.d.ts +107 -0
- package/dist/reorder/model.d.ts.map +1 -0
- package/dist/reorder/model.js +162 -0
- package/dist/reorder/model.js.map +1 -0
- package/dist/richtext/node.d.ts +15 -5
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +40 -11
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +35 -13
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +83 -27
- package/dist/richtext/runs.js.map +1 -1
- package/dist/tabs/index.d.ts +17 -0
- package/dist/tabs/index.d.ts.map +1 -1
- package/dist/tabs/index.js +849 -186
- package/dist/tabs/index.js.map +1 -1
- package/dist/terminal/vt/fonts.d.ts +45 -15
- package/dist/terminal/vt/fonts.d.ts.map +1 -1
- package/dist/terminal/vt/fonts.js +89 -19
- package/dist/terminal/vt/fonts.js.map +1 -1
- package/dist/terminal/vt/node.d.ts +42 -0
- package/dist/terminal/vt/node.d.ts.map +1 -1
- package/dist/terminal/vt/node.js +95 -11
- package/dist/terminal/vt/node.js.map +1 -1
- package/dist/terminal/vt/renderer.d.ts.map +1 -1
- package/dist/terminal/vt/renderer.js +13 -3
- package/dist/terminal/vt/renderer.js.map +1 -1
- package/package.json +14 -3
- package/src/charts/components.ts +14 -7
- package/src/charts/node.ts +81 -26
- package/src/charts/render.ts +16 -6
- package/src/code-editor/node.ts +147 -53
- package/src/flow/draw.ts +104 -35
- package/src/flow/index.ts +33 -13
- package/src/flow/model.ts +4 -2
- package/src/flow/node.ts +158 -59
- package/src/flow/types.ts +46 -21
- package/src/formula/index.ts +4 -2
- package/src/formula/node.ts +24 -5
- package/src/html/controls.ts +4 -1
- package/src/html/css/cascade.ts +18 -7
- package/src/html/css/parse.ts +46 -21
- package/src/html/css/style.ts +6 -1
- package/src/html/css/values.ts +25 -9
- package/src/html/index.ts +1 -0
- package/src/html/layout/boxes.ts +19 -9
- package/src/html/layout/inline.ts +23 -1
- package/src/html/node.ts +110 -17
- package/src/html/paint.ts +17 -2
- package/src/index.ts +102 -0
- package/src/maps/clip.ts +184 -0
- package/src/maps/gzip.ts +66 -0
- package/src/maps/index.ts +316 -0
- package/src/maps/labels.ts +442 -0
- package/src/maps/mvt.ts +857 -0
- package/src/maps/node.ts +1819 -0
- package/src/maps/overlay.ts +639 -0
- package/src/maps/paint.ts +978 -0
- package/src/maps/proj.ts +641 -0
- package/src/maps/sources.ts +425 -0
- package/src/maps/style.ts +309 -0
- package/src/maps/styles.ts +1040 -0
- package/src/maps/tiles.ts +676 -0
- package/src/maps/types.ts +260 -0
- package/src/markdown/ast.ts +85 -9
- package/src/markdown/expressions.ts +223 -0
- package/src/markdown/index.ts +190 -5
- package/src/markdown/parse.ts +230 -15
- package/src/markdown/spans.ts +4 -0
- package/src/markdown/tags.ts +240 -0
- package/src/reorder/hx.ts +31 -0
- package/src/reorder/index.ts +2223 -0
- package/src/reorder/model.ts +232 -0
- package/src/richtext/node.ts +49 -14
- package/src/richtext/runs.ts +115 -30
- package/src/tabs/index.ts +1040 -204
- package/src/terminal/vt/fonts.ts +128 -36
- package/src/terminal/vt/node.ts +105 -12
- package/src/terminal/vt/renderer.ts +13 -3
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// What to draw a tile's layers as.
|
|
2
|
+
//
|
|
3
|
+
// A **subset of the Mapbox/MapLibre GL style specification**, and a subset
|
|
4
|
+
// on purpose. The reason to shape it after that spec rather than invent one
|
|
5
|
+
// is that every provider documents their schema in its terms — Shortbread,
|
|
6
|
+
// OpenMapTiles, Mapbox Streets, Azure Maps, TomTom Orbis all publish "which
|
|
7
|
+
// layer, which field, at which zoom" as GL style fragments — so an author
|
|
8
|
+
// who has read any of those pages already knows what goes here.
|
|
9
|
+
//
|
|
10
|
+
// What is deliberately *not* here is the expression language. GL styles have
|
|
11
|
+
// grown a small typed Lisp (`['case', ['>', ['get', 'x'], 2], …]`) that is
|
|
12
|
+
// a real interpreter with a real cost per feature, and none of it is needed
|
|
13
|
+
// to say "roads, motorways thicker, at zoom 8 and up". So this takes the
|
|
14
|
+
// two older, simpler halves of the same spec:
|
|
15
|
+
//
|
|
16
|
+
// - **the legacy filter syntax** — `['all', ['==', 'kind', 'motorway'], …]`
|
|
17
|
+
// — which is a documented part of the spec, is what most schema
|
|
18
|
+
// documentation still shows, and compiles to a closure in one pass; and
|
|
19
|
+
// - **`stops`** for anything that varies with zoom, which is the older
|
|
20
|
+
// spelling of `['interpolate', ['linear'], ['zoom'], …]`.
|
|
21
|
+
//
|
|
22
|
+
// Both are resolved *once per layer per frame*, never per feature. That is
|
|
23
|
+
// the whole performance argument for the restriction: a dense tile has
|
|
24
|
+
// twelve thousand features and eighteen layers, so anything computed per
|
|
25
|
+
// layer is free and anything computed per feature is the frame.
|
|
26
|
+
import type { FeatureValue } from './mvt.js';
|
|
27
|
+
import { GeomType } from './mvt.js';
|
|
28
|
+
import type { FeatureCursor } from './mvt.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A value that may vary with the zoom.
|
|
32
|
+
*
|
|
33
|
+
* A bare value is constant. `{ stops }` is a list of `[zoom, value]` pairs,
|
|
34
|
+
* ordered by zoom: numbers interpolate linearly between the surrounding
|
|
35
|
+
* pair, and anything else steps at each stop. Outside the range the nearest
|
|
36
|
+
* stop holds, which is what makes a two-stop ramp a sensible whole answer
|
|
37
|
+
* rather than something that vanishes at zoom 0.
|
|
38
|
+
*/
|
|
39
|
+
export type Zoomed<T> = T | { stops: readonly (readonly [number, T])[] };
|
|
40
|
+
|
|
41
|
+
/** Mapbox's legacy filter syntax, which is the part of it a style here
|
|
42
|
+
* speaks. Compiled with {@link compileFilter}. */
|
|
43
|
+
export type MapFilter =
|
|
44
|
+
| readonly ['==' | '!=', string, FeatureValue]
|
|
45
|
+
| readonly ['<' | '<=' | '>' | '>=', string, number]
|
|
46
|
+
| readonly ['in' | '!in', string, ...FeatureValue[]]
|
|
47
|
+
| readonly ['has' | '!has', string]
|
|
48
|
+
| readonly ['all' | 'any' | 'none', ...MapFilter[]]
|
|
49
|
+
// `$type` is the spec's name for the geometry type, and the one filter
|
|
50
|
+
// that does not read a tag.
|
|
51
|
+
| readonly ['geometry', 'point' | 'line' | 'polygon'];
|
|
52
|
+
|
|
53
|
+
interface LayerBase {
|
|
54
|
+
/** This layer's name in the style. Used in a `<Map>` event payload and as
|
|
55
|
+
* the cache key for anything derived from it. */
|
|
56
|
+
id: string;
|
|
57
|
+
/** Which source, when the map has more than one. The first source by
|
|
58
|
+
* default. */
|
|
59
|
+
source?: string;
|
|
60
|
+
/** Which layer of the tile — `'streets'`, `'water_polygons'`. */
|
|
61
|
+
sourceLayer: string;
|
|
62
|
+
/** The zoom range this layer draws in. A layer outside its range costs
|
|
63
|
+
* nothing: it is skipped before its features are touched. */
|
|
64
|
+
minZoom?: number;
|
|
65
|
+
maxZoom?: number;
|
|
66
|
+
filter?: MapFilter;
|
|
67
|
+
/** `false` keeps the layer in the style and stops drawing it — what a
|
|
68
|
+
* layer toggle in an application's UI switches. */
|
|
69
|
+
visible?: boolean;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** An area. */
|
|
73
|
+
export interface FillLayer extends LayerBase {
|
|
74
|
+
type: 'fill';
|
|
75
|
+
color: Zoomed<string>;
|
|
76
|
+
opacity?: Zoomed<number>;
|
|
77
|
+
/** Stroked at one logical pixel around each ring, after the fill. */
|
|
78
|
+
outlineColor?: Zoomed<string>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A line: a road, a river, a boundary, a route. */
|
|
82
|
+
export interface LineLayer extends LayerBase {
|
|
83
|
+
type: 'line';
|
|
84
|
+
color: Zoomed<string>;
|
|
85
|
+
/** Logical pixels. */
|
|
86
|
+
width: Zoomed<number>;
|
|
87
|
+
opacity?: Zoomed<number>;
|
|
88
|
+
/** A dash pattern in logical pixels. */
|
|
89
|
+
dash?: readonly number[];
|
|
90
|
+
cap?: 'butt' | 'round' | 'square';
|
|
91
|
+
join?: 'miter' | 'round' | 'bevel';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** A point, as a disc. */
|
|
95
|
+
export interface CircleLayer extends LayerBase {
|
|
96
|
+
type: 'circle';
|
|
97
|
+
radius: Zoomed<number>;
|
|
98
|
+
color: Zoomed<string>;
|
|
99
|
+
opacity?: Zoomed<number>;
|
|
100
|
+
strokeColor?: Zoomed<string>;
|
|
101
|
+
strokeWidth?: Zoomed<number>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A label.
|
|
106
|
+
*
|
|
107
|
+
* Symbol layers are **not** rasterized into the tile: they are collected as
|
|
108
|
+
* candidates, placed against every other label on screen, and drawn into
|
|
109
|
+
* the frame directly. Three reasons, and each of them is a bug if it is
|
|
110
|
+
* done the other way. Collision is global — two labels in different tiles
|
|
111
|
+
* overlap just as readily as two in one — so a per-tile placement produces
|
|
112
|
+
* a map that reads as clutter at every seam. A label baked into a tile
|
|
113
|
+
* surface is stretched by the fractional zoom, and text is the one thing on
|
|
114
|
+
* a map nobody accepts blurred. And a tile's labels would be clipped at its
|
|
115
|
+
* edge, which is exactly where half of them sit.
|
|
116
|
+
*/
|
|
117
|
+
export interface SymbolLayer extends LayerBase {
|
|
118
|
+
type: 'symbol';
|
|
119
|
+
/** The tag to read the text from — `'name'`, or `'name:en'`. */
|
|
120
|
+
textField: string;
|
|
121
|
+
/** Logical pixels. */
|
|
122
|
+
textSize?: Zoomed<number>;
|
|
123
|
+
textColor?: Zoomed<string>;
|
|
124
|
+
/** Drawn under the glyphs, so a place name stays readable over a road. */
|
|
125
|
+
textHaloColor?: Zoomed<string>;
|
|
126
|
+
textHaloWidth?: Zoomed<number>;
|
|
127
|
+
/**
|
|
128
|
+
* Which label wins a collision, layer-wide. Higher wins; 0 by default,
|
|
129
|
+
* and the style's layer order breaks a tie.
|
|
130
|
+
*
|
|
131
|
+
* Deliberately not a per-feature expression. A schema's own importance
|
|
132
|
+
* field looks like the obvious input and is not usable as one: Shortbread
|
|
133
|
+
* carries `population`, which is 8,000,000 for London and 500 for Soho,
|
|
134
|
+
* so adding it to a rank that also has to order against street names
|
|
135
|
+
* needs a normalization nobody can guess. Splitting by `filter` — a layer
|
|
136
|
+
* for cities, a layer for suburbs — is exact, is what the ordering
|
|
137
|
+
* actually wants, and costs one more entry in a list.
|
|
138
|
+
*/
|
|
139
|
+
rank?: number;
|
|
140
|
+
/**
|
|
141
|
+
* How far apart, in pixels, the same text may repeat within this layer.
|
|
142
|
+
* 250 by default; `0` lets it repeat as often as it fits.
|
|
143
|
+
*
|
|
144
|
+
* Not a nicety. A street is one feature **per segment** in every schema
|
|
145
|
+
* there is, so `street_labels` offers "Oxford Street" a dozen times over
|
|
146
|
+
* for one street, and a placement that only tests overlap accepts all
|
|
147
|
+
* twelve — they do not overlap, they are strung out along the road. The
|
|
148
|
+
* same is true of an administrative area whose name arrives once per
|
|
149
|
+
* polygon part. Repeating a long street's name across a wide view is
|
|
150
|
+
* wanted; repeating it four times in a hundred pixels is not, and the
|
|
151
|
+
* distance is where that line goes.
|
|
152
|
+
*/
|
|
153
|
+
repeatDistance?: number;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export type MapStyleLayer = FillLayer | LineLayer | CircleLayer | SymbolLayer;
|
|
157
|
+
|
|
158
|
+
/** A whole style. */
|
|
159
|
+
export interface MapStyle {
|
|
160
|
+
/** Painted under every tile — the colour of the parts of the world that
|
|
161
|
+
* have no data yet, so it is what a map looks like while it loads. */
|
|
162
|
+
background?: string;
|
|
163
|
+
layers: readonly MapStyleLayer[];
|
|
164
|
+
/** The face labels are set in. The theme's `fontFamily` by default. */
|
|
165
|
+
fontFamily?: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// --- zoom ramps ------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
function isStops<T>(
|
|
171
|
+
value: Zoomed<T>,
|
|
172
|
+
): value is { stops: readonly (readonly [number, T])[] } {
|
|
173
|
+
return (
|
|
174
|
+
typeof value === 'object' &&
|
|
175
|
+
value !== null &&
|
|
176
|
+
Array.isArray((value as { stops?: unknown }).stops)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** A {@link Zoomed} at one zoom. Numbers interpolate, everything else
|
|
181
|
+
* steps. */
|
|
182
|
+
export function resolveZoomed<T>(value: Zoomed<T>, zoom: number): T {
|
|
183
|
+
if (!isStops(value)) return value;
|
|
184
|
+
const stops = value.stops;
|
|
185
|
+
if (stops.length === 0) throw new Error('a zoom ramp needs a stop');
|
|
186
|
+
if (zoom <= stops[0][0]) return stops[0][1];
|
|
187
|
+
const last = stops[stops.length - 1];
|
|
188
|
+
if (zoom >= last[0]) return last[1];
|
|
189
|
+
let i = 1;
|
|
190
|
+
while (i < stops.length && stops[i][0] <= zoom) i++;
|
|
191
|
+
const [z0, v0] = stops[i - 1];
|
|
192
|
+
const [z1, v1] = stops[i];
|
|
193
|
+
if (typeof v0 !== 'number' || typeof v1 !== 'number') return v0;
|
|
194
|
+
const t = z1 === z0 ? 0 : (zoom - z0) / (z1 - z0);
|
|
195
|
+
return (v0 + (v1 - v0) * t) as unknown as T;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// --- filters ---------------------------------------------------------------
|
|
199
|
+
|
|
200
|
+
/** A compiled filter: the shape the rasterizer calls per feature. */
|
|
201
|
+
export type CompiledFilter = (feature: FeatureCursor) => boolean;
|
|
202
|
+
|
|
203
|
+
const ALWAYS: CompiledFilter = () => true;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Compile a filter to a closure, once.
|
|
207
|
+
*
|
|
208
|
+
* The point of compiling rather than interpreting: the operator dispatch,
|
|
209
|
+
* the `in`-set construction and the geometry-type mapping all happen here,
|
|
210
|
+
* per layer, instead of twelve thousand times per tile. A `['in', 'kind',
|
|
211
|
+
* …]` with forty values becomes a `Set` lookup rather than a scan.
|
|
212
|
+
*/
|
|
213
|
+
export function compileFilter(filter: MapFilter | undefined): CompiledFilter {
|
|
214
|
+
if (!filter) return ALWAYS;
|
|
215
|
+
const op = filter[0];
|
|
216
|
+
switch (op) {
|
|
217
|
+
case 'all': {
|
|
218
|
+
const parts = (filter.slice(1) as MapFilter[]).map(compileFilter);
|
|
219
|
+
if (parts.length === 0) return ALWAYS;
|
|
220
|
+
if (parts.length === 1) return parts[0];
|
|
221
|
+
return (f) => {
|
|
222
|
+
for (const part of parts) if (!part(f)) return false;
|
|
223
|
+
return true;
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
case 'any': {
|
|
227
|
+
const parts = (filter.slice(1) as MapFilter[]).map(compileFilter);
|
|
228
|
+
if (parts.length === 0) return ALWAYS;
|
|
229
|
+
return (f) => {
|
|
230
|
+
for (const part of parts) if (part(f)) return true;
|
|
231
|
+
return false;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
case 'none': {
|
|
235
|
+
const parts = (filter.slice(1) as MapFilter[]).map(compileFilter);
|
|
236
|
+
return (f) => {
|
|
237
|
+
for (const part of parts) if (part(f)) return false;
|
|
238
|
+
return true;
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
case 'has': {
|
|
242
|
+
const key = filter[1];
|
|
243
|
+
return (f) => f.get(key) !== undefined;
|
|
244
|
+
}
|
|
245
|
+
case '!has': {
|
|
246
|
+
const key = filter[1];
|
|
247
|
+
return (f) => f.get(key) === undefined;
|
|
248
|
+
}
|
|
249
|
+
case '==': {
|
|
250
|
+
const [, key, want] = filter;
|
|
251
|
+
return (f) => f.get(key) === want;
|
|
252
|
+
}
|
|
253
|
+
case '!=': {
|
|
254
|
+
const [, key, want] = filter;
|
|
255
|
+
return (f) => f.get(key) !== want;
|
|
256
|
+
}
|
|
257
|
+
case '<':
|
|
258
|
+
case '<=':
|
|
259
|
+
case '>':
|
|
260
|
+
case '>=': {
|
|
261
|
+
const [, key, want] = filter;
|
|
262
|
+
return (f) => {
|
|
263
|
+
const value = f.get(key);
|
|
264
|
+
// A missing tag compares false rather than as zero: `['>' , 'pop',
|
|
265
|
+
// 0]` must not select every feature that has no population.
|
|
266
|
+
if (typeof value !== 'number') return false;
|
|
267
|
+
return op === '<'
|
|
268
|
+
? value < want
|
|
269
|
+
: op === '<='
|
|
270
|
+
? value <= want
|
|
271
|
+
: op === '>'
|
|
272
|
+
? value > want
|
|
273
|
+
: value >= want;
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
case 'in': {
|
|
277
|
+
const key = filter[1];
|
|
278
|
+
const set = new Set(filter.slice(2) as FeatureValue[]);
|
|
279
|
+
return (f) => {
|
|
280
|
+
const value = f.get(key);
|
|
281
|
+
return value !== undefined && set.has(value);
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
case '!in': {
|
|
285
|
+
const key = filter[1];
|
|
286
|
+
const set = new Set(filter.slice(2) as FeatureValue[]);
|
|
287
|
+
return (f) => {
|
|
288
|
+
const value = f.get(key);
|
|
289
|
+
return value === undefined || !set.has(value);
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
case 'geometry': {
|
|
293
|
+
const want =
|
|
294
|
+
filter[1] === 'point'
|
|
295
|
+
? GeomType.Point
|
|
296
|
+
: filter[1] === 'line'
|
|
297
|
+
? GeomType.LineString
|
|
298
|
+
: GeomType.Polygon;
|
|
299
|
+
return (f) => f.type === want;
|
|
300
|
+
}
|
|
301
|
+
default: {
|
|
302
|
+
// An unknown operator draws nothing rather than everything: a typo in
|
|
303
|
+
// a filter that quietly selected the whole layer would look like the
|
|
304
|
+
// style working.
|
|
305
|
+
const unknown: never = op;
|
|
306
|
+
throw new Error(`unknown map filter operator ${String(unknown)}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|