@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.
Files changed (565) hide show
  1. package/LICENSE.txt +116 -0
  2. package/README.md +174 -0
  3. package/build/banner.ts +12 -0
  4. package/build/bump-version-changelog.js +26 -0
  5. package/build/check-bundle-size.js +77 -0
  6. package/build/generate-dist-package.js +26 -0
  7. package/build/generate-doc-images.ts +78 -0
  8. package/build/generate-docs.ts +190 -0
  9. package/build/generate-shaders.ts +68 -0
  10. package/build/generate-struct-arrays.ts +438 -0
  11. package/build/generate-style-code.ts +283 -0
  12. package/build/readme.md +56 -0
  13. package/build/release-notes.js +45 -0
  14. package/build/rollup/bundle_prelude.js +29 -0
  15. package/build/rollup/maplibregl.js +22 -0
  16. package/build/rollup_plugins.ts +58 -0
  17. package/dist/LICENSE.txt +116 -0
  18. package/dist/maplibre-gl.css +1 -0
  19. package/dist/maplibre-gl.d.ts +13304 -0
  20. package/dist/maplibre-gl.js +59 -0
  21. package/dist/maplibre-gl.js.map +1 -0
  22. package/dist/package.json +1 -0
  23. package/package.json +192 -0
  24. package/src/css/maplibre-gl.css +854 -0
  25. package/src/css/svg/maplibregl-ctrl-attrib.svg +3 -0
  26. package/src/css/svg/maplibregl-ctrl-compass.svg +4 -0
  27. package/src/css/svg/maplibregl-ctrl-fullscreen.svg +3 -0
  28. package/src/css/svg/maplibregl-ctrl-geolocate.svg +5 -0
  29. package/src/css/svg/maplibregl-ctrl-logo.svg +11 -0
  30. package/src/css/svg/maplibregl-ctrl-shrink.svg +3 -0
  31. package/src/css/svg/maplibregl-ctrl-terrain.svg +3 -0
  32. package/src/css/svg/maplibregl-ctrl-zoom-in.svg +3 -0
  33. package/src/css/svg/maplibregl-ctrl-zoom-out.svg +3 -0
  34. package/src/data/array_types.g.ts +1130 -0
  35. package/src/data/bucket/circle_attributes.ts +8 -0
  36. package/src/data/bucket/circle_bucket.ts +197 -0
  37. package/src/data/bucket/fill_attributes.ts +8 -0
  38. package/src/data/bucket/fill_bucket.test.ts +95 -0
  39. package/src/data/bucket/fill_bucket.ts +228 -0
  40. package/src/data/bucket/fill_extrusion_attributes.ts +13 -0
  41. package/src/data/bucket/fill_extrusion_bucket.ts +305 -0
  42. package/src/data/bucket/heatmap_bucket.ts +12 -0
  43. package/src/data/bucket/line_attributes.ts +8 -0
  44. package/src/data/bucket/line_attributes_ext.ts +8 -0
  45. package/src/data/bucket/line_bucket.test.ts +142 -0
  46. package/src/data/bucket/line_bucket.ts +592 -0
  47. package/src/data/bucket/pattern_attributes.ts +9 -0
  48. package/src/data/bucket/pattern_bucket_features.ts +57 -0
  49. package/src/data/bucket/symbol_attributes.ts +122 -0
  50. package/src/data/bucket/symbol_bucket.test.ts +243 -0
  51. package/src/data/bucket/symbol_bucket.ts +975 -0
  52. package/src/data/bucket.ts +123 -0
  53. package/src/data/dem_data.test.ts +237 -0
  54. package/src/data/dem_data.ts +171 -0
  55. package/src/data/evaluation_feature.ts +18 -0
  56. package/src/data/extent.ts +13 -0
  57. package/src/data/feature_index.ts +337 -0
  58. package/src/data/feature_position_map.test.ts +33 -0
  59. package/src/data/feature_position_map.ts +126 -0
  60. package/src/data/index_array_type.ts +9 -0
  61. package/src/data/load_geometry.test.ts +49 -0
  62. package/src/data/load_geometry.ts +44 -0
  63. package/src/data/pos3d_attributes.ts +5 -0
  64. package/src/data/pos_attributes.ts +5 -0
  65. package/src/data/program_configuration.ts +735 -0
  66. package/src/data/raster_bounds_attributes.ts +6 -0
  67. package/src/data/segment.ts +89 -0
  68. package/src/geo/edge_insets.test.ts +83 -0
  69. package/src/geo/edge_insets.ts +146 -0
  70. package/src/geo/lng_lat.test.ts +65 -0
  71. package/src/geo/lng_lat.ts +169 -0
  72. package/src/geo/lng_lat_bounds.test.ts +357 -0
  73. package/src/geo/lng_lat_bounds.ts +356 -0
  74. package/src/geo/mercator_coordinate.test.ts +34 -0
  75. package/src/geo/mercator_coordinate.ts +157 -0
  76. package/src/geo/projection/projection.ts +72 -0
  77. package/src/geo/transform.test.ts +509 -0
  78. package/src/geo/transform.ts +1072 -0
  79. package/src/gl/color_mode.ts +31 -0
  80. package/src/gl/context.ts +321 -0
  81. package/src/gl/cull_face_mode.ts +22 -0
  82. package/src/gl/depth_mode.ts +26 -0
  83. package/src/gl/framebuffer.ts +48 -0
  84. package/src/gl/index_buffer.ts +55 -0
  85. package/src/gl/render_pool.test.ts +69 -0
  86. package/src/gl/render_pool.ts +93 -0
  87. package/src/gl/state.test.ts +125 -0
  88. package/src/gl/stencil_mode.ts +27 -0
  89. package/src/gl/types.ts +59 -0
  90. package/src/gl/value.ts +534 -0
  91. package/src/gl/vertex_buffer.test.ts +57 -0
  92. package/src/gl/vertex_buffer.ts +111 -0
  93. package/src/gl/webgl2.ts +12 -0
  94. package/src/index.test.ts +121 -0
  95. package/src/index.ts +250 -0
  96. package/src/render/draw_background.ts +53 -0
  97. package/src/render/draw_circle.ts +113 -0
  98. package/src/render/draw_collision_debug.ts +168 -0
  99. package/src/render/draw_custom.test.ts +73 -0
  100. package/src/render/draw_custom.ts +45 -0
  101. package/src/render/draw_debug.test.ts +122 -0
  102. package/src/render/draw_debug.ts +148 -0
  103. package/src/render/draw_fill.test.ts +137 -0
  104. package/src/render/draw_fill.ts +128 -0
  105. package/src/render/draw_fill_extrusion.ts +97 -0
  106. package/src/render/draw_heatmap.ts +228 -0
  107. package/src/render/draw_hillshade.ts +119 -0
  108. package/src/render/draw_line.ts +125 -0
  109. package/src/render/draw_raster.ts +133 -0
  110. package/src/render/draw_sky.ts +44 -0
  111. package/src/render/draw_symbol.test.ts +223 -0
  112. package/src/render/draw_symbol.ts +499 -0
  113. package/src/render/draw_terrain.ts +99 -0
  114. package/src/render/glyph_atlas.ts +83 -0
  115. package/src/render/glyph_manager.test.ts +147 -0
  116. package/src/render/glyph_manager.ts +198 -0
  117. package/src/render/image_atlas.ts +159 -0
  118. package/src/render/image_manager.ts +337 -0
  119. package/src/render/line_atlas.test.ts +38 -0
  120. package/src/render/line_atlas.ts +214 -0
  121. package/src/render/mesh.ts +25 -0
  122. package/src/render/painter.test.ts +51 -0
  123. package/src/render/painter.ts +666 -0
  124. package/src/render/program/background_program.ts +100 -0
  125. package/src/render/program/circle_program.ts +61 -0
  126. package/src/render/program/clipping_mask_program.ts +19 -0
  127. package/src/render/program/collision_program.ts +47 -0
  128. package/src/render/program/debug_program.ts +29 -0
  129. package/src/render/program/fill_extrusion_program.ts +120 -0
  130. package/src/render/program/fill_program.ts +121 -0
  131. package/src/render/program/heatmap_program.ts +76 -0
  132. package/src/render/program/hillshade_program.ts +116 -0
  133. package/src/render/program/line_program.ts +207 -0
  134. package/src/render/program/pattern.ts +102 -0
  135. package/src/render/program/program_uniforms.ts +46 -0
  136. package/src/render/program/raster_program.ts +88 -0
  137. package/src/render/program/sky_program.ts +28 -0
  138. package/src/render/program/symbol_program.ts +266 -0
  139. package/src/render/program/terrain_program.ts +116 -0
  140. package/src/render/program.ts +233 -0
  141. package/src/render/render_to_texture.test.ts +151 -0
  142. package/src/render/render_to_texture.ts +197 -0
  143. package/src/render/terrain.test.ts +322 -0
  144. package/src/render/terrain.ts +464 -0
  145. package/src/render/texture.ts +114 -0
  146. package/src/render/uniform_binding.test.ts +122 -0
  147. package/src/render/uniform_binding.ts +157 -0
  148. package/src/render/update_pattern_positions_in_program.test.ts +74 -0
  149. package/src/render/update_pattern_positions_in_program.ts +50 -0
  150. package/src/render/vertex_array_object.ts +163 -0
  151. package/src/shaders/README.md +42 -0
  152. package/src/shaders/_prelude.fragment.glsl +19 -0
  153. package/src/shaders/_prelude.fragment.glsl.g.ts +2 -0
  154. package/src/shaders/_prelude.vertex.glsl +148 -0
  155. package/src/shaders/_prelude.vertex.glsl.g.ts +2 -0
  156. package/src/shaders/background.fragment.glsl +10 -0
  157. package/src/shaders/background.fragment.glsl.g.ts +2 -0
  158. package/src/shaders/background.vertex.glsl +7 -0
  159. package/src/shaders/background.vertex.glsl.g.ts +2 -0
  160. package/src/shaders/background_pattern.fragment.glsl +28 -0
  161. package/src/shaders/background_pattern.fragment.glsl.g.ts +2 -0
  162. package/src/shaders/background_pattern.vertex.glsl +19 -0
  163. package/src/shaders/background_pattern.vertex.glsl.g.ts +2 -0
  164. package/src/shaders/circle.fragment.glsl +34 -0
  165. package/src/shaders/circle.fragment.glsl.g.ts +2 -0
  166. package/src/shaders/circle.vertex.glsl +68 -0
  167. package/src/shaders/circle.vertex.glsl.g.ts +2 -0
  168. package/src/shaders/clipping_mask.fragment.glsl +3 -0
  169. package/src/shaders/clipping_mask.fragment.glsl.g.ts +2 -0
  170. package/src/shaders/clipping_mask.vertex.glsl +7 -0
  171. package/src/shaders/clipping_mask.vertex.glsl.g.ts +2 -0
  172. package/src/shaders/collision_box.fragment.glsl +20 -0
  173. package/src/shaders/collision_box.fragment.glsl.g.ts +2 -0
  174. package/src/shaders/collision_box.vertex.glsl +27 -0
  175. package/src/shaders/collision_box.vertex.glsl.g.ts +2 -0
  176. package/src/shaders/collision_circle.fragment.glsl +17 -0
  177. package/src/shaders/collision_circle.fragment.glsl.g.ts +2 -0
  178. package/src/shaders/collision_circle.vertex.glsl +59 -0
  179. package/src/shaders/collision_circle.vertex.glsl.g.ts +2 -0
  180. package/src/shaders/debug.fragment.glsl +9 -0
  181. package/src/shaders/debug.fragment.glsl.g.ts +2 -0
  182. package/src/shaders/debug.vertex.glsl +12 -0
  183. package/src/shaders/debug.vertex.glsl.g.ts +2 -0
  184. package/src/shaders/encode_attribute.test.ts +11 -0
  185. package/src/shaders/encode_attribute.ts +13 -0
  186. package/src/shaders/fill.fragment.glsl +13 -0
  187. package/src/shaders/fill.fragment.glsl.g.ts +2 -0
  188. package/src/shaders/fill.vertex.glsl +13 -0
  189. package/src/shaders/fill.vertex.glsl.g.ts +2 -0
  190. package/src/shaders/fill_extrusion.fragment.glsl +9 -0
  191. package/src/shaders/fill_extrusion.fragment.glsl.g.ts +2 -0
  192. package/src/shaders/fill_extrusion.vertex.glsl +84 -0
  193. package/src/shaders/fill_extrusion.vertex.glsl.g.ts +2 -0
  194. package/src/shaders/fill_extrusion_pattern.fragment.glsl +47 -0
  195. package/src/shaders/fill_extrusion_pattern.fragment.glsl.g.ts +2 -0
  196. package/src/shaders/fill_extrusion_pattern.vertex.glsl +96 -0
  197. package/src/shaders/fill_extrusion_pattern.vertex.glsl.g.ts +2 -0
  198. package/src/shaders/fill_outline.fragment.glsl +17 -0
  199. package/src/shaders/fill_outline.fragment.glsl.g.ts +2 -0
  200. package/src/shaders/fill_outline.vertex.glsl +17 -0
  201. package/src/shaders/fill_outline.vertex.glsl.g.ts +2 -0
  202. package/src/shaders/fill_outline_pattern.fragment.glsl +43 -0
  203. package/src/shaders/fill_outline_pattern.fragment.glsl.g.ts +2 -0
  204. package/src/shaders/fill_outline_pattern.vertex.glsl +44 -0
  205. package/src/shaders/fill_outline_pattern.vertex.glsl.g.ts +2 -0
  206. package/src/shaders/fill_pattern.fragment.glsl +39 -0
  207. package/src/shaders/fill_pattern.fragment.glsl.g.ts +2 -0
  208. package/src/shaders/fill_pattern.vertex.glsl +39 -0
  209. package/src/shaders/fill_pattern.vertex.glsl.g.ts +2 -0
  210. package/src/shaders/heatmap.fragment.glsl +22 -0
  211. package/src/shaders/heatmap.fragment.glsl.g.ts +2 -0
  212. package/src/shaders/heatmap.vertex.glsl +54 -0
  213. package/src/shaders/heatmap.vertex.glsl.g.ts +2 -0
  214. package/src/shaders/heatmap_texture.fragment.glsl +15 -0
  215. package/src/shaders/heatmap_texture.fragment.glsl.g.ts +2 -0
  216. package/src/shaders/heatmap_texture.vertex.glsl +11 -0
  217. package/src/shaders/heatmap_texture.vertex.glsl.g.ts +2 -0
  218. package/src/shaders/hillshade.fragment.glsl +52 -0
  219. package/src/shaders/hillshade.fragment.glsl.g.ts +2 -0
  220. package/src/shaders/hillshade.vertex.glsl +11 -0
  221. package/src/shaders/hillshade.vertex.glsl.g.ts +2 -0
  222. package/src/shaders/hillshade_prepare.fragment.glsl +77 -0
  223. package/src/shaders/hillshade_prepare.fragment.glsl.g.ts +2 -0
  224. package/src/shaders/hillshade_prepare.vertex.glsl +15 -0
  225. package/src/shaders/hillshade_prepare.vertex.glsl.g.ts +2 -0
  226. package/src/shaders/line.fragment.glsl +30 -0
  227. package/src/shaders/line.fragment.glsl.g.ts +2 -0
  228. package/src/shaders/line.vertex.glsl +89 -0
  229. package/src/shaders/line.vertex.glsl.g.ts +2 -0
  230. package/src/shaders/line_gradient.fragment.glsl +34 -0
  231. package/src/shaders/line_gradient.fragment.glsl.g.ts +2 -0
  232. package/src/shaders/line_gradient.vertex.glsl +92 -0
  233. package/src/shaders/line_gradient.vertex.glsl.g.ts +2 -0
  234. package/src/shaders/line_pattern.fragment.glsl +77 -0
  235. package/src/shaders/line_pattern.fragment.glsl.g.ts +2 -0
  236. package/src/shaders/line_pattern.vertex.glsl +103 -0
  237. package/src/shaders/line_pattern.vertex.glsl.g.ts +2 -0
  238. package/src/shaders/line_sdf.fragment.glsl +45 -0
  239. package/src/shaders/line_sdf.fragment.glsl.g.ts +2 -0
  240. package/src/shaders/line_sdf.vertex.glsl +101 -0
  241. package/src/shaders/line_sdf.vertex.glsl.g.ts +2 -0
  242. package/src/shaders/raster.fragment.glsl +53 -0
  243. package/src/shaders/raster.fragment.glsl.g.ts +2 -0
  244. package/src/shaders/raster.vertex.glsl +21 -0
  245. package/src/shaders/raster.vertex.glsl.g.ts +2 -0
  246. package/src/shaders/shaders.ts +197 -0
  247. package/src/shaders/sky.fragment.glsl +17 -0
  248. package/src/shaders/sky.fragment.glsl.g.ts +2 -0
  249. package/src/shaders/sky.vertex.glsl +5 -0
  250. package/src/shaders/sky.vertex.glsl.g.ts +2 -0
  251. package/src/shaders/symbol_icon.fragment.glsl +17 -0
  252. package/src/shaders/symbol_icon.fragment.glsl.g.ts +2 -0
  253. package/src/shaders/symbol_icon.vertex.glsl +117 -0
  254. package/src/shaders/symbol_icon.vertex.glsl.g.ts +2 -0
  255. package/src/shaders/symbol_sdf.fragment.glsl +58 -0
  256. package/src/shaders/symbol_sdf.fragment.glsl.g.ts +2 -0
  257. package/src/shaders/symbol_sdf.vertex.glsl +142 -0
  258. package/src/shaders/symbol_sdf.vertex.glsl.g.ts +2 -0
  259. package/src/shaders/symbol_text_and_icon.fragment.glsl +68 -0
  260. package/src/shaders/symbol_text_and_icon.fragment.glsl.g.ts +2 -0
  261. package/src/shaders/symbol_text_and_icon.vertex.glsl +140 -0
  262. package/src/shaders/symbol_text_and_icon.vertex.glsl.g.ts +2 -0
  263. package/src/shaders/terrain.fragment.glsl +32 -0
  264. package/src/shaders/terrain.fragment.glsl.g.ts +2 -0
  265. package/src/shaders/terrain.vertex.glsl +17 -0
  266. package/src/shaders/terrain.vertex.glsl.g.ts +2 -0
  267. package/src/shaders/terrain_coords.fragment.glsl +11 -0
  268. package/src/shaders/terrain_coords.fragment.glsl.g.ts +2 -0
  269. package/src/shaders/terrain_coords.vertex.glsl +13 -0
  270. package/src/shaders/terrain_coords.vertex.glsl.g.ts +2 -0
  271. package/src/shaders/terrain_depth.fragment.glsl +15 -0
  272. package/src/shaders/terrain_depth.fragment.glsl.g.ts +2 -0
  273. package/src/shaders/terrain_depth.vertex.glsl +13 -0
  274. package/src/shaders/terrain_depth.vertex.glsl.g.ts +2 -0
  275. package/src/source/canvas_source.test.ts +210 -0
  276. package/src/source/canvas_source.ts +227 -0
  277. package/src/source/geojson_source.test.ts +492 -0
  278. package/src/source/geojson_source.ts +431 -0
  279. package/src/source/geojson_source_diff.test.ts +364 -0
  280. package/src/source/geojson_source_diff.ts +172 -0
  281. package/src/source/geojson_worker_source.test.ts +399 -0
  282. package/src/source/geojson_worker_source.ts +303 -0
  283. package/src/source/geojson_wrapper.test.ts +32 -0
  284. package/src/source/geojson_wrapper.ts +81 -0
  285. package/src/source/image_source.test.ts +235 -0
  286. package/src/source/image_source.ts +338 -0
  287. package/src/source/load_tilejson.ts +47 -0
  288. package/src/source/pixels_to_tile_units.ts +25 -0
  289. package/src/source/protocol_crud.ts +48 -0
  290. package/src/source/query_features.test.ts +31 -0
  291. package/src/source/query_features.ts +252 -0
  292. package/src/source/raster_dem_tile_source.test.ts +158 -0
  293. package/src/source/raster_dem_tile_source.ts +166 -0
  294. package/src/source/raster_dem_tile_worker_source.test.ts +36 -0
  295. package/src/source/raster_dem_tile_worker_source.ts +38 -0
  296. package/src/source/raster_tile_source.test.ts +206 -0
  297. package/src/source/raster_tile_source.ts +227 -0
  298. package/src/source/rtl_text_plugin_main_thread.test.ts +170 -0
  299. package/src/source/rtl_text_plugin_main_thread.ts +89 -0
  300. package/src/source/rtl_text_plugin_status.ts +33 -0
  301. package/src/source/rtl_text_plugin_worker.ts +49 -0
  302. package/src/source/source.test.ts +41 -0
  303. package/src/source/source.ts +186 -0
  304. package/src/source/source_cache.test.ts +2069 -0
  305. package/src/source/source_cache.ts +1102 -0
  306. package/src/source/source_state.ts +157 -0
  307. package/src/source/terrain_source_cache.test.ts +105 -0
  308. package/src/source/terrain_source_cache.ts +204 -0
  309. package/src/source/tile.test.ts +290 -0
  310. package/src/source/tile.ts +478 -0
  311. package/src/source/tile_bounds.ts +34 -0
  312. package/src/source/tile_cache.test.ts +130 -0
  313. package/src/source/tile_cache.ts +208 -0
  314. package/src/source/tile_id.test.ts +112 -0
  315. package/src/source/tile_id.ts +221 -0
  316. package/src/source/vector_tile_source.test.ts +401 -0
  317. package/src/source/vector_tile_source.ts +283 -0
  318. package/src/source/vector_tile_worker_source.test.ts +396 -0
  319. package/src/source/vector_tile_worker_source.ts +199 -0
  320. package/src/source/video_source.test.ts +124 -0
  321. package/src/source/video_source.ts +204 -0
  322. package/src/source/worker.test.ts +233 -0
  323. package/src/source/worker.ts +301 -0
  324. package/src/source/worker_source.ts +117 -0
  325. package/src/source/worker_tile.test.ts +226 -0
  326. package/src/source/worker_tile.ts +208 -0
  327. package/src/style/create_style_layer.ts +39 -0
  328. package/src/style/evaluation_parameters.ts +62 -0
  329. package/src/style/format_section_override.test.ts +62 -0
  330. package/src/style/format_section_override.ts +50 -0
  331. package/src/style/light.test.ts +86 -0
  332. package/src/style/light.ts +135 -0
  333. package/src/style/load_glyph_range.test.ts +40 -0
  334. package/src/style/load_glyph_range.ts +32 -0
  335. package/src/style/load_sprite.test.ts +227 -0
  336. package/src/style/load_sprite.ts +71 -0
  337. package/src/style/parse_glyph_pbf.ts +42 -0
  338. package/src/style/pauseable_placement.ts +138 -0
  339. package/src/style/properties.ts +726 -0
  340. package/src/style/query_utils.test.ts +107 -0
  341. package/src/style/query_utils.ts +70 -0
  342. package/src/style/sky.ts +127 -0
  343. package/src/style/style.test.ts +2654 -0
  344. package/src/style/style.ts +1814 -0
  345. package/src/style/style_glyph.ts +25 -0
  346. package/src/style/style_image.ts +188 -0
  347. package/src/style/style_layer/background_style_layer.ts +17 -0
  348. package/src/style/style_layer/background_style_layer_properties.g.ts +40 -0
  349. package/src/style/style_layer/circle_style_layer.ts +98 -0
  350. package/src/style/style_layer/circle_style_layer_properties.g.ts +76 -0
  351. package/src/style/style_layer/custom_style_layer.ts +233 -0
  352. package/src/style/style_layer/fill_extrusion_style_layer.ts +224 -0
  353. package/src/style/style_layer/fill_extrusion_style_layer_properties.g.ts +55 -0
  354. package/src/style/style_layer/fill_style_layer.test.ts +37 -0
  355. package/src/style/style_layer/fill_style_layer.ts +65 -0
  356. package/src/style/style_layer/fill_style_layer_properties.g.ts +64 -0
  357. package/src/style/style_layer/heatmap_style_layer.ts +74 -0
  358. package/src/style/style_layer/heatmap_style_layer_properties.g.ts +46 -0
  359. package/src/style/style_layer/hillshade_style_layer.ts +21 -0
  360. package/src/style/style_layer/hillshade_style_layer_properties.g.ts +49 -0
  361. package/src/style/style_layer/line_style_layer.test.ts +50 -0
  362. package/src/style/style_layer/line_style_layer.ts +131 -0
  363. package/src/style/style_layer/line_style_layer_properties.g.ts +88 -0
  364. package/src/style/style_layer/overlap_mode.test.ts +57 -0
  365. package/src/style/style_layer/overlap_mode.ts +25 -0
  366. package/src/style/style_layer/raster_style_layer.ts +17 -0
  367. package/src/style/style_layer/raster_style_layer_properties.g.ts +55 -0
  368. package/src/style/style_layer/symbol_style_layer.ts +195 -0
  369. package/src/style/style_layer/symbol_style_layer_properties.g.ts +218 -0
  370. package/src/style/style_layer/typed_style_layer.ts +9 -0
  371. package/src/style/style_layer/variable_text_anchor.test.ts +117 -0
  372. package/src/style/style_layer/variable_text_anchor.ts +163 -0
  373. package/src/style/style_layer.test.ts +372 -0
  374. package/src/style/style_layer.ts +287 -0
  375. package/src/style/style_layer_index.test.ts +99 -0
  376. package/src/style/style_layer_index.ts +78 -0
  377. package/src/style/validate_style.ts +53 -0
  378. package/src/style/zoom_history.ts +40 -0
  379. package/src/symbol/anchor.test.ts +14 -0
  380. package/src/symbol/anchor.ts +22 -0
  381. package/src/symbol/check_max_angle.test.ts +54 -0
  382. package/src/symbol/check_max_angle.ts +76 -0
  383. package/src/symbol/clip_line.test.ts +154 -0
  384. package/src/symbol/clip_line.ts +66 -0
  385. package/src/symbol/collision_feature.test.ts +98 -0
  386. package/src/symbol/collision_feature.ts +114 -0
  387. package/src/symbol/collision_index.test.ts +19 -0
  388. package/src/symbol/collision_index.ts +618 -0
  389. package/src/symbol/cross_tile_symbol_index.test.ts +260 -0
  390. package/src/symbol/cross_tile_symbol_index.ts +367 -0
  391. package/src/symbol/get_anchors.test.ts +113 -0
  392. package/src/symbol/get_anchors.ts +167 -0
  393. package/src/symbol/grid_index.test.ts +75 -0
  394. package/src/symbol/grid_index.ts +414 -0
  395. package/src/symbol/merge_lines.test.ts +30 -0
  396. package/src/symbol/merge_lines.ts +80 -0
  397. package/src/symbol/one_em.ts +3 -0
  398. package/src/symbol/opacity_state.ts +23 -0
  399. package/src/symbol/path_interpolator.test.ts +134 -0
  400. package/src/symbol/path_interpolator.ts +55 -0
  401. package/src/symbol/placement.ts +1371 -0
  402. package/src/symbol/projection.test.ts +171 -0
  403. package/src/symbol/projection.ts +833 -0
  404. package/src/symbol/quads.test.ts +157 -0
  405. package/src/symbol/quads.ts +349 -0
  406. package/src/symbol/shaping.test.ts +360 -0
  407. package/src/symbol/shaping.ts +911 -0
  408. package/src/symbol/symbol_layout.ts +739 -0
  409. package/src/symbol/symbol_size.ts +129 -0
  410. package/src/symbol/symbol_style_layer.test.ts +103 -0
  411. package/src/symbol/transform_text.ts +27 -0
  412. package/src/ui/anchor.ts +27 -0
  413. package/src/ui/camera.test.ts +2301 -0
  414. package/src/ui/camera.ts +1520 -0
  415. package/src/ui/control/attribution_control.test.ts +543 -0
  416. package/src/ui/control/attribution_control.ts +209 -0
  417. package/src/ui/control/control.ts +67 -0
  418. package/src/ui/control/fullscreen_control.test.ts +114 -0
  419. package/src/ui/control/fullscreen_control.ts +189 -0
  420. package/src/ui/control/geolocate_control.test.ts +619 -0
  421. package/src/ui/control/geolocate_control.ts +725 -0
  422. package/src/ui/control/logo_control.test.ts +88 -0
  423. package/src/ui/control/logo_control.ts +86 -0
  424. package/src/ui/control/navigation_control.test.ts +238 -0
  425. package/src/ui/control/navigation_control.ts +294 -0
  426. package/src/ui/control/scale_control.test.ts +52 -0
  427. package/src/ui/control/scale_control.ts +152 -0
  428. package/src/ui/control/terrain_control.test.ts +58 -0
  429. package/src/ui/control/terrain_control.ts +74 -0
  430. package/src/ui/default_locale.ts +25 -0
  431. package/src/ui/events.ts +758 -0
  432. package/src/ui/handler/box_zoom.test.ts +163 -0
  433. package/src/ui/handler/box_zoom.ts +171 -0
  434. package/src/ui/handler/click_zoom.ts +55 -0
  435. package/src/ui/handler/cooperative_gestures.test.ts +338 -0
  436. package/src/ui/handler/cooperative_gestures.ts +111 -0
  437. package/src/ui/handler/dblclick_zoom.test.ts +151 -0
  438. package/src/ui/handler/drag_handler.ts +174 -0
  439. package/src/ui/handler/drag_move_state_manager.ts +115 -0
  440. package/src/ui/handler/drag_pan.test.ts +487 -0
  441. package/src/ui/handler/drag_rotate.test.ts +859 -0
  442. package/src/ui/handler/handler_util.ts +10 -0
  443. package/src/ui/handler/keyboard.test.ts +235 -0
  444. package/src/ui/handler/keyboard.ts +212 -0
  445. package/src/ui/handler/map_event.test.ts +158 -0
  446. package/src/ui/handler/map_event.ts +161 -0
  447. package/src/ui/handler/mouse.ts +92 -0
  448. package/src/ui/handler/mouse_handler_interface.test.ts +111 -0
  449. package/src/ui/handler/mouse_rotate.test.ts +62 -0
  450. package/src/ui/handler/one_finger_touch_drag.ts +45 -0
  451. package/src/ui/handler/one_finger_touch_drag_handler_interface.test.ts +77 -0
  452. package/src/ui/handler/scroll_zoom.test.ts +382 -0
  453. package/src/ui/handler/scroll_zoom.ts +379 -0
  454. package/src/ui/handler/shim/dblclick_zoom.ts +64 -0
  455. package/src/ui/handler/shim/drag_pan.ts +104 -0
  456. package/src/ui/handler/shim/drag_rotate.ts +76 -0
  457. package/src/ui/handler/shim/two_fingers_touch.ts +112 -0
  458. package/src/ui/handler/tap_drag_zoom.test.ts +113 -0
  459. package/src/ui/handler/tap_drag_zoom.ts +112 -0
  460. package/src/ui/handler/tap_recognizer.ts +138 -0
  461. package/src/ui/handler/tap_zoom.ts +98 -0
  462. package/src/ui/handler/touch_pan.ts +115 -0
  463. package/src/ui/handler/transform-provider.ts +44 -0
  464. package/src/ui/handler/two_fingers_touch.test.ts +283 -0
  465. package/src/ui/handler/two_fingers_touch.ts +336 -0
  466. package/src/ui/handler_inertia.ts +157 -0
  467. package/src/ui/handler_manager.ts +637 -0
  468. package/src/ui/hash.test.ts +404 -0
  469. package/src/ui/hash.ts +153 -0
  470. package/src/ui/map.ts +3393 -0
  471. package/src/ui/map_tests/map_animation.test.ts +61 -0
  472. package/src/ui/map_tests/map_basic.test.ts +223 -0
  473. package/src/ui/map_tests/map_bounds.test.ts +123 -0
  474. package/src/ui/map_tests/map_calculate_camera_options.test.ts +113 -0
  475. package/src/ui/map_tests/map_canvas.test.ts +59 -0
  476. package/src/ui/map_tests/map_control.test.ts +61 -0
  477. package/src/ui/map_tests/map_disable_handlers.test.ts +38 -0
  478. package/src/ui/map_tests/map_events.test.ts +1001 -0
  479. package/src/ui/map_tests/map_feature_state.test.ts +421 -0
  480. package/src/ui/map_tests/map_images.test.ts +175 -0
  481. package/src/ui/map_tests/map_is_moving.test.ts +164 -0
  482. package/src/ui/map_tests/map_is_rotating.test.ts +62 -0
  483. package/src/ui/map_tests/map_is_zooming.test.ts +86 -0
  484. package/src/ui/map_tests/map_layer.test.ts +457 -0
  485. package/src/ui/map_tests/map_options.test.ts +64 -0
  486. package/src/ui/map_tests/map_pitch.test.ts +90 -0
  487. package/src/ui/map_tests/map_pixel_ratio.test.ts +75 -0
  488. package/src/ui/map_tests/map_query_rendered_features.test.ts +93 -0
  489. package/src/ui/map_tests/map_render.test.ts +90 -0
  490. package/src/ui/map_tests/map_request_render_frame.test.ts +51 -0
  491. package/src/ui/map_tests/map_resize.test.ts +120 -0
  492. package/src/ui/map_tests/map_style.test.ts +541 -0
  493. package/src/ui/map_tests/map_terrian.test.ts +104 -0
  494. package/src/ui/map_tests/map_webgl.test.ts +72 -0
  495. package/src/ui/map_tests/map_world_copies.test.ts +103 -0
  496. package/src/ui/map_tests/map_zoom.test.ts +95 -0
  497. package/src/ui/marker.test.ts +1149 -0
  498. package/src/ui/marker.ts +880 -0
  499. package/src/ui/popup.test.ts +827 -0
  500. package/src/ui/popup.ts +717 -0
  501. package/src/util/abort_error.ts +21 -0
  502. package/src/util/actor.test.ts +218 -0
  503. package/src/util/actor.ts +241 -0
  504. package/src/util/actor_messages.ts +149 -0
  505. package/src/util/ajax.test.ts +237 -0
  506. package/src/util/ajax.ts +297 -0
  507. package/src/util/browser.test.ts +23 -0
  508. package/src/util/browser.ts +63 -0
  509. package/src/util/color_ramp.test.ts +105 -0
  510. package/src/util/color_ramp.ts +56 -0
  511. package/src/util/config.ts +29 -0
  512. package/src/util/dictionary_coder.ts +23 -0
  513. package/src/util/dispatcher.test.ts +76 -0
  514. package/src/util/dispatcher.ts +78 -0
  515. package/src/util/dom.ts +135 -0
  516. package/src/util/evented.test.ts +231 -0
  517. package/src/util/evented.ts +178 -0
  518. package/src/util/find_pole_of_inaccessibility.test.ts +21 -0
  519. package/src/util/find_pole_of_inaccessibility.ts +130 -0
  520. package/src/util/geolocation_support.test.ts +43 -0
  521. package/src/util/geolocation_support.ts +23 -0
  522. package/src/util/global_worker_pool.ts +65 -0
  523. package/src/util/image.ts +150 -0
  524. package/src/util/image_request.test.ts +408 -0
  525. package/src/util/image_request.ts +246 -0
  526. package/src/util/intersection_tests.ts +206 -0
  527. package/src/util/is_char_in_unicode_block.test.ts +15 -0
  528. package/src/util/is_char_in_unicode_block.ts +345 -0
  529. package/src/util/offscreen_canvas_distorted.test.ts +13 -0
  530. package/src/util/offscreen_canvas_distorted.ts +39 -0
  531. package/src/util/offscreen_canvas_supported.ts +11 -0
  532. package/src/util/performance.ts +117 -0
  533. package/src/util/primitives.test.ts +140 -0
  534. package/src/util/primitives.ts +143 -0
  535. package/src/util/request_manager.ts +42 -0
  536. package/src/util/resolve_tokens.test.ts +45 -0
  537. package/src/util/resolve_tokens.ts +17 -0
  538. package/src/util/script_detection.test.ts +138 -0
  539. package/src/util/script_detection.ts +376 -0
  540. package/src/util/smart_wrap.test.ts +97 -0
  541. package/src/util/smart_wrap.ts +58 -0
  542. package/src/util/struct_array.test.ts +101 -0
  543. package/src/util/struct_array.ts +246 -0
  544. package/src/util/style.test.ts +34 -0
  545. package/src/util/style.ts +28 -0
  546. package/src/util/task_queue.test.ts +114 -0
  547. package/src/util/task_queue.ts +64 -0
  548. package/src/util/test/util.ts +185 -0
  549. package/src/util/throttle.test.ts +42 -0
  550. package/src/util/throttle.ts +28 -0
  551. package/src/util/throttled_invoker.ts +41 -0
  552. package/src/util/transferable_grid_index.test.ts +56 -0
  553. package/src/util/transferable_grid_index.ts +214 -0
  554. package/src/util/util.test.ts +414 -0
  555. package/src/util/util.ts +724 -0
  556. package/src/util/vectortile_to_geojson.ts +72 -0
  557. package/src/util/verticalize_punctuation.ts +110 -0
  558. package/src/util/web_worker.ts +16 -0
  559. package/src/util/web_worker_transfer.test.ts +153 -0
  560. package/src/util/web_worker_transfer.ts +254 -0
  561. package/src/util/webp_supported.ts +63 -0
  562. package/src/util/worker_pool.test.ts +43 -0
  563. package/src/util/worker_pool.ts +58 -0
  564. package/src/util/world_bounds.test.ts +59 -0
  565. package/src/util/world_bounds.ts +46 -0
