maplibre-gl 3.2.1 → 3.3.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/build/generate-struct-arrays.ts +6 -3
- package/build/generate-style-code.ts +7 -8
- package/dist/maplibre-gl-csp-worker.js +1 -1
- package/dist/maplibre-gl-csp-worker.js.map +1 -1
- package/dist/maplibre-gl-csp.js +1 -1
- package/dist/maplibre-gl-csp.js.map +1 -1
- package/dist/maplibre-gl-dev.js +565 -243
- package/dist/maplibre-gl-dev.js.map +1 -1
- package/dist/maplibre-gl.d.ts +111 -57
- package/dist/maplibre-gl.js +4 -4
- package/dist/maplibre-gl.js.map +1 -1
- package/package.json +20 -20
- package/src/data/array_types.g.ts +110 -24
- package/src/data/bucket/circle_bucket.ts +1 -0
- package/src/data/bucket/line_bucket.ts +1 -0
- package/src/data/bucket/symbol_attributes.ts +7 -1
- package/src/data/bucket/symbol_bucket.ts +5 -1
- package/src/data/feature_index.ts +1 -0
- package/src/data/program_configuration.ts +1 -0
- package/src/data/segment.ts +2 -0
- package/src/geo/transform.ts +1 -0
- package/src/gl/context.ts +1 -0
- package/src/gl/framebuffer.ts +1 -0
- package/src/gl/index_buffer.ts +1 -0
- package/src/gl/render_pool.ts +2 -1
- package/src/gl/vertex_buffer.ts +1 -0
- package/src/render/draw_symbol.ts +8 -9
- package/src/render/image_atlas.ts +1 -0
- package/src/render/line_atlas.ts +1 -0
- package/src/render/painter.ts +1 -0
- package/src/render/program.ts +1 -0
- package/src/render/render_to_texture.ts +31 -14
- package/src/render/terrain.ts +3 -0
- package/src/render/texture.ts +1 -0
- package/src/render/uniform_binding.ts +2 -0
- package/src/render/vertex_array_object.ts +1 -0
- package/src/shaders/symbol_sdf.fragment.glsl +9 -3
- package/src/shaders/symbol_sdf.fragment.glsl.g.ts +1 -1
- package/src/source/canvas_source.ts +1 -3
- package/src/source/geojson_source.ts +1 -3
- package/src/source/image_source.ts +2 -4
- package/src/source/source_cache.ts +1 -0
- package/src/source/source_state.ts +1 -0
- package/src/source/terrain_source_cache.ts +1 -0
- package/src/source/tile.ts +1 -0
- package/src/source/tile_cache.ts +1 -1
- package/src/source/tile_id.ts +1 -0
- package/src/source/vector_tile_worker_source.test.ts +79 -0
- package/src/source/vector_tile_worker_source.ts +26 -1
- package/src/source/worker_source.ts +1 -0
- package/src/style/evaluation_parameters.ts +1 -0
- package/src/style/properties.ts +18 -0
- package/src/style/style.ts +1 -0
- package/src/style/style_glyph.ts +1 -0
- package/src/style/style_layer/background_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/circle_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/fill_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/heatmap_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/hillshade_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/line_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/raster_style_layer_properties.g.ts +1 -6
- package/src/style/style_layer/symbol_style_layer_properties.g.ts +4 -6
- package/src/style/style_layer/variable_text_anchor.test.ts +117 -0
- package/src/style/style_layer/variable_text_anchor.ts +163 -0
- package/src/symbol/collision_index.ts +1 -0
- package/src/symbol/grid_index.ts +1 -0
- package/src/symbol/placement.ts +52 -40
- package/src/symbol/symbol_layout.ts +42 -116
- package/src/ui/camera.ts +8 -8
- package/src/ui/handler/box_zoom.ts +1 -3
- package/src/ui/handler/click_zoom.ts +1 -3
- package/src/ui/handler/keyboard.ts +1 -3
- package/src/ui/handler/scroll_zoom.ts +1 -3
- package/src/ui/handler/shim/dblclick_zoom.ts +1 -3
- package/src/ui/handler/shim/drag_pan.ts +1 -3
- package/src/ui/handler/shim/drag_rotate.ts +1 -3
- package/src/ui/handler/shim/two_fingers_touch.ts +1 -3
- package/src/ui/handler/transform-provider.ts +1 -0
- package/src/ui/handler/two_fingers_touch.ts +1 -3
- package/src/ui/map.ts +7 -6
- package/src/util/ajax.test.ts +33 -0
- package/src/util/ajax.ts +5 -0
- package/src/util/image.ts +1 -0
- package/src/util/image_request.ts +2 -2
- package/src/util/performance.ts +1 -2
- package/src/util/struct_array.ts +5 -1
- package/src/util/test/mock_fetch.ts +51 -0
package/src/style/properties.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type CrossFaded<T> = {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
+
* @internal
|
|
25
26
|
* Implementations of the `Property` interface:
|
|
26
27
|
*
|
|
27
28
|
* * Hold metadata about a property that's independent of any specific value: stuff like the type of the value,
|
|
@@ -49,6 +50,7 @@ export interface Property<T, R> {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/**
|
|
53
|
+
* @internal
|
|
52
54
|
* `PropertyValue` represents the value part of a property key-value unit. It's used to represent both
|
|
53
55
|
* paint and layout property values, and regardless of whether or not their property supports data-driven
|
|
54
56
|
* expressions.
|
|
@@ -95,6 +97,7 @@ export type TransitionParameters = {
|
|
|
95
97
|
};
|
|
96
98
|
|
|
97
99
|
/**
|
|
100
|
+
* @internal
|
|
98
101
|
* Paint properties are _transitionable_: they can change in a fluid manner, interpolating or cross-fading between
|
|
99
102
|
* old and new value. The duration of the transition, and the delay before it begins, is configurable.
|
|
100
103
|
*
|
|
@@ -125,6 +128,7 @@ class TransitionablePropertyValue<T, R> {
|
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
/**
|
|
131
|
+
* @internal
|
|
128
132
|
* `Transitionable` stores a map of all (property name, `TransitionablePropertyValue`) pairs for paint properties of a
|
|
129
133
|
* given layer type. It can calculate the `TransitioningPropertyValue`s for all of them at once, producing a
|
|
130
134
|
* `Transitioning` instance for the same set of properties.
|
|
@@ -196,6 +200,7 @@ export class Transitionable<Props> {
|
|
|
196
200
|
}
|
|
197
201
|
|
|
198
202
|
/**
|
|
203
|
+
* @internal
|
|
199
204
|
* `TransitioningPropertyValue` implements the first of two intermediate steps in the evaluation chain of a paint
|
|
200
205
|
* property value. In this step, transitions between old and new values are handled: as long as the transition is in
|
|
201
206
|
* progress, `TransitioningPropertyValue` maintains a reference to the prior value, and interpolates between it and
|
|
@@ -256,6 +261,7 @@ class TransitioningPropertyValue<T, R> {
|
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
/**
|
|
264
|
+
* @internal
|
|
259
265
|
* `Transitioning` stores a map of all (property name, `TransitioningPropertyValue`) pairs for paint properties of a
|
|
260
266
|
* given layer type. It can calculate the possibly-evaluated values for all of them at once, producing a
|
|
261
267
|
* `PossiblyEvaluated` instance for the same set of properties.
|
|
@@ -311,6 +317,10 @@ export class Layout<Props> {
|
|
|
311
317
|
this._values = (Object.create(properties.defaultPropertyValues) as any);
|
|
312
318
|
}
|
|
313
319
|
|
|
320
|
+
hasValue<S extends keyof Props>(name: S) {
|
|
321
|
+
return this._values[name].value !== undefined;
|
|
322
|
+
}
|
|
323
|
+
|
|
314
324
|
getValue<S extends keyof Props>(name: S) {
|
|
315
325
|
return clone(this._values[name].value);
|
|
316
326
|
}
|
|
@@ -370,6 +380,7 @@ type PossiblyEvaluatedValue<T> = {
|
|
|
370
380
|
} | SourceExpression | CompositeExpression;
|
|
371
381
|
|
|
372
382
|
/**
|
|
383
|
+
* @internal
|
|
373
384
|
* `PossiblyEvaluatedPropertyValue` is used for data-driven paint and layout property values. It holds a
|
|
374
385
|
* `PossiblyEvaluatedValue` and the `GlobalProperties` that were used to generate it. You're not allowed to supply
|
|
375
386
|
* a different set of `GlobalProperties` when performing the final evaluation because they would be ignored in the
|
|
@@ -409,6 +420,7 @@ export class PossiblyEvaluatedPropertyValue<T> {
|
|
|
409
420
|
}
|
|
410
421
|
|
|
411
422
|
/**
|
|
423
|
+
* @internal
|
|
412
424
|
* `PossiblyEvaluated` stores a map of all (property name, `R`) pairs for paint or layout properties of a
|
|
413
425
|
* given layer type.
|
|
414
426
|
*/
|
|
@@ -427,6 +439,7 @@ export class PossiblyEvaluated<Props, PossibleEvaluatedProps> {
|
|
|
427
439
|
}
|
|
428
440
|
|
|
429
441
|
/**
|
|
442
|
+
* @internal
|
|
430
443
|
* An implementation of `Property` for properties that do not permit data-driven (source or composite) expressions.
|
|
431
444
|
* This restriction allows us to declare statically that the result of possibly evaluating this kind of property
|
|
432
445
|
* is in fact always the scalar type `T`, and can be used without further evaluating the value on a per-feature basis.
|
|
@@ -455,6 +468,7 @@ export class DataConstantProperty<T> implements Property<T, T> {
|
|
|
455
468
|
}
|
|
456
469
|
|
|
457
470
|
/**
|
|
471
|
+
* @internal
|
|
458
472
|
* An implementation of `Property` for properties that permit data-driven (source or composite) expressions.
|
|
459
473
|
* The result of possibly evaluating this kind of property is `PossiblyEvaluatedPropertyValue<T>`; obtaining
|
|
460
474
|
* a scalar value `T` requires further evaluation on a per-feature basis.
|
|
@@ -529,6 +543,7 @@ export class DataDrivenProperty<T> implements Property<T, PossiblyEvaluatedPrope
|
|
|
529
543
|
}
|
|
530
544
|
|
|
531
545
|
/**
|
|
546
|
+
* @internal
|
|
532
547
|
* An implementation of `Property` for data driven `line-pattern` which are transitioned by cross-fading
|
|
533
548
|
* rather than interpolation.
|
|
534
549
|
*/
|
|
@@ -594,6 +609,7 @@ export class CrossFadedDataDrivenProperty<T> extends DataDrivenProperty<CrossFad
|
|
|
594
609
|
}
|
|
595
610
|
}
|
|
596
611
|
/**
|
|
612
|
+
* @internal
|
|
597
613
|
* An implementation of `Property` for `*-pattern` and `line-dasharray`, which are transitioned by cross-fading
|
|
598
614
|
* rather than interpolation.
|
|
599
615
|
*/
|
|
@@ -635,6 +651,7 @@ export class CrossFadedProperty<T> implements Property<T, CrossFaded<T>> {
|
|
|
635
651
|
}
|
|
636
652
|
|
|
637
653
|
/**
|
|
654
|
+
* @internal
|
|
638
655
|
* An implementation of `Property` for `heatmap-color` and `line-gradient`. Interpolation is a no-op, and
|
|
639
656
|
* evaluation returns a boolean value in order to indicate its presence, but the real
|
|
640
657
|
* evaluation happens in StyleLayer classes.
|
|
@@ -660,6 +677,7 @@ export class ColorRampProperty implements Property<Color, boolean> {
|
|
|
660
677
|
}
|
|
661
678
|
|
|
662
679
|
/**
|
|
680
|
+
* @internal
|
|
663
681
|
* `Properties` holds objects containing default values for the layout or paint property set of a given
|
|
664
682
|
* layer type. These objects are immutable, and they are used as the prototypes for the `_values` members of
|
|
665
683
|
* `Transitionable`, `Transitioning`, `Layout`, and `PossiblyEvaluated`. This allows these classes to avoid
|
package/src/style/style.ts
CHANGED
package/src/style/style_glyph.ts
CHANGED
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
export type CircleLayoutProps = {
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
export type FillLayoutProps = {
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
export type LineLayoutProps = {
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
|
|
@@ -14,12 +14,7 @@ import {
|
|
|
14
14
|
CrossFaded
|
|
15
15
|
} from '../properties';
|
|
16
16
|
|
|
17
|
-
import type {Color} from '@maplibre/maplibre-gl-style-spec';
|
|
18
|
-
import type {Padding} from '@maplibre/maplibre-gl-style-spec';
|
|
19
|
-
|
|
20
|
-
import type {Formatted} from '@maplibre/maplibre-gl-style-spec';
|
|
21
|
-
|
|
22
|
-
import type {ResolvedImage} from '@maplibre/maplibre-gl-style-spec';
|
|
17
|
+
import type {Color, Formatted, Padding, ResolvedImage, VariableAnchorOffsetCollection} from '@maplibre/maplibre-gl-style-spec';
|
|
23
18
|
import {StylePropertySpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
24
19
|
|
|
25
20
|
import {
|
|
@@ -58,6 +53,7 @@ export type SymbolLayoutProps = {
|
|
|
58
53
|
"text-justify": DataDrivenProperty<"auto" | "left" | "center" | "right">,
|
|
59
54
|
"text-radial-offset": DataDrivenProperty<number>,
|
|
60
55
|
"text-variable-anchor": DataConstantProperty<Array<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">>,
|
|
56
|
+
"text-variable-anchor-offset": DataDrivenProperty<VariableAnchorOffsetCollection>,
|
|
61
57
|
"text-anchor": DataDrivenProperty<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
|
|
62
58
|
"text-max-angle": DataConstantProperty<number>,
|
|
63
59
|
"text-writing-mode": DataConstantProperty<Array<"horizontal" | "vertical">>,
|
|
@@ -104,6 +100,7 @@ export type SymbolLayoutPropsPossiblyEvaluated = {
|
|
|
104
100
|
"text-justify": PossiblyEvaluatedPropertyValue<"auto" | "left" | "center" | "right">,
|
|
105
101
|
"text-radial-offset": PossiblyEvaluatedPropertyValue<number>,
|
|
106
102
|
"text-variable-anchor": Array<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
|
|
103
|
+
"text-variable-anchor-offset": PossiblyEvaluatedPropertyValue<VariableAnchorOffsetCollection>,
|
|
107
104
|
"text-anchor": PossiblyEvaluatedPropertyValue<"center" | "left" | "right" | "top" | "bottom" | "top-left" | "top-right" | "bottom-left" | "bottom-right">,
|
|
108
105
|
"text-max-angle": number,
|
|
109
106
|
"text-writing-mode": Array<"horizontal" | "vertical">,
|
|
@@ -151,6 +148,7 @@ const getLayout = () => layout = layout || new Properties({
|
|
|
151
148
|
"text-justify": new DataDrivenProperty(styleSpec["layout_symbol"]["text-justify"] as any as StylePropertySpecification),
|
|
152
149
|
"text-radial-offset": new DataDrivenProperty(styleSpec["layout_symbol"]["text-radial-offset"] as any as StylePropertySpecification),
|
|
153
150
|
"text-variable-anchor": new DataConstantProperty(styleSpec["layout_symbol"]["text-variable-anchor"] as any as StylePropertySpecification),
|
|
151
|
+
"text-variable-anchor-offset": new DataDrivenProperty(styleSpec["layout_symbol"]["text-variable-anchor-offset"] as any as StylePropertySpecification),
|
|
154
152
|
"text-anchor": new DataDrivenProperty(styleSpec["layout_symbol"]["text-anchor"] as any as StylePropertySpecification),
|
|
155
153
|
"text-max-angle": new DataConstantProperty(styleSpec["layout_symbol"]["text-max-angle"] as any as StylePropertySpecification),
|
|
156
154
|
"text-writing-mode": new DataConstantProperty(styleSpec["layout_symbol"]["text-writing-mode"] as any as StylePropertySpecification),
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {EvaluationParameters} from '../evaluation_parameters';
|
|
2
|
+
import {ZoomHistory} from '../zoom_history';
|
|
3
|
+
import {SymbolStyleLayer} from './symbol_style_layer';
|
|
4
|
+
import {INVALID_TEXT_OFFSET, evaluateVariableOffset, getTextVariableAnchorOffset} from './variable_text_anchor';
|
|
5
|
+
|
|
6
|
+
describe('evaluateVariableOffset', () => {
|
|
7
|
+
test('fromRadialOffset', () => {
|
|
8
|
+
// Radial offset mode is invoked by using INVALID_TEXT_OFFSET as the Y value
|
|
9
|
+
const srcOffset = [10, INVALID_TEXT_OFFSET] as [number, number];
|
|
10
|
+
|
|
11
|
+
expect(evaluateVariableOffset('center', srcOffset)).toEqual([0, 0]);
|
|
12
|
+
|
|
13
|
+
// Top/bottom offsets are shifted by the default baseline (7)
|
|
14
|
+
expect(evaluateVariableOffset('top', srcOffset)).toEqual([0, 3]);
|
|
15
|
+
expect(evaluateVariableOffset('bottom', srcOffset)).toEqual([0, -3]);
|
|
16
|
+
expect(evaluateVariableOffset('left', srcOffset)).toEqual([10, 0]);
|
|
17
|
+
expect(evaluateVariableOffset('right', srcOffset)).toEqual([-10, 0]);
|
|
18
|
+
|
|
19
|
+
const hypotenuse = 10 / Math.SQRT2;
|
|
20
|
+
expect(evaluateVariableOffset('top-left', srcOffset)).toEqual([expect.closeTo(hypotenuse), expect.closeTo(hypotenuse - 7)]);
|
|
21
|
+
expect(evaluateVariableOffset('top-right', srcOffset)).toEqual([expect.closeTo(-hypotenuse), expect.closeTo(hypotenuse - 7)]);
|
|
22
|
+
expect(evaluateVariableOffset('bottom-left', srcOffset)).toEqual([expect.closeTo(hypotenuse), expect.closeTo(-hypotenuse + 7)]);
|
|
23
|
+
expect(evaluateVariableOffset('bottom-right', srcOffset)).toEqual([expect.closeTo(-hypotenuse), expect.closeTo(-hypotenuse + 7)]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('fromTextOffset', () => {
|
|
27
|
+
const srcOffset = [10, -10] as [number, number];
|
|
28
|
+
|
|
29
|
+
expect(evaluateVariableOffset('center', srcOffset)).toEqual([0, 0]);
|
|
30
|
+
|
|
31
|
+
// Top/bottom offsets are shifted by the default baseline (7)
|
|
32
|
+
expect(evaluateVariableOffset('top', srcOffset)).toEqual([0, 3]);
|
|
33
|
+
expect(evaluateVariableOffset('bottom', srcOffset)).toEqual([0, -3]);
|
|
34
|
+
expect(evaluateVariableOffset('left', srcOffset)).toEqual([10, 0]);
|
|
35
|
+
expect(evaluateVariableOffset('right', srcOffset)).toEqual([-10, 0]);
|
|
36
|
+
expect(evaluateVariableOffset('top-left', srcOffset)).toEqual([10, 3]);
|
|
37
|
+
expect(evaluateVariableOffset('top-right', srcOffset)).toEqual([-10, 3]);
|
|
38
|
+
expect(evaluateVariableOffset('bottom-left', srcOffset)).toEqual([10, -3]);
|
|
39
|
+
expect(evaluateVariableOffset('bottom-right', srcOffset)).toEqual([-10, -3]);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function createSymbolLayer(layerProperties) {
|
|
44
|
+
const layer = new SymbolStyleLayer(layerProperties);
|
|
45
|
+
layer.recalculate({zoom: 0, zoomHistory: {} as ZoomHistory} as EvaluationParameters, []);
|
|
46
|
+
return layer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe('getTextVariableAnchorOffset', () => {
|
|
50
|
+
test('defaults - no props set', () => {
|
|
51
|
+
const props = {};
|
|
52
|
+
const layer = createSymbolLayer(props);
|
|
53
|
+
|
|
54
|
+
expect(getTextVariableAnchorOffset(layer, null, null)).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('text-variable-anchor-offset set', () => {
|
|
58
|
+
const props = {layout: {'text-variable-anchor-offset': ['top', [1, 1], 'bottom', [2, 2]]}};
|
|
59
|
+
const layer = createSymbolLayer(props);
|
|
60
|
+
|
|
61
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
62
|
+
expect(offset).toBeDefined();
|
|
63
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
64
|
+
expect(offset.toString()).toBe('["top",[24,17],"bottom",[48,55]]');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('text-variable-anchor set', () => {
|
|
68
|
+
const props = {layout: {'text-variable-anchor': ['top']}};
|
|
69
|
+
const layer = createSymbolLayer(props);
|
|
70
|
+
|
|
71
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
72
|
+
expect(offset).toBeDefined();
|
|
73
|
+
// Default offset (0, 0) converted to EMs, accounting for baseline shift on Y axis
|
|
74
|
+
expect(offset.toString()).toBe('["top",[0,-7]]');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('text-variable-anchor and text-offset set', () => {
|
|
78
|
+
const props = {layout: {'text-variable-anchor': ['top'], 'text-offset': [1, 1]}};
|
|
79
|
+
const layer = createSymbolLayer(props);
|
|
80
|
+
|
|
81
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
82
|
+
expect(offset).toBeDefined();
|
|
83
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
84
|
+
expect(offset.toString()).toBe('["top",[0,17]]');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('text-variable-anchor and text-radial-offset set', () => {
|
|
88
|
+
const props = {layout: {'text-variable-anchor': ['top'], 'text-radial-offset': 2}};
|
|
89
|
+
const layer = createSymbolLayer(props);
|
|
90
|
+
|
|
91
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
92
|
+
expect(offset).toBeDefined();
|
|
93
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
94
|
+
expect(offset.toString()).toBe('["top",[0,41]]');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('text-variable-anchor, text-offset, and text-radial-offset set', () => {
|
|
98
|
+
const props = {layout: {'text-variable-anchor': ['top'], 'text-offset': [1, 1], 'text-radial-offset': 2}};
|
|
99
|
+
const layer = createSymbolLayer(props);
|
|
100
|
+
|
|
101
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
102
|
+
expect(offset).toBeDefined();
|
|
103
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
104
|
+
expect(offset.toString()).toBe('["top",[0,41]]');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('text-variable-anchor and text-variable-anchor-offset set', () => {
|
|
108
|
+
const props = {layout: {'text-variable-anchor-offset': ['top', [1, 1]], 'text-variable-anchor': ['bottom']}};
|
|
109
|
+
const layer = createSymbolLayer(props);
|
|
110
|
+
|
|
111
|
+
const offset = getTextVariableAnchorOffset(layer, null, null);
|
|
112
|
+
expect(offset).toBeDefined();
|
|
113
|
+
// Offset converted to EMs, accounting for baseline shift on Y axis
|
|
114
|
+
expect(offset.toString()).toBe('["top",[24,17]]');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {VariableAnchorOffsetCollection, VariableAnchorOffsetCollectionSpecification} from '@maplibre/maplibre-gl-style-spec';
|
|
2
|
+
import {SymbolFeature} from '../../data/bucket/symbol_bucket';
|
|
3
|
+
import {CanonicalTileID} from '../../source/tile_id';
|
|
4
|
+
import ONE_EM from '../../symbol/one_em';
|
|
5
|
+
import {SymbolStyleLayer} from './symbol_style_layer';
|
|
6
|
+
|
|
7
|
+
export enum TextAnchorEnum {
|
|
8
|
+
'center' = 1,
|
|
9
|
+
'left' = 2,
|
|
10
|
+
'right' = 3,
|
|
11
|
+
'top' = 4,
|
|
12
|
+
'bottom' = 5,
|
|
13
|
+
'top-left' = 6,
|
|
14
|
+
'top-right' = 7,
|
|
15
|
+
'bottom-left' = 8,
|
|
16
|
+
'bottom-right' = 9
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type TextAnchor = keyof typeof TextAnchorEnum;
|
|
20
|
+
|
|
21
|
+
// The radial offset is to the edge of the text box
|
|
22
|
+
// In the horizontal direction, the edge of the text box is where glyphs start
|
|
23
|
+
// But in the vertical direction, the glyphs appear to "start" at the baseline
|
|
24
|
+
// We don't actually load baseline data, but we assume an offset of ONE_EM - 17
|
|
25
|
+
// (see "yOffset" in shaping.js)
|
|
26
|
+
const baselineOffset = 7;
|
|
27
|
+
export const INVALID_TEXT_OFFSET = Number.POSITIVE_INFINITY;
|
|
28
|
+
|
|
29
|
+
export function evaluateVariableOffset(anchor: TextAnchor, offset: [number, number]): [number, number] {
|
|
30
|
+
|
|
31
|
+
function fromRadialOffset(anchor: TextAnchor, radialOffset: number): [number, number] {
|
|
32
|
+
let x = 0, y = 0;
|
|
33
|
+
if (radialOffset < 0) radialOffset = 0; // Ignore negative offset.
|
|
34
|
+
// solve for r where r^2 + r^2 = radialOffset^2
|
|
35
|
+
const hypotenuse = radialOffset / Math.sqrt(2);
|
|
36
|
+
switch (anchor) {
|
|
37
|
+
case 'top-right':
|
|
38
|
+
case 'top-left':
|
|
39
|
+
y = hypotenuse - baselineOffset;
|
|
40
|
+
break;
|
|
41
|
+
case 'bottom-right':
|
|
42
|
+
case 'bottom-left':
|
|
43
|
+
y = -hypotenuse + baselineOffset;
|
|
44
|
+
break;
|
|
45
|
+
case 'bottom':
|
|
46
|
+
y = -radialOffset + baselineOffset;
|
|
47
|
+
break;
|
|
48
|
+
case 'top':
|
|
49
|
+
y = radialOffset - baselineOffset;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
switch (anchor) {
|
|
54
|
+
case 'top-right':
|
|
55
|
+
case 'bottom-right':
|
|
56
|
+
x = -hypotenuse;
|
|
57
|
+
break;
|
|
58
|
+
case 'top-left':
|
|
59
|
+
case 'bottom-left':
|
|
60
|
+
x = hypotenuse;
|
|
61
|
+
break;
|
|
62
|
+
case 'left':
|
|
63
|
+
x = radialOffset;
|
|
64
|
+
break;
|
|
65
|
+
case 'right':
|
|
66
|
+
x = -radialOffset;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return [x, y];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function fromTextOffset(anchor: TextAnchor, offsetX: number, offsetY: number): [number, number] {
|
|
74
|
+
let x = 0, y = 0;
|
|
75
|
+
// Use absolute offset values.
|
|
76
|
+
offsetX = Math.abs(offsetX);
|
|
77
|
+
offsetY = Math.abs(offsetY);
|
|
78
|
+
|
|
79
|
+
switch (anchor) {
|
|
80
|
+
case 'top-right':
|
|
81
|
+
case 'top-left':
|
|
82
|
+
case 'top':
|
|
83
|
+
y = offsetY - baselineOffset;
|
|
84
|
+
break;
|
|
85
|
+
case 'bottom-right':
|
|
86
|
+
case 'bottom-left':
|
|
87
|
+
case 'bottom':
|
|
88
|
+
y = -offsetY + baselineOffset;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
switch (anchor) {
|
|
93
|
+
case 'top-right':
|
|
94
|
+
case 'bottom-right':
|
|
95
|
+
case 'right':
|
|
96
|
+
x = -offsetX;
|
|
97
|
+
break;
|
|
98
|
+
case 'top-left':
|
|
99
|
+
case 'bottom-left':
|
|
100
|
+
case 'left':
|
|
101
|
+
x = offsetX;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return [x, y];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return (offset[1] !== INVALID_TEXT_OFFSET) ? fromTextOffset(anchor, offset[0], offset[1]) : fromRadialOffset(anchor, offset[0]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Helper to support both text-variable-anchor and text-variable-anchor-offset. Offset values converted from EMs to PXs
|
|
112
|
+
export function getTextVariableAnchorOffset(layer: SymbolStyleLayer, feature: SymbolFeature, canonical: CanonicalTileID): VariableAnchorOffsetCollection | null {
|
|
113
|
+
const layout = layer.layout;
|
|
114
|
+
// If style specifies text-variable-anchor-offset, just return it
|
|
115
|
+
const variableAnchorOffset = layout.get('text-variable-anchor-offset')?.evaluate(feature, {}, canonical);
|
|
116
|
+
|
|
117
|
+
if (variableAnchorOffset) {
|
|
118
|
+
const sourceValues = variableAnchorOffset.values;
|
|
119
|
+
const destValues: VariableAnchorOffsetCollectionSpecification = [];
|
|
120
|
+
|
|
121
|
+
// Convert offsets from EM to PX, and apply baseline shift
|
|
122
|
+
for (let i = 0; i < sourceValues.length; i += 2) {
|
|
123
|
+
const anchor = destValues[i] = sourceValues[i] as TextAnchor;
|
|
124
|
+
const offset = (sourceValues[i + 1] as [number, number]).map(t => t * ONE_EM) as [number, number];
|
|
125
|
+
|
|
126
|
+
if (anchor.startsWith('top')) {
|
|
127
|
+
offset[1] -= baselineOffset;
|
|
128
|
+
} else if (anchor.startsWith('bottom')) {
|
|
129
|
+
offset[1] += baselineOffset;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
destValues[i + 1] = offset;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return new VariableAnchorOffsetCollection(destValues);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// If style specifies text-variable-anchor, convert to the new format
|
|
139
|
+
const variableAnchor = layout.get('text-variable-anchor');
|
|
140
|
+
|
|
141
|
+
if (variableAnchor) {
|
|
142
|
+
let textOffset: [number, number];
|
|
143
|
+
const unevaluatedLayout = layer._unevaluatedLayout;
|
|
144
|
+
|
|
145
|
+
// The style spec says don't use `text-offset` and `text-radial-offset` together
|
|
146
|
+
// but doesn't actually specify what happens if you use both. We go with the radial offset.
|
|
147
|
+
if (unevaluatedLayout.getValue('text-radial-offset') !== undefined) {
|
|
148
|
+
textOffset = [layout.get('text-radial-offset').evaluate(feature, {}, canonical) * ONE_EM, INVALID_TEXT_OFFSET];
|
|
149
|
+
} else {
|
|
150
|
+
textOffset = layout.get('text-offset').evaluate(feature, {}, canonical).map(t => t * ONE_EM) as [number, number];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const anchorOffsets: VariableAnchorOffsetCollectionSpecification = [];
|
|
154
|
+
|
|
155
|
+
for (const anchor of variableAnchor) {
|
|
156
|
+
anchorOffsets.push(anchor, evaluateVariableOffset(anchor, textOffset));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return new VariableAnchorOffsetCollection(anchorOffsets);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
@@ -33,6 +33,7 @@ export type FeatureKey = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
+
* @internal
|
|
36
37
|
* A collision index used to prevent symbols from overlapping. It keep tracks of
|
|
37
38
|
* where previous symbols have been placed and is used to check if a new
|
|
38
39
|
* symbol overlaps with any previously added symbols.
|
package/src/symbol/grid_index.ts
CHANGED
|
@@ -48,6 +48,7 @@ function overlapAllowed(overlapA: OverlapMode, overlapB: OverlapMode): boolean {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
|
+
* @internal
|
|
51
52
|
* GridIndex is a data structure for testing the intersection of
|
|
52
53
|
* circles and rectangles in a 2d plane.
|
|
53
54
|
* It is optimized for rapid insertion and querying.
|