@mapwhit/tilerenderer 0.47.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +87 -0
- package/README.md +25 -0
- package/build/min/.dir +0 -0
- package/build/min/package.json +3 -0
- package/build/min/src/shaders/_prelude.fragment.glsl.txt +13 -0
- package/build/min/src/shaders/_prelude.vertex.glsl.txt +14 -0
- package/build/min/src/shaders/background.fragment.glsl.txt +5 -0
- package/build/min/src/shaders/background.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/background_pattern.fragment.glsl.txt +5 -0
- package/build/min/src/shaders/background_pattern.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/circle.fragment.glsl.txt +20 -0
- package/build/min/src/shaders/circle.vertex.glsl.txt +17 -0
- package/build/min/src/shaders/clipping_mask.fragment.glsl.txt +1 -0
- package/build/min/src/shaders/clipping_mask.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/collision_box.fragment.glsl.txt +1 -0
- package/build/min/src/shaders/collision_box.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/collision_circle.fragment.glsl.txt +1 -0
- package/build/min/src/shaders/collision_circle.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/debug.fragment.glsl.txt +1 -0
- package/build/min/src/shaders/debug.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/fill.fragment.glsl.txt +10 -0
- package/build/min/src/shaders/fill.vertex.glsl.txt +7 -0
- package/build/min/src/shaders/fill_extrusion.fragment.glsl.txt +13 -0
- package/build/min/src/shaders/fill_extrusion.vertex.glsl.txt +9 -0
- package/build/min/src/shaders/fill_extrusion_pattern.fragment.glsl.txt +15 -0
- package/build/min/src/shaders/fill_extrusion_pattern.vertex.glsl.txt +11 -0
- package/build/min/src/shaders/fill_outline.fragment.glsl.txt +10 -0
- package/build/min/src/shaders/fill_outline.vertex.glsl.txt +7 -0
- package/build/min/src/shaders/fill_outline_pattern.fragment.glsl.txt +13 -0
- package/build/min/src/shaders/fill_outline_pattern.vertex.glsl.txt +9 -0
- package/build/min/src/shaders/fill_pattern.fragment.glsl.txt +13 -0
- package/build/min/src/shaders/fill_pattern.vertex.glsl.txt +9 -0
- package/build/min/src/shaders/heatmap.fragment.glsl.txt +10 -0
- package/build/min/src/shaders/heatmap.vertex.glsl.txt +8 -0
- package/build/min/src/shaders/heatmap_texture.fragment.glsl.txt +5 -0
- package/build/min/src/shaders/heatmap_texture.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/hillshade.fragment.glsl.txt +7 -0
- package/build/min/src/shaders/hillshade.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/hillshade_prepare.fragment.glsl.txt +8 -0
- package/build/min/src/shaders/hillshade_prepare.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/line.fragment.glsl.txt +12 -0
- package/build/min/src/shaders/line.vertex.glsl.txt +17 -0
- package/build/min/src/shaders/line_gradient.fragment.glsl.txt +10 -0
- package/build/min/src/shaders/line_gradient.vertex.glsl.txt +16 -0
- package/build/min/src/shaders/line_pattern.fragment.glsl.txt +15 -0
- package/build/min/src/shaders/line_pattern.vertex.glsl.txt +20 -0
- package/build/min/src/shaders/line_sdf.fragment.glsl.txt +17 -0
- package/build/min/src/shaders/line_sdf.vertex.glsl.txt +20 -0
- package/build/min/src/shaders/raster.fragment.glsl.txt +5 -0
- package/build/min/src/shaders/raster.vertex.glsl.txt +1 -0
- package/build/min/src/shaders/symbol_icon.fragment.glsl.txt +9 -0
- package/build/min/src/shaders/symbol_icon.vertex.glsl.txt +5 -0
- package/build/min/src/shaders/symbol_sdf.fragment.glsl.txt +19 -0
- package/build/min/src/shaders/symbol_sdf.vertex.glsl.txt +13 -0
- package/package.json +44 -0
- package/src/css/mapbox-gl.css +506 -0
- package/src/css/svg/mapboxgl-ctrl-attrib.svg +3 -0
- package/src/css/svg/mapboxgl-ctrl-compass.svg +4 -0
- package/src/css/svg/mapboxgl-ctrl-fullscreen.svg +3 -0
- package/src/css/svg/mapboxgl-ctrl-geolocate-background.svg +3 -0
- package/src/css/svg/mapboxgl-ctrl-geolocate.svg +3 -0
- package/src/css/svg/mapboxgl-ctrl-logo-compact.svg +2 -0
- package/src/css/svg/mapboxgl-ctrl-logo.svg +1 -0
- package/src/css/svg/mapboxgl-ctrl-shrink.svg +3 -0
- package/src/css/svg/mapboxgl-ctrl-zoom-in.svg +3 -0
- package/src/css/svg/mapboxgl-ctrl-zoom-out.svg +3 -0
- package/src/data/array_types.js +1138 -0
- package/src/data/bucket/circle_attributes.js +5 -0
- package/src/data/bucket/circle_bucket.js +118 -0
- package/src/data/bucket/fill_attributes.js +5 -0
- package/src/data/bucket/fill_bucket.js +166 -0
- package/src/data/bucket/fill_extrusion_attributes.js +11 -0
- package/src/data/bucket/fill_extrusion_bucket.js +247 -0
- package/src/data/bucket/heatmap_bucket.js +12 -0
- package/src/data/bucket/line_attributes.js +11 -0
- package/src/data/bucket/line_bucket.js +625 -0
- package/src/data/bucket/pattern_attributes.js +9 -0
- package/src/data/bucket/pattern_bucket_features.js +44 -0
- package/src/data/bucket/symbol_attributes.js +95 -0
- package/src/data/bucket/symbol_bucket.js +697 -0
- package/src/data/bucket.js +53 -0
- package/src/data/dem_data.js +126 -0
- package/src/data/extent.js +17 -0
- package/src/data/feature_index.js +254 -0
- package/src/data/index_array_type.js +14 -0
- package/src/data/load_geometry.js +42 -0
- package/src/data/pos_attributes.js +3 -0
- package/src/data/program_configuration.js +782 -0
- package/src/data/raster_bounds_attributes.js +6 -0
- package/src/data/segment.js +63 -0
- package/src/geo/coordinate.js +78 -0
- package/src/geo/lng_lat.js +129 -0
- package/src/geo/lng_lat_bounds.js +253 -0
- package/src/geo/transform.js +605 -0
- package/src/gl/color_mode.js +21 -0
- package/src/gl/context.js +193 -0
- package/src/gl/cull_face_mode.js +22 -0
- package/src/gl/depth_mode.js +18 -0
- package/src/gl/framebuffer.js +28 -0
- package/src/gl/index_buffer.js +52 -0
- package/src/gl/stencil_mode.js +17 -0
- package/src/gl/types.js +0 -0
- package/src/gl/value.js +676 -0
- package/src/gl/vertex_buffer.js +101 -0
- package/src/index.js +50 -0
- package/src/render/draw_background.js +60 -0
- package/src/render/draw_circle.js +55 -0
- package/src/render/draw_collision_debug.js +45 -0
- package/src/render/draw_debug.js +429 -0
- package/src/render/draw_fill.js +143 -0
- package/src/render/draw_fill_extrusion.js +101 -0
- package/src/render/draw_heatmap.js +159 -0
- package/src/render/draw_hillshade.js +144 -0
- package/src/render/draw_line.js +99 -0
- package/src/render/draw_raster.js +151 -0
- package/src/render/draw_symbol.js +231 -0
- package/src/render/glyph_atlas.js +55 -0
- package/src/render/glyph_manager.js +145 -0
- package/src/render/image_atlas.js +97 -0
- package/src/render/image_manager.js +183 -0
- package/src/render/line_atlas.js +139 -0
- package/src/render/painter.js +483 -0
- package/src/render/program/background_program.js +46 -0
- package/src/render/program/circle_program.js +40 -0
- package/src/render/program/clipping_mask_program.js +11 -0
- package/src/render/program/collision_program.js +28 -0
- package/src/render/program/debug_program.js +13 -0
- package/src/render/program/fill_extrusion_program.js +76 -0
- package/src/render/program/fill_program.js +60 -0
- package/src/render/program/heatmap_program.js +46 -0
- package/src/render/program/hillshade_program.js +77 -0
- package/src/render/program/line_program.js +119 -0
- package/src/render/program/pattern.js +57 -0
- package/src/render/program/program_uniforms.js +46 -0
- package/src/render/program/raster_program.js +50 -0
- package/src/render/program/symbol_program.js +112 -0
- package/src/render/program.js +133 -0
- package/src/render/texture.js +88 -0
- package/src/render/tile_mask.js +108 -0
- package/src/render/uniform_binding.js +129 -0
- package/src/render/vertex_array_object.js +155 -0
- package/src/shaders/README.md +42 -0
- package/src/shaders/_prelude.fragment.glsl +17 -0
- package/src/shaders/_prelude.vertex.glsl +73 -0
- package/src/shaders/background.fragment.glsl +10 -0
- package/src/shaders/background.vertex.glsl +7 -0
- package/src/shaders/background_pattern.fragment.glsl +28 -0
- package/src/shaders/background_pattern.vertex.glsl +20 -0
- package/src/shaders/circle.fragment.glsl +39 -0
- package/src/shaders/circle.vertex.glsl +63 -0
- package/src/shaders/clipping_mask.fragment.glsl +3 -0
- package/src/shaders/clipping_mask.vertex.glsl +7 -0
- package/src/shaders/collision_box.fragment.glsl +21 -0
- package/src/shaders/collision_box.vertex.glsl +26 -0
- package/src/shaders/collision_circle.fragment.glsl +34 -0
- package/src/shaders/collision_circle.vertex.glsl +36 -0
- package/src/shaders/debug.fragment.glsl +5 -0
- package/src/shaders/debug.vertex.glsl +7 -0
- package/src/shaders/encode_attribute.js +19 -0
- package/src/shaders/fill.fragment.glsl +13 -0
- package/src/shaders/fill.vertex.glsl +13 -0
- package/src/shaders/fill_extrusion.fragment.glsl +16 -0
- package/src/shaders/fill_extrusion.vertex.glsl +66 -0
- package/src/shaders/fill_extrusion_pattern.fragment.glsl +41 -0
- package/src/shaders/fill_extrusion_pattern.vertex.glsl +76 -0
- package/src/shaders/fill_outline.fragment.glsl +17 -0
- package/src/shaders/fill_outline.vertex.glsl +17 -0
- package/src/shaders/fill_outline_pattern.fragment.glsl +43 -0
- package/src/shaders/fill_outline_pattern.vertex.glsl +41 -0
- package/src/shaders/fill_pattern.fragment.glsl +36 -0
- package/src/shaders/fill_pattern.vertex.glsl +36 -0
- package/src/shaders/heatmap.fragment.glsl +21 -0
- package/src/shaders/heatmap.vertex.glsl +53 -0
- package/src/shaders/heatmap_texture.fragment.glsl +14 -0
- package/src/shaders/heatmap_texture.vertex.glsl +11 -0
- package/src/shaders/hillshade.fragment.glsl +52 -0
- package/src/shaders/hillshade.vertex.glsl +11 -0
- package/src/shaders/hillshade_prepare.fragment.glsl +72 -0
- package/src/shaders/hillshade_prepare.vertex.glsl +15 -0
- package/src/shaders/index.js +194 -0
- package/src/shaders/line.fragment.glsl +28 -0
- package/src/shaders/line.vertex.glsl +84 -0
- package/src/shaders/line_gradient.fragment.glsl +34 -0
- package/src/shaders/line_gradient.vertex.glsl +84 -0
- package/src/shaders/line_pattern.fragment.glsl +69 -0
- package/src/shaders/line_pattern.vertex.glsl +88 -0
- package/src/shaders/line_sdf.fragment.glsl +44 -0
- package/src/shaders/line_sdf.vertex.glsl +95 -0
- package/src/shaders/raster.fragment.glsl +52 -0
- package/src/shaders/raster.vertex.glsl +21 -0
- package/src/shaders/symbol_icon.fragment.glsl +17 -0
- package/src/shaders/symbol_icon.vertex.glsl +91 -0
- package/src/shaders/symbol_sdf.fragment.glsl +50 -0
- package/src/shaders/symbol_sdf.vertex.glsl +117 -0
- package/src/source/geojson_source.js +267 -0
- package/src/source/geojson_worker_source.js +210 -0
- package/src/source/geojson_wrapper.js +67 -0
- package/src/source/image_source.js +213 -0
- package/src/source/load_tilejson.js +40 -0
- package/src/source/pixels_to_tile_units.js +17 -0
- package/src/source/query_features.js +198 -0
- package/src/source/raster_dem_tile_source.js +140 -0
- package/src/source/raster_dem_tile_worker_source.js +26 -0
- package/src/source/raster_tile_source.js +126 -0
- package/src/source/rtl_text_plugin.js +63 -0
- package/src/source/source.js +75 -0
- package/src/source/source_cache.js +794 -0
- package/src/source/source_state.js +55 -0
- package/src/source/tile.js +332 -0
- package/src/source/tile_bounds.js +40 -0
- package/src/source/tile_cache.js +122 -0
- package/src/source/tile_id.js +150 -0
- package/src/source/vector_tile_source.js +144 -0
- package/src/source/vector_tile_worker_source.js +126 -0
- package/src/source/worker.js +175 -0
- package/src/source/worker_source.js +14 -0
- package/src/source/worker_tile.js +199 -0
- package/src/style/create_style_layer.js +25 -0
- package/src/style/evaluation_parameters.js +45 -0
- package/src/style/light.js +112 -0
- package/src/style/load_glyph_range.js +17 -0
- package/src/style/load_sprite.js +26 -0
- package/src/style/parse_glyph_pbf.js +45 -0
- package/src/style/pauseable_placement.js +88 -0
- package/src/style/properties.js +691 -0
- package/src/style/query_utils.js +39 -0
- package/src/style/style.js +955 -0
- package/src/style/style_layer/background_style_layer.js +11 -0
- package/src/style/style_layer/background_style_layer_properties.js +25 -0
- package/src/style/style_layer/circle_style_layer.js +93 -0
- package/src/style/style_layer/circle_style_layer_properties.js +76 -0
- package/src/style/style_layer/fill_extrusion_style_layer.js +194 -0
- package/src/style/style_layer/fill_extrusion_style_layer_properties.js +56 -0
- package/src/style/style_layer/fill_style_layer.js +46 -0
- package/src/style/style_layer/fill_style_layer_properties.js +45 -0
- package/src/style/style_layer/heatmap_style_layer.js +51 -0
- package/src/style/style_layer/heatmap_style_layer_properties.js +52 -0
- package/src/style/style_layer/hillshade_style_layer.js +15 -0
- package/src/style/style_layer/hillshade_style_layer_properties.js +43 -0
- package/src/style/style_layer/line_style_layer.js +129 -0
- package/src/style/style_layer/line_style_layer_properties.js +104 -0
- package/src/style/style_layer/raster_style_layer.js +11 -0
- package/src/style/style_layer/raster_style_layer_properties.js +55 -0
- package/src/style/style_layer/symbol_style_layer.js +66 -0
- package/src/style/style_layer/symbol_style_layer_properties.js +288 -0
- package/src/style/style_layer.js +183 -0
- package/src/style/style_layer_index.js +61 -0
- package/src/style/zoom_history.js +36 -0
- package/src/style-spec/deref.js +51 -0
- package/src/style-spec/error/parsing_error.js +8 -0
- package/src/style-spec/error/validation_error.js +10 -0
- package/src/style-spec/expression/compound_expression.js +118 -0
- package/src/style-spec/expression/definitions/array.js +82 -0
- package/src/style-spec/expression/definitions/assertion.js +69 -0
- package/src/style-spec/expression/definitions/at.js +57 -0
- package/src/style-spec/expression/definitions/case.js +73 -0
- package/src/style-spec/expression/definitions/coalesce.js +68 -0
- package/src/style-spec/expression/definitions/coercion.js +96 -0
- package/src/style-spec/expression/definitions/collator.js +102 -0
- package/src/style-spec/expression/definitions/equals.js +93 -0
- package/src/style-spec/expression/definitions/index.js +407 -0
- package/src/style-spec/expression/definitions/interpolate.js +235 -0
- package/src/style-spec/expression/definitions/length.js +54 -0
- package/src/style-spec/expression/definitions/let.js +60 -0
- package/src/style-spec/expression/definitions/literal.js +64 -0
- package/src/style-spec/expression/definitions/match.js +142 -0
- package/src/style-spec/expression/definitions/step.js +116 -0
- package/src/style-spec/expression/definitions/var.js +38 -0
- package/src/style-spec/expression/evaluation_context.js +35 -0
- package/src/style-spec/expression/index.js +329 -0
- package/src/style-spec/expression/is_constant.js +63 -0
- package/src/style-spec/expression/parsing_context.js +213 -0
- package/src/style-spec/expression/parsing_error.js +9 -0
- package/src/style-spec/expression/runtime_error.js +12 -0
- package/src/style-spec/expression/scope.js +34 -0
- package/src/style-spec/expression/stops.js +37 -0
- package/src/style-spec/expression/types.js +77 -0
- package/src/style-spec/expression/values.js +126 -0
- package/src/style-spec/feature_filter/README.md +55 -0
- package/src/style-spec/feature_filter/index.js +158 -0
- package/src/style-spec/function/convert.js +256 -0
- package/src/style-spec/function/index.js +299 -0
- package/src/style-spec/group_by_layout.js +68 -0
- package/src/style-spec/reference/v8.json +5356 -0
- package/src/style-spec/util/color.js +73 -0
- package/src/style-spec/util/color_spaces.js +128 -0
- package/src/style-spec/util/eval_support.js +8 -0
- package/src/style-spec/util/get_type.js +18 -0
- package/src/style-spec/util/interpolate.js +21 -0
- package/src/style-spec/util/properties.js +17 -0
- package/src/style-spec/util/ref_properties.js +1 -0
- package/src/style-spec/util/result.js +19 -0
- package/src/symbol/anchor.js +21 -0
- package/src/symbol/check_max_angle.js +75 -0
- package/src/symbol/clip_line.js +73 -0
- package/src/symbol/collision_feature.js +230 -0
- package/src/symbol/collision_index.js +379 -0
- package/src/symbol/cross_tile_symbol_index.js +270 -0
- package/src/symbol/get_anchors.js +177 -0
- package/src/symbol/grid_index.js +318 -0
- package/src/symbol/mergelines.js +75 -0
- package/src/symbol/opacity_state.js +21 -0
- package/src/symbol/placement.js +563 -0
- package/src/symbol/projection.js +601 -0
- package/src/symbol/quads.js +173 -0
- package/src/symbol/shaping.js +347 -0
- package/src/symbol/symbol_layout.js +519 -0
- package/src/symbol/symbol_size.js +110 -0
- package/src/symbol/transform_text.js +16 -0
- package/src/ui/anchor.js +24 -0
- package/src/ui/bind_handlers.js +199 -0
- package/src/ui/camera.js +954 -0
- package/src/ui/events.js +210 -0
- package/src/ui/handler/box_zoom.js +151 -0
- package/src/ui/handler/dblclick_zoom.js +91 -0
- package/src/ui/handler/drag_pan.js +285 -0
- package/src/ui/handler/drag_rotate.js +290 -0
- package/src/ui/handler/frame.js +28 -0
- package/src/ui/handler/inertia.js +45 -0
- package/src/ui/handler/keyboard.js +148 -0
- package/src/ui/handler/scroll_zoom.js +284 -0
- package/src/ui/handler/touch_zoom_rotate.js +263 -0
- package/src/ui/map.js +1645 -0
- package/src/util/actor.js +104 -0
- package/src/util/async.js +23 -0
- package/src/util/browser.js +61 -0
- package/src/util/callback.js +26 -0
- package/src/util/classify_rings.js +43 -0
- package/src/util/color_ramp.js +24 -0
- package/src/util/config.js +24 -0
- package/src/util/dictionary_coder.js +25 -0
- package/src/util/dispatcher.js +68 -0
- package/src/util/dom.js +102 -0
- package/src/util/evented.js +182 -0
- package/src/util/find_pole_of_inaccessibility.js +129 -0
- package/src/util/global_worker_pool.js +15 -0
- package/src/util/image.js +124 -0
- package/src/util/interpolate.js +5 -0
- package/src/util/intersection_tests.js +207 -0
- package/src/util/is_char_in_unicode_block.js +287 -0
- package/src/util/loader/image.js +32 -0
- package/src/util/object.js +178 -0
- package/src/util/script_detection.js +337 -0
- package/src/util/struct_array.js +197 -0
- package/src/util/task_queue.js +57 -0
- package/src/util/throttle.js +26 -0
- package/src/util/tile_cover.js +114 -0
- package/src/util/token.js +13 -0
- package/src/util/unique_id.js +12 -0
- package/src/util/util.js +192 -0
- package/src/util/vectortile_to_geojson.js +44 -0
- package/src/util/verticalize_punctuation.js +112 -0
- package/src/util/warn.js +21 -0
- package/src/util/web_worker.js +5 -0
- package/src/util/web_worker_transfer.js +228 -0
- package/src/util/worker_pool.js +41 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
const Point = require('@mapbox/point-geometry');
|
|
2
|
+
|
|
3
|
+
const intersectionTests = require('../util/intersection_tests');
|
|
4
|
+
const Grid = require('./grid_index');
|
|
5
|
+
|
|
6
|
+
const projection = require('../symbol/projection');
|
|
7
|
+
|
|
8
|
+
// When a symbol crosses the edge that causes it to be included in
|
|
9
|
+
// collision detection, it will cause changes in the symbols around
|
|
10
|
+
// it. This constant specifies how many pixels to pad the edge of
|
|
11
|
+
// the viewport for collision detection so that the bulk of the changes
|
|
12
|
+
// occur offscreen. Making this constant greater increases label
|
|
13
|
+
// stability, but it's expensive.
|
|
14
|
+
const viewportPadding = 100;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A collision index used to prevent symbols from overlapping. It keep tracks of
|
|
18
|
+
* where previous symbols have been placed and is used to check if a new
|
|
19
|
+
* symbol overlaps with any previously added symbols.
|
|
20
|
+
*
|
|
21
|
+
* There are two steps to insertion: first placeCollisionBox/Circles checks if
|
|
22
|
+
* there's room for a symbol, then insertCollisionBox/Circles actually puts the
|
|
23
|
+
* symbol in the index. The two step process allows paired symbols to be inserted
|
|
24
|
+
* together even if they overlap.
|
|
25
|
+
*
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
class CollisionIndex {
|
|
29
|
+
constructor(
|
|
30
|
+
transform,
|
|
31
|
+
grid = new Grid(transform.width + 2 * viewportPadding, transform.height + 2 * viewportPadding, 25),
|
|
32
|
+
ignoredGrid = new Grid(transform.width + 2 * viewportPadding, transform.height + 2 * viewportPadding, 25)
|
|
33
|
+
) {
|
|
34
|
+
this.transform = transform;
|
|
35
|
+
|
|
36
|
+
this.grid = grid;
|
|
37
|
+
this.ignoredGrid = ignoredGrid;
|
|
38
|
+
this.pitchfactor = Math.cos(transform._pitch) * transform.cameraToCenterDistance;
|
|
39
|
+
|
|
40
|
+
this.screenRightBoundary = transform.width + viewportPadding;
|
|
41
|
+
this.screenBottomBoundary = transform.height + viewportPadding;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
placeCollisionBox(collisionBox, allowOverlap, textPixelRatio, posMatrix, collisionGroupPredicate) {
|
|
45
|
+
const projectedPoint = this.projectAndGetPerspectiveRatio(
|
|
46
|
+
posMatrix,
|
|
47
|
+
collisionBox.anchorPointX,
|
|
48
|
+
collisionBox.anchorPointY
|
|
49
|
+
);
|
|
50
|
+
const tileToViewport = textPixelRatio * projectedPoint.perspectiveRatio;
|
|
51
|
+
const tlX = collisionBox.x1 * tileToViewport + projectedPoint.point.x;
|
|
52
|
+
const tlY = collisionBox.y1 * tileToViewport + projectedPoint.point.y;
|
|
53
|
+
const brX = collisionBox.x2 * tileToViewport + projectedPoint.point.x;
|
|
54
|
+
const brY = collisionBox.y2 * tileToViewport + projectedPoint.point.y;
|
|
55
|
+
|
|
56
|
+
if (!allowOverlap) {
|
|
57
|
+
if (this.grid.hitTest(tlX, tlY, brX, brY, collisionGroupPredicate)) {
|
|
58
|
+
return {
|
|
59
|
+
box: [],
|
|
60
|
+
offscreen: false
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
box: [tlX, tlY, brX, brY],
|
|
66
|
+
offscreen: this.isOffscreen(tlX, tlY, brX, brY)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
approximateTileDistance(tileDistance, lastSegmentAngle, pixelsToTileUnits, cameraToAnchorDistance, pitchWithMap) {
|
|
71
|
+
// This is a quick and dirty solution for chosing which collision circles to use (since collision circles are
|
|
72
|
+
// laid out in tile units). Ideally, I think we should generate collision circles on the fly in viewport coordinates
|
|
73
|
+
// at the time we do collision detection.
|
|
74
|
+
// See https://github.com/mapbox/mapbox-gl-js/issues/5474
|
|
75
|
+
|
|
76
|
+
// incidenceStretch is the ratio of how much y space a label takes up on a tile while drawn perpendicular to the viewport vs
|
|
77
|
+
// how much space it would take up if it were drawn flat on the tile
|
|
78
|
+
// Using law of sines, camera_to_anchor/sin(ground_angle) = camera_to_center/sin(incidence_angle)
|
|
79
|
+
// Incidence angle 90 -> head on, sin(incidence_angle) = 1, no stretch
|
|
80
|
+
// Incidence angle 1 -> very oblique, sin(incidence_angle) =~ 0, lots of stretch
|
|
81
|
+
// ground_angle = u_pitch + PI/2 -> sin(ground_angle) = cos(u_pitch)
|
|
82
|
+
// incidenceStretch = 1 / sin(incidenceAngle)
|
|
83
|
+
|
|
84
|
+
const incidenceStretch = pitchWithMap ? 1 : cameraToAnchorDistance / this.pitchfactor;
|
|
85
|
+
const lastSegmentTile = tileDistance.lastSegmentViewportDistance * pixelsToTileUnits;
|
|
86
|
+
return (
|
|
87
|
+
tileDistance.prevTileDistance +
|
|
88
|
+
lastSegmentTile +
|
|
89
|
+
(incidenceStretch - 1) * lastSegmentTile * Math.abs(Math.sin(lastSegmentAngle))
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
placeCollisionCircles(
|
|
94
|
+
collisionCircles,
|
|
95
|
+
allowOverlap,
|
|
96
|
+
scale,
|
|
97
|
+
textPixelRatio,
|
|
98
|
+
key,
|
|
99
|
+
symbol,
|
|
100
|
+
lineVertexArray,
|
|
101
|
+
glyphOffsetArray,
|
|
102
|
+
fontSize,
|
|
103
|
+
posMatrix,
|
|
104
|
+
labelPlaneMatrix,
|
|
105
|
+
showCollisionCircles,
|
|
106
|
+
pitchWithMap,
|
|
107
|
+
collisionGroupPredicate
|
|
108
|
+
) {
|
|
109
|
+
const placedCollisionCircles = [];
|
|
110
|
+
|
|
111
|
+
const projectedAnchor = this.projectAnchor(posMatrix, symbol.anchorX, symbol.anchorY);
|
|
112
|
+
|
|
113
|
+
const projectionCache = {};
|
|
114
|
+
const fontScale = fontSize / 24;
|
|
115
|
+
const lineOffsetX = symbol.lineOffsetX * fontSize;
|
|
116
|
+
const lineOffsetY = symbol.lineOffsetY * fontSize;
|
|
117
|
+
|
|
118
|
+
const tileUnitAnchorPoint = new Point(symbol.anchorX, symbol.anchorY);
|
|
119
|
+
// projection.project generates NDC coordinates, as opposed to the
|
|
120
|
+
// pixel-based grid coordinates generated by this.projectPoint
|
|
121
|
+
const labelPlaneAnchorPoint = projection.project(tileUnitAnchorPoint, labelPlaneMatrix).point;
|
|
122
|
+
const firstAndLastGlyph = projection.placeFirstAndLastGlyph(
|
|
123
|
+
fontScale,
|
|
124
|
+
glyphOffsetArray,
|
|
125
|
+
lineOffsetX,
|
|
126
|
+
lineOffsetY,
|
|
127
|
+
/*flip*/ false,
|
|
128
|
+
labelPlaneAnchorPoint,
|
|
129
|
+
tileUnitAnchorPoint,
|
|
130
|
+
symbol,
|
|
131
|
+
lineVertexArray,
|
|
132
|
+
labelPlaneMatrix,
|
|
133
|
+
projectionCache,
|
|
134
|
+
/*return tile distance*/ true
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
let collisionDetected = false;
|
|
138
|
+
let entirelyOffscreen = true;
|
|
139
|
+
|
|
140
|
+
const tileToViewport = projectedAnchor.perspectiveRatio * textPixelRatio;
|
|
141
|
+
// pixelsToTileUnits is used for translating line geometry to tile units
|
|
142
|
+
// ... so we care about 'scale' but not 'perspectiveRatio'
|
|
143
|
+
// equivalent to pixel_to_tile_units
|
|
144
|
+
const pixelsToTileUnits = 1 / (textPixelRatio * scale);
|
|
145
|
+
|
|
146
|
+
let firstTileDistance = 0;
|
|
147
|
+
let lastTileDistance = 0;
|
|
148
|
+
if (firstAndLastGlyph) {
|
|
149
|
+
firstTileDistance = this.approximateTileDistance(
|
|
150
|
+
firstAndLastGlyph.first.tileDistance,
|
|
151
|
+
firstAndLastGlyph.first.angle,
|
|
152
|
+
pixelsToTileUnits,
|
|
153
|
+
projectedAnchor.cameraDistance,
|
|
154
|
+
pitchWithMap
|
|
155
|
+
);
|
|
156
|
+
lastTileDistance = this.approximateTileDistance(
|
|
157
|
+
firstAndLastGlyph.last.tileDistance,
|
|
158
|
+
firstAndLastGlyph.last.angle,
|
|
159
|
+
pixelsToTileUnits,
|
|
160
|
+
projectedAnchor.cameraDistance,
|
|
161
|
+
pitchWithMap
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (let k = 0; k < collisionCircles.length; k += 5) {
|
|
166
|
+
const anchorPointX = collisionCircles[k];
|
|
167
|
+
const anchorPointY = collisionCircles[k + 1];
|
|
168
|
+
const tileUnitRadius = collisionCircles[k + 2];
|
|
169
|
+
const boxSignedDistanceFromAnchor = collisionCircles[k + 3];
|
|
170
|
+
if (
|
|
171
|
+
!firstAndLastGlyph ||
|
|
172
|
+
boxSignedDistanceFromAnchor < -firstTileDistance ||
|
|
173
|
+
boxSignedDistanceFromAnchor > lastTileDistance
|
|
174
|
+
) {
|
|
175
|
+
// The label either doesn't fit on its line or we
|
|
176
|
+
// don't need to use this circle because the label
|
|
177
|
+
// doesn't extend this far. Either way, mark the circle unused.
|
|
178
|
+
markCollisionCircleUsed(collisionCircles, k, false);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const projectedPoint = this.projectPoint(posMatrix, anchorPointX, anchorPointY);
|
|
183
|
+
const radius = tileUnitRadius * tileToViewport;
|
|
184
|
+
|
|
185
|
+
const atLeastOneCirclePlaced = placedCollisionCircles.length > 0;
|
|
186
|
+
if (atLeastOneCirclePlaced) {
|
|
187
|
+
const dx = projectedPoint.x - placedCollisionCircles[placedCollisionCircles.length - 4];
|
|
188
|
+
const dy = projectedPoint.y - placedCollisionCircles[placedCollisionCircles.length - 3];
|
|
189
|
+
// The circle edges touch when the distance between their centers is 2x the radius
|
|
190
|
+
// When the distance is 1x the radius, they're doubled up, and we could remove
|
|
191
|
+
// every other circle while keeping them all in touch.
|
|
192
|
+
// We actually start removing circles when the distance is √2x the radius:
|
|
193
|
+
// thinning the number of circles as much as possible is a major performance win,
|
|
194
|
+
// and the small gaps introduced don't make a very noticeable difference.
|
|
195
|
+
const placedTooDensely = radius * radius * 2 > dx * dx + dy * dy;
|
|
196
|
+
if (placedTooDensely) {
|
|
197
|
+
const atLeastOneMoreCircle = k + 8 < collisionCircles.length;
|
|
198
|
+
if (atLeastOneMoreCircle) {
|
|
199
|
+
const nextBoxDistanceToAnchor = collisionCircles[k + 8];
|
|
200
|
+
if (nextBoxDistanceToAnchor > -firstTileDistance && nextBoxDistanceToAnchor < lastTileDistance) {
|
|
201
|
+
// Hide significantly overlapping circles, unless this is the last one we can
|
|
202
|
+
// use, in which case we want to keep it in place even if it's tightly packed
|
|
203
|
+
// with the one before it.
|
|
204
|
+
markCollisionCircleUsed(collisionCircles, k, false);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const collisionBoxArrayIndex = k / 5;
|
|
211
|
+
placedCollisionCircles.push(projectedPoint.x, projectedPoint.y, radius, collisionBoxArrayIndex);
|
|
212
|
+
markCollisionCircleUsed(collisionCircles, k, true);
|
|
213
|
+
|
|
214
|
+
entirelyOffscreen =
|
|
215
|
+
entirelyOffscreen &&
|
|
216
|
+
this.isOffscreen(
|
|
217
|
+
projectedPoint.x - radius,
|
|
218
|
+
projectedPoint.y - radius,
|
|
219
|
+
projectedPoint.x + radius,
|
|
220
|
+
projectedPoint.y + radius
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
if (!allowOverlap) {
|
|
224
|
+
if (this.grid.hitTestCircle(projectedPoint.x, projectedPoint.y, radius, collisionGroupPredicate)) {
|
|
225
|
+
if (!showCollisionCircles) {
|
|
226
|
+
return {
|
|
227
|
+
circles: [],
|
|
228
|
+
offscreen: false
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
// Don't early exit if we're showing the debug circles because we still want to calculate
|
|
232
|
+
// which circles are in use
|
|
233
|
+
collisionDetected = true;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
circles: collisionDetected ? [] : placedCollisionCircles,
|
|
240
|
+
offscreen: entirelyOffscreen
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Because the geometries in the CollisionIndex are an approximation of the shape of
|
|
246
|
+
* symbols on the map, we use the CollisionIndex to look up the symbol part of
|
|
247
|
+
* `queryRenderedFeatures`.
|
|
248
|
+
*
|
|
249
|
+
* @private
|
|
250
|
+
*/
|
|
251
|
+
queryRenderedSymbols(viewportQueryGeometry) {
|
|
252
|
+
if (viewportQueryGeometry.length === 0 || (this.grid.keysLength() === 0 && this.ignoredGrid.keysLength() === 0)) {
|
|
253
|
+
return {};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const query = [];
|
|
257
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
258
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
259
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
260
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
261
|
+
for (const point of viewportQueryGeometry) {
|
|
262
|
+
const gridPoint = new Point(point.x + viewportPadding, point.y + viewportPadding);
|
|
263
|
+
minX = Math.min(minX, gridPoint.x);
|
|
264
|
+
minY = Math.min(minY, gridPoint.y);
|
|
265
|
+
maxX = Math.max(maxX, gridPoint.x);
|
|
266
|
+
maxY = Math.max(maxY, gridPoint.y);
|
|
267
|
+
query.push(gridPoint);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const features = this.grid.query(minX, minY, maxX, maxY).concat(this.ignoredGrid.query(minX, minY, maxX, maxY));
|
|
271
|
+
|
|
272
|
+
const seenFeatures = {};
|
|
273
|
+
const result = {};
|
|
274
|
+
|
|
275
|
+
for (const feature of features) {
|
|
276
|
+
const featureKey = feature.key;
|
|
277
|
+
// Skip already seen features.
|
|
278
|
+
if (seenFeatures[featureKey.bucketInstanceId] === undefined) {
|
|
279
|
+
seenFeatures[featureKey.bucketInstanceId] = {};
|
|
280
|
+
}
|
|
281
|
+
if (seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex]) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Check if query intersects with the feature box
|
|
286
|
+
// "Collision Circles" for line labels are treated as boxes here
|
|
287
|
+
// Since there's no actual collision taking place, the circle vs. square
|
|
288
|
+
// distinction doesn't matter as much, and box geometry is easier
|
|
289
|
+
// to work with.
|
|
290
|
+
const bbox = [
|
|
291
|
+
new Point(feature.x1, feature.y1),
|
|
292
|
+
new Point(feature.x2, feature.y1),
|
|
293
|
+
new Point(feature.x2, feature.y2),
|
|
294
|
+
new Point(feature.x1, feature.y2)
|
|
295
|
+
];
|
|
296
|
+
if (!intersectionTests.polygonIntersectsPolygon(query, bbox)) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
seenFeatures[featureKey.bucketInstanceId][featureKey.featureIndex] = true;
|
|
301
|
+
if (result[featureKey.bucketInstanceId] === undefined) {
|
|
302
|
+
result[featureKey.bucketInstanceId] = [];
|
|
303
|
+
}
|
|
304
|
+
result[featureKey.bucketInstanceId].push(featureKey.featureIndex);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
insertCollisionBox(collisionBox, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
|
|
311
|
+
const grid = ignorePlacement ? this.ignoredGrid : this.grid;
|
|
312
|
+
|
|
313
|
+
const key = {
|
|
314
|
+
bucketInstanceId: bucketInstanceId,
|
|
315
|
+
featureIndex: featureIndex,
|
|
316
|
+
collisionGroupID: collisionGroupID
|
|
317
|
+
};
|
|
318
|
+
grid.insert(key, collisionBox[0], collisionBox[1], collisionBox[2], collisionBox[3]);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
insertCollisionCircles(collisionCircles, ignorePlacement, bucketInstanceId, featureIndex, collisionGroupID) {
|
|
322
|
+
const grid = ignorePlacement ? this.ignoredGrid : this.grid;
|
|
323
|
+
|
|
324
|
+
const key = {
|
|
325
|
+
bucketInstanceId: bucketInstanceId,
|
|
326
|
+
featureIndex: featureIndex,
|
|
327
|
+
collisionGroupID: collisionGroupID
|
|
328
|
+
};
|
|
329
|
+
for (let k = 0; k < collisionCircles.length; k += 4) {
|
|
330
|
+
grid.insertCircle(key, collisionCircles[k], collisionCircles[k + 1], collisionCircles[k + 2]);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
projectAnchor(posMatrix, x, y) {
|
|
335
|
+
const p = [x, y, 0, 1];
|
|
336
|
+
projection.xyTransformMat4(p, p, posMatrix);
|
|
337
|
+
return {
|
|
338
|
+
perspectiveRatio: 0.5 + 0.5 * (this.transform.cameraToCenterDistance / p[3]),
|
|
339
|
+
cameraDistance: p[3]
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
projectPoint(posMatrix, x, y) {
|
|
344
|
+
const p = [x, y, 0, 1];
|
|
345
|
+
projection.xyTransformMat4(p, p, posMatrix);
|
|
346
|
+
return new Point(
|
|
347
|
+
((p[0] / p[3] + 1) / 2) * this.transform.width + viewportPadding,
|
|
348
|
+
((-p[1] / p[3] + 1) / 2) * this.transform.height + viewportPadding
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
projectAndGetPerspectiveRatio(posMatrix, x, y) {
|
|
353
|
+
const p = [x, y, 0, 1];
|
|
354
|
+
projection.xyTransformMat4(p, p, posMatrix);
|
|
355
|
+
const a = new Point(
|
|
356
|
+
((p[0] / p[3] + 1) / 2) * this.transform.width + viewportPadding,
|
|
357
|
+
((-p[1] / p[3] + 1) / 2) * this.transform.height + viewportPadding
|
|
358
|
+
);
|
|
359
|
+
return {
|
|
360
|
+
point: a,
|
|
361
|
+
// See perspective ratio comment in symbol_sdf.vertex
|
|
362
|
+
// We're doing collision detection in viewport space so we need
|
|
363
|
+
// to scale down boxes in the distance
|
|
364
|
+
perspectiveRatio: 0.5 + 0.5 * (this.transform.cameraToCenterDistance / p[3])
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
isOffscreen(x1, y1, x2, y2) {
|
|
369
|
+
return (
|
|
370
|
+
x2 < viewportPadding || x1 >= this.screenRightBoundary || y2 < viewportPadding || y1 > this.screenBottomBoundary
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function markCollisionCircleUsed(collisionCircles, index, used) {
|
|
376
|
+
collisionCircles[index + 4] = used ? 1 : 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
module.exports = CollisionIndex;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
const EXTENT = require('../data/extent');
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
The CrossTileSymbolIndex generally works on the assumption that
|
|
5
|
+
a conceptual "unique symbol" can be identified by the text of
|
|
6
|
+
the label combined with the anchor point. The goal is to assign
|
|
7
|
+
these conceptual "unique symbols" a shared crossTileID that can be
|
|
8
|
+
used by Placement to keep fading opacity states consistent and to
|
|
9
|
+
deduplicate labels.
|
|
10
|
+
|
|
11
|
+
The CrossTileSymbolIndex indexes all the current symbol instances and
|
|
12
|
+
their crossTileIDs. When a symbol bucket gets added or updated, the
|
|
13
|
+
index assigns a crossTileID to each of it's symbol instances by either
|
|
14
|
+
matching it with an existing id or assigning a new one.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Round anchor positions to roughly 4 pixel grid
|
|
18
|
+
const roundingFactor = 512 / EXTENT / 2;
|
|
19
|
+
|
|
20
|
+
class TileLayerIndex {
|
|
21
|
+
constructor(tileID, symbolInstances, bucketInstanceId) {
|
|
22
|
+
this.tileID = tileID;
|
|
23
|
+
this.indexedSymbolInstances = {};
|
|
24
|
+
this.bucketInstanceId = bucketInstanceId;
|
|
25
|
+
|
|
26
|
+
for (const symbolInstance of symbolInstances) {
|
|
27
|
+
const key = symbolInstance.key;
|
|
28
|
+
if (!this.indexedSymbolInstances[key]) {
|
|
29
|
+
this.indexedSymbolInstances[key] = [];
|
|
30
|
+
}
|
|
31
|
+
// This tile may have multiple symbol instances with the same key
|
|
32
|
+
// Store each one along with its coordinates
|
|
33
|
+
this.indexedSymbolInstances[key].push({
|
|
34
|
+
crossTileID: symbolInstance.crossTileID,
|
|
35
|
+
coord: this.getScaledCoordinates(symbolInstance, tileID)
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Converts the coordinates of the input symbol instance into coordinates that be can compared
|
|
41
|
+
// against other symbols in this index. Coordinates are:
|
|
42
|
+
// (1) world-based (so after conversion the source tile is irrelevant)
|
|
43
|
+
// (2) converted to the z-scale of this TileLayerIndex
|
|
44
|
+
// (3) down-sampled by "roundingFactor" from tile coordinate precision in order to be
|
|
45
|
+
// more tolerant of small differences between tiles.
|
|
46
|
+
getScaledCoordinates(symbolInstance, childTileID) {
|
|
47
|
+
const zDifference = childTileID.canonical.z - this.tileID.canonical.z;
|
|
48
|
+
const scale = roundingFactor / 2 ** zDifference;
|
|
49
|
+
const anchor = symbolInstance.anchor;
|
|
50
|
+
return {
|
|
51
|
+
x: Math.floor((childTileID.canonical.x * EXTENT + anchor.x) * scale),
|
|
52
|
+
y: Math.floor((childTileID.canonical.y * EXTENT + anchor.y) * scale)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
findMatches(symbolInstances, newTileID, zoomCrossTileIDs) {
|
|
57
|
+
const tolerance =
|
|
58
|
+
this.tileID.canonical.z < newTileID.canonical.z ? 1 : 2 ** (this.tileID.canonical.z - newTileID.canonical.z);
|
|
59
|
+
|
|
60
|
+
for (const symbolInstance of symbolInstances) {
|
|
61
|
+
if (symbolInstance.crossTileID) {
|
|
62
|
+
// already has a match, skip
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const indexedInstances = this.indexedSymbolInstances[symbolInstance.key];
|
|
67
|
+
if (!indexedInstances) {
|
|
68
|
+
// No symbol with this key in this bucket
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const scaledSymbolCoord = this.getScaledCoordinates(symbolInstance, newTileID);
|
|
73
|
+
|
|
74
|
+
for (const thisTileSymbol of indexedInstances) {
|
|
75
|
+
// Return any symbol with the same keys whose coordinates are within 1
|
|
76
|
+
// grid unit. (with a 4px grid, this covers a 12px by 12px area)
|
|
77
|
+
if (
|
|
78
|
+
Math.abs(thisTileSymbol.coord.x - scaledSymbolCoord.x) <= tolerance &&
|
|
79
|
+
Math.abs(thisTileSymbol.coord.y - scaledSymbolCoord.y) <= tolerance &&
|
|
80
|
+
!zoomCrossTileIDs[thisTileSymbol.crossTileID]
|
|
81
|
+
) {
|
|
82
|
+
// Once we've marked ourselves duplicate against this parent symbol,
|
|
83
|
+
// don't let any other symbols at the same zoom level duplicate against
|
|
84
|
+
// the same parent (see issue #5993)
|
|
85
|
+
zoomCrossTileIDs[thisTileSymbol.crossTileID] = true;
|
|
86
|
+
symbolInstance.crossTileID = thisTileSymbol.crossTileID;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class CrossTileIDs {
|
|
95
|
+
constructor() {
|
|
96
|
+
this.maxCrossTileID = 0;
|
|
97
|
+
}
|
|
98
|
+
generate() {
|
|
99
|
+
return ++this.maxCrossTileID;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class CrossTileSymbolLayerIndex {
|
|
104
|
+
constructor() {
|
|
105
|
+
this.indexes = {};
|
|
106
|
+
this.usedCrossTileIDs = {};
|
|
107
|
+
this.lng = 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
* Sometimes when a user pans across the antimeridian the longitude value gets wrapped.
|
|
112
|
+
* To prevent labels from flashing out and in we adjust the tileID values in the indexes
|
|
113
|
+
* so that they match the new wrapped version of the map.
|
|
114
|
+
*/
|
|
115
|
+
handleWrapJump(lng) {
|
|
116
|
+
const wrapDelta = Math.round((lng - this.lng) / 360);
|
|
117
|
+
if (wrapDelta !== 0) {
|
|
118
|
+
for (const zoom in this.indexes) {
|
|
119
|
+
const zoomIndexes = this.indexes[zoom];
|
|
120
|
+
const newZoomIndex = {};
|
|
121
|
+
for (const key in zoomIndexes) {
|
|
122
|
+
// change the tileID's wrap and add it to a new index
|
|
123
|
+
const index = zoomIndexes[key];
|
|
124
|
+
index.tileID = index.tileID.unwrapTo(index.tileID.wrap + wrapDelta);
|
|
125
|
+
newZoomIndex[index.tileID.key] = index;
|
|
126
|
+
}
|
|
127
|
+
this.indexes[zoom] = newZoomIndex;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
this.lng = lng;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
addBucket(tileID, bucket, crossTileIDs) {
|
|
134
|
+
if (this.indexes[tileID.overscaledZ] && this.indexes[tileID.overscaledZ][tileID.key]) {
|
|
135
|
+
if (this.indexes[tileID.overscaledZ][tileID.key].bucketInstanceId === bucket.bucketInstanceId) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
// We're replacing this bucket with an updated version
|
|
139
|
+
// Remove the old bucket's "used crossTileIDs" now so that
|
|
140
|
+
// the new bucket can claim them.
|
|
141
|
+
// The old index entries themselves stick around until
|
|
142
|
+
// 'removeStaleBuckets' is called.
|
|
143
|
+
this.removeBucketCrossTileIDs(tileID.overscaledZ, this.indexes[tileID.overscaledZ][tileID.key]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (const symbolInstance of bucket.symbolInstances) {
|
|
147
|
+
symbolInstance.crossTileID = 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!this.usedCrossTileIDs[tileID.overscaledZ]) {
|
|
151
|
+
this.usedCrossTileIDs[tileID.overscaledZ] = {};
|
|
152
|
+
}
|
|
153
|
+
const zoomCrossTileIDs = this.usedCrossTileIDs[tileID.overscaledZ];
|
|
154
|
+
|
|
155
|
+
for (const zoom in this.indexes) {
|
|
156
|
+
const zoomIndexes = this.indexes[zoom];
|
|
157
|
+
if (Number(zoom) > tileID.overscaledZ) {
|
|
158
|
+
for (const id in zoomIndexes) {
|
|
159
|
+
const childIndex = zoomIndexes[id];
|
|
160
|
+
if (childIndex.tileID.isChildOf(tileID)) {
|
|
161
|
+
childIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
const parentCoord = tileID.scaledTo(Number(zoom));
|
|
166
|
+
const parentIndex = zoomIndexes[parentCoord.key];
|
|
167
|
+
if (parentIndex) {
|
|
168
|
+
parentIndex.findMatches(bucket.symbolInstances, tileID, zoomCrossTileIDs);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
for (const symbolInstance of bucket.symbolInstances) {
|
|
174
|
+
if (!symbolInstance.crossTileID) {
|
|
175
|
+
// symbol did not match any known symbol, assign a new id
|
|
176
|
+
symbolInstance.crossTileID = crossTileIDs.generate();
|
|
177
|
+
zoomCrossTileIDs[symbolInstance.crossTileID] = true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (this.indexes[tileID.overscaledZ] === undefined) {
|
|
182
|
+
this.indexes[tileID.overscaledZ] = {};
|
|
183
|
+
}
|
|
184
|
+
this.indexes[tileID.overscaledZ][tileID.key] = new TileLayerIndex(
|
|
185
|
+
tileID,
|
|
186
|
+
bucket.symbolInstances,
|
|
187
|
+
bucket.bucketInstanceId
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
removeBucketCrossTileIDs(zoom, removedBucket) {
|
|
194
|
+
for (const key in removedBucket.indexedSymbolInstances) {
|
|
195
|
+
for (const symbolInstance of removedBucket.indexedSymbolInstances[key]) {
|
|
196
|
+
delete this.usedCrossTileIDs[zoom][symbolInstance.crossTileID];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
removeStaleBuckets(currentIDs) {
|
|
202
|
+
let tilesChanged = false;
|
|
203
|
+
for (const z in this.indexes) {
|
|
204
|
+
const zoomIndexes = this.indexes[z];
|
|
205
|
+
for (const tileKey in zoomIndexes) {
|
|
206
|
+
if (!currentIDs[zoomIndexes[tileKey].bucketInstanceId]) {
|
|
207
|
+
this.removeBucketCrossTileIDs(z, zoomIndexes[tileKey]);
|
|
208
|
+
delete zoomIndexes[tileKey];
|
|
209
|
+
tilesChanged = true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return tilesChanged;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
class CrossTileSymbolIndex {
|
|
218
|
+
constructor() {
|
|
219
|
+
this.layerIndexes = {};
|
|
220
|
+
this.crossTileIDs = new CrossTileIDs();
|
|
221
|
+
this.maxBucketInstanceId = 0;
|
|
222
|
+
this.bucketsInCurrentPlacement = {};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
addLayer(styleLayer, tiles, lng) {
|
|
226
|
+
let layerIndex = this.layerIndexes[styleLayer.id];
|
|
227
|
+
if (layerIndex === undefined) {
|
|
228
|
+
layerIndex = this.layerIndexes[styleLayer.id] = new CrossTileSymbolLayerIndex();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
let symbolBucketsChanged = false;
|
|
232
|
+
const currentBucketIDs = {};
|
|
233
|
+
|
|
234
|
+
layerIndex.handleWrapJump(lng);
|
|
235
|
+
|
|
236
|
+
for (const tile of tiles) {
|
|
237
|
+
const symbolBucket = tile.getBucket(styleLayer);
|
|
238
|
+
if (!symbolBucket || styleLayer.id !== symbolBucket.layerIds[0]) continue;
|
|
239
|
+
|
|
240
|
+
if (!symbolBucket.bucketInstanceId) {
|
|
241
|
+
symbolBucket.bucketInstanceId = ++this.maxBucketInstanceId;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (layerIndex.addBucket(tile.tileID, symbolBucket, this.crossTileIDs)) {
|
|
245
|
+
symbolBucketsChanged = true;
|
|
246
|
+
}
|
|
247
|
+
currentBucketIDs[symbolBucket.bucketInstanceId] = true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (layerIndex.removeStaleBuckets(currentBucketIDs)) {
|
|
251
|
+
symbolBucketsChanged = true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return symbolBucketsChanged;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
pruneUnusedLayers(usedLayers) {
|
|
258
|
+
const usedLayerMap = {};
|
|
259
|
+
usedLayers.forEach(usedLayer => {
|
|
260
|
+
usedLayerMap[usedLayer] = true;
|
|
261
|
+
});
|
|
262
|
+
for (const layerId in this.layerIndexes) {
|
|
263
|
+
if (!usedLayerMap[layerId]) {
|
|
264
|
+
delete this.layerIndexes[layerId];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
module.exports = CrossTileSymbolIndex;
|