@scoova/mgl 1.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/LICENSE.txt +116 -0
- package/README.md +174 -0
- package/build/banner.ts +12 -0
- package/build/bump-version-changelog.js +26 -0
- package/build/check-bundle-size.js +77 -0
- package/build/generate-dist-package.js +26 -0
- package/build/generate-doc-images.ts +78 -0
- package/build/generate-docs.ts +190 -0
- package/build/generate-shaders.ts +68 -0
- package/build/generate-struct-arrays.ts +438 -0
- package/build/generate-style-code.ts +283 -0
- package/build/readme.md +56 -0
- package/build/release-notes.js +45 -0
- package/build/rollup/bundle_prelude.js +29 -0
- package/build/rollup/maplibregl.js +22 -0
- package/build/rollup_plugins.ts +58 -0
- package/dist/LICENSE.txt +116 -0
- package/dist/maplibre-gl.css +1 -0
- package/dist/maplibre-gl.d.ts +13304 -0
- package/dist/maplibre-gl.js +59 -0
- package/dist/maplibre-gl.js.map +1 -0
- package/dist/package.json +1 -0
- package/package.json +192 -0
- package/src/css/maplibre-gl.css +854 -0
- package/src/css/svg/maplibregl-ctrl-attrib.svg +3 -0
- package/src/css/svg/maplibregl-ctrl-compass.svg +4 -0
- package/src/css/svg/maplibregl-ctrl-fullscreen.svg +3 -0
- package/src/css/svg/maplibregl-ctrl-geolocate.svg +5 -0
- package/src/css/svg/maplibregl-ctrl-logo.svg +11 -0
- package/src/css/svg/maplibregl-ctrl-shrink.svg +3 -0
- package/src/css/svg/maplibregl-ctrl-terrain.svg +3 -0
- package/src/css/svg/maplibregl-ctrl-zoom-in.svg +3 -0
- package/src/css/svg/maplibregl-ctrl-zoom-out.svg +3 -0
- package/src/data/array_types.g.ts +1130 -0
- package/src/data/bucket/circle_attributes.ts +8 -0
- package/src/data/bucket/circle_bucket.ts +197 -0
- package/src/data/bucket/fill_attributes.ts +8 -0
- package/src/data/bucket/fill_bucket.test.ts +95 -0
- package/src/data/bucket/fill_bucket.ts +228 -0
- package/src/data/bucket/fill_extrusion_attributes.ts +13 -0
- package/src/data/bucket/fill_extrusion_bucket.ts +305 -0
- package/src/data/bucket/heatmap_bucket.ts +12 -0
- package/src/data/bucket/line_attributes.ts +8 -0
- package/src/data/bucket/line_attributes_ext.ts +8 -0
- package/src/data/bucket/line_bucket.test.ts +142 -0
- package/src/data/bucket/line_bucket.ts +592 -0
- package/src/data/bucket/pattern_attributes.ts +9 -0
- package/src/data/bucket/pattern_bucket_features.ts +57 -0
- package/src/data/bucket/symbol_attributes.ts +122 -0
- package/src/data/bucket/symbol_bucket.test.ts +243 -0
- package/src/data/bucket/symbol_bucket.ts +975 -0
- package/src/data/bucket.ts +123 -0
- package/src/data/dem_data.test.ts +237 -0
- package/src/data/dem_data.ts +171 -0
- package/src/data/evaluation_feature.ts +18 -0
- package/src/data/extent.ts +13 -0
- package/src/data/feature_index.ts +337 -0
- package/src/data/feature_position_map.test.ts +33 -0
- package/src/data/feature_position_map.ts +126 -0
- package/src/data/index_array_type.ts +9 -0
- package/src/data/load_geometry.test.ts +49 -0
- package/src/data/load_geometry.ts +44 -0
- package/src/data/pos3d_attributes.ts +5 -0
- package/src/data/pos_attributes.ts +5 -0
- package/src/data/program_configuration.ts +735 -0
- package/src/data/raster_bounds_attributes.ts +6 -0
- package/src/data/segment.ts +89 -0
- package/src/geo/edge_insets.test.ts +83 -0
- package/src/geo/edge_insets.ts +146 -0
- package/src/geo/lng_lat.test.ts +65 -0
- package/src/geo/lng_lat.ts +169 -0
- package/src/geo/lng_lat_bounds.test.ts +357 -0
- package/src/geo/lng_lat_bounds.ts +356 -0
- package/src/geo/mercator_coordinate.test.ts +34 -0
- package/src/geo/mercator_coordinate.ts +157 -0
- package/src/geo/projection/projection.ts +72 -0
- package/src/geo/transform.test.ts +509 -0
- package/src/geo/transform.ts +1072 -0
- package/src/gl/color_mode.ts +31 -0
- package/src/gl/context.ts +321 -0
- package/src/gl/cull_face_mode.ts +22 -0
- package/src/gl/depth_mode.ts +26 -0
- package/src/gl/framebuffer.ts +48 -0
- package/src/gl/index_buffer.ts +55 -0
- package/src/gl/render_pool.test.ts +69 -0
- package/src/gl/render_pool.ts +93 -0
- package/src/gl/state.test.ts +125 -0
- package/src/gl/stencil_mode.ts +27 -0
- package/src/gl/types.ts +59 -0
- package/src/gl/value.ts +534 -0
- package/src/gl/vertex_buffer.test.ts +57 -0
- package/src/gl/vertex_buffer.ts +111 -0
- package/src/gl/webgl2.ts +12 -0
- package/src/index.test.ts +121 -0
- package/src/index.ts +250 -0
- package/src/render/draw_background.ts +53 -0
- package/src/render/draw_circle.ts +113 -0
- package/src/render/draw_collision_debug.ts +168 -0
- package/src/render/draw_custom.test.ts +73 -0
- package/src/render/draw_custom.ts +45 -0
- package/src/render/draw_debug.test.ts +122 -0
- package/src/render/draw_debug.ts +148 -0
- package/src/render/draw_fill.test.ts +137 -0
- package/src/render/draw_fill.ts +128 -0
- package/src/render/draw_fill_extrusion.ts +97 -0
- package/src/render/draw_heatmap.ts +228 -0
- package/src/render/draw_hillshade.ts +119 -0
- package/src/render/draw_line.ts +125 -0
- package/src/render/draw_raster.ts +133 -0
- package/src/render/draw_sky.ts +44 -0
- package/src/render/draw_symbol.test.ts +223 -0
- package/src/render/draw_symbol.ts +499 -0
- package/src/render/draw_terrain.ts +99 -0
- package/src/render/glyph_atlas.ts +83 -0
- package/src/render/glyph_manager.test.ts +147 -0
- package/src/render/glyph_manager.ts +198 -0
- package/src/render/image_atlas.ts +159 -0
- package/src/render/image_manager.ts +337 -0
- package/src/render/line_atlas.test.ts +38 -0
- package/src/render/line_atlas.ts +214 -0
- package/src/render/mesh.ts +25 -0
- package/src/render/painter.test.ts +51 -0
- package/src/render/painter.ts +666 -0
- package/src/render/program/background_program.ts +100 -0
- package/src/render/program/circle_program.ts +61 -0
- package/src/render/program/clipping_mask_program.ts +19 -0
- package/src/render/program/collision_program.ts +47 -0
- package/src/render/program/debug_program.ts +29 -0
- package/src/render/program/fill_extrusion_program.ts +120 -0
- package/src/render/program/fill_program.ts +121 -0
- package/src/render/program/heatmap_program.ts +76 -0
- package/src/render/program/hillshade_program.ts +116 -0
- package/src/render/program/line_program.ts +207 -0
- package/src/render/program/pattern.ts +102 -0
- package/src/render/program/program_uniforms.ts +46 -0
- package/src/render/program/raster_program.ts +88 -0
- package/src/render/program/sky_program.ts +28 -0
- package/src/render/program/symbol_program.ts +266 -0
- package/src/render/program/terrain_program.ts +116 -0
- package/src/render/program.ts +233 -0
- package/src/render/render_to_texture.test.ts +151 -0
- package/src/render/render_to_texture.ts +197 -0
- package/src/render/terrain.test.ts +322 -0
- package/src/render/terrain.ts +464 -0
- package/src/render/texture.ts +114 -0
- package/src/render/uniform_binding.test.ts +122 -0
- package/src/render/uniform_binding.ts +157 -0
- package/src/render/update_pattern_positions_in_program.test.ts +74 -0
- package/src/render/update_pattern_positions_in_program.ts +50 -0
- package/src/render/vertex_array_object.ts +163 -0
- package/src/shaders/README.md +42 -0
- package/src/shaders/_prelude.fragment.glsl +19 -0
- package/src/shaders/_prelude.fragment.glsl.g.ts +2 -0
- package/src/shaders/_prelude.vertex.glsl +148 -0
- package/src/shaders/_prelude.vertex.glsl.g.ts +2 -0
- package/src/shaders/background.fragment.glsl +10 -0
- package/src/shaders/background.fragment.glsl.g.ts +2 -0
- package/src/shaders/background.vertex.glsl +7 -0
- package/src/shaders/background.vertex.glsl.g.ts +2 -0
- package/src/shaders/background_pattern.fragment.glsl +28 -0
- package/src/shaders/background_pattern.fragment.glsl.g.ts +2 -0
- package/src/shaders/background_pattern.vertex.glsl +19 -0
- package/src/shaders/background_pattern.vertex.glsl.g.ts +2 -0
- package/src/shaders/circle.fragment.glsl +34 -0
- package/src/shaders/circle.fragment.glsl.g.ts +2 -0
- package/src/shaders/circle.vertex.glsl +68 -0
- package/src/shaders/circle.vertex.glsl.g.ts +2 -0
- package/src/shaders/clipping_mask.fragment.glsl +3 -0
- package/src/shaders/clipping_mask.fragment.glsl.g.ts +2 -0
- package/src/shaders/clipping_mask.vertex.glsl +7 -0
- package/src/shaders/clipping_mask.vertex.glsl.g.ts +2 -0
- package/src/shaders/collision_box.fragment.glsl +20 -0
- package/src/shaders/collision_box.fragment.glsl.g.ts +2 -0
- package/src/shaders/collision_box.vertex.glsl +27 -0
- package/src/shaders/collision_box.vertex.glsl.g.ts +2 -0
- package/src/shaders/collision_circle.fragment.glsl +17 -0
- package/src/shaders/collision_circle.fragment.glsl.g.ts +2 -0
- package/src/shaders/collision_circle.vertex.glsl +59 -0
- package/src/shaders/collision_circle.vertex.glsl.g.ts +2 -0
- package/src/shaders/debug.fragment.glsl +9 -0
- package/src/shaders/debug.fragment.glsl.g.ts +2 -0
- package/src/shaders/debug.vertex.glsl +12 -0
- package/src/shaders/debug.vertex.glsl.g.ts +2 -0
- package/src/shaders/encode_attribute.test.ts +11 -0
- package/src/shaders/encode_attribute.ts +13 -0
- package/src/shaders/fill.fragment.glsl +13 -0
- package/src/shaders/fill.fragment.glsl.g.ts +2 -0
- package/src/shaders/fill.vertex.glsl +13 -0
- package/src/shaders/fill.vertex.glsl.g.ts +2 -0
- package/src/shaders/fill_extrusion.fragment.glsl +9 -0
- package/src/shaders/fill_extrusion.fragment.glsl.g.ts +2 -0
- package/src/shaders/fill_extrusion.vertex.glsl +84 -0
- package/src/shaders/fill_extrusion.vertex.glsl.g.ts +2 -0
- package/src/shaders/fill_extrusion_pattern.fragment.glsl +47 -0
- package/src/shaders/fill_extrusion_pattern.fragment.glsl.g.ts +2 -0
- package/src/shaders/fill_extrusion_pattern.vertex.glsl +96 -0
- package/src/shaders/fill_extrusion_pattern.vertex.glsl.g.ts +2 -0
- package/src/shaders/fill_outline.fragment.glsl +17 -0
- package/src/shaders/fill_outline.fragment.glsl.g.ts +2 -0
- package/src/shaders/fill_outline.vertex.glsl +17 -0
- package/src/shaders/fill_outline.vertex.glsl.g.ts +2 -0
- package/src/shaders/fill_outline_pattern.fragment.glsl +43 -0
- package/src/shaders/fill_outline_pattern.fragment.glsl.g.ts +2 -0
- package/src/shaders/fill_outline_pattern.vertex.glsl +44 -0
- package/src/shaders/fill_outline_pattern.vertex.glsl.g.ts +2 -0
- package/src/shaders/fill_pattern.fragment.glsl +39 -0
- package/src/shaders/fill_pattern.fragment.glsl.g.ts +2 -0
- package/src/shaders/fill_pattern.vertex.glsl +39 -0
- package/src/shaders/fill_pattern.vertex.glsl.g.ts +2 -0
- package/src/shaders/heatmap.fragment.glsl +22 -0
- package/src/shaders/heatmap.fragment.glsl.g.ts +2 -0
- package/src/shaders/heatmap.vertex.glsl +54 -0
- package/src/shaders/heatmap.vertex.glsl.g.ts +2 -0
- package/src/shaders/heatmap_texture.fragment.glsl +15 -0
- package/src/shaders/heatmap_texture.fragment.glsl.g.ts +2 -0
- package/src/shaders/heatmap_texture.vertex.glsl +11 -0
- package/src/shaders/heatmap_texture.vertex.glsl.g.ts +2 -0
- package/src/shaders/hillshade.fragment.glsl +52 -0
- package/src/shaders/hillshade.fragment.glsl.g.ts +2 -0
- package/src/shaders/hillshade.vertex.glsl +11 -0
- package/src/shaders/hillshade.vertex.glsl.g.ts +2 -0
- package/src/shaders/hillshade_prepare.fragment.glsl +77 -0
- package/src/shaders/hillshade_prepare.fragment.glsl.g.ts +2 -0
- package/src/shaders/hillshade_prepare.vertex.glsl +15 -0
- package/src/shaders/hillshade_prepare.vertex.glsl.g.ts +2 -0
- package/src/shaders/line.fragment.glsl +30 -0
- package/src/shaders/line.fragment.glsl.g.ts +2 -0
- package/src/shaders/line.vertex.glsl +89 -0
- package/src/shaders/line.vertex.glsl.g.ts +2 -0
- package/src/shaders/line_gradient.fragment.glsl +34 -0
- package/src/shaders/line_gradient.fragment.glsl.g.ts +2 -0
- package/src/shaders/line_gradient.vertex.glsl +92 -0
- package/src/shaders/line_gradient.vertex.glsl.g.ts +2 -0
- package/src/shaders/line_pattern.fragment.glsl +77 -0
- package/src/shaders/line_pattern.fragment.glsl.g.ts +2 -0
- package/src/shaders/line_pattern.vertex.glsl +103 -0
- package/src/shaders/line_pattern.vertex.glsl.g.ts +2 -0
- package/src/shaders/line_sdf.fragment.glsl +45 -0
- package/src/shaders/line_sdf.fragment.glsl.g.ts +2 -0
- package/src/shaders/line_sdf.vertex.glsl +101 -0
- package/src/shaders/line_sdf.vertex.glsl.g.ts +2 -0
- package/src/shaders/raster.fragment.glsl +53 -0
- package/src/shaders/raster.fragment.glsl.g.ts +2 -0
- package/src/shaders/raster.vertex.glsl +21 -0
- package/src/shaders/raster.vertex.glsl.g.ts +2 -0
- package/src/shaders/shaders.ts +197 -0
- package/src/shaders/sky.fragment.glsl +17 -0
- package/src/shaders/sky.fragment.glsl.g.ts +2 -0
- package/src/shaders/sky.vertex.glsl +5 -0
- package/src/shaders/sky.vertex.glsl.g.ts +2 -0
- package/src/shaders/symbol_icon.fragment.glsl +17 -0
- package/src/shaders/symbol_icon.fragment.glsl.g.ts +2 -0
- package/src/shaders/symbol_icon.vertex.glsl +117 -0
- package/src/shaders/symbol_icon.vertex.glsl.g.ts +2 -0
- package/src/shaders/symbol_sdf.fragment.glsl +58 -0
- package/src/shaders/symbol_sdf.fragment.glsl.g.ts +2 -0
- package/src/shaders/symbol_sdf.vertex.glsl +142 -0
- package/src/shaders/symbol_sdf.vertex.glsl.g.ts +2 -0
- package/src/shaders/symbol_text_and_icon.fragment.glsl +68 -0
- package/src/shaders/symbol_text_and_icon.fragment.glsl.g.ts +2 -0
- package/src/shaders/symbol_text_and_icon.vertex.glsl +140 -0
- package/src/shaders/symbol_text_and_icon.vertex.glsl.g.ts +2 -0
- package/src/shaders/terrain.fragment.glsl +32 -0
- package/src/shaders/terrain.fragment.glsl.g.ts +2 -0
- package/src/shaders/terrain.vertex.glsl +17 -0
- package/src/shaders/terrain.vertex.glsl.g.ts +2 -0
- package/src/shaders/terrain_coords.fragment.glsl +11 -0
- package/src/shaders/terrain_coords.fragment.glsl.g.ts +2 -0
- package/src/shaders/terrain_coords.vertex.glsl +13 -0
- package/src/shaders/terrain_coords.vertex.glsl.g.ts +2 -0
- package/src/shaders/terrain_depth.fragment.glsl +15 -0
- package/src/shaders/terrain_depth.fragment.glsl.g.ts +2 -0
- package/src/shaders/terrain_depth.vertex.glsl +13 -0
- package/src/shaders/terrain_depth.vertex.glsl.g.ts +2 -0
- package/src/source/canvas_source.test.ts +210 -0
- package/src/source/canvas_source.ts +227 -0
- package/src/source/geojson_source.test.ts +492 -0
- package/src/source/geojson_source.ts +431 -0
- package/src/source/geojson_source_diff.test.ts +364 -0
- package/src/source/geojson_source_diff.ts +172 -0
- package/src/source/geojson_worker_source.test.ts +399 -0
- package/src/source/geojson_worker_source.ts +303 -0
- package/src/source/geojson_wrapper.test.ts +32 -0
- package/src/source/geojson_wrapper.ts +81 -0
- package/src/source/image_source.test.ts +235 -0
- package/src/source/image_source.ts +338 -0
- package/src/source/load_tilejson.ts +47 -0
- package/src/source/pixels_to_tile_units.ts +25 -0
- package/src/source/protocol_crud.ts +48 -0
- package/src/source/query_features.test.ts +31 -0
- package/src/source/query_features.ts +252 -0
- package/src/source/raster_dem_tile_source.test.ts +158 -0
- package/src/source/raster_dem_tile_source.ts +166 -0
- package/src/source/raster_dem_tile_worker_source.test.ts +36 -0
- package/src/source/raster_dem_tile_worker_source.ts +38 -0
- package/src/source/raster_tile_source.test.ts +206 -0
- package/src/source/raster_tile_source.ts +227 -0
- package/src/source/rtl_text_plugin_main_thread.test.ts +170 -0
- package/src/source/rtl_text_plugin_main_thread.ts +89 -0
- package/src/source/rtl_text_plugin_status.ts +33 -0
- package/src/source/rtl_text_plugin_worker.ts +49 -0
- package/src/source/source.test.ts +41 -0
- package/src/source/source.ts +186 -0
- package/src/source/source_cache.test.ts +2069 -0
- package/src/source/source_cache.ts +1102 -0
- package/src/source/source_state.ts +157 -0
- package/src/source/terrain_source_cache.test.ts +105 -0
- package/src/source/terrain_source_cache.ts +204 -0
- package/src/source/tile.test.ts +290 -0
- package/src/source/tile.ts +478 -0
- package/src/source/tile_bounds.ts +34 -0
- package/src/source/tile_cache.test.ts +130 -0
- package/src/source/tile_cache.ts +208 -0
- package/src/source/tile_id.test.ts +112 -0
- package/src/source/tile_id.ts +221 -0
- package/src/source/vector_tile_source.test.ts +401 -0
- package/src/source/vector_tile_source.ts +283 -0
- package/src/source/vector_tile_worker_source.test.ts +396 -0
- package/src/source/vector_tile_worker_source.ts +199 -0
- package/src/source/video_source.test.ts +124 -0
- package/src/source/video_source.ts +204 -0
- package/src/source/worker.test.ts +233 -0
- package/src/source/worker.ts +301 -0
- package/src/source/worker_source.ts +117 -0
- package/src/source/worker_tile.test.ts +226 -0
- package/src/source/worker_tile.ts +208 -0
- package/src/style/create_style_layer.ts +39 -0
- package/src/style/evaluation_parameters.ts +62 -0
- package/src/style/format_section_override.test.ts +62 -0
- package/src/style/format_section_override.ts +50 -0
- package/src/style/light.test.ts +86 -0
- package/src/style/light.ts +135 -0
- package/src/style/load_glyph_range.test.ts +40 -0
- package/src/style/load_glyph_range.ts +32 -0
- package/src/style/load_sprite.test.ts +227 -0
- package/src/style/load_sprite.ts +71 -0
- package/src/style/parse_glyph_pbf.ts +42 -0
- package/src/style/pauseable_placement.ts +138 -0
- package/src/style/properties.ts +726 -0
- package/src/style/query_utils.test.ts +107 -0
- package/src/style/query_utils.ts +70 -0
- package/src/style/sky.ts +127 -0
- package/src/style/style.test.ts +2654 -0
- package/src/style/style.ts +1814 -0
- package/src/style/style_glyph.ts +25 -0
- package/src/style/style_image.ts +188 -0
- package/src/style/style_layer/background_style_layer.ts +17 -0
- package/src/style/style_layer/background_style_layer_properties.g.ts +40 -0
- package/src/style/style_layer/circle_style_layer.ts +98 -0
- package/src/style/style_layer/circle_style_layer_properties.g.ts +76 -0
- package/src/style/style_layer/custom_style_layer.ts +233 -0
- package/src/style/style_layer/fill_extrusion_style_layer.ts +224 -0
- package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +55 -0
- package/src/style/style_layer/fill_style_layer.test.ts +37 -0
- package/src/style/style_layer/fill_style_layer.ts +65 -0
- package/src/style/style_layer/fill_style_layer_properties.g.ts +64 -0
- package/src/style/style_layer/heatmap_style_layer.ts +74 -0
- package/src/style/style_layer/heatmap_style_layer_properties.g.ts +46 -0
- package/src/style/style_layer/hillshade_style_layer.ts +21 -0
- package/src/style/style_layer/hillshade_style_layer_properties.g.ts +49 -0
- package/src/style/style_layer/line_style_layer.test.ts +50 -0
- package/src/style/style_layer/line_style_layer.ts +131 -0
- package/src/style/style_layer/line_style_layer_properties.g.ts +88 -0
- package/src/style/style_layer/overlap_mode.test.ts +57 -0
- package/src/style/style_layer/overlap_mode.ts +25 -0
- package/src/style/style_layer/raster_style_layer.ts +17 -0
- package/src/style/style_layer/raster_style_layer_properties.g.ts +55 -0
- package/src/style/style_layer/symbol_style_layer.ts +195 -0
- package/src/style/style_layer/symbol_style_layer_properties.g.ts +218 -0
- package/src/style/style_layer/typed_style_layer.ts +9 -0
- 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/style/style_layer.test.ts +372 -0
- package/src/style/style_layer.ts +287 -0
- package/src/style/style_layer_index.test.ts +99 -0
- package/src/style/style_layer_index.ts +78 -0
- package/src/style/validate_style.ts +53 -0
- package/src/style/zoom_history.ts +40 -0
- package/src/symbol/anchor.test.ts +14 -0
- package/src/symbol/anchor.ts +22 -0
- package/src/symbol/check_max_angle.test.ts +54 -0
- package/src/symbol/check_max_angle.ts +76 -0
- package/src/symbol/clip_line.test.ts +154 -0
- package/src/symbol/clip_line.ts +66 -0
- package/src/symbol/collision_feature.test.ts +98 -0
- package/src/symbol/collision_feature.ts +114 -0
- package/src/symbol/collision_index.test.ts +19 -0
- package/src/symbol/collision_index.ts +618 -0
- package/src/symbol/cross_tile_symbol_index.test.ts +260 -0
- package/src/symbol/cross_tile_symbol_index.ts +367 -0
- package/src/symbol/get_anchors.test.ts +113 -0
- package/src/symbol/get_anchors.ts +167 -0
- package/src/symbol/grid_index.test.ts +75 -0
- package/src/symbol/grid_index.ts +414 -0
- package/src/symbol/merge_lines.test.ts +30 -0
- package/src/symbol/merge_lines.ts +80 -0
- package/src/symbol/one_em.ts +3 -0
- package/src/symbol/opacity_state.ts +23 -0
- package/src/symbol/path_interpolator.test.ts +134 -0
- package/src/symbol/path_interpolator.ts +55 -0
- package/src/symbol/placement.ts +1371 -0
- package/src/symbol/projection.test.ts +171 -0
- package/src/symbol/projection.ts +833 -0
- package/src/symbol/quads.test.ts +157 -0
- package/src/symbol/quads.ts +349 -0
- package/src/symbol/shaping.test.ts +360 -0
- package/src/symbol/shaping.ts +911 -0
- package/src/symbol/symbol_layout.ts +739 -0
- package/src/symbol/symbol_size.ts +129 -0
- package/src/symbol/symbol_style_layer.test.ts +103 -0
- package/src/symbol/transform_text.ts +27 -0
- package/src/ui/anchor.ts +27 -0
- package/src/ui/camera.test.ts +2301 -0
- package/src/ui/camera.ts +1520 -0
- package/src/ui/control/attribution_control.test.ts +543 -0
- package/src/ui/control/attribution_control.ts +209 -0
- package/src/ui/control/control.ts +67 -0
- package/src/ui/control/fullscreen_control.test.ts +114 -0
- package/src/ui/control/fullscreen_control.ts +189 -0
- package/src/ui/control/geolocate_control.test.ts +619 -0
- package/src/ui/control/geolocate_control.ts +725 -0
- package/src/ui/control/logo_control.test.ts +88 -0
- package/src/ui/control/logo_control.ts +86 -0
- package/src/ui/control/navigation_control.test.ts +238 -0
- package/src/ui/control/navigation_control.ts +294 -0
- package/src/ui/control/scale_control.test.ts +52 -0
- package/src/ui/control/scale_control.ts +152 -0
- package/src/ui/control/terrain_control.test.ts +58 -0
- package/src/ui/control/terrain_control.ts +74 -0
- package/src/ui/default_locale.ts +25 -0
- package/src/ui/events.ts +758 -0
- package/src/ui/handler/box_zoom.test.ts +163 -0
- package/src/ui/handler/box_zoom.ts +171 -0
- package/src/ui/handler/click_zoom.ts +55 -0
- package/src/ui/handler/cooperative_gestures.test.ts +338 -0
- package/src/ui/handler/cooperative_gestures.ts +111 -0
- package/src/ui/handler/dblclick_zoom.test.ts +151 -0
- package/src/ui/handler/drag_handler.ts +174 -0
- package/src/ui/handler/drag_move_state_manager.ts +115 -0
- package/src/ui/handler/drag_pan.test.ts +487 -0
- package/src/ui/handler/drag_rotate.test.ts +859 -0
- package/src/ui/handler/handler_util.ts +10 -0
- package/src/ui/handler/keyboard.test.ts +235 -0
- package/src/ui/handler/keyboard.ts +212 -0
- package/src/ui/handler/map_event.test.ts +158 -0
- package/src/ui/handler/map_event.ts +161 -0
- package/src/ui/handler/mouse.ts +92 -0
- package/src/ui/handler/mouse_handler_interface.test.ts +111 -0
- package/src/ui/handler/mouse_rotate.test.ts +62 -0
- package/src/ui/handler/one_finger_touch_drag.ts +45 -0
- package/src/ui/handler/one_finger_touch_drag_handler_interface.test.ts +77 -0
- package/src/ui/handler/scroll_zoom.test.ts +382 -0
- package/src/ui/handler/scroll_zoom.ts +379 -0
- package/src/ui/handler/shim/dblclick_zoom.ts +64 -0
- package/src/ui/handler/shim/drag_pan.ts +104 -0
- package/src/ui/handler/shim/drag_rotate.ts +76 -0
- package/src/ui/handler/shim/two_fingers_touch.ts +112 -0
- package/src/ui/handler/tap_drag_zoom.test.ts +113 -0
- package/src/ui/handler/tap_drag_zoom.ts +112 -0
- package/src/ui/handler/tap_recognizer.ts +138 -0
- package/src/ui/handler/tap_zoom.ts +98 -0
- package/src/ui/handler/touch_pan.ts +115 -0
- package/src/ui/handler/transform-provider.ts +44 -0
- package/src/ui/handler/two_fingers_touch.test.ts +283 -0
- package/src/ui/handler/two_fingers_touch.ts +336 -0
- package/src/ui/handler_inertia.ts +157 -0
- package/src/ui/handler_manager.ts +637 -0
- package/src/ui/hash.test.ts +404 -0
- package/src/ui/hash.ts +153 -0
- package/src/ui/map.ts +3393 -0
- package/src/ui/map_tests/map_animation.test.ts +61 -0
- package/src/ui/map_tests/map_basic.test.ts +223 -0
- package/src/ui/map_tests/map_bounds.test.ts +123 -0
- package/src/ui/map_tests/map_calculate_camera_options.test.ts +113 -0
- package/src/ui/map_tests/map_canvas.test.ts +59 -0
- package/src/ui/map_tests/map_control.test.ts +61 -0
- package/src/ui/map_tests/map_disable_handlers.test.ts +38 -0
- package/src/ui/map_tests/map_events.test.ts +1001 -0
- package/src/ui/map_tests/map_feature_state.test.ts +421 -0
- package/src/ui/map_tests/map_images.test.ts +175 -0
- package/src/ui/map_tests/map_is_moving.test.ts +164 -0
- package/src/ui/map_tests/map_is_rotating.test.ts +62 -0
- package/src/ui/map_tests/map_is_zooming.test.ts +86 -0
- package/src/ui/map_tests/map_layer.test.ts +457 -0
- package/src/ui/map_tests/map_options.test.ts +64 -0
- package/src/ui/map_tests/map_pitch.test.ts +90 -0
- package/src/ui/map_tests/map_pixel_ratio.test.ts +75 -0
- package/src/ui/map_tests/map_query_rendered_features.test.ts +93 -0
- package/src/ui/map_tests/map_render.test.ts +90 -0
- package/src/ui/map_tests/map_request_render_frame.test.ts +51 -0
- package/src/ui/map_tests/map_resize.test.ts +120 -0
- package/src/ui/map_tests/map_style.test.ts +541 -0
- package/src/ui/map_tests/map_terrian.test.ts +104 -0
- package/src/ui/map_tests/map_webgl.test.ts +72 -0
- package/src/ui/map_tests/map_world_copies.test.ts +103 -0
- package/src/ui/map_tests/map_zoom.test.ts +95 -0
- package/src/ui/marker.test.ts +1149 -0
- package/src/ui/marker.ts +880 -0
- package/src/ui/popup.test.ts +827 -0
- package/src/ui/popup.ts +717 -0
- package/src/util/abort_error.ts +21 -0
- package/src/util/actor.test.ts +218 -0
- package/src/util/actor.ts +241 -0
- package/src/util/actor_messages.ts +149 -0
- package/src/util/ajax.test.ts +237 -0
- package/src/util/ajax.ts +297 -0
- package/src/util/browser.test.ts +23 -0
- package/src/util/browser.ts +63 -0
- package/src/util/color_ramp.test.ts +105 -0
- package/src/util/color_ramp.ts +56 -0
- package/src/util/config.ts +29 -0
- package/src/util/dictionary_coder.ts +23 -0
- package/src/util/dispatcher.test.ts +76 -0
- package/src/util/dispatcher.ts +78 -0
- package/src/util/dom.ts +135 -0
- package/src/util/evented.test.ts +231 -0
- package/src/util/evented.ts +178 -0
- package/src/util/find_pole_of_inaccessibility.test.ts +21 -0
- package/src/util/find_pole_of_inaccessibility.ts +130 -0
- package/src/util/geolocation_support.test.ts +43 -0
- package/src/util/geolocation_support.ts +23 -0
- package/src/util/global_worker_pool.ts +65 -0
- package/src/util/image.ts +150 -0
- package/src/util/image_request.test.ts +408 -0
- package/src/util/image_request.ts +246 -0
- package/src/util/intersection_tests.ts +206 -0
- package/src/util/is_char_in_unicode_block.test.ts +15 -0
- package/src/util/is_char_in_unicode_block.ts +345 -0
- package/src/util/offscreen_canvas_distorted.test.ts +13 -0
- package/src/util/offscreen_canvas_distorted.ts +39 -0
- package/src/util/offscreen_canvas_supported.ts +11 -0
- package/src/util/performance.ts +117 -0
- package/src/util/primitives.test.ts +140 -0
- package/src/util/primitives.ts +143 -0
- package/src/util/request_manager.ts +42 -0
- package/src/util/resolve_tokens.test.ts +45 -0
- package/src/util/resolve_tokens.ts +17 -0
- package/src/util/script_detection.test.ts +138 -0
- package/src/util/script_detection.ts +376 -0
- package/src/util/smart_wrap.test.ts +97 -0
- package/src/util/smart_wrap.ts +58 -0
- package/src/util/struct_array.test.ts +101 -0
- package/src/util/struct_array.ts +246 -0
- package/src/util/style.test.ts +34 -0
- package/src/util/style.ts +28 -0
- package/src/util/task_queue.test.ts +114 -0
- package/src/util/task_queue.ts +64 -0
- package/src/util/test/util.ts +185 -0
- package/src/util/throttle.test.ts +42 -0
- package/src/util/throttle.ts +28 -0
- package/src/util/throttled_invoker.ts +41 -0
- package/src/util/transferable_grid_index.test.ts +56 -0
- package/src/util/transferable_grid_index.ts +214 -0
- package/src/util/util.test.ts +414 -0
- package/src/util/util.ts +724 -0
- package/src/util/vectortile_to_geojson.ts +72 -0
- package/src/util/verticalize_punctuation.ts +110 -0
- package/src/util/web_worker.ts +16 -0
- package/src/util/web_worker_transfer.test.ts +153 -0
- package/src/util/web_worker_transfer.ts +254 -0
- package/src/util/webp_supported.ts +63 -0
- package/src/util/worker_pool.test.ts +43 -0
- package/src/util/worker_pool.ts +58 -0
- package/src/util/world_bounds.test.ts +59 -0
- package/src/util/world_bounds.ts +46 -0
|
@@ -0,0 +1,1371 @@
|
|
|
1
|
+
import {CollisionIndex, viewportPadding} from './collision_index';
|
|
2
|
+
import type {FeatureKey, PlacedBox, PlacedCircles} from './collision_index';
|
|
3
|
+
import {EXTENT} from '../data/extent';
|
|
4
|
+
import * as symbolSize from './symbol_size';
|
|
5
|
+
import * as projection from './projection';
|
|
6
|
+
import {getAnchorJustification} from './symbol_layout';
|
|
7
|
+
import {getAnchorAlignment, WritingMode} from './shaping';
|
|
8
|
+
import {mat4} from 'gl-matrix';
|
|
9
|
+
import {pixelsToTileUnits} from '../source/pixels_to_tile_units';
|
|
10
|
+
import Point from '@mapbox/point-geometry';
|
|
11
|
+
import type {Transform} from '../geo/transform';
|
|
12
|
+
import type {StyleLayer} from '../style/style_layer';
|
|
13
|
+
import {PossiblyEvaluated} from '../style/properties';
|
|
14
|
+
import type {SymbolLayoutProps, SymbolLayoutPropsPossiblyEvaluated} from '../style/style_layer/symbol_style_layer_properties.g';
|
|
15
|
+
import {getOverlapMode, OverlapMode} from '../style/style_layer/overlap_mode';
|
|
16
|
+
|
|
17
|
+
import type {Tile} from '../source/tile';
|
|
18
|
+
import {SymbolBucket, CollisionArrays, SingleCollisionBox} from '../data/bucket/symbol_bucket';
|
|
19
|
+
|
|
20
|
+
import type {CollisionBoxArray, CollisionVertexArray, SymbolInstance, TextAnchorOffset} from '../data/array_types.g';
|
|
21
|
+
import type {FeatureIndex} from '../data/feature_index';
|
|
22
|
+
import type {OverscaledTileID, UnwrappedTileID} from '../source/tile_id';
|
|
23
|
+
import {Terrain} from '../render/terrain';
|
|
24
|
+
import {warnOnce} from '../util/util';
|
|
25
|
+
import {TextAnchor, TextAnchorEnum} from '../style/style_layer/variable_text_anchor';
|
|
26
|
+
import {Projection} from '../geo/projection/projection';
|
|
27
|
+
|
|
28
|
+
class OpacityState {
|
|
29
|
+
opacity: number;
|
|
30
|
+
placed: boolean;
|
|
31
|
+
constructor(prevState: OpacityState, increment: number, placed: boolean, skipFade?: boolean | null) {
|
|
32
|
+
if (prevState) {
|
|
33
|
+
this.opacity = Math.max(0, Math.min(1, prevState.opacity + (prevState.placed ? increment : -increment)));
|
|
34
|
+
} else {
|
|
35
|
+
this.opacity = (skipFade && placed) ? 1 : 0;
|
|
36
|
+
}
|
|
37
|
+
this.placed = placed;
|
|
38
|
+
}
|
|
39
|
+
isHidden() {
|
|
40
|
+
return this.opacity === 0 && !this.placed;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class JointOpacityState {
|
|
45
|
+
text: OpacityState;
|
|
46
|
+
icon: OpacityState;
|
|
47
|
+
constructor(prevState: JointOpacityState, increment: number, placedText: boolean, placedIcon: boolean, skipFade?: boolean | null) {
|
|
48
|
+
this.text = new OpacityState(prevState ? prevState.text : null, increment, placedText, skipFade);
|
|
49
|
+
this.icon = new OpacityState(prevState ? prevState.icon : null, increment, placedIcon, skipFade);
|
|
50
|
+
}
|
|
51
|
+
isHidden() {
|
|
52
|
+
return this.text.isHidden() && this.icon.isHidden();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class JointPlacement {
|
|
57
|
+
text: boolean;
|
|
58
|
+
icon: boolean;
|
|
59
|
+
// skipFade = outside viewport, but within CollisionIndex::viewportPadding px of the edge
|
|
60
|
+
// Because these symbols aren't onscreen yet, we can skip the "fade in" animation,
|
|
61
|
+
// and if a subsequent viewport change brings them into view, they'll be fully
|
|
62
|
+
// visible right away.
|
|
63
|
+
skipFade: boolean;
|
|
64
|
+
constructor(text: boolean, icon: boolean, skipFade: boolean) {
|
|
65
|
+
this.text = text;
|
|
66
|
+
this.icon = icon;
|
|
67
|
+
this.skipFade = skipFade;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
class CollisionCircleArray {
|
|
72
|
+
// Stores collision circles and placement matrices of a bucket for debug rendering.
|
|
73
|
+
invProjMatrix: mat4;
|
|
74
|
+
viewportMatrix: mat4;
|
|
75
|
+
circles: Array<number>;
|
|
76
|
+
|
|
77
|
+
constructor() {
|
|
78
|
+
this.invProjMatrix = mat4.create();
|
|
79
|
+
this.viewportMatrix = mat4.create();
|
|
80
|
+
this.circles = [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class RetainedQueryData {
|
|
85
|
+
bucketInstanceId: number;
|
|
86
|
+
featureIndex: FeatureIndex;
|
|
87
|
+
sourceLayerIndex: number;
|
|
88
|
+
bucketIndex: number;
|
|
89
|
+
tileID: OverscaledTileID;
|
|
90
|
+
featureSortOrder: Array<number>;
|
|
91
|
+
constructor(bucketInstanceId: number,
|
|
92
|
+
featureIndex: FeatureIndex,
|
|
93
|
+
sourceLayerIndex: number,
|
|
94
|
+
bucketIndex: number,
|
|
95
|
+
tileID: OverscaledTileID) {
|
|
96
|
+
this.bucketInstanceId = bucketInstanceId;
|
|
97
|
+
this.featureIndex = featureIndex;
|
|
98
|
+
this.sourceLayerIndex = sourceLayerIndex;
|
|
99
|
+
this.bucketIndex = bucketIndex;
|
|
100
|
+
this.tileID = tileID;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type CollisionGroup = {
|
|
105
|
+
ID: number;
|
|
106
|
+
predicate?: (key: FeatureKey) => boolean;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
class CollisionGroups {
|
|
110
|
+
collisionGroups: {[groupName: string]: CollisionGroup};
|
|
111
|
+
maxGroupID: number;
|
|
112
|
+
crossSourceCollisions: boolean;
|
|
113
|
+
|
|
114
|
+
constructor(crossSourceCollisions: boolean) {
|
|
115
|
+
this.crossSourceCollisions = crossSourceCollisions;
|
|
116
|
+
this.maxGroupID = 0;
|
|
117
|
+
this.collisionGroups = {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
get(sourceID: string) {
|
|
121
|
+
// The predicate/groupID mechanism allows for arbitrary grouping,
|
|
122
|
+
// but the current interface defines one source == one group when
|
|
123
|
+
// crossSourceCollisions == true.
|
|
124
|
+
if (!this.crossSourceCollisions) {
|
|
125
|
+
if (!this.collisionGroups[sourceID]) {
|
|
126
|
+
const nextGroupID = ++this.maxGroupID;
|
|
127
|
+
this.collisionGroups[sourceID] = {
|
|
128
|
+
ID: nextGroupID,
|
|
129
|
+
predicate: (key) => {
|
|
130
|
+
return key.collisionGroupID === nextGroupID;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return this.collisionGroups[sourceID];
|
|
135
|
+
} else {
|
|
136
|
+
return {ID: 0, predicate: null};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function calculateVariableLayoutShift(
|
|
142
|
+
anchor: TextAnchor,
|
|
143
|
+
width: number,
|
|
144
|
+
height: number,
|
|
145
|
+
textOffset: [number, number],
|
|
146
|
+
textBoxScale: number
|
|
147
|
+
): Point {
|
|
148
|
+
const {horizontalAlign, verticalAlign} = getAnchorAlignment(anchor);
|
|
149
|
+
const shiftX = -(horizontalAlign - 0.5) * width;
|
|
150
|
+
const shiftY = -(verticalAlign - 0.5) * height;
|
|
151
|
+
return new Point(
|
|
152
|
+
shiftX + textOffset[0] * textBoxScale,
|
|
153
|
+
shiftY + textOffset[1] * textBoxScale
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export type VariableOffset = {
|
|
158
|
+
textOffset: [number, number];
|
|
159
|
+
width: number;
|
|
160
|
+
height: number;
|
|
161
|
+
anchor: TextAnchor;
|
|
162
|
+
textBoxScale: number;
|
|
163
|
+
prevAnchor?: TextAnchor;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
type TileLayerParameters = {
|
|
167
|
+
bucket: SymbolBucket;
|
|
168
|
+
layout: PossiblyEvaluated<SymbolLayoutProps, SymbolLayoutPropsPossiblyEvaluated>;
|
|
169
|
+
translationText: [number, number];
|
|
170
|
+
translationIcon: [number, number];
|
|
171
|
+
unwrappedTileID: UnwrappedTileID;
|
|
172
|
+
posMatrix: mat4;
|
|
173
|
+
textLabelPlaneMatrix: mat4;
|
|
174
|
+
labelToScreenMatrix: mat4;
|
|
175
|
+
scale: number;
|
|
176
|
+
textPixelRatio: number;
|
|
177
|
+
holdingForFade: boolean;
|
|
178
|
+
collisionBoxArray: CollisionBoxArray;
|
|
179
|
+
partiallyEvaluatedTextSize: {
|
|
180
|
+
uSize: number;
|
|
181
|
+
uSizeT: number;
|
|
182
|
+
};
|
|
183
|
+
collisionGroup: CollisionGroup;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export type BucketPart = {
|
|
187
|
+
sortKey?: number | void;
|
|
188
|
+
symbolInstanceStart: number;
|
|
189
|
+
symbolInstanceEnd: number;
|
|
190
|
+
parameters: TileLayerParameters;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
export type CrossTileID = string | number;
|
|
194
|
+
|
|
195
|
+
export class Placement {
|
|
196
|
+
transform: Transform;
|
|
197
|
+
terrain: Terrain;
|
|
198
|
+
collisionIndex: CollisionIndex;
|
|
199
|
+
placements: {
|
|
200
|
+
[_ in CrossTileID]: JointPlacement;
|
|
201
|
+
};
|
|
202
|
+
opacities: {
|
|
203
|
+
[_ in CrossTileID]: JointOpacityState;
|
|
204
|
+
};
|
|
205
|
+
variableOffsets: {
|
|
206
|
+
[_ in CrossTileID]: VariableOffset;
|
|
207
|
+
};
|
|
208
|
+
placedOrientations: {
|
|
209
|
+
[_ in CrossTileID]: number;
|
|
210
|
+
};
|
|
211
|
+
commitTime: number;
|
|
212
|
+
prevZoomAdjustment: number;
|
|
213
|
+
lastPlacementChangeTime: number;
|
|
214
|
+
stale: boolean;
|
|
215
|
+
fadeDuration: number;
|
|
216
|
+
retainedQueryData: {
|
|
217
|
+
[_: number]: RetainedQueryData;
|
|
218
|
+
};
|
|
219
|
+
collisionGroups: CollisionGroups;
|
|
220
|
+
prevPlacement: Placement;
|
|
221
|
+
zoomAtLastRecencyCheck: number;
|
|
222
|
+
collisionCircleArrays: {
|
|
223
|
+
[k in any]: CollisionCircleArray;
|
|
224
|
+
};
|
|
225
|
+
collisionBoxArrays: Map<number, Map<number, {
|
|
226
|
+
text: number[];
|
|
227
|
+
icon: number[];
|
|
228
|
+
}>>;
|
|
229
|
+
|
|
230
|
+
constructor(transform: Transform, projection: Projection, terrain: Terrain, fadeDuration: number, crossSourceCollisions: boolean, prevPlacement?: Placement) {
|
|
231
|
+
this.transform = transform.clone();
|
|
232
|
+
this.terrain = terrain;
|
|
233
|
+
this.collisionIndex = new CollisionIndex(this.transform, projection);
|
|
234
|
+
this.placements = {};
|
|
235
|
+
this.opacities = {};
|
|
236
|
+
this.variableOffsets = {};
|
|
237
|
+
this.stale = false;
|
|
238
|
+
this.commitTime = 0;
|
|
239
|
+
this.fadeDuration = fadeDuration;
|
|
240
|
+
this.retainedQueryData = {};
|
|
241
|
+
this.collisionGroups = new CollisionGroups(crossSourceCollisions);
|
|
242
|
+
this.collisionCircleArrays = {};
|
|
243
|
+
this.collisionBoxArrays = new Map<number, Map<number, {
|
|
244
|
+
text: number[];
|
|
245
|
+
icon: number[];
|
|
246
|
+
}>>();
|
|
247
|
+
|
|
248
|
+
this.prevPlacement = prevPlacement;
|
|
249
|
+
if (prevPlacement) {
|
|
250
|
+
prevPlacement.prevPlacement = undefined; // Only hold on to one placement back
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
this.placedOrientations = {};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private _getTerrainElevationFunc(tileID: OverscaledTileID) {
|
|
257
|
+
const terrain = this.terrain;
|
|
258
|
+
return terrain ? (x: number, y: number) => terrain.getElevation(tileID, x, y) : null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
getBucketParts(results: Array<BucketPart>, styleLayer: StyleLayer, tile: Tile, sortAcrossTiles: boolean) {
|
|
262
|
+
const symbolBucket = (tile.getBucket(styleLayer) as SymbolBucket);
|
|
263
|
+
const bucketFeatureIndex = tile.latestFeatureIndex;
|
|
264
|
+
if (!symbolBucket || !bucketFeatureIndex || styleLayer.id !== symbolBucket.layerIds[0])
|
|
265
|
+
return;
|
|
266
|
+
|
|
267
|
+
const collisionBoxArray = tile.collisionBoxArray;
|
|
268
|
+
|
|
269
|
+
const layout = symbolBucket.layers[0].layout;
|
|
270
|
+
const paint = symbolBucket.layers[0].paint;
|
|
271
|
+
|
|
272
|
+
const scale = Math.pow(2, this.transform.zoom - tile.tileID.overscaledZ);
|
|
273
|
+
const textPixelRatio = tile.tileSize / EXTENT;
|
|
274
|
+
|
|
275
|
+
const unwrappedTileID = tile.tileID.toUnwrapped();
|
|
276
|
+
|
|
277
|
+
const posMatrix = this.transform.calculatePosMatrix(unwrappedTileID);
|
|
278
|
+
|
|
279
|
+
const pitchWithMap = layout.get('text-pitch-alignment') === 'map';
|
|
280
|
+
const rotateWithMap = layout.get('text-rotation-alignment') === 'map';
|
|
281
|
+
const pixelsToTiles = pixelsToTileUnits(tile, 1, this.transform.zoom);
|
|
282
|
+
|
|
283
|
+
const translationText = this.collisionIndex.mapProjection.translatePosition(
|
|
284
|
+
this.transform,
|
|
285
|
+
tile,
|
|
286
|
+
paint.get('text-translate'),
|
|
287
|
+
paint.get('text-translate-anchor'),);
|
|
288
|
+
|
|
289
|
+
const translationIcon = this.collisionIndex.mapProjection.translatePosition(
|
|
290
|
+
this.transform,
|
|
291
|
+
tile,
|
|
292
|
+
paint.get('icon-translate'),
|
|
293
|
+
paint.get('icon-translate-anchor'),);
|
|
294
|
+
|
|
295
|
+
const textLabelPlaneMatrix = projection.getLabelPlaneMatrix(posMatrix,
|
|
296
|
+
pitchWithMap,
|
|
297
|
+
rotateWithMap,
|
|
298
|
+
this.transform,
|
|
299
|
+
pixelsToTiles);
|
|
300
|
+
|
|
301
|
+
let labelToScreenMatrix = null;
|
|
302
|
+
|
|
303
|
+
if (pitchWithMap) {
|
|
304
|
+
const glMatrix = projection.getGlCoordMatrix(
|
|
305
|
+
posMatrix,
|
|
306
|
+
pitchWithMap,
|
|
307
|
+
rotateWithMap,
|
|
308
|
+
this.transform,
|
|
309
|
+
pixelsToTiles);
|
|
310
|
+
|
|
311
|
+
labelToScreenMatrix = mat4.multiply([] as any, this.transform.labelPlaneMatrix, glMatrix);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// As long as this placement lives, we have to hold onto this bucket's
|
|
315
|
+
// matching FeatureIndex/data for querying purposes
|
|
316
|
+
this.retainedQueryData[symbolBucket.bucketInstanceId] = new RetainedQueryData(
|
|
317
|
+
symbolBucket.bucketInstanceId,
|
|
318
|
+
bucketFeatureIndex,
|
|
319
|
+
symbolBucket.sourceLayerIndex,
|
|
320
|
+
symbolBucket.index,
|
|
321
|
+
tile.tileID
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const parameters: TileLayerParameters = {
|
|
325
|
+
bucket: symbolBucket,
|
|
326
|
+
layout,
|
|
327
|
+
translationText,
|
|
328
|
+
translationIcon,
|
|
329
|
+
posMatrix,
|
|
330
|
+
unwrappedTileID,
|
|
331
|
+
textLabelPlaneMatrix,
|
|
332
|
+
labelToScreenMatrix,
|
|
333
|
+
scale,
|
|
334
|
+
textPixelRatio,
|
|
335
|
+
holdingForFade: tile.holdingForFade(),
|
|
336
|
+
collisionBoxArray,
|
|
337
|
+
partiallyEvaluatedTextSize: symbolSize.evaluateSizeForZoom(symbolBucket.textSizeData, this.transform.zoom),
|
|
338
|
+
collisionGroup: this.collisionGroups.get(symbolBucket.sourceID)
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
if (sortAcrossTiles) {
|
|
342
|
+
for (const range of symbolBucket.sortKeyRanges) {
|
|
343
|
+
const {sortKey, symbolInstanceStart, symbolInstanceEnd} = range;
|
|
344
|
+
results.push({sortKey, symbolInstanceStart, symbolInstanceEnd, parameters});
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
results.push({
|
|
348
|
+
symbolInstanceStart: 0,
|
|
349
|
+
symbolInstanceEnd: symbolBucket.symbolInstances.length,
|
|
350
|
+
parameters
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
attemptAnchorPlacement(
|
|
356
|
+
textAnchorOffset: TextAnchorOffset,
|
|
357
|
+
textBox: SingleCollisionBox,
|
|
358
|
+
width: number,
|
|
359
|
+
height: number,
|
|
360
|
+
textBoxScale: number,
|
|
361
|
+
rotateWithMap: boolean,
|
|
362
|
+
pitchWithMap: boolean,
|
|
363
|
+
textPixelRatio: number,
|
|
364
|
+
posMatrix: mat4,
|
|
365
|
+
unwrappedTileID,
|
|
366
|
+
collisionGroup: CollisionGroup,
|
|
367
|
+
textOverlapMode: OverlapMode,
|
|
368
|
+
symbolInstance: SymbolInstance,
|
|
369
|
+
bucket: SymbolBucket,
|
|
370
|
+
orientation: number,
|
|
371
|
+
translationText: [number, number],
|
|
372
|
+
translationIcon: [number, number],
|
|
373
|
+
iconBox?: SingleCollisionBox | null,
|
|
374
|
+
getElevation?: (x: number, y: number) => number
|
|
375
|
+
): {
|
|
376
|
+
shift: Point;
|
|
377
|
+
placedGlyphBoxes: PlacedBox;
|
|
378
|
+
} {
|
|
379
|
+
|
|
380
|
+
const anchor = TextAnchorEnum[textAnchorOffset.textAnchor] as TextAnchor;
|
|
381
|
+
const textOffset = [textAnchorOffset.textOffset0, textAnchorOffset.textOffset1] as [number, number];
|
|
382
|
+
const shift = calculateVariableLayoutShift(anchor, width, height, textOffset, textBoxScale);
|
|
383
|
+
|
|
384
|
+
const placedGlyphBoxes = this.collisionIndex.placeCollisionBox(
|
|
385
|
+
textBox,
|
|
386
|
+
textOverlapMode,
|
|
387
|
+
textPixelRatio,
|
|
388
|
+
posMatrix,
|
|
389
|
+
unwrappedTileID,
|
|
390
|
+
pitchWithMap,
|
|
391
|
+
rotateWithMap,
|
|
392
|
+
translationText,
|
|
393
|
+
collisionGroup.predicate,
|
|
394
|
+
getElevation,
|
|
395
|
+
shift
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
if (iconBox) {
|
|
399
|
+
const placedIconBoxes = this.collisionIndex.placeCollisionBox(
|
|
400
|
+
iconBox,
|
|
401
|
+
textOverlapMode,
|
|
402
|
+
textPixelRatio,
|
|
403
|
+
posMatrix,
|
|
404
|
+
unwrappedTileID,
|
|
405
|
+
pitchWithMap,
|
|
406
|
+
rotateWithMap,
|
|
407
|
+
translationIcon,
|
|
408
|
+
collisionGroup.predicate,
|
|
409
|
+
getElevation,
|
|
410
|
+
shift
|
|
411
|
+
);
|
|
412
|
+
if (!placedIconBoxes.placeable) return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (placedGlyphBoxes.placeable) {
|
|
416
|
+
let prevAnchor;
|
|
417
|
+
// If this label was placed in the previous placement, record the anchor position
|
|
418
|
+
// to allow us to animate the transition
|
|
419
|
+
if (this.prevPlacement &&
|
|
420
|
+
this.prevPlacement.variableOffsets[symbolInstance.crossTileID] &&
|
|
421
|
+
this.prevPlacement.placements[symbolInstance.crossTileID] &&
|
|
422
|
+
this.prevPlacement.placements[symbolInstance.crossTileID].text) {
|
|
423
|
+
prevAnchor = this.prevPlacement.variableOffsets[symbolInstance.crossTileID].anchor;
|
|
424
|
+
}
|
|
425
|
+
if (symbolInstance.crossTileID === 0) throw new Error('symbolInstance.crossTileID can\'t be 0');
|
|
426
|
+
this.variableOffsets[symbolInstance.crossTileID] = {
|
|
427
|
+
textOffset,
|
|
428
|
+
width,
|
|
429
|
+
height,
|
|
430
|
+
anchor,
|
|
431
|
+
textBoxScale,
|
|
432
|
+
prevAnchor
|
|
433
|
+
};
|
|
434
|
+
this.markUsedJustification(bucket, anchor, symbolInstance, orientation);
|
|
435
|
+
|
|
436
|
+
if (bucket.allowVerticalPlacement) {
|
|
437
|
+
this.markUsedOrientation(bucket, orientation, symbolInstance);
|
|
438
|
+
this.placedOrientations[symbolInstance.crossTileID] = orientation;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return {shift, placedGlyphBoxes};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
placeLayerBucketPart(bucketPart: BucketPart, seenCrossTileIDs: {
|
|
446
|
+
[k in string | number]: boolean;
|
|
447
|
+
}, showCollisionBoxes: boolean) {
|
|
448
|
+
|
|
449
|
+
const {
|
|
450
|
+
bucket,
|
|
451
|
+
layout,
|
|
452
|
+
translationText,
|
|
453
|
+
translationIcon,
|
|
454
|
+
posMatrix,
|
|
455
|
+
unwrappedTileID,
|
|
456
|
+
textLabelPlaneMatrix,
|
|
457
|
+
labelToScreenMatrix,
|
|
458
|
+
textPixelRatio,
|
|
459
|
+
holdingForFade,
|
|
460
|
+
collisionBoxArray,
|
|
461
|
+
partiallyEvaluatedTextSize,
|
|
462
|
+
collisionGroup
|
|
463
|
+
} = bucketPart.parameters;
|
|
464
|
+
|
|
465
|
+
const textOptional = layout.get('text-optional');
|
|
466
|
+
const iconOptional = layout.get('icon-optional');
|
|
467
|
+
const textOverlapMode = getOverlapMode(layout, 'text-overlap', 'text-allow-overlap');
|
|
468
|
+
const textAlwaysOverlap = textOverlapMode === 'always';
|
|
469
|
+
const iconOverlapMode = getOverlapMode(layout, 'icon-overlap', 'icon-allow-overlap');
|
|
470
|
+
const iconAlwaysOverlap = iconOverlapMode === 'always';
|
|
471
|
+
const rotateWithMap = layout.get('text-rotation-alignment') === 'map';
|
|
472
|
+
const pitchWithMap = layout.get('text-pitch-alignment') === 'map';
|
|
473
|
+
const hasIconTextFit = layout.get('icon-text-fit') !== 'none';
|
|
474
|
+
const zOrderByViewportY = layout.get('symbol-z-order') === 'viewport-y';
|
|
475
|
+
|
|
476
|
+
// This logic is similar to the "defaultOpacityState" logic below in updateBucketOpacities
|
|
477
|
+
// If we know a symbol is always supposed to show, force it to be marked visible even if
|
|
478
|
+
// it wasn't placed into the collision index (because some or all of it was outside the range
|
|
479
|
+
// of the collision grid).
|
|
480
|
+
// There is a subtle edge case here we're accepting:
|
|
481
|
+
// Symbol A has text-allow-overlap: true, icon-allow-overlap: true, icon-optional: false
|
|
482
|
+
// A's icon is outside the grid, so doesn't get placed
|
|
483
|
+
// A's text would be inside grid, but doesn't get placed because of icon-optional: false
|
|
484
|
+
// We still show A because of the allow-overlap settings.
|
|
485
|
+
// Symbol B has allow-overlap: false, and gets placed where A's text would be
|
|
486
|
+
// On panning in, there is a short period when Symbol B and Symbol A will overlap
|
|
487
|
+
// This is the reverse of our normal policy of "fade in on pan", but should look like any other
|
|
488
|
+
// collision and hopefully not be too noticeable.
|
|
489
|
+
// See https://github.com/mapbox/mapbox-gl-js/issues/7172
|
|
490
|
+
const alwaysShowText = textAlwaysOverlap && (iconAlwaysOverlap || !bucket.hasIconData() || iconOptional);
|
|
491
|
+
const alwaysShowIcon = iconAlwaysOverlap && (textAlwaysOverlap || !bucket.hasTextData() || textOptional);
|
|
492
|
+
|
|
493
|
+
if (!bucket.collisionArrays && collisionBoxArray) {
|
|
494
|
+
bucket.deserializeCollisionBoxes(collisionBoxArray);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const tileID = this.retainedQueryData[bucket.bucketInstanceId].tileID;
|
|
498
|
+
const getElevation = this._getTerrainElevationFunc(tileID);
|
|
499
|
+
|
|
500
|
+
const placeSymbol = (symbolInstance: SymbolInstance, collisionArrays: CollisionArrays, symbolIndex: number) => {
|
|
501
|
+
if (seenCrossTileIDs[symbolInstance.crossTileID]) return;
|
|
502
|
+
if (holdingForFade) {
|
|
503
|
+
// Mark all symbols from this tile as "not placed", but don't add to seenCrossTileIDs, because we don't
|
|
504
|
+
// know yet if we have a duplicate in a parent tile that _should_ be placed.
|
|
505
|
+
this.placements[symbolInstance.crossTileID] = new JointPlacement(false, false, false);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
let placeText = false;
|
|
510
|
+
let placeIcon = false;
|
|
511
|
+
let offscreen = true;
|
|
512
|
+
let shift = null;
|
|
513
|
+
|
|
514
|
+
let placed: PlacedBox = {box: null, placeable: false, offscreen: null};
|
|
515
|
+
let placedVerticalText = {box: null, placeable: false, offscreen: null};
|
|
516
|
+
|
|
517
|
+
let placedGlyphBoxes: PlacedBox = null;
|
|
518
|
+
let placedGlyphCircles: PlacedCircles = null;
|
|
519
|
+
let placedIconBoxes: PlacedBox = null;
|
|
520
|
+
let textFeatureIndex = 0;
|
|
521
|
+
let verticalTextFeatureIndex = 0;
|
|
522
|
+
let iconFeatureIndex = 0;
|
|
523
|
+
|
|
524
|
+
if (collisionArrays.textFeatureIndex) {
|
|
525
|
+
textFeatureIndex = collisionArrays.textFeatureIndex;
|
|
526
|
+
} else if (symbolInstance.useRuntimeCollisionCircles) {
|
|
527
|
+
textFeatureIndex = symbolInstance.featureIndex;
|
|
528
|
+
}
|
|
529
|
+
if (collisionArrays.verticalTextFeatureIndex) {
|
|
530
|
+
verticalTextFeatureIndex = collisionArrays.verticalTextFeatureIndex;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const textBox = collisionArrays.textBox;
|
|
534
|
+
if (textBox) {
|
|
535
|
+
|
|
536
|
+
const updatePreviousOrientationIfNotPlaced = (isPlaced) => {
|
|
537
|
+
let previousOrientation = WritingMode.horizontal;
|
|
538
|
+
if (bucket.allowVerticalPlacement && !isPlaced && this.prevPlacement) {
|
|
539
|
+
const prevPlacedOrientation = this.prevPlacement.placedOrientations[symbolInstance.crossTileID];
|
|
540
|
+
if (prevPlacedOrientation) {
|
|
541
|
+
this.placedOrientations[symbolInstance.crossTileID] = prevPlacedOrientation;
|
|
542
|
+
previousOrientation = prevPlacedOrientation;
|
|
543
|
+
this.markUsedOrientation(bucket, previousOrientation, symbolInstance);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return previousOrientation;
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
const placeTextForPlacementModes = (placeHorizontalFn, placeVerticalFn) => {
|
|
550
|
+
if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && collisionArrays.verticalTextBox) {
|
|
551
|
+
for (const placementMode of bucket.writingModes) {
|
|
552
|
+
if (placementMode === WritingMode.vertical) {
|
|
553
|
+
placed = placeVerticalFn();
|
|
554
|
+
placedVerticalText = placed;
|
|
555
|
+
} else {
|
|
556
|
+
placed = placeHorizontalFn();
|
|
557
|
+
}
|
|
558
|
+
if (placed && placed.placeable) break;
|
|
559
|
+
}
|
|
560
|
+
} else {
|
|
561
|
+
placed = placeHorizontalFn();
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const textAnchorOffsetStart = symbolInstance.textAnchorOffsetStartIndex;
|
|
566
|
+
const textAnchorOffsetEnd = symbolInstance.textAnchorOffsetEndIndex;
|
|
567
|
+
|
|
568
|
+
// If start+end indices match, text-variable-anchor is not in play.
|
|
569
|
+
if (textAnchorOffsetEnd === textAnchorOffsetStart) {
|
|
570
|
+
const placeBox = (collisionTextBox, orientation) => {
|
|
571
|
+
const placedFeature = this.collisionIndex.placeCollisionBox(
|
|
572
|
+
collisionTextBox,
|
|
573
|
+
textOverlapMode,
|
|
574
|
+
textPixelRatio,
|
|
575
|
+
posMatrix,
|
|
576
|
+
unwrappedTileID,
|
|
577
|
+
pitchWithMap,
|
|
578
|
+
rotateWithMap,
|
|
579
|
+
translationText,
|
|
580
|
+
collisionGroup.predicate,
|
|
581
|
+
getElevation
|
|
582
|
+
);
|
|
583
|
+
if (placedFeature && placedFeature.placeable) {
|
|
584
|
+
this.markUsedOrientation(bucket, orientation, symbolInstance);
|
|
585
|
+
this.placedOrientations[symbolInstance.crossTileID] = orientation;
|
|
586
|
+
}
|
|
587
|
+
return placedFeature;
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const placeHorizontal = () => {
|
|
591
|
+
return placeBox(textBox, WritingMode.horizontal);
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
const placeVertical = () => {
|
|
595
|
+
const verticalTextBox = collisionArrays.verticalTextBox;
|
|
596
|
+
if (bucket.allowVerticalPlacement && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) {
|
|
597
|
+
return placeBox(verticalTextBox, WritingMode.vertical);
|
|
598
|
+
}
|
|
599
|
+
return {box: null, offscreen: null};
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
placeTextForPlacementModes(placeHorizontal, placeVertical);
|
|
603
|
+
updatePreviousOrientationIfNotPlaced(placed && placed.placeable);
|
|
604
|
+
|
|
605
|
+
} else {
|
|
606
|
+
// If this symbol was in the last placement, prefer placement using same anchor, if it's still available
|
|
607
|
+
let prevAnchor = TextAnchorEnum[this.prevPlacement?.variableOffsets[symbolInstance.crossTileID]?.anchor];
|
|
608
|
+
|
|
609
|
+
const placeBoxForVariableAnchors = (collisionTextBox, collisionIconBox, orientation) => {
|
|
610
|
+
const width = collisionTextBox.x2 - collisionTextBox.x1;
|
|
611
|
+
const height = collisionTextBox.y2 - collisionTextBox.y1;
|
|
612
|
+
const textBoxScale = symbolInstance.textBoxScale;
|
|
613
|
+
const variableIconBox = hasIconTextFit && (iconOverlapMode === 'never') ? collisionIconBox : null;
|
|
614
|
+
|
|
615
|
+
let placedBox: PlacedBox = null;
|
|
616
|
+
let placementPasses = (textOverlapMode === 'never') ? 1 : 2;
|
|
617
|
+
let overlapMode: OverlapMode = 'never';
|
|
618
|
+
|
|
619
|
+
if (prevAnchor) {
|
|
620
|
+
placementPasses++;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
for (let pass = 0; pass < placementPasses; pass++) {
|
|
624
|
+
for (let i = textAnchorOffsetStart; i < textAnchorOffsetEnd; i++) {
|
|
625
|
+
const textAnchorOffset = bucket.textAnchorOffsets.get(i);
|
|
626
|
+
|
|
627
|
+
if (prevAnchor && textAnchorOffset.textAnchor !== prevAnchor) {
|
|
628
|
+
continue;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const result = this.attemptAnchorPlacement(
|
|
632
|
+
textAnchorOffset, collisionTextBox, width, height,
|
|
633
|
+
textBoxScale, rotateWithMap, pitchWithMap, textPixelRatio, posMatrix, unwrappedTileID,
|
|
634
|
+
collisionGroup, overlapMode, symbolInstance, bucket, orientation, translationText, translationIcon, variableIconBox, getElevation);
|
|
635
|
+
|
|
636
|
+
if (result) {
|
|
637
|
+
placedBox = result.placedGlyphBoxes;
|
|
638
|
+
if (placedBox && placedBox.placeable) {
|
|
639
|
+
placeText = true;
|
|
640
|
+
shift = result.shift;
|
|
641
|
+
return placedBox;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (prevAnchor) {
|
|
647
|
+
prevAnchor = null;
|
|
648
|
+
} else {
|
|
649
|
+
overlapMode = textOverlapMode;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (showCollisionBoxes && !placedBox) {
|
|
654
|
+
// No box was successfully placed
|
|
655
|
+
// Generate bounds for a fake centered box, so that we can at least display something for collision debug.
|
|
656
|
+
const placedFakeGlyphBox = this.collisionIndex.placeCollisionBox(
|
|
657
|
+
textBox,
|
|
658
|
+
'always', // Skips expensive collision check with already placed boxes
|
|
659
|
+
textPixelRatio,
|
|
660
|
+
posMatrix,
|
|
661
|
+
unwrappedTileID,
|
|
662
|
+
pitchWithMap,
|
|
663
|
+
rotateWithMap,
|
|
664
|
+
translationText,
|
|
665
|
+
collisionGroup.predicate,
|
|
666
|
+
getElevation,
|
|
667
|
+
new Point(0, 0)
|
|
668
|
+
);
|
|
669
|
+
placedBox = {
|
|
670
|
+
box: placedFakeGlyphBox.box,
|
|
671
|
+
offscreen: false,
|
|
672
|
+
placeable: false
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return placedBox;
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
const placeHorizontal = () => {
|
|
680
|
+
return placeBoxForVariableAnchors(textBox, collisionArrays.iconBox, WritingMode.horizontal);
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
const placeVertical = () => {
|
|
684
|
+
const verticalTextBox = collisionArrays.verticalTextBox;
|
|
685
|
+
const wasPlaced = placed && placed.placeable;
|
|
686
|
+
if (bucket.allowVerticalPlacement && !wasPlaced && symbolInstance.numVerticalGlyphVertices > 0 && verticalTextBox) {
|
|
687
|
+
return placeBoxForVariableAnchors(verticalTextBox, collisionArrays.verticalIconBox, WritingMode.vertical);
|
|
688
|
+
}
|
|
689
|
+
return {box: null, occluded: true, offscreen: null};
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
placeTextForPlacementModes(placeHorizontal, placeVertical);
|
|
693
|
+
|
|
694
|
+
if (placed) {
|
|
695
|
+
placeText = placed.placeable;
|
|
696
|
+
offscreen = placed.offscreen;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
const prevOrientation = updatePreviousOrientationIfNotPlaced(placed && placed.placeable);
|
|
700
|
+
|
|
701
|
+
// If we didn't get placed, we still need to copy our position from the last placement for
|
|
702
|
+
// fade animations
|
|
703
|
+
if (!placeText && this.prevPlacement) {
|
|
704
|
+
const prevOffset = this.prevPlacement.variableOffsets[symbolInstance.crossTileID];
|
|
705
|
+
if (prevOffset) {
|
|
706
|
+
this.variableOffsets[symbolInstance.crossTileID] = prevOffset;
|
|
707
|
+
this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, prevOrientation);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
placedGlyphBoxes = placed;
|
|
715
|
+
placeText = placedGlyphBoxes && placedGlyphBoxes.placeable;
|
|
716
|
+
|
|
717
|
+
offscreen = placedGlyphBoxes && placedGlyphBoxes.offscreen;
|
|
718
|
+
|
|
719
|
+
if (symbolInstance.useRuntimeCollisionCircles) {
|
|
720
|
+
const placedSymbol = bucket.text.placedSymbolArray.get(symbolInstance.centerJustifiedTextSymbolIndex);
|
|
721
|
+
const fontSize = symbolSize.evaluateSizeForFeature(bucket.textSizeData, partiallyEvaluatedTextSize, placedSymbol);
|
|
722
|
+
|
|
723
|
+
const textPixelPadding = layout.get('text-padding');
|
|
724
|
+
const circlePixelDiameter = symbolInstance.collisionCircleDiameter;
|
|
725
|
+
|
|
726
|
+
placedGlyphCircles = this.collisionIndex.placeCollisionCircles(
|
|
727
|
+
textOverlapMode,
|
|
728
|
+
placedSymbol,
|
|
729
|
+
bucket.lineVertexArray,
|
|
730
|
+
bucket.glyphOffsetArray,
|
|
731
|
+
fontSize,
|
|
732
|
+
posMatrix,
|
|
733
|
+
unwrappedTileID,
|
|
734
|
+
textLabelPlaneMatrix,
|
|
735
|
+
labelToScreenMatrix,
|
|
736
|
+
showCollisionBoxes,
|
|
737
|
+
pitchWithMap,
|
|
738
|
+
collisionGroup.predicate,
|
|
739
|
+
circlePixelDiameter,
|
|
740
|
+
textPixelPadding,
|
|
741
|
+
translationText,
|
|
742
|
+
getElevation
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
if (placedGlyphCircles.circles.length && placedGlyphCircles.collisionDetected && !showCollisionBoxes) {
|
|
746
|
+
warnOnce('Collisions detected, but collision boxes are not shown');
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// If text-overlap is set to 'always', force "placedCircles" to true
|
|
750
|
+
// In theory there should always be at least one circle placed
|
|
751
|
+
// in this case, but for now quirks in text-anchor
|
|
752
|
+
// and text-offset may prevent that from being true.
|
|
753
|
+
placeText = textAlwaysOverlap || (placedGlyphCircles.circles.length > 0 && !placedGlyphCircles.collisionDetected);
|
|
754
|
+
offscreen = offscreen && placedGlyphCircles.offscreen;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (collisionArrays.iconFeatureIndex) {
|
|
758
|
+
iconFeatureIndex = collisionArrays.iconFeatureIndex;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (collisionArrays.iconBox) {
|
|
762
|
+
const placeIconFeature = iconBox => {
|
|
763
|
+
return this.collisionIndex.placeCollisionBox(
|
|
764
|
+
iconBox,
|
|
765
|
+
iconOverlapMode,
|
|
766
|
+
textPixelRatio,
|
|
767
|
+
posMatrix,
|
|
768
|
+
unwrappedTileID,
|
|
769
|
+
pitchWithMap,
|
|
770
|
+
rotateWithMap,
|
|
771
|
+
translationIcon,
|
|
772
|
+
collisionGroup.predicate,
|
|
773
|
+
getElevation,
|
|
774
|
+
(hasIconTextFit && shift) ? shift : undefined,
|
|
775
|
+
);
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
if (placedVerticalText && placedVerticalText.placeable && collisionArrays.verticalIconBox) {
|
|
779
|
+
placedIconBoxes = placeIconFeature(collisionArrays.verticalIconBox);
|
|
780
|
+
placeIcon = placedIconBoxes.placeable;
|
|
781
|
+
} else {
|
|
782
|
+
placedIconBoxes = placeIconFeature(collisionArrays.iconBox);
|
|
783
|
+
placeIcon = placedIconBoxes.placeable;
|
|
784
|
+
}
|
|
785
|
+
offscreen = offscreen && placedIconBoxes.offscreen;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
const iconWithoutText = textOptional ||
|
|
789
|
+
(symbolInstance.numHorizontalGlyphVertices === 0 && symbolInstance.numVerticalGlyphVertices === 0);
|
|
790
|
+
const textWithoutIcon = iconOptional || symbolInstance.numIconVertices === 0;
|
|
791
|
+
|
|
792
|
+
// Combine the scales for icons and text.
|
|
793
|
+
if (!iconWithoutText && !textWithoutIcon) {
|
|
794
|
+
placeIcon = placeText = placeIcon && placeText;
|
|
795
|
+
} else if (!textWithoutIcon) {
|
|
796
|
+
placeText = placeIcon && placeText;
|
|
797
|
+
} else if (!iconWithoutText) {
|
|
798
|
+
placeIcon = placeIcon && placeText;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const hasTextBox = placeText && placedGlyphBoxes.placeable;
|
|
802
|
+
const hasIconBox = placeIcon && placedIconBoxes.placeable;
|
|
803
|
+
|
|
804
|
+
if (hasTextBox) {
|
|
805
|
+
if (placedVerticalText && placedVerticalText.placeable && verticalTextFeatureIndex) {
|
|
806
|
+
this.collisionIndex.insertCollisionBox(
|
|
807
|
+
placedGlyphBoxes.box,
|
|
808
|
+
textOverlapMode,
|
|
809
|
+
layout.get('text-ignore-placement'),
|
|
810
|
+
bucket.bucketInstanceId,
|
|
811
|
+
verticalTextFeatureIndex,
|
|
812
|
+
collisionGroup.ID);
|
|
813
|
+
} else {
|
|
814
|
+
this.collisionIndex.insertCollisionBox(
|
|
815
|
+
placedGlyphBoxes.box,
|
|
816
|
+
textOverlapMode,
|
|
817
|
+
layout.get('text-ignore-placement'),
|
|
818
|
+
bucket.bucketInstanceId,
|
|
819
|
+
textFeatureIndex,
|
|
820
|
+
collisionGroup.ID);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
}
|
|
824
|
+
if (hasIconBox) {
|
|
825
|
+
this.collisionIndex.insertCollisionBox(
|
|
826
|
+
placedIconBoxes.box,
|
|
827
|
+
iconOverlapMode,
|
|
828
|
+
layout.get('icon-ignore-placement'),
|
|
829
|
+
bucket.bucketInstanceId,
|
|
830
|
+
iconFeatureIndex,
|
|
831
|
+
collisionGroup.ID);
|
|
832
|
+
}
|
|
833
|
+
if (placedGlyphCircles) {
|
|
834
|
+
if (placeText) {
|
|
835
|
+
this.collisionIndex.insertCollisionCircles(
|
|
836
|
+
placedGlyphCircles.circles,
|
|
837
|
+
textOverlapMode,
|
|
838
|
+
layout.get('text-ignore-placement'),
|
|
839
|
+
bucket.bucketInstanceId,
|
|
840
|
+
textFeatureIndex,
|
|
841
|
+
collisionGroup.ID);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (showCollisionBoxes) {
|
|
846
|
+
this.storeCollisionData(bucket.bucketInstanceId, symbolIndex, collisionArrays, placedGlyphBoxes, placedIconBoxes, placedGlyphCircles);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (symbolInstance.crossTileID === 0) throw new Error('symbolInstance.crossTileID can\'t be 0');
|
|
850
|
+
if (bucket.bucketInstanceId === 0) throw new Error('bucket.bucketInstanceId can\'t be 0');
|
|
851
|
+
|
|
852
|
+
this.placements[symbolInstance.crossTileID] = new JointPlacement(placeText || alwaysShowText, placeIcon || alwaysShowIcon, offscreen || bucket.justReloaded);
|
|
853
|
+
seenCrossTileIDs[symbolInstance.crossTileID] = true;
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
if (zOrderByViewportY) {
|
|
857
|
+
if (bucketPart.symbolInstanceStart !== 0) throw new Error('bucket.bucketInstanceId should be 0');
|
|
858
|
+
const symbolIndexes = bucket.getSortedSymbolIndexes(this.transform.angle);
|
|
859
|
+
for (let i = symbolIndexes.length - 1; i >= 0; --i) {
|
|
860
|
+
const symbolIndex = symbolIndexes[i];
|
|
861
|
+
placeSymbol(bucket.symbolInstances.get(symbolIndex), bucket.collisionArrays[symbolIndex], symbolIndex);
|
|
862
|
+
}
|
|
863
|
+
} else {
|
|
864
|
+
for (let i = bucketPart.symbolInstanceStart; i < bucketPart.symbolInstanceEnd; i++) {
|
|
865
|
+
placeSymbol(bucket.symbolInstances.get(i), bucket.collisionArrays[i], i);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (showCollisionBoxes && bucket.bucketInstanceId in this.collisionCircleArrays) {
|
|
870
|
+
const circleArray = this.collisionCircleArrays[bucket.bucketInstanceId];
|
|
871
|
+
|
|
872
|
+
// Store viewport and inverse projection matrices per bucket
|
|
873
|
+
mat4.invert(circleArray.invProjMatrix, posMatrix);
|
|
874
|
+
circleArray.viewportMatrix = this.collisionIndex.getViewportMatrix();
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
bucket.justReloaded = false;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
storeCollisionData(bucketInstanceId: number, symbolIndex: number, collisionArrays: CollisionArrays, placedGlyphBoxes: PlacedBox, placedIconBoxes: PlacedBox, placedGlyphCircles: PlacedCircles): void {
|
|
881
|
+
if (collisionArrays.textBox || collisionArrays.iconBox) {
|
|
882
|
+
// Store the actually used collision box for debug draw
|
|
883
|
+
let boxArray: Map<number, {
|
|
884
|
+
text: number[];
|
|
885
|
+
icon: number[];
|
|
886
|
+
}>;
|
|
887
|
+
|
|
888
|
+
if (this.collisionBoxArrays.has(bucketInstanceId)) {
|
|
889
|
+
boxArray = this.collisionBoxArrays.get(bucketInstanceId);
|
|
890
|
+
} else {
|
|
891
|
+
boxArray = new Map<number, {
|
|
892
|
+
text: number[];
|
|
893
|
+
icon: number[];
|
|
894
|
+
}>();
|
|
895
|
+
this.collisionBoxArrays.set(bucketInstanceId, boxArray);
|
|
896
|
+
}
|
|
897
|
+
let realCollisionBox: {
|
|
898
|
+
text: number[];
|
|
899
|
+
icon: number[];
|
|
900
|
+
};
|
|
901
|
+
|
|
902
|
+
if (boxArray.has(symbolIndex)) {
|
|
903
|
+
realCollisionBox = boxArray.get(symbolIndex);
|
|
904
|
+
} else {
|
|
905
|
+
realCollisionBox = {
|
|
906
|
+
text: null,
|
|
907
|
+
icon: null
|
|
908
|
+
};
|
|
909
|
+
boxArray.set(symbolIndex, realCollisionBox);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
if (collisionArrays.textBox) {
|
|
913
|
+
realCollisionBox.text = placedGlyphBoxes.box;
|
|
914
|
+
}
|
|
915
|
+
if (collisionArrays.iconBox) {
|
|
916
|
+
realCollisionBox.icon = placedIconBoxes.box;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
if (placedGlyphCircles) {
|
|
921
|
+
let circleArray = this.collisionCircleArrays[bucketInstanceId];
|
|
922
|
+
|
|
923
|
+
// Group collision circles together by bucket. Circles can't be pushed forward for rendering yet as the symbol placement
|
|
924
|
+
// for a bucket is not guaranteed to be complete before the commit-function has been called
|
|
925
|
+
if (circleArray === undefined)
|
|
926
|
+
circleArray = this.collisionCircleArrays[bucketInstanceId] = new CollisionCircleArray();
|
|
927
|
+
|
|
928
|
+
for (let i = 0; i < placedGlyphCircles.circles.length; i += 4) {
|
|
929
|
+
circleArray.circles.push(placedGlyphCircles.circles[i + 0]); // x
|
|
930
|
+
circleArray.circles.push(placedGlyphCircles.circles[i + 1]); // y
|
|
931
|
+
circleArray.circles.push(placedGlyphCircles.circles[i + 2]); // radius
|
|
932
|
+
circleArray.circles.push(placedGlyphCircles.collisionDetected ? 1 : 0); // collisionDetected-flag
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
markUsedJustification(bucket: SymbolBucket, placedAnchor: TextAnchor, symbolInstance: SymbolInstance, orientation: number) {
|
|
938
|
+
const justifications = {
|
|
939
|
+
'left': symbolInstance.leftJustifiedTextSymbolIndex,
|
|
940
|
+
'center': symbolInstance.centerJustifiedTextSymbolIndex,
|
|
941
|
+
'right': symbolInstance.rightJustifiedTextSymbolIndex
|
|
942
|
+
};
|
|
943
|
+
|
|
944
|
+
let autoIndex;
|
|
945
|
+
if (orientation === WritingMode.vertical) {
|
|
946
|
+
autoIndex = symbolInstance.verticalPlacedTextSymbolIndex;
|
|
947
|
+
} else {
|
|
948
|
+
autoIndex = justifications[getAnchorJustification(placedAnchor)];
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const indexes = [
|
|
952
|
+
symbolInstance.leftJustifiedTextSymbolIndex,
|
|
953
|
+
symbolInstance.centerJustifiedTextSymbolIndex,
|
|
954
|
+
symbolInstance.rightJustifiedTextSymbolIndex,
|
|
955
|
+
symbolInstance.verticalPlacedTextSymbolIndex
|
|
956
|
+
];
|
|
957
|
+
|
|
958
|
+
for (const index of indexes) {
|
|
959
|
+
if (index >= 0) {
|
|
960
|
+
if (autoIndex >= 0 && index !== autoIndex) {
|
|
961
|
+
// There are multiple justifications and this one isn't it: shift offscreen
|
|
962
|
+
bucket.text.placedSymbolArray.get(index).crossTileID = 0;
|
|
963
|
+
} else {
|
|
964
|
+
// Either this is the chosen justification or the justification is hardwired: use this one
|
|
965
|
+
bucket.text.placedSymbolArray.get(index).crossTileID = symbolInstance.crossTileID;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
markUsedOrientation(bucket: SymbolBucket, orientation: number, symbolInstance: SymbolInstance) {
|
|
972
|
+
const horizontal = (orientation === WritingMode.horizontal || orientation === WritingMode.horizontalOnly) ? orientation : 0;
|
|
973
|
+
const vertical = orientation === WritingMode.vertical ? orientation : 0;
|
|
974
|
+
|
|
975
|
+
const horizontalIndexes = [
|
|
976
|
+
symbolInstance.leftJustifiedTextSymbolIndex,
|
|
977
|
+
symbolInstance.centerJustifiedTextSymbolIndex,
|
|
978
|
+
symbolInstance.rightJustifiedTextSymbolIndex
|
|
979
|
+
];
|
|
980
|
+
|
|
981
|
+
for (const index of horizontalIndexes) {
|
|
982
|
+
bucket.text.placedSymbolArray.get(index).placedOrientation = horizontal;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
if (symbolInstance.verticalPlacedTextSymbolIndex) {
|
|
986
|
+
bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).placedOrientation = vertical;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
commit(now: number): void {
|
|
991
|
+
this.commitTime = now;
|
|
992
|
+
this.zoomAtLastRecencyCheck = this.transform.zoom;
|
|
993
|
+
|
|
994
|
+
const prevPlacement = this.prevPlacement;
|
|
995
|
+
let placementChanged = false;
|
|
996
|
+
|
|
997
|
+
this.prevZoomAdjustment = prevPlacement ? prevPlacement.zoomAdjustment(this.transform.zoom) : 0;
|
|
998
|
+
const increment = prevPlacement ? prevPlacement.symbolFadeChange(now) : 1;
|
|
999
|
+
|
|
1000
|
+
const prevOpacities = prevPlacement ? prevPlacement.opacities : {};
|
|
1001
|
+
const prevOffsets = prevPlacement ? prevPlacement.variableOffsets : {};
|
|
1002
|
+
const prevOrientations = prevPlacement ? prevPlacement.placedOrientations : {};
|
|
1003
|
+
|
|
1004
|
+
// add the opacities from the current placement, and copy their current values from the previous placement
|
|
1005
|
+
for (const crossTileID in this.placements) {
|
|
1006
|
+
const jointPlacement = this.placements[crossTileID];
|
|
1007
|
+
const prevOpacity = prevOpacities[crossTileID];
|
|
1008
|
+
if (prevOpacity) {
|
|
1009
|
+
this.opacities[crossTileID] = new JointOpacityState(prevOpacity, increment, jointPlacement.text, jointPlacement.icon);
|
|
1010
|
+
placementChanged = placementChanged ||
|
|
1011
|
+
jointPlacement.text !== prevOpacity.text.placed ||
|
|
1012
|
+
jointPlacement.icon !== prevOpacity.icon.placed;
|
|
1013
|
+
} else {
|
|
1014
|
+
this.opacities[crossTileID] = new JointOpacityState(null, increment, jointPlacement.text, jointPlacement.icon, jointPlacement.skipFade);
|
|
1015
|
+
placementChanged = placementChanged || jointPlacement.text || jointPlacement.icon;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// copy and update values from the previous placement that aren't in the current placement but haven't finished fading
|
|
1020
|
+
for (const crossTileID in prevOpacities) {
|
|
1021
|
+
const prevOpacity = prevOpacities[crossTileID];
|
|
1022
|
+
if (!this.opacities[crossTileID]) {
|
|
1023
|
+
const jointOpacity = new JointOpacityState(prevOpacity, increment, false, false);
|
|
1024
|
+
if (!jointOpacity.isHidden()) {
|
|
1025
|
+
this.opacities[crossTileID] = jointOpacity;
|
|
1026
|
+
placementChanged = placementChanged || prevOpacity.text.placed || prevOpacity.icon.placed;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
for (const crossTileID in prevOffsets) {
|
|
1031
|
+
if (!this.variableOffsets[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) {
|
|
1032
|
+
this.variableOffsets[crossTileID] = prevOffsets[crossTileID];
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
for (const crossTileID in prevOrientations) {
|
|
1037
|
+
if (!this.placedOrientations[crossTileID] && this.opacities[crossTileID] && !this.opacities[crossTileID].isHidden()) {
|
|
1038
|
+
this.placedOrientations[crossTileID] = prevOrientations[crossTileID];
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// this.lastPlacementChangeTime is the time of the last commit() that
|
|
1043
|
+
// resulted in a placement change -- in other words, the start time of
|
|
1044
|
+
// the last symbol fade animation
|
|
1045
|
+
if (prevPlacement && prevPlacement.lastPlacementChangeTime === undefined) {
|
|
1046
|
+
throw new Error('Last placement time for previous placement is not defined');
|
|
1047
|
+
}
|
|
1048
|
+
if (placementChanged) {
|
|
1049
|
+
this.lastPlacementChangeTime = now;
|
|
1050
|
+
} else if (typeof this.lastPlacementChangeTime !== 'number') {
|
|
1051
|
+
this.lastPlacementChangeTime = prevPlacement ? prevPlacement.lastPlacementChangeTime : now;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
updateLayerOpacities(styleLayer: StyleLayer, tiles: Array<Tile>) {
|
|
1056
|
+
const seenCrossTileIDs = {};
|
|
1057
|
+
for (const tile of tiles) {
|
|
1058
|
+
const symbolBucket = tile.getBucket(styleLayer) as SymbolBucket;
|
|
1059
|
+
if (symbolBucket && tile.latestFeatureIndex && styleLayer.id === symbolBucket.layerIds[0]) {
|
|
1060
|
+
this.updateBucketOpacities(symbolBucket, tile.tileID, seenCrossTileIDs, tile.collisionBoxArray);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
updateBucketOpacities(bucket: SymbolBucket, tileID: OverscaledTileID, seenCrossTileIDs: {
|
|
1066
|
+
[k in string | number]: boolean;
|
|
1067
|
+
}, collisionBoxArray?: CollisionBoxArray | null) {
|
|
1068
|
+
if (bucket.hasTextData()) {
|
|
1069
|
+
bucket.text.opacityVertexArray.clear();
|
|
1070
|
+
bucket.text.hasVisibleVertices = false;
|
|
1071
|
+
}
|
|
1072
|
+
if (bucket.hasIconData()) {
|
|
1073
|
+
bucket.icon.opacityVertexArray.clear();
|
|
1074
|
+
bucket.icon.hasVisibleVertices = false;
|
|
1075
|
+
}
|
|
1076
|
+
if (bucket.hasIconCollisionBoxData()) bucket.iconCollisionBox.collisionVertexArray.clear();
|
|
1077
|
+
if (bucket.hasTextCollisionBoxData()) bucket.textCollisionBox.collisionVertexArray.clear();
|
|
1078
|
+
|
|
1079
|
+
const layer = bucket.layers[0];
|
|
1080
|
+
const layout = layer.layout;
|
|
1081
|
+
const duplicateOpacityState = new JointOpacityState(null, 0, false, false, true);
|
|
1082
|
+
const textAllowOverlap = layout.get('text-allow-overlap');
|
|
1083
|
+
const iconAllowOverlap = layout.get('icon-allow-overlap');
|
|
1084
|
+
const hasVariablePlacement = layer._unevaluatedLayout.hasValue('text-variable-anchor') || layer._unevaluatedLayout.hasValue('text-variable-anchor-offset');
|
|
1085
|
+
const rotateWithMap = layout.get('text-rotation-alignment') === 'map';
|
|
1086
|
+
const pitchWithMap = layout.get('text-pitch-alignment') === 'map';
|
|
1087
|
+
const hasIconTextFit = layout.get('icon-text-fit') !== 'none';
|
|
1088
|
+
// If allow-overlap is true, we can show symbols before placement runs on them
|
|
1089
|
+
// But we have to wait for placement if we potentially depend on a paired icon/text
|
|
1090
|
+
// with allow-overlap: false.
|
|
1091
|
+
// See https://github.com/mapbox/mapbox-gl-js/issues/7032
|
|
1092
|
+
const defaultOpacityState = new JointOpacityState(null, 0,
|
|
1093
|
+
textAllowOverlap && (iconAllowOverlap || !bucket.hasIconData() || layout.get('icon-optional')),
|
|
1094
|
+
iconAllowOverlap && (textAllowOverlap || !bucket.hasTextData() || layout.get('text-optional')),
|
|
1095
|
+
true);
|
|
1096
|
+
|
|
1097
|
+
if (!bucket.collisionArrays && collisionBoxArray && ((bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData()))) {
|
|
1098
|
+
bucket.deserializeCollisionBoxes(collisionBoxArray);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
const addOpacities = (iconOrText, numVertices: number, opacity: number) => {
|
|
1102
|
+
for (let i = 0; i < numVertices / 4; i++) {
|
|
1103
|
+
iconOrText.opacityVertexArray.emplaceBack(opacity);
|
|
1104
|
+
}
|
|
1105
|
+
iconOrText.hasVisibleVertices = iconOrText.hasVisibleVertices || (opacity !== PACKED_HIDDEN_OPACITY);
|
|
1106
|
+
};
|
|
1107
|
+
|
|
1108
|
+
const boxArrays = this.collisionBoxArrays.get(bucket.bucketInstanceId);
|
|
1109
|
+
|
|
1110
|
+
for (let s = 0; s < bucket.symbolInstances.length; s++) {
|
|
1111
|
+
const symbolInstance = bucket.symbolInstances.get(s);
|
|
1112
|
+
const {
|
|
1113
|
+
numHorizontalGlyphVertices,
|
|
1114
|
+
numVerticalGlyphVertices,
|
|
1115
|
+
crossTileID
|
|
1116
|
+
} = symbolInstance;
|
|
1117
|
+
|
|
1118
|
+
const isDuplicate = seenCrossTileIDs[crossTileID];
|
|
1119
|
+
|
|
1120
|
+
let opacityState = this.opacities[crossTileID];
|
|
1121
|
+
if (isDuplicate) {
|
|
1122
|
+
opacityState = duplicateOpacityState;
|
|
1123
|
+
} else if (!opacityState) {
|
|
1124
|
+
opacityState = defaultOpacityState;
|
|
1125
|
+
// store the state so that future placements use it as a starting point
|
|
1126
|
+
this.opacities[crossTileID] = opacityState;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
seenCrossTileIDs[crossTileID] = true;
|
|
1130
|
+
|
|
1131
|
+
const hasText = numHorizontalGlyphVertices > 0 || numVerticalGlyphVertices > 0;
|
|
1132
|
+
const hasIcon = symbolInstance.numIconVertices > 0;
|
|
1133
|
+
|
|
1134
|
+
const placedOrientation = this.placedOrientations[symbolInstance.crossTileID];
|
|
1135
|
+
const horizontalHidden = placedOrientation === WritingMode.vertical;
|
|
1136
|
+
const verticalHidden = placedOrientation === WritingMode.horizontal || placedOrientation === WritingMode.horizontalOnly;
|
|
1137
|
+
|
|
1138
|
+
if (hasText) {
|
|
1139
|
+
const packedOpacity = packOpacity(opacityState.text);
|
|
1140
|
+
// Vertical text fades in/out on collision the same way as corresponding
|
|
1141
|
+
// horizontal text. Switch between vertical/horizontal should be instantaneous
|
|
1142
|
+
const horizontalOpacity = horizontalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
|
|
1143
|
+
addOpacities(bucket.text, numHorizontalGlyphVertices, horizontalOpacity);
|
|
1144
|
+
const verticalOpacity = verticalHidden ? PACKED_HIDDEN_OPACITY : packedOpacity;
|
|
1145
|
+
addOpacities(bucket.text, numVerticalGlyphVertices, verticalOpacity);
|
|
1146
|
+
|
|
1147
|
+
// If this label is completely faded, mark it so that we don't have to calculate
|
|
1148
|
+
// its position at render time. If this layer has variable placement, shift the various
|
|
1149
|
+
// symbol instances appropriately so that symbols from buckets that have yet to be placed
|
|
1150
|
+
// offset appropriately.
|
|
1151
|
+
const symbolHidden = opacityState.text.isHidden();
|
|
1152
|
+
[
|
|
1153
|
+
symbolInstance.rightJustifiedTextSymbolIndex,
|
|
1154
|
+
symbolInstance.centerJustifiedTextSymbolIndex,
|
|
1155
|
+
symbolInstance.leftJustifiedTextSymbolIndex
|
|
1156
|
+
].forEach(index => {
|
|
1157
|
+
if (index >= 0) {
|
|
1158
|
+
bucket.text.placedSymbolArray.get(index).hidden = symbolHidden || horizontalHidden ? 1 : 0;
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
if (symbolInstance.verticalPlacedTextSymbolIndex >= 0) {
|
|
1163
|
+
bucket.text.placedSymbolArray.get(symbolInstance.verticalPlacedTextSymbolIndex).hidden = symbolHidden || verticalHidden ? 1 : 0;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
const prevOffset = this.variableOffsets[symbolInstance.crossTileID];
|
|
1167
|
+
if (prevOffset) {
|
|
1168
|
+
this.markUsedJustification(bucket, prevOffset.anchor, symbolInstance, placedOrientation);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
const prevOrientation = this.placedOrientations[symbolInstance.crossTileID];
|
|
1172
|
+
if (prevOrientation) {
|
|
1173
|
+
this.markUsedJustification(bucket, 'left', symbolInstance, prevOrientation);
|
|
1174
|
+
this.markUsedOrientation(bucket, prevOrientation, symbolInstance);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
if (hasIcon) {
|
|
1179
|
+
const packedOpacity = packOpacity(opacityState.icon);
|
|
1180
|
+
|
|
1181
|
+
const useHorizontal = !(hasIconTextFit && symbolInstance.verticalPlacedIconSymbolIndex && horizontalHidden);
|
|
1182
|
+
|
|
1183
|
+
if (symbolInstance.placedIconSymbolIndex >= 0) {
|
|
1184
|
+
const horizontalOpacity = useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
|
|
1185
|
+
addOpacities(bucket.icon, symbolInstance.numIconVertices, horizontalOpacity);
|
|
1186
|
+
bucket.icon.placedSymbolArray.get(symbolInstance.placedIconSymbolIndex).hidden =
|
|
1187
|
+
(opacityState.icon.isHidden() as any);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
if (symbolInstance.verticalPlacedIconSymbolIndex >= 0) {
|
|
1191
|
+
const verticalOpacity = !useHorizontal ? packedOpacity : PACKED_HIDDEN_OPACITY;
|
|
1192
|
+
addOpacities(bucket.icon, symbolInstance.numVerticalIconVertices, verticalOpacity);
|
|
1193
|
+
bucket.icon.placedSymbolArray.get(symbolInstance.verticalPlacedIconSymbolIndex).hidden =
|
|
1194
|
+
(opacityState.icon.isHidden() as any);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
const realBoxes = (boxArrays && boxArrays.has(s)) ? boxArrays.get(s) : {
|
|
1199
|
+
text: null,
|
|
1200
|
+
icon: null
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
if (bucket.hasIconCollisionBoxData() || bucket.hasTextCollisionBoxData()) {
|
|
1204
|
+
const collisionArrays = bucket.collisionArrays[s];
|
|
1205
|
+
if (collisionArrays) {
|
|
1206
|
+
let shift = new Point(0, 0);
|
|
1207
|
+
if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
|
|
1208
|
+
let used = true;
|
|
1209
|
+
if (hasVariablePlacement) {
|
|
1210
|
+
const variableOffset = this.variableOffsets[crossTileID];
|
|
1211
|
+
if (variableOffset) {
|
|
1212
|
+
// This will show either the currently placed position or the last
|
|
1213
|
+
// successfully placed position (so you can visualize what collision
|
|
1214
|
+
// just made the symbol disappear, and the most likely place for the
|
|
1215
|
+
// symbol to come back)
|
|
1216
|
+
shift = calculateVariableLayoutShift(variableOffset.anchor,
|
|
1217
|
+
variableOffset.width,
|
|
1218
|
+
variableOffset.height,
|
|
1219
|
+
variableOffset.textOffset,
|
|
1220
|
+
variableOffset.textBoxScale);
|
|
1221
|
+
if (rotateWithMap) {
|
|
1222
|
+
shift._rotate(pitchWithMap ? this.transform.angle : -this.transform.angle);
|
|
1223
|
+
}
|
|
1224
|
+
} else {
|
|
1225
|
+
// No offset -> this symbol hasn't been placed since coming on-screen
|
|
1226
|
+
// No single box is particularly meaningful and all of them would be too noisy
|
|
1227
|
+
// Use the center box just to show something's there, but mark it "not used"
|
|
1228
|
+
used = false;
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
if (collisionArrays.textBox || collisionArrays.verticalTextBox) {
|
|
1233
|
+
let hidden: boolean;
|
|
1234
|
+
if (collisionArrays.textBox) {
|
|
1235
|
+
hidden = horizontalHidden;
|
|
1236
|
+
}
|
|
1237
|
+
if (collisionArrays.verticalTextBox) {
|
|
1238
|
+
hidden = verticalHidden;
|
|
1239
|
+
}
|
|
1240
|
+
updateCollisionVertices(bucket.textCollisionBox.collisionVertexArray, opacityState.text.placed, !used || hidden, realBoxes.text, shift.x, shift.y);
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
if (collisionArrays.iconBox || collisionArrays.verticalIconBox) {
|
|
1245
|
+
const verticalIconUsed = Boolean(!verticalHidden && collisionArrays.verticalIconBox);
|
|
1246
|
+
let hidden: boolean;
|
|
1247
|
+
if (collisionArrays.iconBox) {
|
|
1248
|
+
hidden = verticalIconUsed;
|
|
1249
|
+
}
|
|
1250
|
+
if (collisionArrays.verticalIconBox) {
|
|
1251
|
+
hidden = !verticalIconUsed;
|
|
1252
|
+
}
|
|
1253
|
+
updateCollisionVertices(bucket.iconCollisionBox.collisionVertexArray, opacityState.icon.placed, hidden, realBoxes.icon,
|
|
1254
|
+
hasIconTextFit ? shift.x : 0,
|
|
1255
|
+
hasIconTextFit ? shift.y : 0);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
bucket.sortFeatures(this.transform.angle);
|
|
1262
|
+
if (this.retainedQueryData[bucket.bucketInstanceId]) {
|
|
1263
|
+
this.retainedQueryData[bucket.bucketInstanceId].featureSortOrder = bucket.featureSortOrder;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
if (bucket.hasTextData() && bucket.text.opacityVertexBuffer) {
|
|
1267
|
+
bucket.text.opacityVertexBuffer.updateData(bucket.text.opacityVertexArray);
|
|
1268
|
+
}
|
|
1269
|
+
if (bucket.hasIconData() && bucket.icon.opacityVertexBuffer) {
|
|
1270
|
+
bucket.icon.opacityVertexBuffer.updateData(bucket.icon.opacityVertexArray);
|
|
1271
|
+
}
|
|
1272
|
+
if (bucket.hasIconCollisionBoxData() && bucket.iconCollisionBox.collisionVertexBuffer) {
|
|
1273
|
+
bucket.iconCollisionBox.collisionVertexBuffer.updateData(bucket.iconCollisionBox.collisionVertexArray);
|
|
1274
|
+
}
|
|
1275
|
+
if (bucket.hasTextCollisionBoxData() && bucket.textCollisionBox.collisionVertexBuffer) {
|
|
1276
|
+
bucket.textCollisionBox.collisionVertexBuffer.updateData(bucket.textCollisionBox.collisionVertexArray);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
if (bucket.text.opacityVertexArray.length !== bucket.text.layoutVertexArray.length / 4) throw new Error(`bucket.text.opacityVertexArray.length (= ${bucket.text.opacityVertexArray.length}) !== bucket.text.layoutVertexArray.length (= ${bucket.text.layoutVertexArray.length}) / 4`);
|
|
1280
|
+
if (bucket.icon.opacityVertexArray.length !== bucket.icon.layoutVertexArray.length / 4) throw new Error(`bucket.icon.opacityVertexArray.length (= ${bucket.icon.opacityVertexArray.length}) !== bucket.icon.layoutVertexArray.length (= ${bucket.icon.layoutVertexArray.length}) / 4`);
|
|
1281
|
+
|
|
1282
|
+
// Push generated collision circles to the bucket for debug rendering
|
|
1283
|
+
if (bucket.bucketInstanceId in this.collisionCircleArrays) {
|
|
1284
|
+
const instance = this.collisionCircleArrays[bucket.bucketInstanceId];
|
|
1285
|
+
|
|
1286
|
+
bucket.placementInvProjMatrix = instance.invProjMatrix;
|
|
1287
|
+
bucket.placementViewportMatrix = instance.viewportMatrix;
|
|
1288
|
+
bucket.collisionCircleArray = instance.circles;
|
|
1289
|
+
|
|
1290
|
+
delete this.collisionCircleArrays[bucket.bucketInstanceId];
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
symbolFadeChange(now: number) {
|
|
1295
|
+
return this.fadeDuration === 0 ?
|
|
1296
|
+
1 :
|
|
1297
|
+
((now - this.commitTime) / this.fadeDuration + this.prevZoomAdjustment);
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
zoomAdjustment(zoom: number) {
|
|
1301
|
+
// When zooming out quickly, labels can overlap each other. This
|
|
1302
|
+
// adjustment is used to reduce the interval between placement calculations
|
|
1303
|
+
// and to reduce the fade duration when zooming out quickly. Discovering the
|
|
1304
|
+
// collisions more quickly and fading them more quickly reduces the unwanted effect.
|
|
1305
|
+
return Math.max(0, (this.transform.zoom - zoom) / 1.5);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
hasTransitions(now: number) {
|
|
1309
|
+
return this.stale ||
|
|
1310
|
+
now - this.lastPlacementChangeTime < this.fadeDuration;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
stillRecent(now: number, zoom: number) {
|
|
1314
|
+
// The adjustment makes placement more frequent when zooming.
|
|
1315
|
+
// This condition applies the adjustment only after the map has
|
|
1316
|
+
// stopped zooming. This avoids adding extra jank while zooming.
|
|
1317
|
+
const durationAdjustment = this.zoomAtLastRecencyCheck === zoom ?
|
|
1318
|
+
(1 - this.zoomAdjustment(zoom)) :
|
|
1319
|
+
1;
|
|
1320
|
+
this.zoomAtLastRecencyCheck = zoom;
|
|
1321
|
+
|
|
1322
|
+
return this.commitTime + this.fadeDuration * durationAdjustment > now;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
setStale() {
|
|
1326
|
+
this.stale = true;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
function updateCollisionVertices(collisionVertexArray: CollisionVertexArray, placed: boolean, notUsed: boolean | number, realBox: Array<number>, shiftX?: number, shiftY?: number) {
|
|
1331
|
+
if (!realBox || realBox.length === 0) {
|
|
1332
|
+
realBox = [0, 0, 0, 0];
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
const tlX = realBox[0] - viewportPadding;
|
|
1336
|
+
const tlY = realBox[1] - viewportPadding;
|
|
1337
|
+
const brX = realBox[2] - viewportPadding;
|
|
1338
|
+
const brY = realBox[3] - viewportPadding;
|
|
1339
|
+
|
|
1340
|
+
collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, tlX, tlY);
|
|
1341
|
+
collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, brX, tlY);
|
|
1342
|
+
collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, brX, brY);
|
|
1343
|
+
collisionVertexArray.emplaceBack(placed ? 1 : 0, notUsed ? 1 : 0, shiftX || 0, shiftY || 0, tlX, brY);
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// All four vertices for a glyph will have the same opacity state
|
|
1347
|
+
// So we pack the opacity into a uint8, and then repeat it four times
|
|
1348
|
+
// to make a single uint32 that we can upload for each glyph in the
|
|
1349
|
+
// label.
|
|
1350
|
+
const shift25 = Math.pow(2, 25);
|
|
1351
|
+
const shift24 = Math.pow(2, 24);
|
|
1352
|
+
const shift17 = Math.pow(2, 17);
|
|
1353
|
+
const shift16 = Math.pow(2, 16);
|
|
1354
|
+
const shift9 = Math.pow(2, 9);
|
|
1355
|
+
const shift8 = Math.pow(2, 8);
|
|
1356
|
+
const shift1 = Math.pow(2, 1);
|
|
1357
|
+
function packOpacity(opacityState: OpacityState): number {
|
|
1358
|
+
if (opacityState.opacity === 0 && !opacityState.placed) {
|
|
1359
|
+
return 0;
|
|
1360
|
+
} else if (opacityState.opacity === 1 && opacityState.placed) {
|
|
1361
|
+
return 4294967295;
|
|
1362
|
+
}
|
|
1363
|
+
const targetBit = opacityState.placed ? 1 : 0;
|
|
1364
|
+
const opacityBits = Math.floor(opacityState.opacity * 127);
|
|
1365
|
+
return opacityBits * shift25 + targetBit * shift24 +
|
|
1366
|
+
opacityBits * shift17 + targetBit * shift16 +
|
|
1367
|
+
opacityBits * shift9 + targetBit * shift8 +
|
|
1368
|
+
opacityBits * shift1 + targetBit;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
const PACKED_HIDDEN_OPACITY = 0;
|