@tangram.gl/tangram-renderer 0.0.0-stage → 0.0.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 +59 -2
- package/dist/core.js +36923 -0
- package/dist/core.js.map +6 -0
- package/dist/index.js +32 -0
- package/dist/loaders-gl-worker.js +82 -0
- package/dist/loaders-gl-worker.js.map +6 -0
- package/dist/map-logic.js +2 -0
- package/dist/map-logic.js.map +6 -0
- package/dist/projected-basemaps-worker.js +3 -0
- package/dist/projected-basemaps-worker.js.map +6 -0
- package/dist/shader-modules.js +141 -0
- package/dist/shader-modules.js.map +6 -0
- package/dist/style-schema.js +126 -0
- package/dist/style-schema.js.map +6 -0
- package/dist/tangram-style.schema.json +882 -0
- package/dist/tangram.debug.mjs +37139 -0
- package/dist/tangram.debug.mjs.map +6 -0
- package/dist/tangram.min.mjs +4950 -0
- package/dist/types/builders/earcut.d.ts +15 -0
- package/dist/types/core.d.ts +29 -0
- package/dist/types/gl/constants.d.ts +2 -0
- package/dist/types/gl/globe_mesh.d.ts +48 -0
- package/dist/types/gl/glsl.d.ts +17 -0
- package/dist/types/gl/vertex-constants.d.ts +4 -0
- package/dist/types/gl/vertex-types.d.ts +34 -0
- package/dist/types/gl/vertex_data.d.ts +32 -0
- package/dist/types/gl/vertex_elements.d.ts +8 -0
- package/dist/types/gl/vertex_layout.d.ts +32 -0
- package/dist/types/glsl.d.ts +14 -0
- package/dist/types/gpu/luma_device_renderer.d.ts +66 -0
- package/dist/types/gpu/resource_lifecycle.d.ts +4 -0
- package/dist/types/gpu/tangram_gpu_backend.d.ts +151 -0
- package/dist/types/index.d.ts +155 -0
- package/dist/types/lights/light-definitions.d.ts +121 -0
- package/dist/types/map-logic/draped-terrain-bounds.d.ts +41 -0
- package/dist/types/map-logic/globe-terrain-preparation.d.ts +85 -0
- package/dist/types/map-logic/globe-terrain.d.ts +54 -0
- package/dist/types/map-logic/index.d.ts +33 -0
- package/dist/types/map-logic/label-identity.d.ts +22 -0
- package/dist/types/map-logic/label-placement.d.ts +32 -0
- package/dist/types/map-logic/screen-bounds.d.ts +15 -0
- package/dist/types/map-logic/terrain-data.d.ts +55 -0
- package/dist/types/map-logic/terrain-error.d.ts +51 -0
- package/dist/types/map-logic/terrain-ground-topology.d.ts +26 -0
- package/dist/types/map-logic/terrain-height-field.d.ts +59 -0
- package/dist/types/map-logic/terrain-navigation.d.ts +27 -0
- package/dist/types/map-logic/terrain-neighborhood.d.ts +5 -0
- package/dist/types/map-logic/terrain-query.d.ts +33 -0
- package/dist/types/map-logic/terrain-resource-budget.d.ts +51 -0
- package/dist/types/map-logic/terrain-transition-topology.d.ts +23 -0
- package/dist/types/map-logic/tile-build-queue.d.ts +51 -0
- package/dist/types/map-logic/tile-cache-policy.d.ts +46 -0
- package/dist/types/map-logic/tile-residency.d.ts +17 -0
- package/dist/types/map-logic/triangle-query-index.d.ts +87 -0
- package/dist/types/procedures/js-yaml.d.ts +15 -0
- package/dist/types/procedures/mesh-projector.d.ts +107 -0
- package/dist/types/procedures/mvt-dependencies.d.ts +20 -0
- package/dist/types/procedures/projected-coordinate-transform.d.ts +27 -0
- package/dist/types/procedures/projection-batch-executor.d.ts +53 -0
- package/dist/types/procedures/scene-yaml-legacy.d.ts +6 -0
- package/dist/types/procedures/terrain-draping.d.ts +165 -0
- package/dist/types/procedures/terrain-globe.d.ts +6 -0
- package/dist/types/procedures/terrain-styled-topology.d.ts +34 -0
- package/dist/types/procedures/web-mercator-legacy.d.ts +4 -0
- package/dist/types/procedures/web-mercator-math.d.ts +4 -0
- package/dist/types/scene/globals.d.ts +11 -0
- package/dist/types/scene/ground_footprint.d.ts +16 -0
- package/dist/types/scene/host_frame.d.ts +54 -0
- package/dist/types/scene/projection_math.d.ts +52 -0
- package/dist/types/scene/scene-resource-types.d.ts +177 -0
- package/dist/types/scene/scene_bundle.d.ts +69 -0
- package/dist/types/scene/visibility_adapter.d.ts +106 -0
- package/dist/types/selection/draped-terrain-surface.d.ts +78 -0
- package/dist/types/selection/terrain_surface.d.ts +149 -0
- package/dist/types/shader-modules/globe-horizon.d.ts +15 -0
- package/dist/types/shader-modules/height-decode.d.ts +13 -0
- package/dist/types/shader-modules/hillshade.d.ts +13 -0
- package/dist/types/shader-modules/index.d.ts +6 -0
- package/dist/types/shader-modules/planar.d.ts +12 -0
- package/dist/types/shader-modules/sphere-map.d.ts +13 -0
- package/dist/types/shader-modules/triplanar.d.ts +15 -0
- package/dist/types/sources/decoded_tile_store.d.ts +95 -0
- package/dist/types/sources/tile_source_metadata.d.ts +32 -0
- package/dist/types/styles/text/fontfaceobserver.d.ts +10 -0
- package/dist/types/tile/tile_id.d.ts +37 -0
- package/dist/types/tile/tile_traversal_adapter.d.ts +63 -0
- package/dist/types/types.d.ts +293 -0
- package/dist/types/utils/geo.d.ts +57 -0
- package/dist/types/utils/hash.d.ts +1 -0
- package/dist/types/utils/log.d.ts +27 -0
- package/dist/types/utils/props.d.ts +6 -0
- package/dist/types/utils/subscribe.d.ts +10 -0
- package/dist/types/utils/thread.d.ts +6 -0
- package/dist/types/utils/urls.d.ts +26 -0
- package/dist/types/utils/utils.d.ts +26 -0
- package/dist/types/utils/version.d.ts +2 -0
- package/dist/types/utils/worker-types.d.ts +68 -0
- package/dist/types/utils/worker_broker.d.ts +3 -0
- package/package.json +103 -3
- package/src/builders/common.ts +41 -0
- package/src/builders/earcut.d.ts +15 -0
- package/src/builders/geometry-types.ts +60 -0
- package/src/builders/points.ts +115 -0
- package/src/builders/polygons.ts +225 -0
- package/src/builders/polylines.ts +682 -0
- package/src/builders/wireframe.ts +31 -0
- package/src/core.d.ts +29 -0
- package/src/core.ts +33 -0
- package/src/experimental/loaders-gl-worker.ts +19 -0
- package/src/experimental/projected-basemaps-worker.ts +13 -0
- package/src/experimental/projected-mesh-cache.ts +137 -0
- package/src/experimental/projected-mesh-domain.ts +110 -0
- package/src/experimental/projected-mesh-refinement.ts +193 -0
- package/src/experimental/projected-mesh.ts +399 -0
- package/src/experimental/projected-normal.ts +26 -0
- package/src/experimental/projected-symbol-mesh.ts +74 -0
- package/src/gl/constants.ts +18 -0
- package/src/gl/context.ts +103 -0
- package/src/gl/extensions.ts +30 -0
- package/src/gl/globe_mesh.ts +285 -0
- package/src/gl/glsl.ts +257 -0
- package/src/gl/mesh-types.ts +56 -0
- package/src/gl/render_state.ts +111 -0
- package/src/gl/shader_program.ts +1310 -0
- package/src/gl/texture.ts +814 -0
- package/src/gl/uniform_buffer.ts +340 -0
- package/src/gl/vao.ts +154 -0
- package/src/gl/vbo_mesh.ts +349 -0
- package/src/gl/vertex-constants.ts +19 -0
- package/src/gl/vertex-types.ts +35 -0
- package/src/gl/vertex_data.ts +126 -0
- package/src/gl/vertex_elements.ts +54 -0
- package/src/gl/vertex_layout.ts +241 -0
- package/src/glsl.d.ts +14 -0
- package/src/gpu/luma_device_renderer.ts +579 -0
- package/src/gpu/pixel_readback.ts +51 -0
- package/src/gpu/resource_lifecycle.ts +32 -0
- package/src/gpu/tangram_gpu_backend.ts +195 -0
- package/src/index.d.ts +155 -0
- package/src/index.ts +105 -0
- package/src/labels/collision-types.ts +94 -0
- package/src/labels/collision.ts +273 -0
- package/src/labels/collision_grid.ts +61 -0
- package/src/labels/intersect.ts +30 -0
- package/src/labels/label-types.ts +91 -0
- package/src/labels/label.ts +207 -0
- package/src/labels/label_line.ts +801 -0
- package/src/labels/label_point.ts +131 -0
- package/src/labels/main-pass-types.ts +73 -0
- package/src/labels/main_pass.ts +221 -0
- package/src/labels/point_anchor.ts +100 -0
- package/src/labels/point_placement.ts +150 -0
- package/src/labels/projected-pass.ts +259 -0
- package/src/labels/repeat_group.ts +72 -0
- package/src/lights/ambient_light.glsl +18 -0
- package/src/lights/directional_light.glsl +39 -0
- package/src/lights/geographic-lights.ts +21 -0
- package/src/lights/light-definitions.ts +285 -0
- package/src/lights/light.ts +590 -0
- package/src/lights/lighting-uniforms.ts +80 -0
- package/src/lights/lighting-wgsl.ts +116 -0
- package/src/lights/material.glsl +237 -0
- package/src/lights/material.ts +145 -0
- package/src/lights/native-falloff.ts +37 -0
- package/src/lights/point_light.glsl +96 -0
- package/src/lights/spot_light.glsl +111 -0
- package/src/map-logic/draped-terrain-bounds.ts +71 -0
- package/src/map-logic/globe-terrain-preparation.ts +217 -0
- package/src/map-logic/globe-terrain.ts +296 -0
- package/src/map-logic/index.ts +37 -0
- package/src/map-logic/label-identity.ts +36 -0
- package/src/map-logic/label-placement.ts +117 -0
- package/src/map-logic/screen-bounds.ts +34 -0
- package/src/map-logic/terrain-data.ts +107 -0
- package/src/map-logic/terrain-error.ts +134 -0
- package/src/map-logic/terrain-ground-topology.ts +164 -0
- package/src/map-logic/terrain-height-field.ts +254 -0
- package/src/map-logic/terrain-navigation.ts +50 -0
- package/src/map-logic/terrain-neighborhood.ts +43 -0
- package/src/map-logic/terrain-query.ts +51 -0
- package/src/map-logic/terrain-resource-budget.ts +124 -0
- package/src/map-logic/terrain-transition-topology.ts +164 -0
- package/src/map-logic/tile-build-queue.ts +110 -0
- package/src/map-logic/tile-cache-policy.ts +89 -0
- package/src/map-logic/tile-residency.ts +50 -0
- package/src/map-logic/triangle-query-index.ts +265 -0
- package/src/procedures/js-yaml.d.ts +15 -0
- package/src/procedures/mesh-projector.ts +191 -0
- package/src/procedures/mlt-loaders.ts +55 -0
- package/src/procedures/mvt-dependencies.d.ts +20 -0
- package/src/procedures/mvt-legacy.ts +150 -0
- package/src/procedures/mvt-loaders.ts +53 -0
- package/src/procedures/mvt-parser.ts +48 -0
- package/src/procedures/mvt-properties.ts +36 -0
- package/src/procedures/mvt-tile-provider.ts +72 -0
- package/src/procedures/pmtiles-loader.ts +75 -0
- package/src/procedures/projected-coordinate-transform.ts +79 -0
- package/src/procedures/projection-batch-executor.ts +140 -0
- package/src/procedures/scene-yaml-legacy.ts +16 -0
- package/src/procedures/scene-yaml-loaders.ts +10 -0
- package/src/procedures/terrain-draping.ts +713 -0
- package/src/procedures/terrain-globe.ts +32 -0
- package/src/procedures/terrain-styled-topology.ts +184 -0
- package/src/procedures/web-mercator-legacy.ts +24 -0
- package/src/procedures/web-mercator-math.ts +50 -0
- package/src/scene/camera.ts +298 -0
- package/src/scene/camera_base.ts +116 -0
- package/src/scene/classic_scene.ts +20 -0
- package/src/scene/external_camera.ts +101 -0
- package/src/scene/globals.ts +120 -0
- package/src/scene/ground_footprint.ts +139 -0
- package/src/scene/host_frame.ts +326 -0
- package/src/scene/projected_tile_lod.ts +125 -0
- package/src/scene/projection_math.ts +142 -0
- package/src/scene/projection_shaders.ts +183 -0
- package/src/scene/renderer.ts +516 -0
- package/src/scene/scene-resource-types.ts +174 -0
- package/src/scene/scene.ts +2378 -0
- package/src/scene/scene_bundle.ts +304 -0
- package/src/scene/scene_debug.ts +129 -0
- package/src/scene/scene_loader.ts +402 -0
- package/src/scene/scene_worker.ts +528 -0
- package/src/scene/view.ts +683 -0
- package/src/scene/visibility_adapter.ts +319 -0
- package/src/scene/worker_url.ts +15 -0
- package/src/selection/draped-terrain-surface.ts +377 -0
- package/src/selection/selection-types.ts +79 -0
- package/src/selection/selection.ts +542 -0
- package/src/selection/selection_fragment.glsl +18 -0
- package/src/selection/selection_globals.glsl +9 -0
- package/src/selection/selection_pixels.ts +59 -0
- package/src/selection/selection_vertex.glsl +15 -0
- package/src/selection/terrain_surface.ts +298 -0
- package/src/shader-modules/globe-horizon.ts +52 -0
- package/src/shader-modules/height-decode.ts +41 -0
- package/src/shader-modules/hillshade.ts +70 -0
- package/src/shader-modules/index.ts +10 -0
- package/src/shader-modules/planar.ts +24 -0
- package/src/shader-modules/sphere-map.ts +38 -0
- package/src/shader-modules/triplanar.ts +45 -0
- package/src/sources/aligned_tile_source.ts +122 -0
- package/src/sources/data_source.ts +655 -0
- package/src/sources/decoded_tile_store.ts +245 -0
- package/src/sources/geojson.ts +300 -0
- package/src/sources/mvt.ts +247 -0
- package/src/sources/raster.ts +341 -0
- package/src/sources/shared_tile_source_adapter.ts +117 -0
- package/src/sources/sources.ts +10 -0
- package/src/sources/tile_source_adapter.ts +84 -0
- package/src/sources/tile_source_metadata.ts +65 -0
- package/src/sources/tile_source_state.ts +112 -0
- package/src/sources/topojson.ts +90 -0
- package/src/styles/feature_annotations.ts +16 -0
- package/src/styles/filter.ts +190 -0
- package/src/styles/geometry-style-types.ts +209 -0
- package/src/styles/globe_visibility_wgsl.ts +22 -0
- package/src/styles/layer-types.ts +45 -0
- package/src/styles/layer.ts +586 -0
- package/src/styles/lines/dasharray.ts +49 -0
- package/src/styles/lines/lines.ts +707 -0
- package/src/styles/lines/lines_wgsl.ts +224 -0
- package/src/styles/points/point-runtime-types.ts +125 -0
- package/src/styles/points/points.ts +1079 -0
- package/src/styles/points/points_fragment.glsl +126 -0
- package/src/styles/points/points_vertex.glsl +230 -0
- package/src/styles/points/points_wgsl.ts +189 -0
- package/src/styles/polygons/polygons.ts +268 -0
- package/src/styles/polygons/polygons_fragment.glsl +173 -0
- package/src/styles/polygons/polygons_vertex.glsl +213 -0
- package/src/styles/polygons/polygons_wgsl.ts +127 -0
- package/src/styles/property-types.ts +125 -0
- package/src/styles/raster/raster.ts +33 -0
- package/src/styles/raster/raster_globals.glsl +46 -0
- package/src/styles/style-mixing-types.ts +101 -0
- package/src/styles/style-runtime-types.ts +220 -0
- package/src/styles/style-schema.ts +133 -0
- package/src/styles/style.ts +1033 -0
- package/src/styles/style_globals.glsl +69 -0
- package/src/styles/style_manager.ts +464 -0
- package/src/styles/style_parser.ts +620 -0
- package/src/styles/text/font_manager.ts +180 -0
- package/src/styles/text/fontfaceobserver.d.ts +10 -0
- package/src/styles/text/text-types.ts +220 -0
- package/src/styles/text/text.ts +378 -0
- package/src/styles/text/text_canvas.ts +773 -0
- package/src/styles/text/text_labels.ts +436 -0
- package/src/styles/text/text_segments.ts +142 -0
- package/src/styles/text/text_settings.ts +228 -0
- package/src/styles/text/text_wgsl.ts +159 -0
- package/src/styles/text/text_wrap.ts +194 -0
- package/src/tile/globe_tile_preload.ts +40 -0
- package/src/tile/tangram_tileset_2d.ts +108 -0
- package/src/tile/tile-build-types.ts +155 -0
- package/src/tile/tile.ts +807 -0
- package/src/tile/tile_id.ts +172 -0
- package/src/tile/tile_manager.ts +889 -0
- package/src/tile/tile_pyramid.ts +119 -0
- package/src/tile/tile_resource_cache.ts +91 -0
- package/src/tile/tile_traversal_adapter.ts +132 -0
- package/src/types.ts +332 -0
- package/src/utils/debounce.ts +17 -0
- package/src/utils/debug_settings.ts +43 -0
- package/src/utils/errors.ts +12 -0
- package/src/utils/functions.ts +95 -0
- package/src/utils/geo.ts +364 -0
- package/src/utils/hash.ts +14 -0
- package/src/utils/log.ts +108 -0
- package/src/utils/media_capture.ts +193 -0
- package/src/utils/merge.ts +44 -0
- package/src/utils/obb.ts +134 -0
- package/src/utils/props.ts +43 -0
- package/src/utils/slice.ts +14 -0
- package/src/utils/subscribe.ts +58 -0
- package/src/utils/task.ts +154 -0
- package/src/utils/thread.ts +26 -0
- package/src/utils/urls.ts +223 -0
- package/src/utils/utils.ts +310 -0
- package/src/utils/vector.ts +197 -0
- package/src/utils/version.ts +8 -0
- package/src/utils/worker-types.ts +79 -0
- package/src/utils/worker_broker.ts +523 -0
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
// Tangram
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) 2013-2016 Brett Camper and Mapzen
|
|
4
|
+
// Copyright (c) 2026 vis.gl contributors
|
|
5
|
+
|
|
6
|
+
import type {LayerConfig, LayerDrawGroup, LayerDrawGroups, LayerMatchContext, LayerPropertyMatch, LayerCombinationCache, SceneLayer, SceneLayerDefinition} from './layer-types';
|
|
7
|
+
import type {PropertyFunction} from './property-types';
|
|
8
|
+
|
|
9
|
+
import StyleParser from './style_parser';
|
|
10
|
+
import { compileFunctionStrings } from '../utils/functions';
|
|
11
|
+
import log from '../utils/log';
|
|
12
|
+
import mergeObjects from '../utils/merge';
|
|
13
|
+
import Geo from '../utils/geo';
|
|
14
|
+
import { buildFilter } from './filter';
|
|
15
|
+
|
|
16
|
+
// N.B.: 'visible' is legacy compatibility for 'enabled'
|
|
17
|
+
const reserved = ['filter', 'draw', 'visible', 'enabled', 'data', 'exclusive', 'priority'];
|
|
18
|
+
|
|
19
|
+
let layer_cache: LayerCombinationCache = {};
|
|
20
|
+
export function layerCache () {
|
|
21
|
+
return layer_cache;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function cacheKey (layers: number[]): string | number {
|
|
25
|
+
if (layers.length > 1) {
|
|
26
|
+
var k = layers[0] as string | number;
|
|
27
|
+
for (var i=1; i < layers.length; i++) {
|
|
28
|
+
k += '/' + layers[i];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return k;
|
|
32
|
+
}
|
|
33
|
+
return layers[0];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Merge matching layer trees into a final draw group
|
|
37
|
+
export function mergeTrees(matchingTrees: (Array<LayerDrawGroups | undefined> | false | undefined)[], group: string): LayerDrawGroup | null {
|
|
38
|
+
let draws: LayerDrawGroup[], treeDepth = 0;
|
|
39
|
+
|
|
40
|
+
// Find deepest tree
|
|
41
|
+
for (let t=0; t < matchingTrees.length; t++) {
|
|
42
|
+
if ((matchingTrees[t] as Array<LayerDrawGroups | undefined>).length > treeDepth) {
|
|
43
|
+
treeDepth = (matchingTrees[t] as Array<LayerDrawGroups | undefined>).length;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// No layers to parse
|
|
48
|
+
if (treeDepth === 0) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Merged draw group object
|
|
53
|
+
let draw: LayerDrawGroup = {
|
|
54
|
+
visible: true, // visible by default
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Iterate trees in parallel
|
|
58
|
+
for (let x=0; x < treeDepth; x++) {
|
|
59
|
+
// Pull out the requested draw group, for each tree, at this depth (avoiding duplicates at the same level in tree)
|
|
60
|
+
draws = [];
|
|
61
|
+
matchingTrees.forEach(tree => {
|
|
62
|
+
if ((tree as LayerDrawGroups[])[x] && (tree as LayerDrawGroups[])[x][group]! && draws.indexOf((tree as LayerDrawGroups[])[x][group]!) === -1) {
|
|
63
|
+
draws.push((tree as LayerDrawGroups[])[x][group]!);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
if (draws.length === 0) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Merge draw objects
|
|
71
|
+
mergeObjects(draw, ...draws);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Short-circuit if not visible
|
|
75
|
+
if (draw.visible === false) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return draw;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const blacklist = ['any', 'all', 'not', 'none'];
|
|
83
|
+
|
|
84
|
+
/** Lazy feature-filter and inherited draw state shared by tree and leaf nodes. */
|
|
85
|
+
class Layer {
|
|
86
|
+
declare static id: number;
|
|
87
|
+
declare id: number;
|
|
88
|
+
declare config_data: {source?: unknown; layer?: unknown; all_layers?: unknown} | null | undefined;
|
|
89
|
+
declare parent: LayerTree | null | undefined;
|
|
90
|
+
declare name: string;
|
|
91
|
+
declare full_name: string;
|
|
92
|
+
declare draw: LayerDrawGroups | undefined;
|
|
93
|
+
declare filter: unknown;
|
|
94
|
+
declare filter_original: unknown;
|
|
95
|
+
declare exclusive: boolean;
|
|
96
|
+
declare priority: number;
|
|
97
|
+
declare styles: Record<string, unknown> | undefined;
|
|
98
|
+
declare is_built: boolean;
|
|
99
|
+
declare enabled: boolean;
|
|
100
|
+
declare visible?: boolean;
|
|
101
|
+
declare calculatedDraw: Array<LayerDrawGroups | undefined>;
|
|
102
|
+
declare zooms?: Record<number, boolean>;
|
|
103
|
+
declare feature_prop_matches?: LayerPropertyMatch[];
|
|
104
|
+
declare context_prop_matches?: LayerPropertyMatch[];
|
|
105
|
+
declare children_to_parse?: SceneLayerDefinition | null;
|
|
106
|
+
declare is_leaf?: boolean;
|
|
107
|
+
declare is_tree?: boolean;
|
|
108
|
+
/** Children exist only on tree nodes, not leaf objects. */
|
|
109
|
+
declare layers?: SceneLayer[];
|
|
110
|
+
|
|
111
|
+
constructor({ layer, name, parent, draw, visible, enabled, filter, exclusive, priority, styles }: LayerConfig) {
|
|
112
|
+
this.id = Layer.id++;
|
|
113
|
+
this.config_data = layer.data == null ? layer.data : isLayerData(layer.data) ? layer.data : undefined;
|
|
114
|
+
this.parent = parent;
|
|
115
|
+
this.name = name;
|
|
116
|
+
this.full_name = this.parent ? (this.parent.full_name + ':' + this.name) : this.name;
|
|
117
|
+
this.draw = draw as LayerDrawGroups | undefined;
|
|
118
|
+
this.filter = filter;
|
|
119
|
+
this.exclusive = (exclusive === true);
|
|
120
|
+
this.priority = (priority != null ? priority : Number.MAX_SAFE_INTEGER);
|
|
121
|
+
this.styles = styles;
|
|
122
|
+
this.is_built = false;
|
|
123
|
+
|
|
124
|
+
enabled = (enabled === undefined) ? visible : enabled; // `visible` property is backwards compatible for `enabled`
|
|
125
|
+
this.enabled = (enabled !== false); // layer is enabled unless explicitly set to disabled
|
|
126
|
+
|
|
127
|
+
// Denormalize layer name to draw groups
|
|
128
|
+
if (this.draw) {
|
|
129
|
+
for (let group in this.draw) {
|
|
130
|
+
this.draw[group] = (this.draw[group] == null) ? {} : this.draw[group];
|
|
131
|
+
if (typeof this.draw[group] !== 'object') {
|
|
132
|
+
// Invalid draw group
|
|
133
|
+
let msg = `Draw group '${group}' for layer ${this.full_name} is invalid, must be an object, `;
|
|
134
|
+
msg += `but was set to \`${group}: ${this.draw[group]}\` instead`;
|
|
135
|
+
log('warn', msg); // TODO: fire external event that clients to subscribe to
|
|
136
|
+
|
|
137
|
+
delete this.draw[group];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
build () {
|
|
144
|
+
log('trace', `Building layer '${this.full_name}'`);
|
|
145
|
+
this.buildFilter();
|
|
146
|
+
this.buildDraw();
|
|
147
|
+
this.is_built = true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
buildDraw() {
|
|
151
|
+
this.draw = compileFunctionStrings(this.draw, StyleParser.wrapFunction) as LayerDrawGroups | undefined;
|
|
152
|
+
this.calculatedDraw = calculateDraw(this);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
buildFilter() {
|
|
156
|
+
this.filter_original = this.filter;
|
|
157
|
+
this.filter = compileFunctionStrings(this.filter, StyleParser.wrapFunction);
|
|
158
|
+
|
|
159
|
+
let type = typeof this.filter;
|
|
160
|
+
if (this.filter != null && type !== 'object' && type !== 'function') {
|
|
161
|
+
// Invalid filter
|
|
162
|
+
let msg = `Filter for layer ${this.full_name} is invalid, filter value must be an object or function, `;
|
|
163
|
+
msg += `but was set to \`filter: ${this.filter}\` instead`;
|
|
164
|
+
log('warn', msg); // TODO: fire external event that clients to subscribe to
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
this.buildZooms();
|
|
170
|
+
this.buildPropMatches();
|
|
171
|
+
if (this.filter != null && (typeof this.filter === 'function' || Object.keys(this.filter).length > 0)) {
|
|
172
|
+
this.filter = buildFilter(this.filter, FilterOptions);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
this.filter = null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch(e) {
|
|
179
|
+
// Invalid filter
|
|
180
|
+
let msg = `Filter for layer ${this.full_name} is invalid, \`filter: ${JSON.stringify(this.filter)}\` `;
|
|
181
|
+
msg += `failed with error '${(e as Error).message}', stack trace: ${(e as Error).stack}`;
|
|
182
|
+
log('warn', msg); // TODO: fire external event that clients to subscribe to
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Zooms often cull large swaths of the layer tree, so they get special treatment and are checked first
|
|
187
|
+
buildZooms() {
|
|
188
|
+
let zoom = this.filter && (this.filter as Record<string, unknown>).$zoom; // has an explicit zoom filter
|
|
189
|
+
let ztype = typeof zoom;
|
|
190
|
+
if (zoom != null) {
|
|
191
|
+
this.zooms = {};
|
|
192
|
+
|
|
193
|
+
if (ztype === 'number') {
|
|
194
|
+
this.zooms[zoom as number] = true;
|
|
195
|
+
}
|
|
196
|
+
else if (Array.isArray(zoom)) {
|
|
197
|
+
for (let z=0; z < zoom.length; z++) {
|
|
198
|
+
this.zooms[zoom[z]] = true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else if (ztype === 'object' && ((zoom as {min?: number}).min != null || (zoom as {max?: number}).max != null)) {
|
|
202
|
+
let zmin = (zoom as {min?: number}).min || 0;
|
|
203
|
+
let zmax = (zoom as {max?: number}).max || Geo.max_style_zoom;
|
|
204
|
+
for (let z=zmin; z < zmax; z++) {
|
|
205
|
+
this.zooms[z] = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
delete (this.filter as Record<string, unknown>).$zoom; // don't process zoom through usual generic filter logic
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
buildPropMatches() {
|
|
214
|
+
if (!this.filter || Array.isArray(this.filter) || typeof this.filter === 'function') {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
Object.keys(this.filter).forEach(key => {
|
|
219
|
+
if (blacklist.indexOf(key) === -1) {
|
|
220
|
+
let val = (this.filter as Record<string, unknown>)[key];
|
|
221
|
+
let type = typeof val;
|
|
222
|
+
let array = Array.isArray(val);
|
|
223
|
+
|
|
224
|
+
if (!(array || type === 'string' || type === 'number')) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (key[0] === '$') {
|
|
229
|
+
// Context property
|
|
230
|
+
this.context_prop_matches = this.context_prop_matches || [];
|
|
231
|
+
this.context_prop_matches.push([key.substring(1), array ? val as unknown[] : [val]]);
|
|
232
|
+
delete (this.filter as Record<string, unknown>)[key];
|
|
233
|
+
}
|
|
234
|
+
else if (key.indexOf('.') === -1) { // exclude nested feature properties
|
|
235
|
+
// Single-level feature property
|
|
236
|
+
this.feature_prop_matches = this.feature_prop_matches || [];
|
|
237
|
+
this.feature_prop_matches.push([key, array ? val as unknown[] : [val]]);
|
|
238
|
+
delete (this.filter as Record<string, unknown>)[key];
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
doPropMatches (context: LayerMatchContext): boolean {
|
|
245
|
+
if (this.feature_prop_matches) {
|
|
246
|
+
for (let r=0; r < this.feature_prop_matches.length; r++) {
|
|
247
|
+
let match = this.feature_prop_matches[r];
|
|
248
|
+
let val = context.feature.properties[match[0]];
|
|
249
|
+
if (val == null || match[1].indexOf(val) === -1) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (this.context_prop_matches) {
|
|
256
|
+
for (let r=0; r < this.context_prop_matches.length; r++) {
|
|
257
|
+
let match = this.context_prop_matches[r];
|
|
258
|
+
let val = context[match[0]];
|
|
259
|
+
if (val == null || match[1].indexOf(val) === -1) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
doesMatch (context: LayerMatchContext): boolean {
|
|
269
|
+
if (!this.enabled) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!this.is_built) {
|
|
274
|
+
this.build();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// zoom pre-filter: skip rest of filter if out of layer zoom range
|
|
278
|
+
if (this.zooms != null && !this.zooms[context.zoom]) {
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// direct feature property matches
|
|
283
|
+
if (!this.doPropMatches(context)) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// any remaining filter (more complex matches or dynamic function)
|
|
288
|
+
let match;
|
|
289
|
+
if (this.filter instanceof Function){
|
|
290
|
+
try {
|
|
291
|
+
match = (this.filter as PropertyFunction)(context);
|
|
292
|
+
}
|
|
293
|
+
catch (error) {
|
|
294
|
+
// Filter function error
|
|
295
|
+
let msg = `Filter for this ${this.full_name}: \`filter: ${this.filter_original}\` `;
|
|
296
|
+
msg += `failed with error '${(error as Error).message}', stack trace: ${(error as Error).stack}`;
|
|
297
|
+
log('error', msg, context.feature);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
match = this.filter == null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (match) {
|
|
305
|
+
if (this.children_to_parse) {
|
|
306
|
+
// Only tree nodes schedule non-empty children. The lazy state is
|
|
307
|
+
// retained on the shared base class for legacy construction.
|
|
308
|
+
parseLayerChildren(this as unknown as LayerTree, this.children_to_parse, this.styles);
|
|
309
|
+
delete this.children_to_parse;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
Layer.id = 0;
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
/** Terminal scene node with no child traversal state. */
|
|
323
|
+
export class LayerLeaf extends Layer {
|
|
324
|
+
constructor (config: LayerConfig) {
|
|
325
|
+
super(config);
|
|
326
|
+
this.is_leaf = true;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Root/branch node with deferred children and cached matching draw combinations. */
|
|
332
|
+
export class LayerTree extends Layer {
|
|
333
|
+
/** Traversable children, populated only after the parent matches. */
|
|
334
|
+
declare layers: SceneLayer[];
|
|
335
|
+
constructor (config: LayerConfig) {
|
|
336
|
+
super(config);
|
|
337
|
+
this.is_tree = true;
|
|
338
|
+
this.layers = config.layers || [];
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
addLayer (layer: SceneLayer): void {
|
|
342
|
+
this.layers.push(layer);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
buildDrawGroups (context: LayerMatchContext): LayerDrawGroups | null | undefined {
|
|
346
|
+
let layers: SceneLayer[] = [], layer_ids: number[] = [];
|
|
347
|
+
matchFeature(context, [this], layers, layer_ids);
|
|
348
|
+
|
|
349
|
+
if (layers.length > 0) {
|
|
350
|
+
let cache_key = cacheKey(layer_ids);
|
|
351
|
+
|
|
352
|
+
// Only evaluate each layer combination once (undefined means not yet evaluated,
|
|
353
|
+
// null means evaluated with no draw object)
|
|
354
|
+
if (layer_cache[cache_key] === undefined) {
|
|
355
|
+
// Find all the unique visible draw blocks for this layer tree
|
|
356
|
+
let draw_groups = layers.map(x => x && x.visible !== false && x.calculatedDraw);
|
|
357
|
+
let draw_keys: Record<string, boolean> = {};
|
|
358
|
+
|
|
359
|
+
for (let r=0; r < draw_groups.length; r++) {
|
|
360
|
+
let stack = draw_groups[r];
|
|
361
|
+
if (!stack) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
for (let g=0; g < stack.length; g++) {
|
|
365
|
+
let group = stack[g];
|
|
366
|
+
for (let key in group) {
|
|
367
|
+
draw_keys[key] = true;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Calculate each draw group
|
|
373
|
+
for (let draw_key in draw_keys) {
|
|
374
|
+
layer_cache[cache_key] = layer_cache[cache_key] || {};
|
|
375
|
+
layer_cache[cache_key]![draw_key] = mergeTrees(draw_groups, draw_key);
|
|
376
|
+
|
|
377
|
+
// Only save the ones that weren't null
|
|
378
|
+
if (!layer_cache[cache_key]![draw_key]) {
|
|
379
|
+
delete layer_cache[cache_key]![draw_key];
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
layer_cache[cache_key]![draw_key]!.key = cache_key + '/' + draw_key;
|
|
383
|
+
layer_cache[cache_key]![draw_key]!.layers = layers.map(x => x && x.full_name);
|
|
384
|
+
layer_cache[cache_key]![draw_key]!.group = draw_key;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// No layers evaluated
|
|
389
|
+
if (layer_cache[cache_key] && Object.keys(layer_cache[cache_key]).length === 0) {
|
|
390
|
+
layer_cache[cache_key] = null;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
return layer_cache[cache_key];
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export const FilterOptions = {
|
|
400
|
+
// Handle unit conversions on filter ranges
|
|
401
|
+
rangeTransform(val: unknown): unknown {
|
|
402
|
+
if (typeof val === 'string' && val.trim().slice(-3) === 'px2') {
|
|
403
|
+
return `${parseFloat(val)} * context.meters_per_pixel_sq`;
|
|
404
|
+
}
|
|
405
|
+
return val;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
export function isReserved(key: string): boolean {
|
|
410
|
+
return reserved.indexOf(key) > -1;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function isEmpty(obj: object): boolean {
|
|
414
|
+
return Object.keys(obj).length === 0;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function groupProps(obj: SceneLayerDefinition): [SceneLayerDefinition, SceneLayerDefinition] {
|
|
418
|
+
let reserved: SceneLayerDefinition = {}, children: SceneLayerDefinition = {};
|
|
419
|
+
|
|
420
|
+
for (let key in obj) {
|
|
421
|
+
if (isReserved(key)) {
|
|
422
|
+
reserved[key] = obj[key];
|
|
423
|
+
} else {
|
|
424
|
+
children[key] = obj[key];
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return [reserved, children];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export function calculateDraw(layer: Layer): Array<LayerDrawGroups | undefined> {
|
|
431
|
+
|
|
432
|
+
let draw: Array<LayerDrawGroups | undefined> = [];
|
|
433
|
+
|
|
434
|
+
if (layer.parent) {
|
|
435
|
+
let cs = layer.parent.calculatedDraw || [];
|
|
436
|
+
draw.push(...cs);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
draw.push(layer.draw);
|
|
440
|
+
return draw;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export function parseLayerNode(name: string, layer: SceneLayerDefinition | null | undefined, parent: null, styles?: Record<string, unknown>): LayerTree;
|
|
444
|
+
export function parseLayerNode(name: string, layer: SceneLayerDefinition | null | undefined, parent?: LayerTree | null, styles?: Record<string, unknown>): SceneLayer;
|
|
445
|
+
export function parseLayerNode(name: string, layer: SceneLayerDefinition | null | undefined, parent?: LayerTree | null, styles?: Record<string, unknown>): SceneLayer {
|
|
446
|
+
|
|
447
|
+
layer = (layer == null) ? {} : layer;
|
|
448
|
+
|
|
449
|
+
let properties = { name, layer, parent, styles };
|
|
450
|
+
let [reserved, children] = groupProps(layer);
|
|
451
|
+
let empty = isEmpty(children);
|
|
452
|
+
let Create;
|
|
453
|
+
|
|
454
|
+
if (empty && parent != null) {
|
|
455
|
+
Create = LayerLeaf;
|
|
456
|
+
} else {
|
|
457
|
+
Create = LayerTree;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
let r = new Create(Object.assign(properties, reserved) as LayerConfig);
|
|
461
|
+
|
|
462
|
+
// only process child layers if this layer is enabled
|
|
463
|
+
if (r.enabled) {
|
|
464
|
+
if (parent) {
|
|
465
|
+
parent.addLayer(r);
|
|
466
|
+
}
|
|
467
|
+
r.children_to_parse = empty ? null : children;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return r;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function parseLayerChildren (parent: LayerTree, children: SceneLayerDefinition, styles?: Record<string, unknown>): void {
|
|
474
|
+
for (let key in children) {
|
|
475
|
+
let child = children[key];
|
|
476
|
+
if (typeof child === 'object' && !Array.isArray(child)) {
|
|
477
|
+
parseLayerNode(key, child as SceneLayerDefinition | null, parent, styles);
|
|
478
|
+
} else {
|
|
479
|
+
// Invalid layer
|
|
480
|
+
let msg = `Layer value must be an object: cannot create layer '${key}: ${JSON.stringify(child)}'`;
|
|
481
|
+
msg += `, under parent layer '${parent.full_name}'.`;
|
|
482
|
+
|
|
483
|
+
// If the parent is a style name, this may be an incorrectly nested layer
|
|
484
|
+
if (styles![parent.name]) {
|
|
485
|
+
msg += ` The parent name '${parent.name}' is also the name of a style, did you mean to create a 'draw' group`;
|
|
486
|
+
if (parent.parent) {
|
|
487
|
+
msg += ` under '${parent.parent.name}'`;
|
|
488
|
+
}
|
|
489
|
+
msg += ' instead?';
|
|
490
|
+
}
|
|
491
|
+
log('warn', msg); // TODO: fire external event that clients to subscribe to
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Sort sub-layers so they are applied deterministically when multiple layers modify the same properties
|
|
496
|
+
// Sort order is: exclusive layers first, then by explicit layer priority, then by layer name
|
|
497
|
+
parent.layers.sort(((a: SceneLayer, b: SceneLayer) => {
|
|
498
|
+
// Exclusive layers come first
|
|
499
|
+
// If an exclusive layer matches, no further sibling layers are matched
|
|
500
|
+
if (a.exclusive < b.exclusive) return 1;
|
|
501
|
+
else if (a.exclusive > b.exclusive) return -1;
|
|
502
|
+
|
|
503
|
+
// When sub-sorting exclusive layers, sort the higher priority layers first, since only one exlcusive layer
|
|
504
|
+
// can match and the first one that matches should be the highest priority.
|
|
505
|
+
// When sub-sorting non-exclusive layers, sort the lower priority layers first, since multiple layers may
|
|
506
|
+
// match, and when they are merged in order, the later layers will overwrite the earlier ones -- so we want
|
|
507
|
+
// the higher priority ones to match last so that they "win".
|
|
508
|
+
const direction = (a.exclusive ? 1 : -1);
|
|
509
|
+
|
|
510
|
+
// Sub-sort by explicit priority
|
|
511
|
+
if (a.priority > b.priority) return direction;
|
|
512
|
+
else if (a.priority < b.priority) return -direction;
|
|
513
|
+
|
|
514
|
+
// Sub-sort by layer name as last resort
|
|
515
|
+
if (a.full_name < b.full_name) return direction;
|
|
516
|
+
else if (a.full_name > b.full_name) return -direction;
|
|
517
|
+
}) as (a: SceneLayer, b: SceneLayer) => number);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
/** Source selectors must be objects; malformed scalar metadata cannot select a source. */
|
|
522
|
+
function isLayerData(value: unknown): value is {source?: unknown; layer?: unknown; all_layers?: unknown} {
|
|
523
|
+
return value !== null && typeof value === 'object';
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function parseLayers (layers: Record<string, SceneLayerDefinition | null | undefined>, styles?: Record<string, unknown>): Record<string, LayerTree> {
|
|
527
|
+
layer_cache = {}; // clear layer cache
|
|
528
|
+
let layer_trees: Record<string, LayerTree> = {};
|
|
529
|
+
|
|
530
|
+
for (let key in layers) {
|
|
531
|
+
let layer = layers[key];
|
|
532
|
+
if (layer) {
|
|
533
|
+
layer_trees[key] = parseLayerNode(key, layer, null, styles);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return layer_trees;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export function matchFeature(context: LayerMatchContext, layers: SceneLayer[], collected_layers: SceneLayer[], collected_layers_ids: number[]): boolean | undefined {
|
|
541
|
+
let matched = false;
|
|
542
|
+
let child_matched: boolean | undefined = false;
|
|
543
|
+
|
|
544
|
+
if (layers.length === 0) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
for (let r=0; r < layers.length; r++) {
|
|
549
|
+
let current = layers[r];
|
|
550
|
+
|
|
551
|
+
if (current.is_leaf) {
|
|
552
|
+
if (current.doesMatch(context)) {
|
|
553
|
+
matched = true;
|
|
554
|
+
collected_layers.push(current);
|
|
555
|
+
collected_layers_ids.push(current.id);
|
|
556
|
+
|
|
557
|
+
if (current.exclusive) {
|
|
558
|
+
break; // only one exclusive layer can match, stop matching further sibling layers
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
} else if (current.is_tree) {
|
|
563
|
+
if (current.doesMatch(context)) {
|
|
564
|
+
matched = true;
|
|
565
|
+
|
|
566
|
+
child_matched = matchFeature(
|
|
567
|
+
context,
|
|
568
|
+
current.layers!,
|
|
569
|
+
collected_layers,
|
|
570
|
+
collected_layers_ids
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
if (!child_matched) {
|
|
574
|
+
collected_layers.push(current);
|
|
575
|
+
collected_layers_ids.push(current.id);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (current.exclusive) {
|
|
579
|
+
break; // only one exclusive layer can match, stop matching further sibling layers
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return matched;
|
|
586
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Tangram
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) 2013-2016 Brett Camper and Mapzen
|
|
4
|
+
// Copyright (c) 2026 vis.gl contributors
|
|
5
|
+
|
|
6
|
+
// Renders an array specifying a line pattern of alternating dashes and spaces,
|
|
7
|
+
// similar to an SVG `dasharray` or Canvas setLineDash(), into a byte array of RGBA pixels
|
|
8
|
+
// Returns:
|
|
9
|
+
// {
|
|
10
|
+
// pixel: rendered image in Uint8Array buffer
|
|
11
|
+
// length: pixel length of rendered dash pattern (sum of all dashes and spaces)
|
|
12
|
+
// }
|
|
13
|
+
//
|
|
14
|
+
// https://www.w3.org/TR/SVG/painting.html#StrokeDasharrayProperty
|
|
15
|
+
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dasharray
|
|
16
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash
|
|
17
|
+
|
|
18
|
+
const default_dash_color = [255, 255, 255, 255];
|
|
19
|
+
const default_background_color = [0, 0, 0, 0];
|
|
20
|
+
|
|
21
|
+
type DashArrayOptions = {dash_color?: number[]; background_color?: number[]; scale?: number};
|
|
22
|
+
|
|
23
|
+
export default function renderDashArray (pattern: number[], options: DashArrayOptions = {}): {pixels: Uint8Array; length: number} {
|
|
24
|
+
const dash_pixel = options.dash_color || default_dash_color;
|
|
25
|
+
const background_color = options.background_color || default_background_color;
|
|
26
|
+
const dashes = pattern;
|
|
27
|
+
const scale = options.scale || 1;
|
|
28
|
+
|
|
29
|
+
// If pattern is odd, repeat it to make it even (see SVG spec)
|
|
30
|
+
if (dashes.length % 2 === 1) {
|
|
31
|
+
Array.prototype.push.apply(dashes, dashes);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let dash = true;
|
|
35
|
+
let pixels: any = [];
|
|
36
|
+
for (let i=0; i < dashes.length; i++) {
|
|
37
|
+
let segment = Math.floor(dashes[i] * scale);
|
|
38
|
+
for (let s=0; s < segment; s++) {
|
|
39
|
+
Array.prototype.push.apply(pixels, dash ? dash_pixel : background_color);
|
|
40
|
+
}
|
|
41
|
+
dash = !dash; // alternate between dashes and spaces
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
pixels.reverse(); // flip Y (GL textures are upside down)
|
|
45
|
+
pixels = new Uint8Array(pixels); // convert to typed array
|
|
46
|
+
const length = pixels.length / 4; // one RGBA byte sequences to one pixel
|
|
47
|
+
|
|
48
|
+
return { pixels, length };
|
|
49
|
+
}
|