package/src/ui/map.ts ADDED
@@ -0,0 +1,3393 @@
1
+ import {extend, warnOnce, uniqueId, isImageBitmap, Complete} from '../util/util';
2
+ import {browser} from '../util/browser';
3
+ import {DOM} from '../util/dom';
4
+ import packageJSON from '../../package.json' with {type: 'json'};
5
+ import {GetResourceResponse, getJSON} from '../util/ajax';
6
+ import {ImageRequest} from '../util/image_request';
7
+ import {RequestManager, ResourceType} from '../util/request_manager';
8
+ import {Style, StyleSwapOptions} from '../style/style';
9
+ import {EvaluationParameters} from '../style/evaluation_parameters';
10
+ import {Painter} from '../render/painter';
11
+ import {Transform} from '../geo/transform';
12
+ import {Hash} from './hash';
13
+ import {HandlerManager} from './handler_manager';
14
+ import {Camera, CameraOptions, CameraUpdateTransformFunction, FitBoundsOptions} from './camera';
15
+ import {LngLat} from '../geo/lng_lat';
16
+ import {LngLatBounds} from '../geo/lng_lat_bounds';
17
+ import Point from '@mapbox/point-geometry';
18
+ import {AttributionControl, AttributionControlOptions, defaultAttributionControlOptions} from './control/attribution_control';
19
+ import {LogoControl} from './control/logo_control';
20
+ import {RGBAImage} from '../util/image';
21
+ import {Event, ErrorEvent, Listener} from '../util/evented';
22
+ import {MapEventType, MapLayerEventType, MapMouseEvent, MapSourceDataEvent, MapStyleDataEvent} from './events';
23
+ import {TaskQueue} from '../util/task_queue';
24
+ import {throttle} from '../util/throttle';
25
+ import {webpSupported} from '../util/webp_supported';
26
+ import {PerformanceMarkers, PerformanceUtils} from '../util/performance';
27
+ import {Source} from '../source/source';
28
+ import {StyleLayer} from '../style/style_layer';
29
+ import {Terrain} from '../render/terrain';
30
+ import {RenderToTexture} from '../render/render_to_texture';
31
+ import {config} from '../util/config';
32
+ import {defaultLocale} from './default_locale';
33
+
34
+ import type {RequestTransformFunction} from '../util/request_manager';
35
+ import type {LngLatLike} from '../geo/lng_lat';
36
+ import type {LngLatBoundsLike} from '../geo/lng_lat_bounds';
37
+ import type {AddLayerObject, FeatureIdentifier, StyleOptions, StyleSetterOptions} from '../style/style';
38
+ import type {MapDataEvent} from './events';
39
+ import type {StyleImage, StyleImageInterface, StyleImageMetadata} from '../style/style_image';
40
+ import type {PointLike} from './camera';
41
+ import type {ScrollZoomHandler} from './handler/scroll_zoom';
42
+ import type {BoxZoomHandler} from './handler/box_zoom';
43
+ import type {AroundCenterOptions, TwoFingersTouchPitchHandler} from './handler/two_fingers_touch';
44
+ import type {DragRotateHandler} from './handler/shim/drag_rotate';
45
+ import type {DragPanHandler, DragPanOptions} from './handler/shim/drag_pan';
46
+ import type {CooperativeGesturesHandler, GestureOptions} from './handler/cooperative_gestures';
47
+ import type {KeyboardHandler} from './handler/keyboard';
48
+ import type {DoubleClickZoomHandler} from './handler/shim/dblclick_zoom';
49
+ import type {TwoFingersTouchZoomRotateHandler} from './handler/shim/two_fingers_touch';
50
+ import type {TaskID} from '../util/task_queue';
51
+ import type {
52
+ FilterSpecification,
53
+ StyleSpecification,
54
+ LightSpecification,
55
+ SourceSpecification,
56
+ TerrainSpecification,
57
+ SkySpecification
58
+ } from '@maplibre/maplibre-gl-style-spec';
59
+ import type {CanvasSourceSpecification} from '../source/canvas_source';
60
+ import type {MapGeoJSONFeature} from '../util/vectortile_to_geojson';
61
+ import type {ControlPosition, IControl} from './control/control';
62
+ import type {QueryRenderedFeaturesOptions, QuerySourceFeatureOptions} from '../source/query_features';
63
+
64
+ const version = packageJSON.version;
65
+
66
+ /**
67
+ * The {@link Map} options object.
68
+ */
69
+ export type MapOptions = {
70
+ /**
71
+ * If `true`, the map's position (zoom, center latitude, center longitude, bearing, and pitch) will be synced with the hash fragment of the page's URL.
72
+ * For example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`.
73
+ * An additional string may optionally be provided to indicate a parameter-styled hash,
74
+ * e.g. http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where foo
75
+ * is a custom parameter and bar is an arbitrary hash distinct from the map hash.
76
+ * @defaultValue false
77
+ */
78
+ hash?: boolean | string;
79
+ /**
80
+ * If `false`, no mouse, touch, or keyboard listeners will be attached to the map, so it will not respond to interaction.
81
+ * @defaultValue true
82
+ */
83
+ interactive?: boolean;
84
+ /**
85
+ * The HTML element in which MapLibre GL JS will render the map, or the element's string `id`. The specified element must have no children.
86
+ */
87
+ container: HTMLElement | string;
88
+ /**
89
+ * The threshold, measured in degrees, that determines when the map's
90
+ * bearing will snap to north. For example, with a `bearingSnap` of 7, if the user rotates
91
+ * the map within 7 degrees of north, the map will automatically snap to exact north.
92
+ * @defaultValue 7
93
+ */
94
+ bearingSnap?: number;
95
+ /**
96
+ * If set, an {@link AttributionControl} will be added to the map with the provided options.
97
+ * To disable the attribution control, pass `false`.
98
+ * Note: showing the logo of MapLibre is not required for using MapLibre.
99
+ * @defaultValue compact: true, customAttribution: "MapLibre ...".
100
+ */
101
+ attributionControl?: false | AttributionControlOptions;
102
+ /**
103
+ * If `true`, the MapLibre logo will be shown.
104
+ */
105
+ maplibreLogo?: boolean;
106
+ /**
107
+ * A string representing the position of the MapLibre wordmark on the map. Valid options are `top-left`,`top-right`, `bottom-left`, or `bottom-right`.
108
+ * @defaultValue 'bottom-left'
109
+ */
110
+ logoPosition?: ControlPosition;
111
+ /**
112
+ * If `true`, map creation will fail if the performance of MapLibre GL JS would be dramatically worse than expected
113
+ * (i.e. a software renderer would be used).
114
+ * @defaultValue false
115
+ */
116
+ failIfMajorPerformanceCaveat?: boolean;
117
+ /**
118
+ * If `true`, the map's canvas can be exported to a PNG using `map.getCanvas().toDataURL()`. This is `false` by default as a performance optimization.
119
+ * @defaultValue false
120
+ */
121
+ preserveDrawingBuffer?: boolean;
122
+ /**
123
+ * If `true`, the gl context will be created with MSAA antialiasing, which can be useful for antialiasing custom layers.
124
+ * Disabled by default as a performance optimization.
125
+ */
126
+ antialias?: boolean;
127
+ /**
128
+ * If `false`, the map won't attempt to re-request tiles once they expire per their HTTP `cacheControl`/`expires` headers.
129
+ * @defaultValue true
130
+ */
131
+ refreshExpiredTiles?: boolean;
132
+ /**
133
+ * If set, the map will be constrained to the given bounds.
134
+ */
135
+ maxBounds?: LngLatBoundsLike;
136
+ /**
137
+ * If `true`, the "scroll to zoom" interaction is enabled. {@link AroundCenterOptions} are passed as options to {@link ScrollZoomHandler#enable}.
138
+ * @defaultValue true
139
+ */
140
+ scrollZoom?: boolean | AroundCenterOptions;
141
+ /**
142
+ * The minimum zoom level of the map (0-24).
143
+ * @defaultValue 0
144
+ */
145
+ minZoom?: number | null;
146
+ /**
147
+ * The maximum zoom level of the map (0-24).
148
+ * @defaultValue 22
149
+ */
150
+ maxZoom?: number | null;
151
+ /**
152
+ * The minimum pitch of the map (0-85). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.
153
+ * @defaultValue 0
154
+ */
155
+ minPitch?: number | null;
156
+ /**
157
+ * The maximum pitch of the map (0-85). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.
158
+ * @defaultValue 60
159
+ */
160
+ maxPitch?: number | null;
161
+ /**
162
+ * If `true`, the "box zoom" interaction is enabled (see {@link BoxZoomHandler}).
163
+ * @defaultValue true
164
+ */
165
+ boxZoom?: boolean;
166
+ /**
167
+ * If `true`, the "drag to rotate" interaction is enabled (see {@link DragRotateHandler}).
168
+ * @defaultValue true
169
+ */
170
+ dragRotate?: boolean;
171
+ /**
172
+ * If `true`, the "drag to pan" interaction is enabled. An `Object` value is passed as options to {@link DragPanHandler#enable}.
173
+ * @defaultValue true
174
+ */
175
+ dragPan?: boolean | DragPanOptions;
176
+ /**
177
+ * If `true`, keyboard shortcuts are enabled (see {@link KeyboardHandler}).
178
+ * @defaultValue true
179
+ */
180
+ keyboard?: boolean;
181
+ /**
182
+ * If `true`, the "double click to zoom" interaction is enabled (see {@link DoubleClickZoomHandler}).
183
+ * @defaultValue true
184
+ */
185
+ doubleClickZoom?: boolean;
186
+ /**
187
+ * If `true`, the "pinch to rotate and zoom" interaction is enabled. An `Object` value is passed as options to {@link TwoFingersTouchZoomRotateHandler#enable}.
188
+ * @defaultValue true
189
+ */
190
+ touchZoomRotate?: boolean | AroundCenterOptions;
191
+ /**
192
+ * If `true`, the "drag to pitch" interaction is enabled. An `Object` value is passed as options to {@link TwoFingersTouchPitchHandler#enable}.
193
+ * @defaultValue true
194
+ */
195
+ touchPitch?: boolean | AroundCenterOptions;
196
+ /**
197
+ * If `true` or set to an options object, the map is only accessible on desktop while holding Command/Ctrl and only accessible on mobile with two fingers. Interacting with the map using normal gestures will trigger an informational screen. With this option enabled, "drag to pitch" requires a three-finger gesture. Cooperative gestures are disabled when a map enters fullscreen using {@link FullscreenControl}.
198
+ * @defaultValue false
199
+ */
200
+ cooperativeGestures?: GestureOptions;
201
+ /**
202
+ * If `true`, the map will automatically resize when the browser window resizes.
203
+ * @defaultValue true
204
+ */
205
+ trackResize?: boolean;
206
+ /**
207
+ * The initial geographical centerpoint of the map. If `center` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `[0, 0]` Note: MapLibre GL JS uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON.
208
+ * @defaultValue [0, 0]
209
+ */
210
+ center?: LngLatLike;
211
+ /**
212
+ * The initial zoom level of the map. If `zoom` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`.
213
+ * @defaultValue 0
214
+ */
215
+ zoom?: number;
216
+ /**
217
+ * The initial bearing (rotation) of the map, measured in degrees counter-clockwise from north. If `bearing` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`.
218
+ * @defaultValue 0
219
+ */
220
+ bearing?: number;
221
+ /**
222
+ * The initial pitch (tilt) of the map, measured in degrees away from the plane of the screen (0-85). If `pitch` is not specified in the constructor options, MapLibre GL JS will look for it in the map's style object. If it is not specified in the style, either, it will default to `0`. Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.
223
+ * @defaultValue 0
224
+ */
225
+ pitch?: number;
226
+ /**
227
+ * If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:
228
+ *
229
+ * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
230
+ * container, there will be blank space beyond 180 and -180 degrees longitude.
231
+ * - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
232
+ * map and the other on the left edge of the map) at every zoom level.
233
+ * @defaultValue true
234
+ */
235
+ renderWorldCopies?: boolean;
236
+ /**
237
+ * The maximum number of tiles stored in the tile cache for a given source. If omitted, the cache will be dynamically sized based on the current viewport which can be set using `maxTileCacheZoomLevels` constructor options.
238
+ * @defaultValue null
239
+ */
240
+ maxTileCacheSize?: number | null;
241
+ /**
242
+ * The maximum number of zoom levels for which to store tiles for a given source. Tile cache dynamic size is calculated by multiplying `maxTileCacheZoomLevels` with the approximate number of tiles in the viewport for a given source.
243
+ * @defaultValue 5
244
+ */
245
+ maxTileCacheZoomLevels?: number;
246
+ /**
247
+ * A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests.
248
+ * Expected to return an object with a `url` property and optionally `headers` and `credentials` properties.
249
+ * @defaultValue null
250
+ */
251
+ transformRequest?: RequestTransformFunction | null;
252
+ /**
253
+ * A callback run before the map's camera is moved due to user input or animation. The callback can be used to modify the new center, zoom, pitch and bearing.
254
+ * Expected to return an object containing center, zoom, pitch or bearing values to overwrite.
255
+ * @defaultValue null
256
+ */
257
+ transformCameraUpdate?: CameraUpdateTransformFunction | null;
258
+ /**
259
+ * A patch to apply to the default localization table for UI strings, e.g. control tooltips. The `locale` object maps namespaced UI string IDs to translated strings in the target language; see `src/ui/default_locale.js` for an example with all supported string IDs. The object may specify all UI strings (thereby adding support for a new translation) or only a subset of strings (thereby patching the default translation table).
260
+ * @defaultValue null
261
+ */
262
+ locale?: any;
263
+ /**
264
+ * Controls the duration of the fade-in/fade-out animation for label collisions after initial map load, in milliseconds. This setting affects all symbol layers. This setting does not affect the duration of runtime styling transitions or raster tile cross-fading.
265
+ * @defaultValue 300
266
+ */
267
+ fadeDuration?: number;
268
+ /**
269
+ * If `true`, symbols from multiple sources can collide with each other during collision detection. If `false`, collision detection is run separately for the symbols in each source.
270
+ * @defaultValue true
271
+ */
272
+ crossSourceCollisions?: boolean;
273
+ /**
274
+ * If `true`, Resource Timing API information will be collected for requests made by GeoJSON and Vector Tile web workers (this information is normally inaccessible from the main Javascript thread). Information will be returned in a `resourceTiming` property of relevant `data` events.
275
+ * @defaultValue false
276
+ */
277
+ collectResourceTiming?: boolean;
278
+ /**
279
+ * The max number of pixels a user can shift the mouse pointer during a click for it to be considered a valid click (as opposed to a mouse drag).
280
+ * @defaultValue 3
281
+ */
282
+ clickTolerance?: number;
283
+ /**
284
+ * The initial bounds of the map. If `bounds` is specified, it overrides `center` and `zoom` constructor options.
285
+ */
286
+ bounds?: LngLatBoundsLike;
287
+ /**
288
+ * A {@link FitBoundsOptions} options object to use _only_ when fitting the initial `bounds` provided above.
289
+ */
290
+ fitBoundsOptions?: FitBoundsOptions;
291
+ /**
292
+ * Defines a CSS
293
+ * font-family for locally overriding generation of Chinese, Japanese, and Korean characters.
294
+ * For these characters, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold).
295
+ * Set to `false`, to enable font settings from the map's style for these glyph ranges.
296
+ * The purpose of this option is to avoid bandwidth-intensive glyph server requests. (See [Use locally generated ideographs](https://maplibre.org/maplibre-gl-js/docs/examples/local-ideographs).)
297
+ * @defaultValue 'sans-serif'
298
+ */
299
+ localIdeographFontFamily?: string | false;
300
+ /**
301
+ * The map's MapLibre style. This must be a JSON object conforming to
302
+ * the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/),
303
+ * or a URL to such JSON.
304
+ * When the style is not specified, calling {@link Map#setStyle} is required to render the map.
305
+ */
306
+ style?: StyleSpecification | string;
307
+ /**
308
+ * If `false`, the map's pitch (tilt) control with "drag to rotate" interaction will be disabled.
309
+ * @defaultValue true
310
+ */
311
+ pitchWithRotate?: boolean;
312
+ /**
313
+ * The pixel ratio.
314
+ * The canvas' `width` attribute will be `container.clientWidth * pixelRatio` and its `height` attribute will be `container.clientHeight * pixelRatio`. Defaults to `devicePixelRatio` if not specified.
315
+ */
316
+ pixelRatio?: number;
317
+ /**
318
+ * If false, style validation will be skipped. Useful in production environment.
319
+ * @defaultValue true
320
+ */
321
+ validateStyle?: boolean;
322
+ /**
323
+ * The canvas' `width` and `height` max size. The values are passed as an array where the first element is max width and the second element is max height.
324
+ * You shouldn't set this above WebGl `MAX_TEXTURE_SIZE`.
325
+ * @defaultValue [4096, 4096].
326
+ */
327
+ maxCanvasSize?: [number, number];
328
+ /**
329
+ * Determines whether to cancel, or retain, tiles from the current viewport which are still loading but which belong to a farther (smaller) zoom level than the current one.
330
+ * * If `true`, when zooming in, tiles which didn't manage to load for previous zoom levels will become canceled. This might save some computing resources for slower devices, but the map details might appear more abruptly at the end of the zoom.
331
+ * * If `false`, when zooming in, the previous zoom level(s) tiles will progressively appear, giving a smoother map details experience. However, more tiles will be rendered in a short period of time.
332
+ * @defaultValue true
333
+ */
334
+ cancelPendingTileRequestsWhileZooming?: boolean;
335
+ };
336
+
337
+ export type AddImageOptions = {
338
+
339
+ }
340
+
341
+ // This type is used inside map since all properties are assigned a default value.
342
+ export type CompleteMapOptions = Complete<MapOptions>;
343
+
344
+ type DelegatedListener = {
345
+ layers: string[];
346
+ listener: Listener;
347
+ delegates: {[E in keyof MapEventType]?: Delegate<MapEventType[E]>};
348
+ }
349
+
350
+ type Delegate<E extends Event = Event> = (e: E) => void;
351
+
352
+ const defaultMinZoom = -2;
353
+ const defaultMaxZoom = 22;
354
+
355
+ // the default values, but also the valid range
356
+ const defaultMinPitch = 0;
357
+ const defaultMaxPitch = 60;
358
+
359
+ // use this variable to check maxPitch for validity
360
+ const maxPitchThreshold = 85;
361
+
362
+ const defaultOptions: Readonly<Partial<MapOptions>> = {
363
+ hash: false,
364
+ interactive: true,
365
+ bearingSnap: 7,
366
+ attributionControl: defaultAttributionControlOptions,
367
+ maplibreLogo: false,
368
+ failIfMajorPerformanceCaveat: false,
369
+ preserveDrawingBuffer: false,
370
+ refreshExpiredTiles: true,
371
+
372
+ scrollZoom: true,
373
+ minZoom: defaultMinZoom,
374
+ maxZoom: defaultMaxZoom,
375
+ minPitch: defaultMinPitch,
376
+ maxPitch: defaultMaxPitch,
377
+
378
+ boxZoom: true,
379
+ dragRotate: true,
380
+ dragPan: true,
381
+ keyboard: true,
382
+ doubleClickZoom: true,
383
+ touchZoomRotate: true,
384
+ touchPitch: true,
385
+ cooperativeGestures: false,
386
+
387
+ trackResize: true,
388
+
389
+ center: [0, 0],
390
+ zoom: 0,
391
+ bearing: 0,
392
+ pitch: 0,
393
+
394
+ renderWorldCopies: true,
395
+ maxTileCacheSize: null,
396
+ maxTileCacheZoomLevels: config.MAX_TILE_CACHE_ZOOM_LEVELS,
397
+ transformRequest: null,
398
+ transformCameraUpdate: null,
399
+ fadeDuration: 300,
400
+ crossSourceCollisions: true,
401
+ clickTolerance: 3,
402
+ localIdeographFontFamily: 'sans-serif',
403
+ pitchWithRotate: true,
404
+ validateStyle: true,
405
+ /**Because GL MAX_TEXTURE_SIZE is usually at least 4096px. */
406
+ maxCanvasSize: [4096, 4096],
407
+ cancelPendingTileRequestsWhileZooming: true
408
+ };
409
+
410
+ /**
411
+ * The `Map` object represents the map on your page. It exposes methods
412
+ * and properties that enable you to programmatically change the map,
413
+ * and fires events as users interact with it.
414
+ *
415
+ * You create a `Map` by specifying a `container` and other options, see {@link MapOptions} for the full list.
416
+ * Then MapLibre GL JS initializes the map on the page and returns your `Map` object.
417
+ *
418
+ * @group Main
419
+ *
420
+ * @example
421
+ * ```ts
422
+ * let map = new Map({
423
+ * container: 'map',
424
+ * center: [-122.420679, 37.772537],
425
+ * zoom: 13,
426
+ * style: style_object,
427
+ * hash: true,
428
+ * transformRequest: (url, resourceType)=> {
429
+ * if(resourceType === 'Source' && url.startsWith('http://myHost')) {
430
+ * return {
431
+ * url: url.replace('http', 'https'),
432
+ * headers: { 'my-custom-header': true},
433
+ * credentials: 'include' // Include cookies for cross-origin requests
434
+ * }
435
+ * }
436
+ * }
437
+ * });
438
+ * ```
439
+ * @see [Display a map](https://maplibre.org/maplibre-gl-js/docs/examples/simple-map/)
440
+ */
441
+ export class Map extends Camera {
442
+ style: Style;
443
+ painter: Painter;
444
+
445
+ _container: HTMLElement;
446
+ _canvasContainer: HTMLElement;
447
+ _controlContainer: HTMLElement;
448
+ _controlPositions: Record<string, HTMLElement>;
449
+ _interactive: boolean;
450
+ _showTileBoundaries: boolean;
451
+ _showCollisionBoxes: boolean;
452
+ _showPadding: boolean;
453
+ _showOverdrawInspector: boolean;
454
+ _repaint: boolean;
455
+ _vertices: boolean;
456
+ _canvas: HTMLCanvasElement;
457
+ _maxTileCacheSize: number | null;
458
+ _maxTileCacheZoomLevels: number;
459
+ _frameRequest: AbortController;
460
+ _styleDirty: boolean;
461
+ _sourcesDirty: boolean;
462
+ _placementDirty: boolean;
463
+
464
+ _loaded: boolean;
465
+ _idleTriggered = false;
466
+ // accounts for placement finishing as well
467
+ _fullyLoaded: boolean;
468
+ _trackResize: boolean;
469
+ _resizeObserver: ResizeObserver;
470
+ _preserveDrawingBuffer: boolean;
471
+ _failIfMajorPerformanceCaveat: boolean;
472
+ _antialias: boolean;
473
+ _refreshExpiredTiles: boolean;
474
+ _hash: Hash;
475
+ _delegatedListeners: Record<string, DelegatedListener[]>;
476
+ _fadeDuration: number;
477
+ _crossSourceCollisions: boolean;
478
+ _crossFadingFactor = 1;
479
+ _collectResourceTiming: boolean;
480
+ _renderTaskQueue = new TaskQueue();
481
+ _controls: Array<IControl> = [];
482
+ _mapId = uniqueId();
483
+ _localIdeographFontFamily: string | false;
484
+ _validateStyle: boolean;
485
+ _requestManager: RequestManager;
486
+ _locale: typeof defaultLocale;
487
+ _removed: boolean;
488
+ _clickTolerance: number;
489
+ _overridePixelRatio: number | null | undefined;
490
+ _maxCanvasSize: [number, number];
491
+ _terrainDataCallback: (e: MapStyleDataEvent | MapSourceDataEvent) => void;
492
+
493
+ /**
494
+ * @internal
495
+ * image queue throttling handle. To be used later when clean up
496
+ */
497
+ _imageQueueHandle: number;
498
+
499
+ /**
500
+ * The map's {@link ScrollZoomHandler}, which implements zooming in and out with a scroll wheel or trackpad.
501
+ * Find more details and examples using `scrollZoom` in the {@link ScrollZoomHandler} section.
502
+ */
503
+ scrollZoom: ScrollZoomHandler;
504
+
505
+ /**
506
+ * The map's {@link BoxZoomHandler}, which implements zooming using a drag gesture with the Shift key pressed.
507
+ * Find more details and examples using `boxZoom` in the {@link BoxZoomHandler} section.
508
+ */
509
+ boxZoom: BoxZoomHandler;
510
+
511
+ /**
512
+ * The map's {@link DragRotateHandler}, which implements rotating the map while dragging with the right
513
+ * mouse button or with the Control key pressed. Find more details and examples using `dragRotate`
514
+ * in the {@link DragRotateHandler} section.
515
+ */
516
+ dragRotate: DragRotateHandler;
517
+
518
+ /**
519
+ * The map's {@link DragPanHandler}, which implements dragging the map with a mouse or touch gesture.
520
+ * Find more details and examples using `dragPan` in the {@link DragPanHandler} section.
521
+ */
522
+ dragPan: DragPanHandler;
523
+
524
+ /**
525
+ * The map's {@link KeyboardHandler}, which allows the user to zoom, rotate, and pan the map using keyboard
526
+ * shortcuts. Find more details and examples using `keyboard` in the {@link KeyboardHandler} section.
527
+ */
528
+ keyboard: KeyboardHandler;
529
+
530
+ /**
531
+ * The map's {@link DoubleClickZoomHandler}, which allows the user to zoom by double clicking.
532
+ * Find more details and examples using `doubleClickZoom` in the {@link DoubleClickZoomHandler} section.
533
+ */
534
+ doubleClickZoom: DoubleClickZoomHandler;
535
+
536
+ /**
537
+ * The map's {@link TwoFingersTouchZoomRotateHandler}, which allows the user to zoom or rotate the map with touch gestures.
538
+ * Find more details and examples using `touchZoomRotate` in the {@link TwoFingersTouchZoomRotateHandler} section.
539
+ */
540
+ touchZoomRotate: TwoFingersTouchZoomRotateHandler;
541
+
542
+ /**
543
+ * The map's {@link TwoFingersTouchPitchHandler}, which allows the user to pitch the map with touch gestures.
544
+ * Find more details and examples using `touchPitch` in the {@link TwoFingersTouchPitchHandler} section.
545
+ */
546
+ touchPitch: TwoFingersTouchPitchHandler;
547
+
548
+ /**
549
+ * The map's {@link CooperativeGesturesHandler}, which allows the user to see cooperative gesture info when user tries to zoom in/out.
550
+ * Find more details and examples using `cooperativeGestures` in the {@link CooperativeGesturesHandler} section.
551
+ */
552
+ cooperativeGestures: CooperativeGesturesHandler;
553
+
554
+ /**
555
+ * The map's property which determines whether to cancel, or retain, tiles from the current viewport which are still loading but which belong to a farther (smaller) zoom level than the current one.
556
+ * * If `true`, when zooming in, tiles which didn't manage to load for previous zoom levels will become canceled. This might save some computing resources for slower devices, but the map details might appear more abruptly at the end of the zoom.
557
+ * * If `false`, when zooming in, the previous zoom level(s) tiles will progressively appear, giving a smoother map details experience. However, more tiles will be rendered in a short period of time.
558
+ * @defaultValue true
559
+ */
560
+ cancelPendingTileRequestsWhileZooming: boolean;
561
+
562
+ constructor(options: MapOptions) {
563
+ PerformanceUtils.mark(PerformanceMarkers.create);
564
+
565
+ const resolvedOptions = {...defaultOptions, ...options} as CompleteMapOptions;
566
+
567
+ if (resolvedOptions.minZoom != null && resolvedOptions.maxZoom != null && resolvedOptions.minZoom > resolvedOptions.maxZoom) {
568
+ throw new Error('maxZoom must be greater than or equal to minZoom');
569
+ }
570
+
571
+ if (resolvedOptions.minPitch != null && resolvedOptions.maxPitch != null && resolvedOptions.minPitch > resolvedOptions.maxPitch) {
572
+ throw new Error('maxPitch must be greater than or equal to minPitch');
573
+ }
574
+
575
+ if (resolvedOptions.minPitch != null && resolvedOptions.minPitch < defaultMinPitch) {
576
+ throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
577
+ }
578
+
579
+ if (resolvedOptions.maxPitch != null && resolvedOptions.maxPitch > maxPitchThreshold) {
580
+ throw new Error(`maxPitch must be less than or equal to ${maxPitchThreshold}`);
581
+ }
582
+
583
+ const transform = new Transform(resolvedOptions.minZoom, resolvedOptions.maxZoom, resolvedOptions.minPitch, resolvedOptions.maxPitch, resolvedOptions.renderWorldCopies);
584
+ super(transform, {bearingSnap: resolvedOptions.bearingSnap});
585
+
586
+ this._interactive = resolvedOptions.interactive;
587
+ this._maxTileCacheSize = resolvedOptions.maxTileCacheSize;
588
+ this._maxTileCacheZoomLevels = resolvedOptions.maxTileCacheZoomLevels;
589
+ this._failIfMajorPerformanceCaveat = resolvedOptions.failIfMajorPerformanceCaveat === true;
590
+ this._preserveDrawingBuffer = resolvedOptions.preserveDrawingBuffer === true;
591
+ this._antialias = resolvedOptions.antialias === true;
592
+ this._trackResize = resolvedOptions.trackResize === true;
593
+ this._bearingSnap = resolvedOptions.bearingSnap;
594
+ this._refreshExpiredTiles = resolvedOptions.refreshExpiredTiles === true;
595
+ this._fadeDuration = resolvedOptions.fadeDuration;
596
+ this._crossSourceCollisions = resolvedOptions.crossSourceCollisions === true;
597
+ this._collectResourceTiming = resolvedOptions.collectResourceTiming === true;
598
+ this._locale = {...defaultLocale, ...resolvedOptions.locale};
599
+ this._clickTolerance = resolvedOptions.clickTolerance;
600
+ this._overridePixelRatio = resolvedOptions.pixelRatio;
601
+ this._maxCanvasSize = resolvedOptions.maxCanvasSize;
602
+ this.transformCameraUpdate = resolvedOptions.transformCameraUpdate;
603
+ this.cancelPendingTileRequestsWhileZooming = resolvedOptions.cancelPendingTileRequestsWhileZooming === true;
604
+
605
+ this._imageQueueHandle = ImageRequest.addThrottleControl(() => this.isMoving());
606
+
607
+ this._requestManager = new RequestManager(resolvedOptions.transformRequest);
608
+
609
+ if (typeof resolvedOptions.container === 'string') {
610
+ this._container = document.getElementById(resolvedOptions.container);
611
+ if (!this._container) {
612
+ throw new Error(`Container '${resolvedOptions.container}' not found.`);
613
+ }
614
+ } else if (resolvedOptions.container instanceof HTMLElement) {
615
+ this._container = resolvedOptions.container;
616
+ } else {
617
+ throw new Error('Invalid type: \'container\' must be a String or HTMLElement.');
618
+ }
619
+
620
+ if (resolvedOptions.maxBounds) {
621
+ this.setMaxBounds(resolvedOptions.maxBounds);
622
+ }
623
+
624
+ this._setupContainer();
625
+ this._setupPainter();
626
+
627
+ this.on('move', () => this._update(false))
628
+ .on('moveend', () => this._update(false))
629
+ .on('zoom', () => this._update(true))
630
+ .on('terrain', () => {
631
+ this.painter.terrainFacilitator.dirty = true;
632
+ this._update(true);
633
+ })
634
+ .once('idle', () => { this._idleTriggered = true; });
635
+
636
+ if (typeof window !== 'undefined') {
637
+ addEventListener('online', this._onWindowOnline, false);
638
+ let initialResizeEventCaptured = false;
639
+ const throttledResizeCallback = throttle((entries: ResizeObserverEntry[]) => {
640
+ if (this._trackResize && !this._removed) {
641
+ this.resize(entries);
642
+ this.redraw();
643
+ }
644
+ }, 50);
645
+ this._resizeObserver = new ResizeObserver((entries) => {
646
+ if (!initialResizeEventCaptured) {
647
+ initialResizeEventCaptured = true;
648
+ return;
649
+ }
650
+ throttledResizeCallback(entries);
651
+ });
652
+ this._resizeObserver.observe(this._container);
653
+ }
654
+
655
+ this.handlers = new HandlerManager(this, resolvedOptions);
656
+
657
+ const hashName = (typeof resolvedOptions.hash === 'string' && resolvedOptions.hash) || undefined;
658
+ this._hash = resolvedOptions.hash && (new Hash(hashName)).addTo(this);
659
+ // don't set position from options if set through hash
660
+ if (!this._hash || !this._hash._onHashChange()) {
661
+ this.jumpTo({
662
+ center: resolvedOptions.center,
663
+ zoom: resolvedOptions.zoom,
664
+ bearing: resolvedOptions.bearing,
665
+ pitch: resolvedOptions.pitch
666
+ });
667
+
668
+ if (resolvedOptions.bounds) {
669
+ this.resize();
670
+ this.fitBounds(resolvedOptions.bounds, extend({}, resolvedOptions.fitBoundsOptions, {duration: 0}));
671
+ }
672
+ }
673
+
674
+ this.resize();
675
+
676
+ this._localIdeographFontFamily = resolvedOptions.localIdeographFontFamily;
677
+ this._validateStyle = resolvedOptions.validateStyle;
678
+
679
+ if (resolvedOptions.style) this.setStyle(resolvedOptions.style, {localIdeographFontFamily: resolvedOptions.localIdeographFontFamily});
680
+
681
+ if (resolvedOptions.attributionControl)
682
+ this.addControl(new AttributionControl(typeof resolvedOptions.attributionControl === 'boolean' ? undefined : resolvedOptions.attributionControl));
683
+
684
+ if (resolvedOptions.maplibreLogo)
685
+ this.addControl(new LogoControl(), resolvedOptions.logoPosition);
686
+
687
+ this.on('style.load', () => {
688
+ if (this.transform.unmodified) {
689
+ this.jumpTo(this.style.stylesheet as any);
690
+ }
691
+ });
692
+ this.on('data', (event: MapDataEvent) => {
693
+ this._update(event.dataType === 'style');
694
+ this.fire(new Event(`${event.dataType}data`, event));
695
+ });
696
+ this.on('dataloading', (event: MapDataEvent) => {
697
+ this.fire(new Event(`${event.dataType}dataloading`, event));
698
+ });
699
+ this.on('dataabort', (event: MapDataEvent) => {
700
+ this.fire(new Event('sourcedataabort', event));
701
+ });
702
+ }
703
+
704
+ /**
705
+ * @internal
706
+ * Returns a unique number for this map instance which is used for the MapLoadEvent
707
+ * to make sure we only fire one event per instantiated map object.
708
+ * @returns the uniq map ID
709
+ */
710
+ _getMapId() {
711
+ return this._mapId;
712
+ }
713
+
714
+ /**
715
+ * Adds an {@link IControl} to the map, calling `control.onAdd(this)`.
716
+ *
717
+ * An {@link ErrorEvent} will be fired if the image parameter is invald.
718
+ *
719
+ * @param control - The {@link IControl} to add.
720
+ * @param position - position on the map to which the control will be added.
721
+ * Valid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`.
722
+ * @example
723
+ * Add zoom and rotation controls to the map.
724
+ * ```ts
725
+ * map.addControl(new NavigationControl());
726
+ * ```
727
+ * @see [Display map navigation controls](https://maplibre.org/maplibre-gl-js/docs/examples/navigation/)
728
+ */
729
+ addControl(control: IControl, position?: ControlPosition): Map {
730
+ if (position === undefined) {
731
+ if (control.getDefaultPosition) {
732
+ position = control.getDefaultPosition();
733
+ } else {
734
+ position = 'top-right';
735
+ }
736
+ }
737
+ if (!control || !control.onAdd) {
738
+ return this.fire(new ErrorEvent(new Error(
739
+ 'Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.')));
740
+ }
741
+ const controlElement = control.onAdd(this);
742
+ this._controls.push(control);
743
+
744
+ const positionContainer = this._controlPositions[position];
745
+ if (position.indexOf('bottom') !== -1) {
746
+ positionContainer.insertBefore(controlElement, positionContainer.firstChild);
747
+ } else {
748
+ positionContainer.appendChild(controlElement);
749
+ }
750
+ return this;
751
+ }
752
+
753
+ /**
754
+ * Removes the control from the map.
755
+ *
756
+ * An {@link ErrorEvent} will be fired if the image parameter is invald.
757
+ *
758
+ * @param control - The {@link IControl} to remove.
759
+ * @example
760
+ * ```ts
761
+ * // Define a new navigation control.
762
+ * let navigation = new NavigationControl();
763
+ * // Add zoom and rotation controls to the map.
764
+ * map.addControl(navigation);
765
+ * // Remove zoom and rotation controls from the map.
766
+ * map.removeControl(navigation);
767
+ * ```
768
+ */
769
+ removeControl(control: IControl): Map {
770
+ if (!control || !control.onRemove) {
771
+ return this.fire(new ErrorEvent(new Error(
772
+ 'Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.')));
773
+ }
774
+ const ci = this._controls.indexOf(control);
775
+ if (ci > -1) this._controls.splice(ci, 1);
776
+ control.onRemove(this);
777
+ return this;
778
+ }
779
+
780
+ /**
781
+ * Checks if a control exists on the map.
782
+ *
783
+ * @param control - The {@link IControl} to check.
784
+ * @returns true if map contains control.
785
+ * @example
786
+ * ```ts
787
+ * // Define a new navigation control.
788
+ * let navigation = new NavigationControl();
789
+ * // Add zoom and rotation controls to the map.
790
+ * map.addControl(navigation);
791
+ * // Check that the navigation control exists on the map.
792
+ * map.hasControl(navigation);
793
+ * ```
794
+ */
795
+ hasControl(control: IControl): boolean {
796
+ return this._controls.indexOf(control) > -1;
797
+ }
798
+
799
+ calculateCameraOptionsFromTo(from: LngLat, altitudeFrom: number, to: LngLat, altitudeTo?: number): CameraOptions {
800
+ if (altitudeTo == null && this.terrain) {
801
+ altitudeTo = this.terrain.getElevationForLngLatZoom(to, this.transform.tileZoom);
802
+ }
803
+ return super.calculateCameraOptionsFromTo(from, altitudeFrom, to, altitudeTo);
804
+ }
805
+
806
+ /**
807
+ * Resizes the map according to the dimensions of its
808
+ * `container` element.
809
+ *
810
+ * Checks if the map container size changed and updates the map if it has changed.
811
+ * This method must be called after the map's `container` is resized programmatically
812
+ * or when the map is shown after being initially hidden with CSS.
813
+ *
814
+ * Triggers the following events: `movestart`, `move`, `moveend`, and `resize`.
815
+ *
816
+ * @param eventData - Additional properties to be passed to `movestart`, `move`, `resize`, and `moveend`
817
+ * events that get triggered as a result of resize. This can be useful for differentiating the
818
+ * source of an event (for example, user-initiated or programmatically-triggered events).
819
+ * @example
820
+ * Resize the map when the map container is shown after being initially hidden with CSS.
821
+ * ```ts
822
+ * let mapDiv = document.getElementById('map');
823
+ * if (mapDiv.style.visibility === true) map.resize();
824
+ * ```
825
+ */
826
+ resize(eventData?: any): Map {
827
+ const dimensions = this._containerDimensions();
828
+ const width = dimensions[0];
829
+ const height = dimensions[1];
830
+
831
+ const clampedPixelRatio = this._getClampedPixelRatio(width, height);
832
+ this._resizeCanvas(width, height, clampedPixelRatio);
833
+ this.painter.resize(width, height, clampedPixelRatio);
834
+
835
+ // check if we've reached GL limits, in that case further clamps pixelRatio
836
+ if (this.painter.overLimit()) {
837
+ const gl = this.painter.context.gl;
838
+ // store updated _maxCanvasSize value
839
+ this._maxCanvasSize = [gl.drawingBufferWidth, gl.drawingBufferHeight];
840
+ const clampedPixelRatio = this._getClampedPixelRatio(width, height);
841
+ this._resizeCanvas(width, height, clampedPixelRatio);
842
+ this.painter.resize(width, height, clampedPixelRatio);
843
+ }
844
+
845
+ this.transform.resize(width, height);
846
+ this._requestedCameraState?.resize(width, height);
847
+
848
+ const fireMoving = !this._moving;
849
+ if (fireMoving) {
850
+ this.stop();
851
+ this.fire(new Event('movestart', eventData))
852
+ .fire(new Event('move', eventData));
853
+ }
854
+
855
+ this.fire(new Event('resize', eventData));
856
+
857
+ if (fireMoving) this.fire(new Event('moveend', eventData));
858
+
859
+ return this;
860
+ }
861
+
862
+ /**
863
+ * @internal
864
+ * Return the map's pixel ratio eventually scaled down to respect maxCanvasSize.
865
+ * Internally you should use this and not getPixelRatio().
866
+ */
867
+ _getClampedPixelRatio(width: number, height: number): number {
868
+ const {0: maxCanvasWidth, 1: maxCanvasHeight} = this._maxCanvasSize;
869
+ const pixelRatio = this.getPixelRatio();
870
+
871
+ const canvasWidth = width * pixelRatio;
872
+ const canvasHeight = height * pixelRatio;
873
+
874
+ const widthScaleFactor = canvasWidth > maxCanvasWidth ? (maxCanvasWidth / canvasWidth) : 1;
875
+ const heightScaleFactor = canvasHeight > maxCanvasHeight ? (maxCanvasHeight / canvasHeight) : 1;
876
+
877
+ return Math.min(widthScaleFactor, heightScaleFactor) * pixelRatio;
878
+ }
879
+
880
+ /**
881
+ * Returns the map's pixel ratio.
882
+ * Note that the pixel ratio actually applied may be lower to respect maxCanvasSize.
883
+ * @returns The pixel ratio.
884
+ */
885
+ getPixelRatio(): number {
886
+ return this._overridePixelRatio ?? devicePixelRatio;
887
+ }
888
+
889
+ /**
890
+ * Sets the map's pixel ratio. This allows to override `devicePixelRatio`.
891
+ * After this call, the canvas' `width` attribute will be `container.clientWidth * pixelRatio`
892
+ * and its height attribute will be `container.clientHeight * pixelRatio`.
893
+ * Set this to null to disable `devicePixelRatio` override.
894
+ * Note that the pixel ratio actually applied may be lower to respect maxCanvasSize.
895
+ * @param pixelRatio - The pixel ratio.
896
+ */
897
+ setPixelRatio(pixelRatio: number) {
898
+ this._overridePixelRatio = pixelRatio;
899
+ this.resize();
900
+ }
901
+
902
+ /**
903
+ * Returns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not
904
+ * an axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region.
905
+ * @returns The geographical bounds of the map as {@link LngLatBounds}.
906
+ * @example
907
+ * ```ts
908
+ * let bounds = map.getBounds();
909
+ * ```
910
+ */
911
+ getBounds(): LngLatBounds {
912
+ return this.transform.getBounds();
913
+ }
914
+
915
+ /**
916
+ * Returns the maximum geographical bounds the map is constrained to, or `null` if none set.
917
+ * @returns The map object.
918
+ * @example
919
+ * ```ts
920
+ * let maxBounds = map.getMaxBounds();
921
+ * ```
922
+ */
923
+ getMaxBounds(): LngLatBounds | null {
924
+ return this.transform.getMaxBounds();
925
+ }
926
+
927
+ /**
928
+ * Sets or clears the map's geographical bounds.
929
+ *
930
+ * Pan and zoom operations are constrained within these bounds.
931
+ * If a pan or zoom is performed that would
932
+ * display regions outside these bounds, the map will
933
+ * instead display a position and zoom level
934
+ * as close as possible to the operation's request while still
935
+ * remaining within the bounds.
936
+ *
937
+ * @param bounds - The maximum bounds to set. If `null` or `undefined` is provided, the function removes the map's maximum bounds.
938
+ * @example
939
+ * Define bounds that conform to the `LngLatBoundsLike` object as set the max bounds.
940
+ * ```ts
941
+ * let bounds = [
942
+ * [-74.04728, 40.68392], // [west, south]
943
+ * [-73.91058, 40.87764] // [east, north]
944
+ * ];
945
+ * map.setMaxBounds(bounds);
946
+ * ```
947
+ */
948
+ setMaxBounds(bounds?: LngLatBoundsLike | null): Map {
949
+ this.transform.setMaxBounds(LngLatBounds.convert(bounds));
950
+ return this._update();
951
+ }
952
+
953
+ /**
954
+ * Sets or clears the map's minimum zoom level.
955
+ * If the map's current zoom level is lower than the new minimum,
956
+ * the map will zoom to the new minimum.
957
+ *
958
+ * It is not always possible to zoom out and reach the set `minZoom`.
959
+ * Other factors such as map height may restrict zooming. For example,
960
+ * if the map is 512px tall it will not be possible to zoom below zoom 0
961
+ * no matter what the `minZoom` is set to.
962
+ *
963
+ * A {@link ErrorEvent} event will be fired if minZoom is out of bounds.
964
+ *
965
+ * @param minZoom - The minimum zoom level to set (-2 - 24).
966
+ * If `null` or `undefined` is provided, the function removes the current minimum zoom (i.e. sets it to -2).
967
+ * @example
968
+ * ```ts
969
+ * map.setMinZoom(12.25);
970
+ * ```
971
+ */
972
+ setMinZoom(minZoom?: number | null): Map {
973
+
974
+ minZoom = minZoom === null || minZoom === undefined ? defaultMinZoom : minZoom;
975
+
976
+ if (minZoom >= defaultMinZoom && minZoom <= this.transform.maxZoom) {
977
+ this.transform.minZoom = minZoom;
978
+ this._update();
979
+
980
+ if (this.getZoom() < minZoom) this.setZoom(minZoom);
981
+
982
+ return this;
983
+
984
+ } else throw new Error(`minZoom must be between ${defaultMinZoom} and the current maxZoom, inclusive`);
985
+ }
986
+
987
+ /**
988
+ * Returns the map's minimum allowable zoom level.
989
+ *
990
+ * @returns minZoom
991
+ * @example
992
+ * ```ts
993
+ * let minZoom = map.getMinZoom();
994
+ * ```
995
+ */
996
+ getMinZoom(): number { return this.transform.minZoom; }
997
+
998
+ /**
999
+ * Sets or clears the map's maximum zoom level.
1000
+ * If the map's current zoom level is higher than the new maximum,
1001
+ * the map will zoom to the new maximum.
1002
+ *
1003
+ * A {@link ErrorEvent} event will be fired if minZoom is out of bounds.
1004
+ *
1005
+ * @param maxZoom - The maximum zoom level to set.
1006
+ * If `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22).
1007
+ * @example
1008
+ * ```ts
1009
+ * map.setMaxZoom(18.75);
1010
+ * ```
1011
+ */
1012
+ setMaxZoom(maxZoom?: number | null): Map {
1013
+
1014
+ maxZoom = maxZoom === null || maxZoom === undefined ? defaultMaxZoom : maxZoom;
1015
+
1016
+ if (maxZoom >= this.transform.minZoom) {
1017
+ this.transform.maxZoom = maxZoom;
1018
+ this._update();
1019
+
1020
+ if (this.getZoom() > maxZoom) this.setZoom(maxZoom);
1021
+
1022
+ return this;
1023
+
1024
+ } else throw new Error('maxZoom must be greater than the current minZoom');
1025
+ }
1026
+
1027
+ /**
1028
+ * Returns the map's maximum allowable zoom level.
1029
+ *
1030
+ * @returns The maxZoom
1031
+ * @example
1032
+ * ```ts
1033
+ * let maxZoom = map.getMaxZoom();
1034
+ * ```
1035
+ */
1036
+ getMaxZoom(): number { return this.transform.maxZoom; }
1037
+
1038
+ /**
1039
+ * Sets or clears the map's minimum pitch.
1040
+ * If the map's current pitch is lower than the new minimum,
1041
+ * the map will pitch to the new minimum.
1042
+ *
1043
+ * A {@link ErrorEvent} event will be fired if minPitch is out of bounds.
1044
+ *
1045
+ * @param minPitch - The minimum pitch to set (0-85). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.
1046
+ * If `null` or `undefined` is provided, the function removes the current minimum pitch (i.e. sets it to 0).
1047
+ */
1048
+ setMinPitch(minPitch?: number | null): Map {
1049
+
1050
+ minPitch = minPitch === null || minPitch === undefined ? defaultMinPitch : minPitch;
1051
+
1052
+ if (minPitch < defaultMinPitch) {
1053
+ throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
1054
+ }
1055
+
1056
+ if (minPitch >= defaultMinPitch && minPitch <= this.transform.maxPitch) {
1057
+ this.transform.minPitch = minPitch;
1058
+ this._update();
1059
+
1060
+ if (this.getPitch() < minPitch) this.setPitch(minPitch);
1061
+
1062
+ return this;
1063
+
1064
+ } else throw new Error(`minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive`);
1065
+ }
1066
+
1067
+ /**
1068
+ * Returns the map's minimum allowable pitch.
1069
+ *
1070
+ * @returns The minPitch
1071
+ */
1072
+ getMinPitch(): number { return this.transform.minPitch; }
1073
+
1074
+ /**
1075
+ * Sets or clears the map's maximum pitch.
1076
+ * If the map's current pitch is higher than the new maximum,
1077
+ * the map will pitch to the new maximum.
1078
+ *
1079
+ * A {@link ErrorEvent} event will be fired if maxPitch is out of bounds.
1080
+ *
1081
+ * @param maxPitch - The maximum pitch to set (0-85). Values greater than 60 degrees are experimental and may result in rendering issues. If you encounter any, please raise an issue with details in the MapLibre project.
1082
+ * If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 60).
1083
+ */
1084
+ setMaxPitch(maxPitch?: number | null): Map {
1085
+
1086
+ maxPitch = maxPitch === null || maxPitch === undefined ? defaultMaxPitch : maxPitch;
1087
+
1088
+ if (maxPitch > maxPitchThreshold) {
1089
+ throw new Error(`maxPitch must be less than or equal to ${maxPitchThreshold}`);
1090
+ }
1091
+
1092
+ if (maxPitch >= this.transform.minPitch) {
1093
+ this.transform.maxPitch = maxPitch;
1094
+ this._update();
1095
+
1096
+ if (this.getPitch() > maxPitch) this.setPitch(maxPitch);
1097
+
1098
+ return this;
1099
+
1100
+ } else throw new Error('maxPitch must be greater than the current minPitch');
1101
+ }
1102
+
1103
+ /**
1104
+ * Returns the map's maximum allowable pitch.
1105
+ *
1106
+ * @returns The maxPitch
1107
+ */
1108
+ getMaxPitch(): number { return this.transform.maxPitch; }
1109
+
1110
+ /**
1111
+ * Returns the state of `renderWorldCopies`. If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:
1112
+ *
1113
+ * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
1114
+ * container, there will be blank space beyond 180 and -180 degrees longitude.
1115
+ * - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
1116
+ * map and the other on the left edge of the map) at every zoom level.
1117
+ * @returns The renderWorldCopies
1118
+ * @example
1119
+ * ```ts
1120
+ * let worldCopiesRendered = map.getRenderWorldCopies();
1121
+ * ```
1122
+ * @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
1123
+ */
1124
+ getRenderWorldCopies(): boolean { return this.transform.renderWorldCopies; }
1125
+
1126
+ /**
1127
+ * Sets the state of `renderWorldCopies`.
1128
+ *
1129
+ * @param renderWorldCopies - If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:
1130
+ *
1131
+ * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
1132
+ * container, there will be blank space beyond 180 and -180 degrees longitude.
1133
+ * - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
1134
+ * map and the other on the left edge of the map) at every zoom level.
1135
+ *
1136
+ * `undefined` is treated as `true`, `null` is treated as `false`.
1137
+ * @example
1138
+ * ```ts
1139
+ * map.setRenderWorldCopies(true);
1140
+ * ```
1141
+ * @see [Render world copies](https://maplibre.org/maplibre-gl-js/docs/examples/render-world-copies/)
1142
+ */
1143
+ setRenderWorldCopies(renderWorldCopies?: boolean | null): Map {
1144
+ this.transform.renderWorldCopies = renderWorldCopies;
1145
+ return this._update();
1146
+ }
1147
+
1148
+ /**
1149
+ * Returns a [Point](https://github.com/mapbox/point-geometry) representing pixel coordinates, relative to the map's `container`,
1150
+ * that correspond to the specified geographical location.
1151
+ *
1152
+ * @param lnglat - The geographical location to project.
1153
+ * @returns The [Point](https://github.com/mapbox/point-geometry) corresponding to `lnglat`, relative to the map's `container`.
1154
+ * @example
1155
+ * ```ts
1156
+ * let coordinate = [-122.420679, 37.772537];
1157
+ * let point = map.project(coordinate);
1158
+ * ```
1159
+ */
1160
+ project(lnglat: LngLatLike): Point {
1161
+ return this.transform.locationPoint(LngLat.convert(lnglat), this.style && this.terrain);
1162
+ }
1163
+
1164
+ /**
1165
+ * Returns a {@link LngLat} representing geographical coordinates that correspond
1166
+ * to the specified pixel coordinates.
1167
+ *
1168
+ * @param point - The pixel coordinates to unproject.
1169
+ * @returns The {@link LngLat} corresponding to `point`.
1170
+ * @example
1171
+ * ```ts
1172
+ * map.on('click', (e) => {
1173
+ * // When the map is clicked, get the geographic coordinate.
1174
+ * let coordinate = map.unproject(e.point);
1175
+ * });
1176
+ * ```
1177
+ */
1178
+ unproject(point: PointLike): LngLat {
1179
+ return this.transform.pointLocation(Point.convert(point), this.terrain);
1180
+ }
1181
+
1182
+ /**
1183
+ * Returns true if the map is panning, zooming, rotating, or pitching due to a camera animation or user gesture.
1184
+ * @returns true if the map is moving.
1185
+ * @example
1186
+ * ```ts
1187
+ * let isMoving = map.isMoving();
1188
+ * ```
1189
+ */
1190
+ isMoving(): boolean {
1191
+ return this._moving || this.handlers?.isMoving();
1192
+ }
1193
+
1194
+ /**
1195
+ * Returns true if the map is zooming due to a camera animation or user gesture.
1196
+ * @returns true if the map is zooming.
1197
+ * @example
1198
+ * ```ts
1199
+ * let isZooming = map.isZooming();
1200
+ * ```
1201
+ */
1202
+ isZooming(): boolean {
1203
+ return this._zooming || this.handlers?.isZooming();
1204
+ }
1205
+
1206
+ /**
1207
+ * Returns true if the map is rotating due to a camera animation or user gesture.
1208
+ * @returns true if the map is rotating.
1209
+ * @example
1210
+ * ```ts
1211
+ * map.isRotating();
1212
+ * ```
1213
+ */
1214
+ isRotating(): boolean {
1215
+ return this._rotating || this.handlers?.isRotating();
1216
+ }
1217
+
1218
+ _createDelegatedListener(type: keyof MapEventType | string, layerIds: string[], listener: Listener): DelegatedListener {
1219
+ if (type === 'mouseenter' || type === 'mouseover') {
1220
+ let mousein = false;
1221
+ const mousemove = (e) => {
1222
+ const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
1223
+ const features = existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, {layers: existingLayers}) : [];
1224
+ if (!features.length) {
1225
+ mousein = false;
1226
+ } else if (!mousein) {
1227
+ mousein = true;
1228
+ listener.call(this, new MapMouseEvent(type, this, e.originalEvent, {features}));
1229
+ }
1230
+ };
1231
+ const mouseout = () => {
1232
+ mousein = false;
1233
+ };
1234
+ return {layers: layerIds, listener, delegates: {mousemove, mouseout}};
1235
+ } else if (type === 'mouseleave' || type === 'mouseout') {
1236
+ let mousein = false;
1237
+ const mousemove = (e) => {
1238
+ const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
1239
+ const features = existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, {layers: existingLayers}) : [];
1240
+ if (features.length) {
1241
+ mousein = true;
1242
+ } else if (mousein) {
1243
+ mousein = false;
1244
+ listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
1245
+ }
1246
+ };
1247
+ const mouseout = (e) => {
1248
+ if (mousein) {
1249
+ mousein = false;
1250
+ listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
1251
+ }
1252
+ };
1253
+ return {layers: layerIds, listener, delegates: {mousemove, mouseout}};
1254
+ } else {
1255
+ const delegate = (e) => {
1256
+ const existingLayers = layerIds.filter((layerId) => this.getLayer(layerId));
1257
+ const features = existingLayers.length !== 0 ? this.queryRenderedFeatures(e.point, {layers: existingLayers}) : [];
1258
+ if (features.length) {
1259
+ // Here we need to mutate the original event, so that preventDefault works as expected.
1260
+ e.features = features;
1261
+ listener.call(this, e);
1262
+ delete e.features;
1263
+ }
1264
+ };
1265
+ return {layers: layerIds, listener, delegates: {[type]: delegate}};
1266
+ }
1267
+ }
1268
+
1269
+ _saveDelegatedListener(type: keyof MapEventType | string, delegatedListener: DelegatedListener): void {
1270
+ this._delegatedListeners = this._delegatedListeners || {};
1271
+ this._delegatedListeners[type] = this._delegatedListeners[type] || [];
1272
+ this._delegatedListeners[type].push(delegatedListener);
1273
+ }
1274
+
1275
+ _removeDelegatedListener(type: string, layerIds: string[], listener: Listener) {
1276
+ if (!this._delegatedListeners || !this._delegatedListeners[type]) {
1277
+ return;
1278
+ }
1279
+
1280
+ const listeners = this._delegatedListeners[type];
1281
+ for (let i = 0; i < listeners.length; i++) {
1282
+ const delegatedListener = listeners[i];
1283
+ if (
1284
+ delegatedListener.listener === listener &&
1285
+ delegatedListener.layers.length === layerIds.length &&
1286
+ delegatedListener.layers.every((layerId: string) => layerIds.includes(layerId))
1287
+ ) {
1288
+ for (const event in delegatedListener.delegates) {
1289
+ this.off(event, delegatedListener.delegates[event]);
1290
+ }
1291
+ listeners.splice(i, 1);
1292
+ return;
1293
+ }
1294
+ }
1295
+ }
1296
+
1297
+ /**
1298
+ * @event
1299
+ * Adds a listener for events of a specified type, optionally limited to features in a specified style layer(s).
1300
+ * See {@link MapEventType} and {@link MapLayerEventType} for a full list of events and their description.
1301
+ *
1302
+ * | Event | Compatible with `layerId` |
1303
+ * |------------------------|---------------------------|
1304
+ * | `mousedown` | yes |
1305
+ * | `mouseup` | yes |
1306
+ * | `mouseover` | yes |
1307
+ * | `mouseout` | yes |
1308
+ * | `mousemove` | yes |
1309
+ * | `mouseenter` | yes (required) |
1310
+ * | `mouseleave` | yes (required) |
1311
+ * | `click` | yes |
1312
+ * | `dblclick` | yes |
1313
+ * | `contextmenu` | yes |
1314
+ * | `touchstart` | yes |
1315
+ * | `touchend` | yes |
1316
+ * | `touchcancel` | yes |
1317
+ * | `wheel` | |
1318
+ * | `resize` | |
1319
+ * | `remove` | |
1320
+ * | `touchmove` | |
1321
+ * | `movestart` | |
1322
+ * | `move` | |
1323
+ * | `moveend` | |
1324
+ * | `dragstart` | |
1325
+ * | `drag` | |
1326
+ * | `dragend` | |
1327
+ * | `zoomstart` | |
1328
+ * | `zoom` | |
1329
+ * | `zoomend` | |
1330
+ * | `rotatestart` | |
1331
+ * | `rotate` | |
1332
+ * | `rotateend` | |
1333
+ * | `pitchstart` | |
1334
+ * | `pitch` | |
1335
+ * | `pitchend` | |
1336
+ * | `boxzoomstart` | |
1337
+ * | `boxzoomend` | |
1338
+ * | `boxzoomcancel` | |
1339
+ * | `webglcontextlost` | |
1340
+ * | `webglcontextrestored` | |
1341
+ * | `load` | |
1342
+ * | `render` | |
1343
+ * | `idle` | |
1344
+ * | `error` | |
1345
+ * | `data` | |
1346
+ * | `styledata` | |
1347
+ * | `sourcedata` | |
1348
+ * | `dataloading` | |
1349
+ * | `styledataloading` | |
1350
+ * | `sourcedataloading` | |
1351
+ * | `styleimagemissing` | |
1352
+ * | `dataabort` | |
1353
+ * | `sourcedataabort` | |
1354
+ *
1355
+ * @param type - The event type to listen for. Events compatible with the optional `layerId` parameter are triggered
1356
+ * when the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas.
1357
+ * @param layer - The ID of a style layer or a listener if no ID is provided. Event will only be triggered if its location
1358
+ * is within a visible feature in this layer. The event will have a `features` property containing
1359
+ * an array of the matching features. If `layer` is not supplied, the event will not have a `features` property.
1360
+ * Please note that many event types are not compatible with the optional `layer` parameter.
1361
+ * @param listener - The function to be called when the event is fired.
1362
+ * @example
1363
+ * ```ts
1364
+ * // Set an event listener that will fire
1365
+ * // when the map has finished loading
1366
+ * map.on('load', () => {
1367
+ * // Once the map has finished loading,
1368
+ * // add a new layer
1369
+ * map.addLayer({
1370
+ * id: 'points-of-interest',
1371
+ * source: {
1372
+ * type: 'vector',
1373
+ * url: 'https://maplibre.org/maplibre-style-spec/'
1374
+ * },
1375
+ * 'source-layer': 'poi_label',
1376
+ * type: 'circle',
1377
+ * paint: {
1378
+ * // MapLibre Style Specification paint properties
1379
+ * },
1380
+ * layout: {
1381
+ * // MapLibre Style Specification layout properties
1382
+ * }
1383
+ * });
1384
+ * });
1385
+ * ```
1386
+ * @example
1387
+ * ```ts
1388
+ * // Set an event listener that will fire
1389
+ * // when a feature on the countries layer of the map is clicked
1390
+ * map.on('click', 'countries', (e) => {
1391
+ * new Popup()
1392
+ * .setLngLat(e.lngLat)
1393
+ * .setHTML(`Country name: ${e.features[0].properties.name}`)
1394
+ * .addTo(map);
1395
+ * });
1396
+ * ```
1397
+ * @see [Display popup on click](https://maplibre.org/maplibre-gl-js/docs/examples/popup-on-click/)
1398
+ * @see [Center the map on a clicked symbol](https://maplibre.org/maplibre-gl-js/docs/examples/center-on-symbol/)
1399
+ * @see [Create a hover effect](https://maplibre.org/maplibre-gl-js/docs/examples/hover-styles/)
1400
+ * @see [Create a draggable marker](https://maplibre.org/maplibre-gl-js/docs/examples/drag-a-point/)
1401
+ */
1402
+ on<T extends keyof MapLayerEventType>(
1403
+ type: T,
1404
+ layer: string,
1405
+ listener: (ev: MapLayerEventType[T] & Object) => void,
1406
+ ): Map;
1407
+ /**
1408
+ * Overload of the `on` method that allows to listen to events specifying multiple layers.
1409
+ * @event
1410
+ * @param type - The type of the event.
1411
+ * @param layerIds - The array of style layer IDs.
1412
+ * @param listener - The listener callback.
1413
+ */
1414
+ on<T extends keyof MapLayerEventType>(
1415
+ type: T,
1416
+ layerIds: string[],
1417
+ listener: (ev: MapLayerEventType[T] & Object) => void
1418
+ ): this;
1419
+ /**
1420
+ * Overload of the `on` method that allows to listen to events without specifying a layer.
1421
+ * @event
1422
+ * @param type - The type of the event.
1423
+ * @param listener - The listener callback.
1424
+ */
1425
+ on<T extends keyof MapEventType>(type: T, listener: (ev: MapEventType[T] & Object) => void): this;
1426
+ /**
1427
+ * Overload of the `on` method that allows to listen to events without specifying a layer.
1428
+ * @event
1429
+ * @param type - The type of the event.
1430
+ * @param listener - The listener callback.
1431
+ */
1432
+ on(type: keyof MapEventType | string, listener: Listener): this;
1433
+ on(type: keyof MapEventType | string, layerIdsOrListener: string | string[] | Listener, listener?: Listener): this {
1434
+ if (listener === undefined) {
1435
+ return super.on(type, layerIdsOrListener as Listener);
1436
+ }
1437
+
1438
+ const layerIds = typeof layerIdsOrListener === 'string' ? [layerIdsOrListener] : layerIdsOrListener as string[];
1439
+
1440
+ const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
1441
+
1442
+ this._saveDelegatedListener(type, delegatedListener);
1443
+
1444
+ for (const event in delegatedListener.delegates) {
1445
+ this.on(event, delegatedListener.delegates[event]);
1446
+ }
1447
+
1448
+ return this;
1449
+ }
1450
+
1451
+ /**
1452
+ * Adds a listener that will be called only once to a specified event type, optionally limited to features in a specified style layer.
1453
+ *
1454
+ * @event
1455
+ * @param type - The event type to listen for; one of `'mousedown'`, `'mouseup'`, `'click'`, `'dblclick'`,
1456
+ * `'mousemove'`, `'mouseenter'`, `'mouseleave'`, `'mouseover'`, `'mouseout'`, `'contextmenu'`, `'touchstart'`,
1457
+ * `'touchend'`, or `'touchcancel'`. `mouseenter` and `mouseover` events are triggered when the cursor enters
1458
+ * a visible portion of the specified layer from outside that layer or outside the map canvas. `mouseleave`
1459
+ * and `mouseout` events are triggered when the cursor leaves a visible portion of the specified layer, or leaves
1460
+ * the map canvas.
1461
+ * @param layer - The ID of a style layer or a listener if no ID is provided. Only events whose location is within a visible
1462
+ * feature in this layer will trigger the listener. The event will have a `features` property containing
1463
+ * an array of the matching features.
1464
+ * @param listener - The function to be called when the event is fired.
1465
+ * @returns `this` if listener is provided, promise otherwise to allow easier usage of async/await
1466
+ */
1467
+ once<T extends keyof MapLayerEventType>(
1468
+ type: T,
1469
+ layer: string,
1470
+ listener?: (ev: MapLayerEventType[T] & Object) => void,
1471
+ ): this | Promise<MapLayerEventType[T] & Object>;
1472
+ /**
1473
+ * Overload of the `once` method that allows to listen to events specifying multiple layers.
1474
+ * @event
1475
+ * @param type - The type of the event.
1476
+ * @param layerIds - The array of style layer IDs.
1477
+ * @param listener - The listener callback.
1478
+ */
1479
+ once<T extends keyof MapLayerEventType>(
1480
+ type: T,
1481
+ layerIds: string[],
1482
+ listener?: (ev: MapLayerEventType[T] & Object) => void
1483
+ ): this | Promise<any>;
1484
+ /**
1485
+ * Overload of the `once` method that allows to listen to events without specifying a layer.
1486
+ * @event
1487
+ * @param type - The type of the event.
1488
+ * @param listener - The listener callback.
1489
+ */
1490
+ once<T extends keyof MapEventType>(type: T, listener?: (ev: MapEventType[T] & Object) => void): this | Promise<any>;
1491
+ /**
1492
+ * Overload of the `once` method that allows to listen to events without specifying a layer.
1493
+ * @event
1494
+ * @param type - The type of the event.
1495
+ * @param listener - The listener callback.
1496
+ */
1497
+ once(type: keyof MapEventType | string, listener?: Listener): this | Promise<any>;
1498
+ once(type: keyof MapEventType | string, layerIdsOrListener: string | string[] | Listener, listener?: Listener): this | Promise<any> {
1499
+ if (listener === undefined) {
1500
+ return super.once(type, layerIdsOrListener as Listener);
1501
+ }
1502
+
1503
+ const layerIds = typeof layerIdsOrListener === 'string' ? [layerIdsOrListener] : layerIdsOrListener as string[];
1504
+
1505
+ const delegatedListener = this._createDelegatedListener(type, layerIds, listener);
1506
+
1507
+ for (const key in delegatedListener.delegates) {
1508
+ const delegate: Delegate = delegatedListener.delegates[key];
1509
+ delegatedListener.delegates[key] = (...args: Parameters<Delegate>) => {
1510
+ this._removeDelegatedListener(type, layerIds, listener);
1511
+ delegate(...args);
1512
+ };
1513
+ }
1514
+
1515
+ this._saveDelegatedListener(type, delegatedListener);
1516
+
1517
+ for (const event in delegatedListener.delegates) {
1518
+ this.once(event, delegatedListener.delegates[event]);
1519
+ }
1520
+
1521
+ return this;
1522
+ }
1523
+
1524
+ /**
1525
+ * Removes an event listener for events previously added with `Map#on`.
1526
+ *
1527
+ * @event
1528
+ * @param type - The event type previously used to install the listener.
1529
+ * @param layer - The layer ID or listener previously used to install the listener.
1530
+ * @param listener - The function previously installed as a listener.
1531
+ */
1532
+ off<T extends keyof MapLayerEventType>(
1533
+ type: T,
1534
+ layer: string,
1535
+ listener: (ev: MapLayerEventType[T] & Object) => void,
1536
+ ): this;
1537
+ /**
1538
+ * Overload of the `off` method that allows to remove an event created with multiple layers.
1539
+ * Provide the same layer IDs as to `on` or `once`, when the listener was registered.
1540
+ * @event
1541
+ * @param type - The type of the event.
1542
+ * @param layers - The layer IDs previously used to install the listener.
1543
+ * @param listener - The function previously installed as a listener.
1544
+ */
1545
+ off<T extends keyof MapLayerEventType>(
1546
+ type: T,
1547
+ layers: string[],
1548
+ listener: (ev: MapLayerEventType[T] & Object) => void,
1549
+ ): this;
1550
+ /**
1551
+ * Overload of the `off` method that allows to remove an event created without specifying a layer.
1552
+ * @event
1553
+ * @param type - The type of the event.
1554
+ * @param listener - The function previously installed as a listener.
1555
+ */
1556
+ off<T extends keyof MapEventType>(type: T, listener: (ev: MapEventType[T] & Object) => void): this;
1557
+ /**
1558
+ * Overload of the `off` method that allows to remove an event created without specifying a layer.
1559
+ * @event
1560
+ * @param type - The type of the event.
1561
+ * @param listener - The function previously installed as a listener.
1562
+ */
1563
+ off(type: keyof MapEventType | string, listener: Listener): this;
1564
+ off(type: keyof MapEventType | string, layerIdsOrListener: string | string[] | Listener, listener?: Listener): this {
1565
+ if (listener === undefined) {
1566
+ return super.off(type, layerIdsOrListener as Listener);
1567
+ }
1568
+
1569
+ const layerIds = typeof layerIdsOrListener === 'string' ? [layerIdsOrListener] : layerIdsOrListener as string[];
1570
+ this._removeDelegatedListener(type, layerIds, listener);
1571
+
1572
+ return this;
1573
+ }
1574
+
1575
+ /**
1576
+ * Returns an array of MapGeoJSONFeature objects
1577
+ * representing visible features that satisfy the query parameters.
1578
+ *
1579
+ * @param geometryOrOptions - (optional) The geometry of the query region:
1580
+ * either a single point or southwest and northeast points describing a bounding box.
1581
+ * Omitting this parameter (i.e. calling {@link Map#queryRenderedFeatures} with zero arguments,
1582
+ * or with only a `options` argument) is equivalent to passing a bounding box encompassing the entire
1583
+ * map viewport.
1584
+ * The geometryOrOptions can receive a {@link QueryRenderedFeaturesOptions} only to support a situation where the function receives only one parameter which is the options parameter.
1585
+ * @param options - (optional) Options object.
1586
+ *
1587
+ * @returns An array of MapGeoJSONFeature objects.
1588
+ *
1589
+ * The `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only
1590
+ * string and numeric property values are supported (i.e. `null`, `Array`, and `Object` values are not supported).
1591
+ *
1592
+ * Each feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object
1593
+ * representing the style layer to which the feature belongs. Layout and paint properties in this object contain values
1594
+ * which are fully evaluated for the given zoom level and feature.
1595
+ *
1596
+ * Only features that are currently rendered are included. Some features will **not** be included, like:
1597
+ *
1598
+ * - Features from layers whose `visibility` property is `"none"`.
1599
+ * - Features from layers whose zoom range excludes the current zoom level.
1600
+ * - Symbol features that have been hidden due to text or icon collision.
1601
+ *
1602
+ * Features from all other layers are included, including features that may have no visible
1603
+ * contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to
1604
+ * 0.
1605
+ *
1606
+ * The topmost rendered feature appears first in the returned array, and subsequent features are sorted by
1607
+ * descending z-order. Features that are rendered multiple times (due to wrapping across the antemeridian at low
1608
+ * zoom levels) are returned only once (though subject to the following caveat).
1609
+ *
1610
+ * Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
1611
+ * geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
1612
+ * times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
1613
+ * The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
1614
+ * rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
1615
+ * will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
1616
+ * tiles due to tile buffering.
1617
+ *
1618
+ * @example
1619
+ * Find all features at a point
1620
+ * ```ts
1621
+ * let features = map.queryRenderedFeatures(
1622
+ * [20, 35],
1623
+ * { layers: ['my-layer-name'] }
1624
+ * );
1625
+ * ```
1626
+ *
1627
+ * @example
1628
+ * Find all features within a static bounding box
1629
+ * ```ts
1630
+ * let features = map.queryRenderedFeatures(
1631
+ * [[10, 20], [30, 50]],
1632
+ * { layers: ['my-layer-name'] }
1633
+ * );
1634
+ * ```
1635
+ *
1636
+ * @example
1637
+ * Find all features within a bounding box around a point
1638
+ * ```ts
1639
+ * let width = 10;
1640
+ * let height = 20;
1641
+ * let features = map.queryRenderedFeatures([
1642
+ * [point.x - width / 2, point.y - height / 2],
1643
+ * [point.x + width / 2, point.y + height / 2]
1644
+ * ], { layers: ['my-layer-name'] });
1645
+ * ```
1646
+ *
1647
+ * @example
1648
+ * Query all rendered features from a single layer
1649
+ * ```ts
1650
+ * let features = map.queryRenderedFeatures({ layers: ['my-layer-name'] });
1651
+ * ```
1652
+ * @see [Get features under the mouse pointer](https://maplibre.org/maplibre-gl-js/docs/examples/queryrenderedfeatures/)
1653
+ */
1654
+ queryRenderedFeatures(geometryOrOptions?: PointLike | [PointLike, PointLike] | QueryRenderedFeaturesOptions, options?: QueryRenderedFeaturesOptions): MapGeoJSONFeature[] {
1655
+ if (!this.style) {
1656
+ return [];
1657
+ }
1658
+ let queryGeometry;
1659
+ const isGeometry = geometryOrOptions instanceof Point || Array.isArray(geometryOrOptions);
1660
+ const geometry = isGeometry ? geometryOrOptions : [[0, 0], [this.transform.width, this.transform.height]];
1661
+ options = options || (isGeometry ? {} : geometryOrOptions) || {};
1662
+
1663
+ if (geometry instanceof Point || typeof geometry[0] === 'number') {
1664
+ queryGeometry = [Point.convert(geometry as PointLike)];
1665
+ } else {
1666
+ const tl = Point.convert(geometry[0] as PointLike);
1667
+ const br = Point.convert(geometry[1] as PointLike);
1668
+ queryGeometry = [tl, new Point(br.x, tl.y), br, new Point(tl.x, br.y), tl];
1669
+ }
1670
+
1671
+ return this.style.queryRenderedFeatures(queryGeometry, options, this.transform);
1672
+ }
1673
+
1674
+ /**
1675
+ * Returns an array of MapGeoJSONFeature objects
1676
+ * representing features within the specified vector tile or GeoJSON source that satisfy the query parameters.
1677
+ *
1678
+ * @param sourceId - The ID of the vector tile or GeoJSON source to query.
1679
+ * @param parameters - The options object.
1680
+ * @returns An array of MapGeoJSONFeature objects.
1681
+ *
1682
+ * In contrast to {@link Map#queryRenderedFeatures}, this function returns all features matching the query parameters,
1683
+ * whether or not they are rendered by the current style (i.e. visible). The domain of the query includes all currently-loaded
1684
+ * vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently
1685
+ * visible viewport.
1686
+ *
1687
+ * Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
1688
+ * geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
1689
+ * times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
1690
+ * The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
1691
+ * rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
1692
+ * will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
1693
+ * tiles due to tile buffering.
1694
+ *
1695
+ * @example
1696
+ * Find all features in one source layer in a vector source
1697
+ * ```ts
1698
+ * let features = map.querySourceFeatures('your-source-id', {
1699
+ * sourceLayer: 'your-source-layer'
1700
+ * });
1701
+ * ```
1702
+ *
1703
+ */
1704
+ querySourceFeatures(sourceId: string, parameters?: QuerySourceFeatureOptions | null): MapGeoJSONFeature[] {
1705
+ return this.style.querySourceFeatures(sourceId, parameters);
1706
+ }
1707
+
1708
+ /**
1709
+ * Updates the map's MapLibre style object with a new value.
1710
+ *
1711
+ * If a style is already set when this is used and options.diff is set to true, the map renderer will attempt to compare the given style
1712
+ * against the map's current state and perform only the changes necessary to make the map style match the desired state. Changes in sprites
1713
+ * (images used for icons and patterns) and glyphs (fonts for label text) **cannot** be diffed. If the sprites or fonts used in the current
1714
+ * style and the given style are different in any way, the map renderer will force a full update, removing the current style and building
1715
+ * the given one from scratch.
1716
+ *
1717
+ *
1718
+ * @param style - A JSON object conforming to the schema described in the
1719
+ * [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL to such JSON.
1720
+ * @param options - The options object.
1721
+ *
1722
+ * @example
1723
+ * ```ts
1724
+ * map.setStyle("https://demotiles.maplibre.org/style.json");
1725
+ *
1726
+ * map.setStyle('https://demotiles.maplibre.org/style.json', {
1727
+ * transformStyle: (previousStyle, nextStyle) => ({
1728
+ * ...nextStyle,
1729
+ * sources: {
1730
+ * ...nextStyle.sources,
1731
+ * // copy a source from previous style
1732
+ * 'osm': previousStyle.sources.osm
1733
+ * },
1734
+ * layers: [
1735
+ * // background layer
1736
+ * nextStyle.layers[0],
1737
+ * // copy a layer from previous style
1738
+ * previousStyle.layers[0],
1739
+ * // other layers from the next style
1740
+ * ...nextStyle.layers.slice(1).map(layer => {
1741
+ * // hide the layers we don't need from demotiles style
1742
+ * if (layer.id.startsWith('geolines')) {
1743
+ * layer.layout = {...layer.layout || {}, visibility: 'none'};
1744
+ * // filter out US polygons
1745
+ * } else if (layer.id.startsWith('coastline') || layer.id.startsWith('countries')) {
1746
+ * layer.filter = ['!=', ['get', 'ADM0_A3'], 'USA'];
1747
+ * }
1748
+ * return layer;
1749
+ * })
1750
+ * ]
1751
+ * })
1752
+ * });
1753
+ * ```
1754
+ */
1755
+ setStyle(style: StyleSpecification | string | null, options?: StyleSwapOptions & StyleOptions): this {
1756
+ options = extend({},
1757
+ {
1758
+ localIdeographFontFamily: this._localIdeographFontFamily,
1759
+ validate: this._validateStyle
1760
+ }, options);
1761
+
1762
+ if ((options.diff !== false && options.localIdeographFontFamily === this._localIdeographFontFamily) && this.style && style) {
1763
+ this._diffStyle(style, options);
1764
+ return this;
1765
+ } else {
1766
+ this._localIdeographFontFamily = options.localIdeographFontFamily;
1767
+ return this._updateStyle(style, options);
1768
+ }
1769
+ }
1770
+
1771
+ /**
1772
+ * Updates the requestManager's transform request with a new function
1773
+ *
1774
+ * @param transformRequest - A callback run before the Map makes a request for an external URL. The callback can be used to modify the url, set headers, or set the credentials property for cross-origin requests.
1775
+ * Expected to return an object with a `url` property and optionally `headers` and `credentials` properties
1776
+ *
1777
+ * @example
1778
+ * ```ts
1779
+ * map.setTransformRequest((url: string, resourceType: string) => {});
1780
+ * ```
1781
+ */
1782
+ setTransformRequest(transformRequest: RequestTransformFunction): this {
1783
+ this._requestManager.setTransformRequest(transformRequest);
1784
+ return this;
1785
+ }
1786
+
1787
+ _getUIString(key: keyof typeof defaultLocale) {
1788
+ const str = this._locale[key];
1789
+ if (str == null) {
1790
+ throw new Error(`Missing UI string '${key}'`);
1791
+ }
1792
+
1793
+ return str;
1794
+ }
1795
+
1796
+ _updateStyle(style: StyleSpecification | string | null, options?: StyleSwapOptions & StyleOptions) {
1797
+ // transformStyle relies on having previous style serialized, if it is not loaded yet, delay _updateStyle until previous style is loaded
1798
+ if (options.transformStyle && this.style && !this.style._loaded) {
1799
+ this.style.once('style.load', () => this._updateStyle(style, options));
1800
+ return;
1801
+ }
1802
+
1803
+ const previousStyle = this.style && options.transformStyle ? this.style.serialize() : undefined;
1804
+ if (this.style) {
1805
+ this.style.setEventedParent(null);
1806
+
1807
+ // Only release workers when map is getting disposed
1808
+ this.style._remove(!style);
1809
+ }
1810
+
1811
+ if (!style) {
1812
+ delete this.style;
1813
+ return this;
1814
+ } else {
1815
+ this.style = new Style(this, options || {});
1816
+ }
1817
+
1818
+ this.style.setEventedParent(this, {style: this.style});
1819
+
1820
+ if (typeof style === 'string') {
1821
+ this.style.loadURL(style, options, previousStyle);
1822
+ } else {
1823
+ this.style.loadJSON(style, options, previousStyle);
1824
+ }
1825
+
1826
+ return this;
1827
+ }
1828
+
1829
+ _lazyInitEmptyStyle() {
1830
+ if (!this.style) {
1831
+ this.style = new Style(this, {});
1832
+ this.style.setEventedParent(this, {style: this.style});
1833
+ this.style.loadEmpty();
1834
+ }
1835
+ }
1836
+
1837
+ _diffStyle(style: StyleSpecification | string, options?: StyleSwapOptions & StyleOptions) {
1838
+ if (typeof style === 'string') {
1839
+ const url = style;
1840
+ const request = this._requestManager.transformRequest(url, ResourceType.Style);
1841
+ getJSON<StyleSpecification>(request, new AbortController()).then((response) => {
1842
+ this._updateDiff(response.data, options);
1843
+ }).catch((error) => {
1844
+ if (error) {
1845
+ this.fire(new ErrorEvent(error));
1846
+ }
1847
+ });
1848
+ } else if (typeof style === 'object') {
1849
+ this._updateDiff(style, options);
1850
+ }
1851
+ }
1852
+
1853
+ _updateDiff(style: StyleSpecification, options?: StyleSwapOptions & StyleOptions) {
1854
+ try {
1855
+ if (this.style.setState(style, options)) {
1856
+ this._update(true);
1857
+ }
1858
+ } catch (e) {
1859
+ warnOnce(
1860
+ `Unable to perform style diff: ${e.message || e.error || e}. Rebuilding the style from scratch.`
1861
+ );
1862
+ this._updateStyle(style, options);
1863
+ }
1864
+ }
1865
+
1866
+ /**
1867
+ * Returns the map's MapLibre style object, a JSON object which can be used to recreate the map's style.
1868
+ *
1869
+ * @returns The map's style JSON object.
1870
+ *
1871
+ * @example
1872
+ * ```ts
1873
+ * let styleJson = map.getStyle();
1874
+ * ```
1875
+ *
1876
+ */
1877
+ getStyle(): StyleSpecification {
1878
+ if (this.style) {
1879
+ return this.style.serialize();
1880
+ }
1881
+ }
1882
+
1883
+ /**
1884
+ * Returns a Boolean indicating whether the map's style is fully loaded.
1885
+ *
1886
+ * @returns A Boolean indicating whether the style is fully loaded.
1887
+ *
1888
+ * @example
1889
+ * ```ts
1890
+ * let styleLoadStatus = map.isStyleLoaded();
1891
+ * ```
1892
+ */
1893
+ isStyleLoaded(): boolean | void {
1894
+ if (!this.style) return warnOnce('There is no style added to the map.');
1895
+ return this.style.loaded();
1896
+ }
1897
+
1898
+ /**
1899
+ * Adds a source to the map's style.
1900
+ *
1901
+ * Events triggered:
1902
+ *
1903
+ * Triggers the `source.add` event.
1904
+ *
1905
+ * @param id - The ID of the source to add. Must not conflict with existing sources.
1906
+ * @param source - The source object, conforming to the
1907
+ * MapLibre Style Specification's [source definition](https://maplibre.org/maplibre-style-spec/sources) or
1908
+ * {@link CanvasSourceSpecification}.
1909
+ * @example
1910
+ * ```ts
1911
+ * map.addSource('my-data', {
1912
+ * type: 'vector',
1913
+ * url: 'https://demotiles.maplibre.org/tiles/tiles.json'
1914
+ * });
1915
+ * ```
1916
+ * @example
1917
+ * ```ts
1918
+ * map.addSource('my-data', {
1919
+ * "type": "geojson",
1920
+ * "data": {
1921
+ * "type": "Feature",
1922
+ * "geometry": {
1923
+ * "type": "Point",
1924
+ * "coordinates": [-77.0323, 38.9131]
1925
+ * },
1926
+ * "properties": {
1927
+ * "title": "Mapbox DC",
1928
+ * "marker-symbol": "monument"
1929
+ * }
1930
+ * }
1931
+ * });
1932
+ * ```
1933
+ * @see GeoJSON source: [Add live realtime data](https://maplibre.org/maplibre-gl-js/docs/examples/live-geojson/)
1934
+ */
1935
+ addSource(id: string, source: SourceSpecification | CanvasSourceSpecification): this {
1936
+ this._lazyInitEmptyStyle();
1937
+ this.style.addSource(id, source);
1938
+ return this._update(true);
1939
+ }
1940
+
1941
+ /**
1942
+ * Returns a Boolean indicating whether the source is loaded. Returns `true` if the source with
1943
+ * the given ID in the map's style has no outstanding network requests, otherwise `false`.
1944
+ *
1945
+ * A {@link ErrorEvent} event will be fired if there is no source wit the specified ID.
1946
+ *
1947
+ * @param id - The ID of the source to be checked.
1948
+ * @returns A Boolean indicating whether the source is loaded.
1949
+ * @example
1950
+ * ```ts
1951
+ * let sourceLoaded = map.isSourceLoaded('bathymetry-data');
1952
+ * ```
1953
+ */
1954
+ isSourceLoaded(id: string): boolean {
1955
+ const source = this.style && this.style.sourceCaches[id];
1956
+ if (source === undefined) {
1957
+ this.fire(new ErrorEvent(new Error(`There is no source with ID '${id}'`)));
1958
+ return;
1959
+ }
1960
+ return source.loaded();
1961
+ }
1962
+
1963
+ /**
1964
+ * Loads a 3D terrain mesh, based on a "raster-dem" source.
1965
+ *
1966
+ * Triggers the `terrain` event.
1967
+ *
1968
+ * @param options - Options object.
1969
+ * @example
1970
+ * ```ts
1971
+ * map.setTerrain({ source: 'terrain' });
1972
+ * ```
1973
+ */
1974
+ setTerrain(options: TerrainSpecification | null): this {
1975
+ this.style._checkLoaded();
1976
+
1977
+ // clear event handlers
1978
+ if (this._terrainDataCallback) this.style.off('data', this._terrainDataCallback);
1979
+
1980
+ if (!options) {
1981
+ // remove terrain
1982
+ if (this.terrain) this.terrain.sourceCache.destruct();
1983
+ this.terrain = null;
1984
+ if (this.painter.renderToTexture) this.painter.renderToTexture.destruct();
1985
+ this.painter.renderToTexture = null;
1986
+ this.transform.minElevationForCurrentTile = 0;
1987
+ this.transform.elevation = 0;
1988
+ } else {
1989
+ // add terrain
1990
+ const sourceCache = this.style.sourceCaches[options.source];
1991
+ if (!sourceCache) throw new Error(`cannot load terrain, because there exists no source with ID: ${options.source}`);
1992
+ // Update terrain tiles when adding new terrain
1993
+ if (this.terrain === null) sourceCache.reload();
1994
+ // Warn once if user is using the same source for hillshade and terrain
1995
+ for (const index in this.style._layers) {
1996
+ const thisLayer = this.style._layers[index];
1997
+ if (thisLayer.type === 'hillshade' && thisLayer.source === options.source) {
1998
+ warnOnce('You are using the same source for a hillshade layer and for 3D terrain. Please consider using two separate sources to improve rendering quality.');
1999
+ }
2000
+ }
2001
+ this.terrain = new Terrain(this.painter, sourceCache, options);
2002
+ this.painter.renderToTexture = new RenderToTexture(this.painter, this.terrain);
2003
+ this.transform.minElevationForCurrentTile = this.terrain.getMinTileElevationForLngLatZoom(this.transform.center, this.transform.tileZoom);
2004
+ this.transform.elevation = this.terrain.getElevationForLngLatZoom(this.transform.center, this.transform.tileZoom);
2005
+ this._terrainDataCallback = e => {
2006
+ if (e.dataType === 'style') {
2007
+ this.terrain.sourceCache.freeRtt();
2008
+ } else if (e.dataType === 'source' && e.tile) {
2009
+ if (e.sourceId === options.source && !this._elevationFreeze) {
2010
+ this.transform.minElevationForCurrentTile = this.terrain.getMinTileElevationForLngLatZoom(this.transform.center, this.transform.tileZoom);
2011
+ this.transform.elevation = this.terrain.getElevationForLngLatZoom(this.transform.center, this.transform.tileZoom);
2012
+ }
2013
+ this.terrain.sourceCache.freeRtt(e.tile.tileID);
2014
+ }
2015
+ };
2016
+ this.style.on('data', this._terrainDataCallback);
2017
+ }
2018
+
2019
+ this.fire(new Event('terrain', {terrain: options}));
2020
+ return this;
2021
+ }
2022
+
2023
+ /**
2024
+ * Get the terrain-options if terrain is loaded
2025
+ * @returns the TerrainSpecification passed to setTerrain
2026
+ * @example
2027
+ * ```ts
2028
+ * map.getTerrain(); // { source: 'terrain' };
2029
+ * ```
2030
+ */
2031
+ getTerrain(): TerrainSpecification | null {
2032
+ return this.terrain?.options ?? null;
2033
+ }
2034
+
2035
+ /**
2036
+ * Returns a Boolean indicating whether all tiles in the viewport from all sources on
2037
+ * the style are loaded.
2038
+ *
2039
+ * @returns A Boolean indicating whether all tiles are loaded.
2040
+ * @example
2041
+ * ```ts
2042
+ * let tilesLoaded = map.areTilesLoaded();
2043
+ * ```
2044
+ */
2045
+ areTilesLoaded(): boolean {
2046
+ const sources = this.style && this.style.sourceCaches;
2047
+ for (const id in sources) {
2048
+ const source = sources[id];
2049
+ const tiles = source._tiles;
2050
+ for (const t in tiles) {
2051
+ const tile = tiles[t];
2052
+ if (!(tile.state === 'loaded' || tile.state === 'errored')) return false;
2053
+ }
2054
+ }
2055
+ return true;
2056
+ }
2057
+
2058
+ /**
2059
+ * Removes a source from the map's style.
2060
+ *
2061
+ * @param id - The ID of the source to remove.
2062
+ * @example
2063
+ * ```ts
2064
+ * map.removeSource('bathymetry-data');
2065
+ * ```
2066
+ */
2067
+ removeSource(id: string): Map {
2068
+ this.style.removeSource(id);
2069
+ return this._update(true);
2070
+ }
2071
+
2072
+ /**
2073
+ * Returns the source with the specified ID in the map's style.
2074
+ *
2075
+ * This method is often used to update a source using the instance members for the relevant
2076
+ * source type as defined in classes that derive from {@link Source}.
2077
+ * For example, setting the `data` for a GeoJSON source or updating the `url` and `coordinates`
2078
+ * of an image source.
2079
+ *
2080
+ * @param id - The ID of the source to get.
2081
+ * @returns The style source with the specified ID or `undefined` if the ID
2082
+ * corresponds to no existing sources.
2083
+ * The shape of the object varies by source type.
2084
+ * A list of options for each source type is available on the MapLibre Style Specification's
2085
+ * [Sources](https://maplibre.org/maplibre-style-spec/sources/) page.
2086
+ * @example
2087
+ * ```ts
2088
+ * let sourceObject = map.getSource('points');
2089
+ * ```
2090
+ * @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/drag-a-point/)
2091
+ * @see [Animate a point](https://maplibre.org/maplibre-gl-js/docs/examples/animate-point-along-line/)
2092
+ * @see [Add live realtime data](https://maplibre.org/maplibre-gl-js/docs/examples/live-geojson/)
2093
+ */
2094
+ getSource<TSource extends Source>(id: string): TSource | undefined {
2095
+ return this.style.getSource(id) as TSource;
2096
+ }
2097
+
2098
+ /**
2099
+ * Add an image to the style. This image can be displayed on the map like any other icon in the style's
2100
+ * sprite using the image's ID with
2101
+ * [`icon-image`](https://maplibre.org/maplibre-style-spec/layers/#layout-symbol-icon-image),
2102
+ * [`background-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-background-background-pattern),
2103
+ * [`fill-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-fill-fill-pattern),
2104
+ * or [`line-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-line-line-pattern).
2105
+ *
2106
+ * A {@link ErrorEvent} event will be fired if the image parameter is invalid or there is not enough space in the sprite to add this image.
2107
+ *
2108
+ * @param id - The ID of the image.
2109
+ * @param image - The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
2110
+ * properties with the same format as `ImageData`.
2111
+ * @param options - Options object.
2112
+ * @example
2113
+ * ```ts
2114
+ * // If the style's sprite does not already contain an image with ID 'cat',
2115
+ * // add the image 'cat-icon.png' to the style's sprite with the ID 'cat'.
2116
+ * const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Cat_silhouette.svg/400px-Cat_silhouette.svg.png');
2117
+ * if (!map.hasImage('cat')) map.addImage('cat', image.data);
2118
+ *
2119
+ * // Add a stretchable image that can be used with `icon-text-fit`
2120
+ * // In this example, the image is 600px wide by 400px high.
2121
+ * const image = await map.loadImage('https://upload.wikimedia.org/wikipedia/commons/8/89/Black_and_White_Boxed_%28bordered%29.png');
2122
+ * if (map.hasImage('border-image')) return;
2123
+ * map.addImage('border-image', image.data, {
2124
+ * content: [16, 16, 300, 384], // place text over left half of image, avoiding the 16px border
2125
+ * stretchX: [[16, 584]], // stretch everything horizontally except the 16px border
2126
+ * stretchY: [[16, 384]], // stretch everything vertically except the 16px border
2127
+ * });
2128
+ * ```
2129
+ * @see Use `HTMLImageElement`: [Add an icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-image/)
2130
+ * @see Use `ImageData`: [Add a generated icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-image-generated/)
2131
+ */
2132
+ addImage(id: string,
2133
+ image: HTMLImageElement | ImageBitmap | ImageData | {
2134
+ width: number;
2135
+ height: number;
2136
+ data: Uint8Array | Uint8ClampedArray;
2137
+ } | StyleImageInterface,
2138
+ options: Partial<StyleImageMetadata> = {}): this {
2139
+ const {
2140
+ pixelRatio = 1,
2141
+ sdf = false,
2142
+ stretchX,
2143
+ stretchY,
2144
+ content,
2145
+ textFitWidth,
2146
+ textFitHeight
2147
+ } = options;
2148
+ this._lazyInitEmptyStyle();
2149
+ const version = 0;
2150
+
2151
+ if (image instanceof HTMLImageElement || isImageBitmap(image)) {
2152
+ const {width, height, data} = browser.getImageData(image);
2153
+ this.style.addImage(id, {data: new RGBAImage({width, height}, data), pixelRatio, stretchX, stretchY, content, textFitWidth, textFitHeight, sdf, version});
2154
+ } else if (image.width === undefined || image.height === undefined) {
2155
+ return this.fire(new ErrorEvent(new Error(
2156
+ 'Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' +
2157
+ 'or object with `width`, `height`, and `data` properties with the same format as `ImageData`')));
2158
+ } else {
2159
+ const {width, height, data} = image as ImageData;
2160
+ const userImage = (image as any as StyleImageInterface);
2161
+
2162
+ this.style.addImage(id, {
2163
+ data: new RGBAImage({width, height}, new Uint8Array(data)),
2164
+ pixelRatio,
2165
+ stretchX,
2166
+ stretchY,
2167
+ content,
2168
+ textFitWidth,
2169
+ textFitHeight,
2170
+ sdf,
2171
+ version,
2172
+ userImage
2173
+ });
2174
+
2175
+ if (userImage.onAdd) {
2176
+ userImage.onAdd(this, id);
2177
+ }
2178
+ return this;
2179
+ }
2180
+ }
2181
+
2182
+ /**
2183
+ * Update an existing image in a style. This image can be displayed on the map like any other icon in the style's
2184
+ * sprite using the image's ID with
2185
+ * [`icon-image`](https://maplibre.org/maplibre-style-spec/layers/#layout-symbol-icon-image),
2186
+ * [`background-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-background-background-pattern),
2187
+ * [`fill-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-fill-fill-pattern),
2188
+ * or [`line-pattern`](https://maplibre.org/maplibre-style-spec/layers/#paint-line-line-pattern).
2189
+ *
2190
+ * An {@link ErrorEvent} will be fired if the image parameter is invald.
2191
+ *
2192
+ * @param id - The ID of the image.
2193
+ * @param image - The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
2194
+ * properties with the same format as `ImageData`.
2195
+ * @example
2196
+ * ```ts
2197
+ * // If an image with the ID 'cat' already exists in the style's sprite,
2198
+ * // replace that image with a new image, 'other-cat-icon.png'.
2199
+ * if (map.hasImage('cat')) map.updateImage('cat', './other-cat-icon.png');
2200
+ * ```
2201
+ */
2202
+ updateImage(id: string,
2203
+ image: HTMLImageElement | ImageBitmap | ImageData | {
2204
+ width: number;
2205
+ height: number;
2206
+ data: Uint8Array | Uint8ClampedArray;
2207
+ } | StyleImageInterface): this {
2208
+
2209
+ const existingImage = this.style.getImage(id);
2210
+ if (!existingImage) {
2211
+ return this.fire(new ErrorEvent(new Error(
2212
+ 'The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.')));
2213
+ }
2214
+ const imageData = (image instanceof HTMLImageElement || isImageBitmap(image)) ?
2215
+ browser.getImageData(image) :
2216
+ image;
2217
+ const {width, height, data} = imageData;
2218
+
2219
+ if (width === undefined || height === undefined) {
2220
+ return this.fire(new ErrorEvent(new Error(
2221
+ 'Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' +
2222
+ 'or object with `width`, `height`, and `data` properties with the same format as `ImageData`')));
2223
+ }
2224
+
2225
+ if (width !== existingImage.data.width || height !== existingImage.data.height) {
2226
+ return this.fire(new ErrorEvent(new Error(
2227
+ 'The width and height of the updated image must be that same as the previous version of the image')));
2228
+ }
2229
+
2230
+ const copy = !(image instanceof HTMLImageElement || isImageBitmap(image));
2231
+ existingImage.data.replace(data, copy);
2232
+
2233
+ this.style.updateImage(id, existingImage);
2234
+ return this;
2235
+ }
2236
+
2237
+ /**
2238
+ * Returns an image, specified by ID, currently available in the map.
2239
+ * This includes both images from the style's original sprite
2240
+ * and any images that have been added at runtime using {@link Map#addImage}.
2241
+ *
2242
+ * @param id - The ID of the image.
2243
+ * @returns An image in the map with the specified ID.
2244
+ *
2245
+ * @example
2246
+ * ```ts
2247
+ * let coffeeShopIcon = map.getImage("coffee_cup");
2248
+ * ```
2249
+ */
2250
+ getImage(id: string): StyleImage {
2251
+ return this.style.getImage(id);
2252
+ }
2253
+
2254
+ /**
2255
+ * Check whether or not an image with a specific ID exists in the style. This checks both images
2256
+ * in the style's original sprite and any images
2257
+ * that have been added at runtime using {@link Map#addImage}.
2258
+ *
2259
+ * An {@link ErrorEvent} will be fired if the image parameter is invald.
2260
+ *
2261
+ * @param id - The ID of the image.
2262
+ *
2263
+ * @returns A Boolean indicating whether the image exists.
2264
+ * @example
2265
+ * Check if an image with the ID 'cat' exists in the style's sprite.
2266
+ * ```ts
2267
+ * let catIconExists = map.hasImage('cat');
2268
+ * ```
2269
+ */
2270
+ hasImage(id: string): boolean {
2271
+ if (!id) {
2272
+ this.fire(new ErrorEvent(new Error('Missing required image id')));
2273
+ return false;
2274
+ }
2275
+
2276
+ return !!this.style.getImage(id);
2277
+ }
2278
+
2279
+ /**
2280
+ * Remove an image from a style. This can be an image from the style's original
2281
+ * sprite or any images
2282
+ * that have been added at runtime using {@link Map#addImage}.
2283
+ *
2284
+ * @param id - The ID of the image.
2285
+ *
2286
+ * @example
2287
+ * ```ts
2288
+ * // If an image with the ID 'cat' exists in
2289
+ * // the style's sprite, remove it.
2290
+ * if (map.hasImage('cat')) map.removeImage('cat');
2291
+ * ```
2292
+ */
2293
+ removeImage(id: string) {
2294
+ this.style.removeImage(id);
2295
+ }
2296
+
2297
+ /**
2298
+ * Load an image from an external URL to be used with {@link Map#addImage}. External
2299
+ * domains must support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS).
2300
+ *
2301
+ * @param url - The URL of the image file. Image file must be in png, webp, or jpg format.
2302
+ * @returns a promise that is resolved when the image is loaded
2303
+ *
2304
+ * @example
2305
+ * Load an image from an external URL.
2306
+ * ```ts
2307
+ * const response = await map.loadImage('https://picsum.photos/50/50');
2308
+ * // Add the loaded image to the style's sprite with the ID 'photo'.
2309
+ * map.addImage('photo', response.data);
2310
+ * ```
2311
+ * @see [Add an icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-image/)
2312
+ */
2313
+ loadImage(url: string): Promise<GetResourceResponse<HTMLImageElement | ImageBitmap>> {
2314
+ return ImageRequest.getImage(this._requestManager.transformRequest(url, ResourceType.Image), new AbortController());
2315
+ }
2316
+
2317
+ /**
2318
+ * Returns an Array of strings containing the IDs of all images currently available in the map.
2319
+ * This includes both images from the style's original sprite
2320
+ * and any images that have been added at runtime using {@link Map#addImage}.
2321
+ *
2322
+ * @returns An Array of strings containing the names of all sprites/images currently available in the map.
2323
+ *
2324
+ * @example
2325
+ * ```ts
2326
+ * let allImages = map.listImages();
2327
+ * ```
2328
+ */
2329
+ listImages(): Array<string> {
2330
+ return this.style.listImages();
2331
+ }
2332
+
2333
+ /**
2334
+ * Adds a [MapLibre style layer](https://maplibre.org/maplibre-style-spec/layers)
2335
+ * to the map's style.
2336
+ *
2337
+ * A layer defines how data from a specified source will be styled. Read more about layer types
2338
+ * and available paint and layout properties in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/layers).
2339
+ *
2340
+ * @param layer - The layer to add,
2341
+ * conforming to either the MapLibre Style Specification's [layer definition](https://maplibre.org/maplibre-style-spec/layers) or,
2342
+ * less commonly, the {@link CustomLayerInterface} specification. Can also be a layer definition with an embedded source definition.
2343
+ * The MapLibre Style Specification's layer definition is appropriate for most layers.
2344
+ *
2345
+ * @param beforeId - The ID of an existing layer to insert the new layer before,
2346
+ * resulting in the new layer appearing visually beneath the existing layer.
2347
+ * If this argument is not specified, the layer will be appended to the end of the layers array
2348
+ * and appear visually above all other layers.
2349
+ *
2350
+ * @example
2351
+ * Add a circle layer with a vector source
2352
+ * ```ts
2353
+ * map.addLayer({
2354
+ * id: 'points-of-interest',
2355
+ * source: {
2356
+ * type: 'vector',
2357
+ * url: 'https://demotiles.maplibre.org/tiles/tiles.json'
2358
+ * },
2359
+ * 'source-layer': 'poi_label',
2360
+ * type: 'circle',
2361
+ * paint: {
2362
+ * // MapLibre Style Specification paint properties
2363
+ * },
2364
+ * layout: {
2365
+ * // MapLibre Style Specification layout properties
2366
+ * }
2367
+ * });
2368
+ * ```
2369
+ *
2370
+ * @example
2371
+ * Define a source before using it to create a new layer
2372
+ * ```ts
2373
+ * map.addSource('state-data', {
2374
+ * type: 'geojson',
2375
+ * data: 'path/to/data.geojson'
2376
+ * });
2377
+ *
2378
+ * map.addLayer({
2379
+ * id: 'states',
2380
+ * // References the GeoJSON source defined above
2381
+ * // and does not require a `source-layer`
2382
+ * source: 'state-data',
2383
+ * type: 'symbol',
2384
+ * layout: {
2385
+ * // Set the label content to the
2386
+ * // feature's `name` property
2387
+ * text-field: ['get', 'name']
2388
+ * }
2389
+ * });
2390
+ * ```
2391
+ *
2392
+ * @example
2393
+ * Add a new symbol layer before an existing layer
2394
+ * ```ts
2395
+ * map.addLayer({
2396
+ * id: 'states',
2397
+ * // References a source that's already been defined
2398
+ * source: 'state-data',
2399
+ * type: 'symbol',
2400
+ * layout: {
2401
+ * // Set the label content to the
2402
+ * // feature's `name` property
2403
+ * text-field: ['get', 'name']
2404
+ * }
2405
+ * // Add the layer before the existing `cities` layer
2406
+ * }, 'cities');
2407
+ * ```
2408
+ * @see [Create and style clusters](https://maplibre.org/maplibre-gl-js/docs/examples/cluster/)
2409
+ * @see [Add a vector tile source](https://maplibre.org/maplibre-gl-js/docs/examples/vector-source/)
2410
+ * @see [Add a WMS source](https://maplibre.org/maplibre-gl-js/docs/examples/wms/)
2411
+ */
2412
+ addLayer(layer: AddLayerObject, beforeId?: string) {
2413
+ this._lazyInitEmptyStyle();
2414
+ this.style.addLayer(layer, beforeId);
2415
+ return this._update(true);
2416
+ }
2417
+
2418
+ /**
2419
+ * Moves a layer to a different z-position.
2420
+ *
2421
+ * @param id - The ID of the layer to move.
2422
+ * @param beforeId - The ID of an existing layer to insert the new layer before. When viewing the map, the `id` layer will appear beneath the `beforeId` layer. If `beforeId` is omitted, the layer will be appended to the end of the layers array and appear above all other layers on the map.
2423
+ *
2424
+ * @example
2425
+ * Move a layer with ID 'polygon' before the layer with ID 'country-label'. The `polygon` layer will appear beneath the `country-label` layer on the map.
2426
+ * ```ts
2427
+ * map.moveLayer('polygon', 'country-label');
2428
+ * ```
2429
+ */
2430
+ moveLayer(id: string, beforeId?: string): this {
2431
+ this.style.moveLayer(id, beforeId);
2432
+ return this._update(true);
2433
+ }
2434
+
2435
+ /**
2436
+ * Removes the layer with the given ID from the map's style.
2437
+ *
2438
+ * An {@link ErrorEvent} will be fired if the image parameter is invald.
2439
+ *
2440
+ * @param id - The ID of the layer to remove
2441
+ *
2442
+ * @example
2443
+ * If a layer with ID 'state-data' exists, remove it.
2444
+ * ```ts
2445
+ * if (map.getLayer('state-data')) map.removeLayer('state-data');
2446
+ * ```
2447
+ */
2448
+ removeLayer(id: string): this {
2449
+ this.style.removeLayer(id);
2450
+ return this._update(true);
2451
+ }
2452
+
2453
+ /**
2454
+ * Returns the layer with the specified ID in the map's style.
2455
+ *
2456
+ * @param id - The ID of the layer to get.
2457
+ * @returns The layer with the specified ID, or `undefined`
2458
+ * if the ID corresponds to no existing layers.
2459
+ *
2460
+ * @example
2461
+ * ```ts
2462
+ * let stateDataLayer = map.getLayer('state-data');
2463
+ * ```
2464
+ * @see [Filter symbols by toggling a list](https://maplibre.org/maplibre-gl-js/docs/examples/filter-markers/)
2465
+ * @see [Filter symbols by text input](https://maplibre.org/maplibre-gl-js/docs/examples/filter-markers-by-input/)
2466
+ */
2467
+ getLayer(id: string): StyleLayer | undefined {
2468
+ return this.style.getLayer(id);
2469
+ }
2470
+
2471
+ /**
2472
+ * Return the ids of all layers currently in the style, including custom layers, in order.
2473
+ *
2474
+ * @returns ids of layers, in order
2475
+ *
2476
+ * @example
2477
+ * ```ts
2478
+ * const orderedLayerIds = map.getLayersOrder();
2479
+ * ```
2480
+ */
2481
+ getLayersOrder(): string[] {
2482
+ return this.style.getLayersOrder();
2483
+ }
2484
+
2485
+ /**
2486
+ * Sets the zoom extent for the specified style layer. The zoom extent includes the
2487
+ * [minimum zoom level](https://maplibre.org/maplibre-style-spec/layers/#minzoom)
2488
+ * and [maximum zoom level](https://maplibre.org/maplibre-style-spec/layers/#maxzoom))
2489
+ * at which the layer will be rendered.
2490
+ *
2491
+ * Note: For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
2492
+ * minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
2493
+ * zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
2494
+ * layer will not be rendered at all zoom levels in the zoom range.
2495
+ *
2496
+ * @param layerId - The ID of the layer to which the zoom extent will be applied.
2497
+ * @param minzoom - The minimum zoom to set (0-24).
2498
+ * @param maxzoom - The maximum zoom to set (0-24).
2499
+ *
2500
+ * @example
2501
+ * ```ts
2502
+ * map.setLayerZoomRange('my-layer', 2, 5);
2503
+ * ```
2504
+ */
2505
+ setLayerZoomRange(layerId: string, minzoom: number, maxzoom: number): this {
2506
+ this.style.setLayerZoomRange(layerId, minzoom, maxzoom);
2507
+ return this._update(true);
2508
+ }
2509
+
2510
+ /**
2511
+ * Sets the filter for the specified style layer.
2512
+ *
2513
+ * Filters control which features a style layer renders from its source.
2514
+ * Any feature for which the filter expression evaluates to `true` will be
2515
+ * rendered on the map. Those that are false will be hidden.
2516
+ *
2517
+ * Use `setFilter` to show a subset of your source data.
2518
+ *
2519
+ * To clear the filter, pass `null` or `undefined` as the second parameter.
2520
+ *
2521
+ * @param layerId - The ID of the layer to which the filter will be applied.
2522
+ * @param filter - The filter, conforming to the MapLibre Style Specification's
2523
+ * [filter definition](https://maplibre.org/maplibre-style-spec/layers/#filter). If `null` or `undefined` is provided, the function removes any existing filter from the layer.
2524
+ * @param options - Options object.
2525
+ *
2526
+ * @example
2527
+ * Display only features with the 'name' property 'USA'
2528
+ * ```ts
2529
+ * map.setFilter('my-layer', ['==', ['get', 'name'], 'USA']);
2530
+ * ```
2531
+ * @example
2532
+ * Display only features with five or more 'available-spots'
2533
+ * ```ts
2534
+ * map.setFilter('bike-docks', ['>=', ['get', 'available-spots'], 5]);
2535
+ * ```
2536
+ * @example
2537
+ * Remove the filter for the 'bike-docks' style layer
2538
+ * ```ts
2539
+ * map.setFilter('bike-docks', null);
2540
+ * ```
2541
+ * @see [Create a timeline animation](https://maplibre.org/maplibre-gl-js/docs/examples/timeline-animation/)
2542
+ */
2543
+ setFilter(layerId: string, filter?: FilterSpecification | null, options: StyleSetterOptions = {}) {
2544
+ this.style.setFilter(layerId, filter, options);
2545
+ return this._update(true);
2546
+ }
2547
+
2548
+ /**
2549
+ * Returns the filter applied to the specified style layer.
2550
+ *
2551
+ * @param layerId - The ID of the style layer whose filter to get.
2552
+ * @returns The layer's filter.
2553
+ */
2554
+ getFilter(layerId: string): FilterSpecification | void {
2555
+ return this.style.getFilter(layerId);
2556
+ }
2557
+
2558
+ /**
2559
+ * Sets the value of a paint property in the specified style layer.
2560
+ *
2561
+ * @param layerId - The ID of the layer to set the paint property in.
2562
+ * @param name - The name of the paint property to set.
2563
+ * @param value - The value of the paint property to set.
2564
+ * Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
2565
+ * Pass `null` to unset the existing value.
2566
+ * @param options - Options object.
2567
+ * @example
2568
+ * ```ts
2569
+ * map.setPaintProperty('my-layer', 'fill-color', '#faafee');
2570
+ * ```
2571
+ * @see [Change a layer's color with buttons](https://maplibre.org/maplibre-gl-js/docs/examples/color-switcher/)
2572
+ * @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/drag-a-point/)
2573
+ */
2574
+ setPaintProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}): this {
2575
+ this.style.setPaintProperty(layerId, name, value, options);
2576
+ return this._update(true);
2577
+ }
2578
+
2579
+ /**
2580
+ * Returns the value of a paint property in the specified style layer.
2581
+ *
2582
+ * @param layerId - The ID of the layer to get the paint property from.
2583
+ * @param name - The name of a paint property to get.
2584
+ * @returns The value of the specified paint property.
2585
+ */
2586
+ getPaintProperty(layerId: string, name: string) {
2587
+ return this.style.getPaintProperty(layerId, name);
2588
+ }
2589
+
2590
+ /**
2591
+ * Sets the value of a layout property in the specified style layer.
2592
+ *
2593
+ * @param layerId - The ID of the layer to set the layout property in.
2594
+ * @param name - The name of the layout property to set.
2595
+ * @param value - The value of the layout property. Must be of a type appropriate for the property, as defined in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/).
2596
+ * @param options - The options object.
2597
+ * @example
2598
+ * ```ts
2599
+ * map.setLayoutProperty('my-layer', 'visibility', 'none');
2600
+ * ```
2601
+ */
2602
+ setLayoutProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}): this {
2603
+ this.style.setLayoutProperty(layerId, name, value, options);
2604
+ return this._update(true);
2605
+ }
2606
+
2607
+ /**
2608
+ * Returns the value of a layout property in the specified style layer.
2609
+ *
2610
+ * @param layerId - The ID of the layer to get the layout property from.
2611
+ * @param name - The name of the layout property to get.
2612
+ * @returns The value of the specified layout property.
2613
+ */
2614
+ getLayoutProperty(layerId: string, name: string) {
2615
+ return this.style.getLayoutProperty(layerId, name);
2616
+ }
2617
+
2618
+ /**
2619
+ * Sets the value of the style's glyphs property.
2620
+ *
2621
+ * @param glyphsUrl - Glyph URL to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/glyphs/).
2622
+ * @param options - Options object.
2623
+ * @example
2624
+ * ```ts
2625
+ * map.setGlyphs('https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf');
2626
+ * ```
2627
+ */
2628
+ setGlyphs(glyphsUrl: string | null, options: StyleSetterOptions = {}): this {
2629
+ this._lazyInitEmptyStyle();
2630
+ this.style.setGlyphs(glyphsUrl, options);
2631
+ return this._update(true);
2632
+ }
2633
+
2634
+ /**
2635
+ * Returns the value of the style's glyphs URL
2636
+ *
2637
+ * @returns glyphs Style's glyphs url
2638
+ */
2639
+ getGlyphs(): string | null {
2640
+ return this.style.getGlyphsUrl();
2641
+ }
2642
+
2643
+ /**
2644
+ * Adds a sprite to the map's style. Fires the `style` event.
2645
+ *
2646
+ * @param id - The ID of the sprite to add. Must not conflict with existing sprites.
2647
+ * @param url - The URL to load the sprite from
2648
+ * @param options - Options object.
2649
+ * @example
2650
+ * ```ts
2651
+ * map.addSprite('sprite-two', 'http://example.com/sprite-two');
2652
+ * ```
2653
+ */
2654
+ addSprite(id: string, url: string, options: StyleSetterOptions = {}): this {
2655
+ this._lazyInitEmptyStyle();
2656
+ this.style.addSprite(id, url, options, (err) => {
2657
+ if (!err) {
2658
+ this._update(true);
2659
+ }
2660
+ });
2661
+ return this;
2662
+ }
2663
+
2664
+ /**
2665
+ * Removes the sprite from the map's style. Fires the `style` event.
2666
+ *
2667
+ * @param id - The ID of the sprite to remove. If the sprite is declared as a single URL, the ID must be "default".
2668
+ * @example
2669
+ * ```ts
2670
+ * map.removeSprite('sprite-two');
2671
+ * map.removeSprite('default');
2672
+ * ```
2673
+ */
2674
+ removeSprite(id: string) {
2675
+ this._lazyInitEmptyStyle();
2676
+ this.style.removeSprite(id);
2677
+ return this._update(true);
2678
+ }
2679
+
2680
+ /**
2681
+ * Returns the as-is value of the style's sprite.
2682
+ *
2683
+ * @returns style's sprite list of id-url pairs
2684
+ */
2685
+ getSprite(): {id: string; url: string}[] {
2686
+ return this.style.getSprite();
2687
+ }
2688
+
2689
+ /**
2690
+ * Sets the value of the style's sprite property.
2691
+ *
2692
+ * @param spriteUrl - Sprite URL to set.
2693
+ * @param options - Options object.
2694
+ * @example
2695
+ * ```ts
2696
+ * map.setSprite('YOUR_SPRITE_URL');
2697
+ * ```
2698
+ */
2699
+ setSprite(spriteUrl: string | null, options: StyleSetterOptions = {}) {
2700
+ this._lazyInitEmptyStyle();
2701
+ this.style.setSprite(spriteUrl, options, (err) => {
2702
+ if (!err) {
2703
+ this._update(true);
2704
+ }
2705
+ });
2706
+ return this;
2707
+ }
2708
+
2709
+ /**
2710
+ * Sets the any combination of light values.
2711
+ *
2712
+ * @param light - Light properties to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/light).
2713
+ * @param options - Options object.
2714
+ *
2715
+ * @example
2716
+ * ```ts
2717
+ * let layerVisibility = map.getLayoutProperty('my-layer', 'visibility');
2718
+ * ```
2719
+ */
2720
+ setLight(light: LightSpecification, options: StyleSetterOptions = {}) {
2721
+ this._lazyInitEmptyStyle();
2722
+ this.style.setLight(light, options);
2723
+ return this._update(true);
2724
+ }
2725
+
2726
+ /**
2727
+ * Returns the value of the light object.
2728
+ *
2729
+ * @returns light Light properties of the style.
2730
+ */
2731
+ getLight(): LightSpecification {
2732
+ return this.style.getLight();
2733
+ }
2734
+
2735
+ /**
2736
+ * Loads sky and fog defined by {@link SkySpecification} onto the map.
2737
+ * Note: The fog only shows when using the terrain 3D feature.
2738
+ * @param sky - Sky properties to set. Must conform to the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/sky/).
2739
+ * @returns `this`
2740
+ * @example
2741
+ * ```ts
2742
+ * map.setSky({ 'sky-color': '#00f' });
2743
+ * ```
2744
+ */
2745
+ setSky(sky: SkySpecification) {
2746
+ this._lazyInitEmptyStyle();
2747
+ this.style.setSky(sky);
2748
+ return this._update(true);
2749
+ }
2750
+
2751
+ /**
2752
+ * Returns the value of the sky object.
2753
+ *
2754
+ * @returns the sky properties of the style.
2755
+ * @example
2756
+ * ```ts
2757
+ * map.getSky();
2758
+ * ```
2759
+ */
2760
+ getSky() {
2761
+ return this.style.getSky();
2762
+ }
2763
+
2764
+ /**
2765
+ * Sets the `state` of a feature.
2766
+ * A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
2767
+ * When using this method, the `state` object is merged with any existing key-value pairs in the feature's state.
2768
+ * Features are identified by their `feature.id` attribute, which can be any number or string.
2769
+ *
2770
+ * This method can only be used with sources that have a `feature.id` attribute. The `feature.id` attribute can be defined in three ways:
2771
+ *
2772
+ * - For vector or GeoJSON sources, including an `id` attribute in the original data file.
2773
+ * - For vector or GeoJSON sources, using the [`promoteId`](https://maplibre.org/maplibre-style-spec/sources/#vector-promoteId) option at the time the source is defined.
2774
+ * - For GeoJSON sources, using the [`generateId`](https://maplibre.org/maplibre-style-spec/sources/#geojson-generateId) option to auto-assign an `id` based on the feature's index in the source data. If you change feature data using `map.getSource('some id').setData(..)`, you may need to re-apply state taking into account updated `id` values.
2775
+ *
2776
+ * _Note: You can use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling._
2777
+ *
2778
+ * @param feature - Feature identifier. Feature objects returned from
2779
+ * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers.
2780
+ * @param state - A set of key-value pairs. The values should be valid JSON types.
2781
+ *
2782
+ * @example
2783
+ * ```ts
2784
+ * // When the mouse moves over the `my-layer` layer, update
2785
+ * // the feature state for the feature under the mouse
2786
+ * map.on('mousemove', 'my-layer', (e) => {
2787
+ * if (e.features.length > 0) {
2788
+ * map.setFeatureState({
2789
+ * source: 'my-source',
2790
+ * sourceLayer: 'my-source-layer',
2791
+ * id: e.features[0].id,
2792
+ * }, {
2793
+ * hover: true
2794
+ * });
2795
+ * }
2796
+ * });
2797
+ * ```
2798
+ * @see [Create a hover effect](https://maplibre.org/maplibre-gl-js/docs/examples/hover-styles/)
2799
+ */
2800
+ setFeatureState(feature: FeatureIdentifier, state: any): this {
2801
+ this.style.setFeatureState(feature, state);
2802
+ return this._update();
2803
+ }
2804
+
2805
+ /**
2806
+ * Removes the `state` of a feature, setting it back to the default behavior.
2807
+ * If only a `target.source` is specified, it will remove the state for all features from that source.
2808
+ * If `target.id` is also specified, it will remove all keys for that feature's state.
2809
+ * If `key` is also specified, it removes only that key from that feature's state.
2810
+ * Features are identified by their `feature.id` attribute, which can be any number or string.
2811
+ *
2812
+ * @param target - Identifier of where to remove state. It can be a source, a feature, or a specific key of feature.
2813
+ * Feature objects returned from {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers.
2814
+ * @param key - (optional) The key in the feature state to reset.
2815
+ * @example
2816
+ * Reset the entire state object for all features in the `my-source` source
2817
+ * ```ts
2818
+ * map.removeFeatureState({
2819
+ * source: 'my-source'
2820
+ * });
2821
+ * ```
2822
+ *
2823
+ * @example
2824
+ * When the mouse leaves the `my-layer` layer,
2825
+ * reset the entire state object for the
2826
+ * feature under the mouse
2827
+ * ```ts
2828
+ * map.on('mouseleave', 'my-layer', (e) => {
2829
+ * map.removeFeatureState({
2830
+ * source: 'my-source',
2831
+ * sourceLayer: 'my-source-layer',
2832
+ * id: e.features[0].id
2833
+ * });
2834
+ * });
2835
+ * ```
2836
+ *
2837
+ * @example
2838
+ * When the mouse leaves the `my-layer` layer,
2839
+ * reset only the `hover` key-value pair in the
2840
+ * state for the feature under the mouse
2841
+ * ```ts
2842
+ * map.on('mouseleave', 'my-layer', (e) => {
2843
+ * map.removeFeatureState({
2844
+ * source: 'my-source',
2845
+ * sourceLayer: 'my-source-layer',
2846
+ * id: e.features[0].id
2847
+ * }, 'hover');
2848
+ * });
2849
+ * ```
2850
+ */
2851
+ removeFeatureState(target: FeatureIdentifier, key?: string): this {
2852
+ this.style.removeFeatureState(target, key);
2853
+ return this._update();
2854
+ }
2855
+
2856
+ /**
2857
+ * Gets the `state` of a feature.
2858
+ * A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
2859
+ * Features are identified by their `feature.id` attribute, which can be any number or string.
2860
+ *
2861
+ * _Note: To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://maplibre.org/maplibre-style-spec/expressions/#feature-state)._
2862
+ *
2863
+ * @param feature - Feature identifier. Feature objects returned from
2864
+ * {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers.
2865
+ * @returns The state of the feature: a set of key-value pairs that was assigned to the feature at runtime.
2866
+ *
2867
+ * @example
2868
+ * When the mouse moves over the `my-layer` layer,
2869
+ * get the feature state for the feature under the mouse
2870
+ * ```ts
2871
+ * map.on('mousemove', 'my-layer', (e) => {
2872
+ * if (e.features.length > 0) {
2873
+ * map.getFeatureState({
2874
+ * source: 'my-source',
2875
+ * sourceLayer: 'my-source-layer',
2876
+ * id: e.features[0].id
2877
+ * });
2878
+ * }
2879
+ * });
2880
+ * ```
2881
+ */
2882
+ getFeatureState(feature: FeatureIdentifier): any {
2883
+ return this.style.getFeatureState(feature);
2884
+ }
2885
+
2886
+ /**
2887
+ * Returns the map's containing HTML element.
2888
+ *
2889
+ * @returns The map's container.
2890
+ */
2891
+ getContainer(): HTMLElement {
2892
+ return this._container;
2893
+ }
2894
+
2895
+ /**
2896
+ * Returns the HTML element containing the map's `<canvas>` element.
2897
+ *
2898
+ * If you want to add non-GL overlays to the map, you should append them to this element.
2899
+ *
2900
+ * This is the element to which event bindings for map interactivity (such as panning and zooming) are
2901
+ * attached. It will receive bubbled events from child elements such as the `<canvas>`, but not from
2902
+ * map controls.
2903
+ *
2904
+ * @returns The container of the map's `<canvas>`.
2905
+ * @see [Create a draggable point](https://maplibre.org/maplibre-gl-js/docs/examples/drag-a-point/)
2906
+ */
2907
+ getCanvasContainer(): HTMLElement {
2908
+ return this._canvasContainer;
2909
+ }
2910
+
2911
+ /**
2912
+ * Returns the map's `<canvas>` element.
2913
+ *
2914
+ * @returns The map's `<canvas>` element.
2915
+ * @see [Measure distances](https://maplibre.org/maplibre-gl-js/docs/examples/measure/)
2916
+ * @see [Display a popup on hover](https://maplibre.org/maplibre-gl-js/docs/examples/popup-on-hover/)
2917
+ * @see [Center the map on a clicked symbol](https://maplibre.org/maplibre-gl-js/docs/examples/center-on-symbol/)
2918
+ */
2919
+ getCanvas(): HTMLCanvasElement {
2920
+ return this._canvas;
2921
+ }
2922
+
2923
+ _containerDimensions() {
2924
+ let width = 0;
2925
+ let height = 0;
2926
+
2927
+ if (this._container) {
2928
+ width = this._container.clientWidth || 400;
2929
+ height = this._container.clientHeight || 300;
2930
+ }
2931
+
2932
+ return [width, height];
2933
+ }
2934
+
2935
+ _setupContainer() {
2936
+ const container = this._container;
2937
+ container.classList.add('maplibregl-map');
2938
+
2939
+ const canvasContainer = this._canvasContainer = DOM.create('div', 'maplibregl-canvas-container', container);
2940
+ if (this._interactive) {
2941
+ canvasContainer.classList.add('maplibregl-interactive');
2942
+ }
2943
+
2944
+ this._canvas = DOM.create('canvas', 'maplibregl-canvas', canvasContainer);
2945
+ this._canvas.addEventListener('webglcontextlost', this._contextLost, false);
2946
+ this._canvas.addEventListener('webglcontextrestored', this._contextRestored, false);
2947
+ this._canvas.setAttribute('tabindex', this._interactive ? '0' : '-1');
2948
+ this._canvas.setAttribute('aria-label', this._getUIString('Map.Title'));
2949
+ this._canvas.setAttribute('role', 'region');
2950
+
2951
+ const dimensions = this._containerDimensions();
2952
+ const clampedPixelRatio = this._getClampedPixelRatio(dimensions[0], dimensions[1]);
2953
+ this._resizeCanvas(dimensions[0], dimensions[1], clampedPixelRatio);
2954
+
2955
+ const controlContainer = this._controlContainer = DOM.create('div', 'maplibregl-control-container', container);
2956
+ const positions = this._controlPositions = {};
2957
+ ['top-left', 'top-right', 'bottom-left', 'bottom-right'].forEach((positionName) => {
2958
+ positions[positionName] = DOM.create('div', `maplibregl-ctrl-${positionName} `, controlContainer);
2959
+ });
2960
+
2961
+ this._container.addEventListener('scroll', this._onMapScroll, false);
2962
+ }
2963
+
2964
+ _resizeCanvas(width: number, height: number, pixelRatio: number) {
2965
+ // Request the required canvas size taking the pixelratio into account.
2966
+ this._canvas.width = Math.floor(pixelRatio * width);
2967
+ this._canvas.height = Math.floor(pixelRatio * height);
2968
+
2969
+ // Maintain the same canvas size, potentially downscaling it for HiDPI displays
2970
+ this._canvas.style.width = `${width}px`;
2971
+ this._canvas.style.height = `${height}px`;
2972
+ }
2973
+
2974
+ _setupPainter() {
2975
+
2976
+ const attributes = {
2977
+ alpha: true,
2978
+ stencil: true,
2979
+ depth: true,
2980
+ failIfMajorPerformanceCaveat: this._failIfMajorPerformanceCaveat,
2981
+ preserveDrawingBuffer: this._preserveDrawingBuffer,
2982
+ antialias: this._antialias || false
2983
+ };
2984
+
2985
+ let webglcontextcreationerrorDetailObject: any = null;
2986
+ this._canvas.addEventListener('webglcontextcreationerror', (args: WebGLContextEvent) => {
2987
+ webglcontextcreationerrorDetailObject = {requestedAttributes: attributes};
2988
+ if (args) {
2989
+ webglcontextcreationerrorDetailObject.statusMessage = args.statusMessage;
2990
+ webglcontextcreationerrorDetailObject.type = args.type;
2991
+ }
2992
+ }, {once: true});
2993
+
2994
+ const gl =
2995
+ this._canvas.getContext('webgl2', attributes) as WebGL2RenderingContext ||
2996
+ this._canvas.getContext('webgl', attributes) as WebGLRenderingContext;
2997
+
2998
+ if (!gl) {
2999
+ const msg = 'Failed to initialize WebGL';
3000
+ if (webglcontextcreationerrorDetailObject) {
3001
+ webglcontextcreationerrorDetailObject.message = msg;
3002
+ throw new Error(JSON.stringify(webglcontextcreationerrorDetailObject));
3003
+ } else {
3004
+ throw new Error(msg);
3005
+ }
3006
+ }
3007
+
3008
+ this.painter = new Painter(gl, this.transform);
3009
+
3010
+ webpSupported.testSupport(gl);
3011
+ }
3012
+
3013
+ _contextLost = (event: any) => {
3014
+ event.preventDefault();
3015
+ if (this._frameRequest) {
3016
+ this._frameRequest.abort();
3017
+ this._frameRequest = null;
3018
+ }
3019
+ this.fire(new Event('webglcontextlost', {originalEvent: event}));
3020
+ };
3021
+
3022
+ _contextRestored = (event: any) => {
3023
+ this._setupPainter();
3024
+ this.resize();
3025
+ this._update();
3026
+ this.fire(new Event('webglcontextrestored', {originalEvent: event}));
3027
+ };
3028
+
3029
+ _onMapScroll = (event: any) => {
3030
+ if (event.target !== this._container) return;
3031
+
3032
+ // Revert any scroll which would move the canvas outside of the view
3033
+ this._container.scrollTop = 0;
3034
+ this._container.scrollLeft = 0;
3035
+ return false;
3036
+ };
3037
+
3038
+ /**
3039
+ * Returns a Boolean indicating whether the map is fully loaded.
3040
+ *
3041
+ * Returns `false` if the style is not yet fully loaded,
3042
+ * or if there has been a change to the sources or style that
3043
+ * has not yet fully loaded.
3044
+ *
3045
+ * @returns A Boolean indicating whether the map is fully loaded.
3046
+ */
3047
+ loaded(): boolean {
3048
+ return !this._styleDirty && !this._sourcesDirty && !!this.style && this.style.loaded();
3049
+ }
3050
+
3051
+ /**
3052
+ * @internal
3053
+ * Update this map's style and sources, and re-render the map.
3054
+ *
3055
+ * @param updateStyle - mark the map's style for reprocessing as
3056
+ * well as its sources
3057
+ */
3058
+ _update(updateStyle?: boolean) {
3059
+ if (!this.style || !this.style._loaded) return this;
3060
+
3061
+ this._styleDirty = this._styleDirty || updateStyle;
3062
+ this._sourcesDirty = true;
3063
+ this.triggerRepaint();
3064
+
3065
+ return this;
3066
+ }
3067
+
3068
+ /**
3069
+ * @internal
3070
+ * Request that the given callback be executed during the next render
3071
+ * frame. Schedule a render frame if one is not already scheduled.
3072
+ *
3073
+ * @returns An id that can be used to cancel the callback
3074
+ */
3075
+ _requestRenderFrame(callback: () => void): TaskID {
3076
+ this._update();
3077
+ return this._renderTaskQueue.add(callback);
3078
+ }
3079
+
3080
+ _cancelRenderFrame(id: TaskID) {
3081
+ this._renderTaskQueue.remove(id);
3082
+ }
3083
+
3084
+ /**
3085
+ * @internal
3086
+ * Call when a (re-)render of the map is required:
3087
+ *
3088
+ * - The style has changed (`setPaintProperty()`, etc.)
3089
+ * - Source data has changed (e.g. tiles have finished loading)
3090
+ * - The map has is moving (or just finished moving)
3091
+ * - A transition is in progress
3092
+ *
3093
+ * @param paintStartTimeStamp - The time when the animation frame began executing.
3094
+ */
3095
+ _render(paintStartTimeStamp: number) {
3096
+ const fadeDuration = this._idleTriggered ? this._fadeDuration : 0;
3097
+
3098
+ // A custom layer may have used the context asynchronously. Mark the state as dirty.
3099
+ this.painter.context.setDirty();
3100
+ this.painter.setBaseState();
3101
+
3102
+ this._renderTaskQueue.run(paintStartTimeStamp);
3103
+ // A task queue callback may have fired a user event which may have removed the map
3104
+ if (this._removed) return;
3105
+
3106
+ let crossFading = false;
3107
+
3108
+ // If the style has changed, the map is being zoomed, or a transition or fade is in progress:
3109
+ // - Apply style changes (in a batch)
3110
+ // - Recalculate paint properties.
3111
+ if (this.style && this._styleDirty) {
3112
+ this._styleDirty = false;
3113
+
3114
+ const zoom = this.transform.zoom;
3115
+ const now = browser.now();
3116
+ this.style.zoomHistory.update(zoom, now);
3117
+
3118
+ const parameters = new EvaluationParameters(zoom, {
3119
+ now,
3120
+ fadeDuration,
3121
+ zoomHistory: this.style.zoomHistory,
3122
+ transition: this.style.getTransition()
3123
+ });
3124
+
3125
+ const factor = parameters.crossFadingFactor();
3126
+ if (factor !== 1 || factor !== this._crossFadingFactor) {
3127
+ crossFading = true;
3128
+ this._crossFadingFactor = factor;
3129
+ }
3130
+
3131
+ this.style.update(parameters);
3132
+ }
3133
+
3134
+ // If we are in _render for any reason other than an in-progress paint
3135
+ // transition, update source caches to check for and load any tiles we
3136
+ // need for the current transform
3137
+ if (this.style && this._sourcesDirty) {
3138
+ this._sourcesDirty = false;
3139
+ this.style._updateSources(this.transform);
3140
+ }
3141
+
3142
+ // update terrain stuff
3143
+ if (this.terrain) {
3144
+ this.terrain.sourceCache.update(this.transform, this.terrain);
3145
+ this.transform.minElevationForCurrentTile = this.terrain.getMinTileElevationForLngLatZoom(this.transform.center, this.transform.tileZoom);
3146
+ if (!this._elevationFreeze) {
3147
+ this.transform.elevation = this.terrain.getElevationForLngLatZoom(this.transform.center, this.transform.tileZoom);
3148
+ }
3149
+ } else {
3150
+ this.transform.minElevationForCurrentTile = 0;
3151
+ this.transform.elevation = 0;
3152
+ }
3153
+
3154
+ this._placementDirty = this.style && this.style._updatePlacement(this.painter.transform, this.showCollisionBoxes, fadeDuration, this._crossSourceCollisions);
3155
+
3156
+ // Actually draw
3157
+ this.painter.render(this.style, {
3158
+ showTileBoundaries: this.showTileBoundaries,
3159
+ showOverdrawInspector: this._showOverdrawInspector,
3160
+ rotating: this.isRotating(),
3161
+ zooming: this.isZooming(),
3162
+ moving: this.isMoving(),
3163
+ fadeDuration,
3164
+ showPadding: this.showPadding,
3165
+ });
3166
+
3167
+ this.fire(new Event('render'));
3168
+
3169
+ if (this.loaded() && !this._loaded) {
3170
+ this._loaded = true;
3171
+ PerformanceUtils.mark(PerformanceMarkers.load);
3172
+ this.fire(new Event('load'));
3173
+ }
3174
+
3175
+ if (this.style && (this.style.hasTransitions() || crossFading)) {
3176
+ this._styleDirty = true;
3177
+ }
3178
+
3179
+ if (this.style && !this._placementDirty) {
3180
+ // Since no fade operations are in progress, we can release
3181
+ // all tiles held for fading. If we didn't do this, the tiles
3182
+ // would just sit in the SourceCaches until the next render
3183
+ this.style._releaseSymbolFadeTiles();
3184
+ }
3185
+
3186
+ // Schedule another render frame if it's needed.
3187
+ //
3188
+ // Even though `_styleDirty` and `_sourcesDirty` are reset in this
3189
+ // method, synchronous events fired during Style#update or
3190
+ // Style#_updateSources could have caused them to be set again.
3191
+ const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty;
3192
+ if (somethingDirty || this._repaint) {
3193
+ this.triggerRepaint();
3194
+ } else if (!this.isMoving() && this.loaded()) {
3195
+ this.fire(new Event('idle'));
3196
+ }
3197
+
3198
+ if (this._loaded && !this._fullyLoaded && !somethingDirty) {
3199
+ this._fullyLoaded = true;
3200
+ PerformanceUtils.mark(PerformanceMarkers.fullLoad);
3201
+ }
3202
+
3203
+ return this;
3204
+ }
3205
+
3206
+ /**
3207
+ * Force a synchronous redraw of the map.
3208
+ * @example
3209
+ * ```ts
3210
+ * map.redraw();
3211
+ * ```
3212
+ */
3213
+ redraw(): this {
3214
+ if (this.style) {
3215
+ // cancel the scheduled update
3216
+ if (this._frameRequest) {
3217
+ this._frameRequest.abort();
3218
+ this._frameRequest = null;
3219
+ }
3220
+ this._render(0);
3221
+ }
3222
+ return this;
3223
+ }
3224
+
3225
+ /**
3226
+ * Clean up and release all internal resources associated with this map.
3227
+ *
3228
+ * This includes DOM elements, event bindings, web workers, and WebGL resources.
3229
+ *
3230
+ * Use this method when you are done using the map and wish to ensure that it no
3231
+ * longer consumes browser resources. Afterwards, you must not call any other
3232
+ * methods on the map.
3233
+ */
3234
+ remove() {
3235
+ if (this._hash) this._hash.remove();
3236
+
3237
+ for (const control of this._controls) control.onRemove(this);
3238
+ this._controls = [];
3239
+
3240
+ if (this._frameRequest) {
3241
+ this._frameRequest.abort();
3242
+ this._frameRequest = null;
3243
+ }
3244
+ this._renderTaskQueue.clear();
3245
+ this.painter.destroy();
3246
+ this.handlers.destroy();
3247
+ delete this.handlers;
3248
+ this.setStyle(null);
3249
+ if (typeof window !== 'undefined') {
3250
+ removeEventListener('online', this._onWindowOnline, false);
3251
+ }
3252
+
3253
+ ImageRequest.removeThrottleControl(this._imageQueueHandle);
3254
+
3255
+ this._resizeObserver?.disconnect();
3256
+ const extension = this.painter.context.gl.getExtension('WEBGL_lose_context');
3257
+ if (extension?.loseContext) extension.loseContext();
3258
+ this._canvas.removeEventListener('webglcontextrestored', this._contextRestored, false);
3259
+ this._canvas.removeEventListener('webglcontextlost', this._contextLost, false);
3260
+ DOM.remove(this._canvasContainer);
3261
+ DOM.remove(this._controlContainer);
3262
+ this._container.classList.remove('maplibregl-map');
3263
+
3264
+ PerformanceUtils.clearMetrics();
3265
+
3266
+ this._removed = true;
3267
+ this.fire(new Event('remove'));
3268
+ }
3269
+
3270
+ /**
3271
+ * Trigger the rendering of a single frame. Use this method with custom layers to
3272
+ * repaint the map when the layer changes. Calling this multiple times before the
3273
+ * next frame is rendered will still result in only a single frame being rendered.
3274
+ * @example
3275
+ * ```ts
3276
+ * map.triggerRepaint();
3277
+ * ```
3278
+ * @see [Add a 3D model](https://maplibre.org/maplibre-gl-js/docs/examples/add-3d-model/)
3279
+ * @see [Add an animated icon to the map](https://maplibre.org/maplibre-gl-js/docs/examples/add-image-animated/)
3280
+ */
3281
+ triggerRepaint() {
3282
+ if (this.style && !this._frameRequest) {
3283
+ this._frameRequest = new AbortController();
3284
+ browser.frameAsync(this._frameRequest).then((paintStartTimeStamp: number) => {
3285
+ PerformanceUtils.frame(paintStartTimeStamp);
3286
+ this._frameRequest = null;
3287
+ this._render(paintStartTimeStamp);
3288
+ }).catch(() => {}); // ignore abort error
3289
+ }
3290
+ }
3291
+
3292
+ _onWindowOnline = () => {
3293
+ this._update();
3294
+ };
3295
+
3296
+ /**
3297
+ * Gets and sets a Boolean indicating whether the map will render an outline
3298
+ * around each tile and the tile ID. These tile boundaries are useful for
3299
+ * debugging.
3300
+ *
3301
+ * The uncompressed file size of the first vector source is drawn in the top left
3302
+ * corner of each tile, next to the tile ID.
3303
+ *
3304
+ * @example
3305
+ * ```ts
3306
+ * map.showTileBoundaries = true;
3307
+ * ```
3308
+ */
3309
+ get showTileBoundaries(): boolean { return !!this._showTileBoundaries; }
3310
+ set showTileBoundaries(value: boolean) {
3311
+ if (this._showTileBoundaries === value) return;
3312
+ this._showTileBoundaries = value;
3313
+ this._update();
3314
+ }
3315
+
3316
+ /**
3317
+ * Gets and sets a Boolean indicating whether the map will visualize
3318
+ * the padding offsets.
3319
+ */
3320
+ get showPadding(): boolean { return !!this._showPadding; }
3321
+ set showPadding(value: boolean) {
3322
+ if (this._showPadding === value) return;
3323
+ this._showPadding = value;
3324
+ this._update();
3325
+ }
3326
+
3327
+ /**
3328
+ * Gets and sets a Boolean indicating whether the map will render boxes
3329
+ * around all symbols in the data source, revealing which symbols
3330
+ * were rendered or which were hidden due to collisions.
3331
+ * This information is useful for debugging.
3332
+ */
3333
+ get showCollisionBoxes(): boolean { return !!this._showCollisionBoxes; }
3334
+ set showCollisionBoxes(value: boolean) {
3335
+ if (this._showCollisionBoxes === value) return;
3336
+ this._showCollisionBoxes = value;
3337
+ if (value) {
3338
+ // When we turn collision boxes on we have to generate them for existing tiles
3339
+ // When we turn them off, there's no cost to leaving existing boxes in place
3340
+ this.style._generateCollisionBoxes();
3341
+ } else {
3342
+ // Otherwise, call an update to remove collision boxes
3343
+ this._update();
3344
+ }
3345
+ }
3346
+
3347
+ /**
3348
+ * Gets and sets a Boolean indicating whether the map should color-code
3349
+ * each fragment to show how many times it has been shaded.
3350
+ * White fragments have been shaded 8 or more times.
3351
+ * Black fragments have been shaded 0 times.
3352
+ * This information is useful for debugging.
3353
+ */
3354
+ get showOverdrawInspector(): boolean { return !!this._showOverdrawInspector; }
3355
+ set showOverdrawInspector(value: boolean) {
3356
+ if (this._showOverdrawInspector === value) return;
3357
+ this._showOverdrawInspector = value;
3358
+ this._update();
3359
+ }
3360
+
3361
+ /**
3362
+ * Gets and sets a Boolean indicating whether the map will
3363
+ * continuously repaint. This information is useful for analyzing performance.
3364
+ */
3365
+ get repaint(): boolean { return !!this._repaint; }
3366
+ set repaint(value: boolean) {
3367
+ if (this._repaint !== value) {
3368
+ this._repaint = value;
3369
+ this.triggerRepaint();
3370
+ }
3371
+ }
3372
+ // show vertices
3373
+ get vertices(): boolean { return !!this._vertices; }
3374
+ set vertices(value: boolean) { this._vertices = value; this._update(); }
3375
+
3376
+ /**
3377
+ * Returns the package version of the library
3378
+ * @returns Package version of the library
3379
+ */
3380
+ get version(): string {
3381
+ return version;
3382
+ }
3383
+
3384
+ /**
3385
+ * Returns the elevation for the point where the camera is looking.
3386
+ * This value corresponds to:
3387
+ * "meters above sea level" * "exaggeration"
3388
+ * @returns The elevation.
3389
+ */
3390
+ getCameraTargetElevation(): number {
3391
+ return this.transform.elevation;
3392
+ }
3393
+ }