Spaces:
Sleeping
Sleeping
File size: 132,602 Bytes
4a288a7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 | // @flow
import {version} from '../../package.json';
import {extend, bindAll, warnOnce, uniqueId} from '../util/util';
import browser from '../util/browser';
import window from '../util/window';
const {HTMLImageElement, HTMLElement, ImageBitmap} = window;
import DOM from '../util/dom';
import {getImage, getJSON, ResourceType} from '../util/ajax';
import {RequestManager} from '../util/mapbox';
import Style from '../style/style';
import EvaluationParameters from '../style/evaluation_parameters';
import Painter from '../render/painter';
import Transform from '../geo/transform';
import Hash from './hash';
import HandlerManager from './handler_manager';
import Camera from './camera';
import LngLat from '../geo/lng_lat';
import LngLatBounds from '../geo/lng_lat_bounds';
import Point from '@mapbox/point-geometry';
import AttributionControl from './control/attribution_control';
import LogoControl from './control/logo_control';
import isSupported from '@mapbox/mapbox-gl-supported';
import {RGBAImage} from '../util/image';
import {Event, ErrorEvent} from '../util/evented';
import {MapMouseEvent} from './events';
import TaskQueue from '../util/task_queue';
import webpSupported from '../util/webp_supported';
import {PerformanceMarkers, PerformanceUtils} from '../util/performance';
import {setCacheLimits} from '../util/tile_request_cache';
import type {PointLike} from '@mapbox/point-geometry';
import type {RequestTransformFunction} from '../util/mapbox';
import type {LngLatLike} from '../geo/lng_lat';
import type {LngLatBoundsLike} from '../geo/lng_lat_bounds';
import type {StyleOptions, StyleSetterOptions} from '../style/style';
import type {MapEvent, MapDataEvent} from './events';
import type {CustomLayerInterface} from '../style/style_layer/custom_style_layer';
import type {StyleImageInterface, StyleImageMetadata} from '../style/style_image';
import type ScrollZoomHandler from './handler/scroll_zoom';
import type BoxZoomHandler from './handler/box_zoom';
import type {TouchPitchHandler} from './handler/touch_zoom_rotate';
import type DragRotateHandler from './handler/shim/drag_rotate';
import type DragPanHandler, {DragPanOptions} from './handler/shim/drag_pan';
import type KeyboardHandler from './handler/keyboard';
import type DoubleClickZoomHandler from './handler/shim/dblclick_zoom';
import type TouchZoomRotateHandler from './handler/shim/touch_zoom_rotate';
import defaultLocale from './default_locale';
import type {TaskID} from '../util/task_queue';
import type {Cancelable} from '../types/cancelable';
import type {
LayerSpecification,
FilterSpecification,
StyleSpecification,
LightSpecification,
SourceSpecification
} from '../style-spec/types';
type ControlPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
/* eslint-disable no-use-before-define */
type IControl = {
onAdd(map: Map): HTMLElement;
onRemove(map: Map): void;
+getDefaultPosition?: () => ControlPosition;
}
/* eslint-enable no-use-before-define */
type MapOptions = {
hash?: boolean | string,
interactive?: boolean,
container: HTMLElement | string,
bearingSnap?: number,
attributionControl?: boolean,
customAttribution?: string | Array<string>,
logoPosition?: ControlPosition,
failIfMajorPerformanceCaveat?: boolean,
preserveDrawingBuffer?: boolean,
antialias?: boolean,
refreshExpiredTiles?: boolean,
maxBounds?: LngLatBoundsLike,
scrollZoom?: boolean,
minZoom?: ?number,
maxZoom?: ?number,
minPitch?: ?number,
maxPitch?: ?number,
boxZoom?: boolean,
dragRotate?: boolean,
dragPan?: DragPanOptions,
keyboard?: boolean,
doubleClickZoom?: boolean,
touchZoomRotate?: boolean,
touchPitch?: boolean,
trackResize?: boolean,
center?: LngLatLike,
zoom?: number,
bearing?: number,
pitch?: number,
renderWorldCopies?: boolean,
maxTileCacheSize?: number,
transformRequest?: RequestTransformFunction,
accessToken: string,
locale?: Object
};
const defaultMinZoom = -2;
const defaultMaxZoom = 22;
// the default values, but also the valid range
const defaultMinPitch = 0;
const defaultMaxPitch = 60;
const defaultOptions = {
center: [0, 0],
zoom: 0,
bearing: 0,
pitch: 0,
minZoom: defaultMinZoom,
maxZoom: defaultMaxZoom,
minPitch: defaultMinPitch,
maxPitch: defaultMaxPitch,
interactive: true,
scrollZoom: true,
boxZoom: true,
dragRotate: true,
dragPan: true,
keyboard: true,
doubleClickZoom: true,
touchZoomRotate: true,
touchPitch: true,
bearingSnap: 7,
clickTolerance: 3,
pitchWithRotate: true,
hash: false,
attributionControl: true,
failIfMajorPerformanceCaveat: false,
preserveDrawingBuffer: false,
trackResize: true,
renderWorldCopies: true,
refreshExpiredTiles: true,
maxTileCacheSize: null,
localIdeographFontFamily: 'sans-serif',
transformRequest: null,
accessToken: null,
fadeDuration: 300,
crossSourceCollisions: true
};
/**
* The `Map` object represents the map on your page. It exposes methods
* and properties that enable you to programmatically change the map,
* and fires events as users interact with it.
*
* You create a `Map` by specifying a `container` and other options.
* Then Mapbox GL JS initializes the map on the page and returns your `Map`
* object.
*
* @extends Evented
* @param {Object} options
* @param {HTMLElement|string} options.container The HTML element in which Mapbox GL JS will render the map, or the element's string `id`. The specified element must have no children.
* @param {number} [options.minZoom=0] The minimum zoom level of the map (0-24).
* @param {number} [options.maxZoom=22] The maximum zoom level of the map (0-24).
* @param {number} [options.minPitch=0] The minimum pitch of the map (0-60).
* @param {number} [options.maxPitch=60] The maximum pitch of the map (0-60).
* @param {Object|string} [options.style] The map's Mapbox style. This must be an a JSON object conforming to
* the schema described in the [Mapbox Style Specification](https://mapbox.com/mapbox-gl-style-spec/), or a URL to
* such JSON.
*
* To load a style from the Mapbox API, you can use a URL of the form `mapbox://styles/:owner/:style`,
* where `:owner` is your Mapbox account name and `:style` is the style ID. Or you can use one of the following
* [the predefined Mapbox styles](https://www.mapbox.com/maps/):
*
* * `mapbox://styles/mapbox/streets-v11`
* * `mapbox://styles/mapbox/outdoors-v11`
* * `mapbox://styles/mapbox/light-v10`
* * `mapbox://styles/mapbox/dark-v10`
* * `mapbox://styles/mapbox/satellite-v9`
* * `mapbox://styles/mapbox/satellite-streets-v11`
* * `mapbox://styles/mapbox/navigation-preview-day-v4`
* * `mapbox://styles/mapbox/navigation-preview-night-v4`
* * `mapbox://styles/mapbox/navigation-guidance-day-v4`
* * `mapbox://styles/mapbox/navigation-guidance-night-v4`
*
* Tilesets hosted with Mapbox can be style-optimized if you append `?optimize=true` to the end of your style URL, like `mapbox://styles/mapbox/streets-v11?optimize=true`.
* Learn more about style-optimized vector tiles in our [API documentation](https://www.mapbox.com/api-documentation/maps/#retrieve-tiles).
*
* @param {(boolean|string)} [options.hash=false] 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.
* For example, `http://path/to/my/page.html#2.59/39.26/53.07/-24.1/60`.
* An additional string may optionally be provided to indicate a parameter-styled hash,
* e.g. http://path/to/my/page.html#map=2.59/39.26/53.07/-24.1/60&foo=bar, where foo
* is a custom parameter and bar is an arbitrary hash distinct from the map hash.
* @param {boolean} [options.interactive=true] If `false`, no mouse, touch, or keyboard listeners will be attached to the map, so it will not respond to interaction.
* @param {number} [options.bearingSnap=7] The threshold, measured in degrees, that determines when the map's
* bearing will snap to north. For example, with a `bearingSnap` of 7, if the user rotates
* the map within 7 degrees of north, the map will automatically snap to exact north.
* @param {boolean} [options.pitchWithRotate=true] If `false`, the map's pitch (tilt) control with "drag to rotate" interaction will be disabled.
* @param {number} [options.clickTolerance=3] 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).
* @param {boolean} [options.attributionControl=true] If `true`, an {@link AttributionControl} will be added to the map.
* @param {string | Array<string>} [options.customAttribution] String or strings to show in an {@link AttributionControl}. Only applicable if `options.attributionControl` is `true`.
* @param {string} [options.logoPosition='bottom-left'] A string representing the position of the Mapbox wordmark on the map. Valid options are `top-left`,`top-right`, `bottom-left`, `bottom-right`.
* @param {boolean} [options.failIfMajorPerformanceCaveat=false] If `true`, map creation will fail if the performance of Mapbox
* GL JS would be dramatically worse than expected (i.e. a software renderer would be used).
* @param {boolean} [options.preserveDrawingBuffer=false] 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.
* @param {boolean} [options.antialias] If `true`, the gl context will be created with MSAA antialiasing, which can be useful for antialiasing custom layers. this is `false` by default as a performance optimization.
* @param {boolean} [options.refreshExpiredTiles=true] If `false`, the map won't attempt to re-request tiles once they expire per their HTTP `cacheControl`/`expires` headers.
* @param {LngLatBoundsLike} [options.maxBounds] If set, the map will be constrained to the given bounds.
* @param {boolean|Object} [options.scrollZoom=true] If `true`, the "scroll to zoom" interaction is enabled. An `Object` value is passed as options to {@link ScrollZoomHandler#enable}.
* @param {boolean} [options.boxZoom=true] If `true`, the "box zoom" interaction is enabled (see {@link BoxZoomHandler}).
* @param {boolean} [options.dragRotate=true] If `true`, the "drag to rotate" interaction is enabled (see {@link DragRotateHandler}).
* @param {boolean|Object} [options.dragPan=true] If `true`, the "drag to pan" interaction is enabled. An `Object` value is passed as options to {@link DragPanHandler#enable}.
* @param {boolean} [options.keyboard=true] If `true`, keyboard shortcuts are enabled (see {@link KeyboardHandler}).
* @param {boolean} [options.doubleClickZoom=true] If `true`, the "double click to zoom" interaction is enabled (see {@link DoubleClickZoomHandler}).
* @param {boolean|Object} [options.touchZoomRotate=true] If `true`, the "pinch to rotate and zoom" interaction is enabled. An `Object` value is passed as options to {@link TouchZoomRotateHandler#enable}.
* @param {boolean|Object} [options.touchPitch=true] If `true`, the "drag to pitch" interaction is enabled. An `Object` value is passed as options to {@link TouchPitchHandler#enable}.
* @param {boolean} [options.trackResize=true] If `true`, the map will automatically resize when the browser window resizes.
* @param {LngLatLike} [options.center=[0, 0]] The inital geographical centerpoint of the map. If `center` is not specified in the constructor options, Mapbox 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: Mapbox GL uses longitude, latitude coordinate order (as opposed to latitude, longitude) to match GeoJSON.
* @param {number} [options.zoom=0] The initial zoom level of the map. If `zoom` is not specified in the constructor options, Mapbox 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`.
* @param {number} [options.bearing=0] The initial bearing (rotation) of the map, measured in degrees counter-clockwise from north. If `bearing` is not specified in the constructor options, Mapbox 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`.
* @param {number} [options.pitch=0] The initial pitch (tilt) of the map, measured in degrees away from the plane of the screen (0-60). If `pitch` is not specified in the constructor options, Mapbox 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`.
* @param {LngLatBoundsLike} [options.bounds] The initial bounds of the map. If `bounds` is specified, it overrides `center` and `zoom` constructor options.
* @param {Object} [options.fitBoundsOptions] A {@link Map#fitBounds} options object to use _only_ when fitting the initial `bounds` provided above.
* @param {boolean} [options.renderWorldCopies=true] If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:
* - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
* container, there will be blank space beyond 180 and -180 degrees longitude.
* - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
* map and the other on the left edge of the map) at every zoom level.
* @param {number} [options.maxTileCacheSize=null] 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.
* @param {string} [options.localIdeographFontFamily='sans-serif'] Defines a CSS
* font-family for locally overriding generation of glyphs in the 'CJK Unified Ideographs', 'Hiragana', 'Katakana' and 'Hangul Syllables' ranges.
* In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold).
* Set to `false`, to enable font settings from the map's style for these glyph ranges. Note that [Mapbox Studio](https://studio.mapbox.com/) sets this value to `false` by default.
* The purpose of this option is to avoid bandwidth-intensive glyph server requests. (See [Use locally generated ideographs](https://www.mapbox.com/mapbox-gl-js/example/local-ideographs).)
* @param {RequestTransformFunction} [options.transformRequest=null] 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.
* Expected to return an object with a `url` property and optionally `headers` and `credentials` properties.
* @param {boolean} [options.collectResourceTiming=false] 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.
* @param {number} [options.fadeDuration=300] Controls the duration of the fade-in/fade-out animation for label collisions, in milliseconds. This setting affects all symbol layers. This setting does not affect the duration of runtime styling transitions or raster tile cross-fading.
* @param {boolean} [options.crossSourceCollisions=true] 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.
* @param {string} [options.accessToken=null] If specified, map will use this token instead of the one defined in mapboxgl.accessToken.
* @param {Object} [options.locale=null] 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).
* @example
* var map = new mapboxgl.Map({
* container: 'map',
* center: [-122.420679, 37.772537],
* zoom: 13,
* style: style_object,
* hash: true,
* transformRequest: (url, resourceType)=> {
* if(resourceType === 'Source' && url.startsWith('http://myHost')) {
* return {
* url: url.replace('http', 'https'),
* headers: { 'my-custom-header': true},
* credentials: 'include' // Include cookies for cross-origin requests
* }
* }
* }
* });
* @see [Display a map](https://www.mapbox.com/mapbox-gl-js/examples/)
*/
class Map extends Camera {
style: Style;
painter: Painter;
handlers: HandlerManager;
_container: HTMLElement;
_missingCSSCanary: HTMLElement;
_canvasContainer: HTMLElement;
_controlContainer: HTMLElement;
_controlPositions: {[_: string]: HTMLElement};
_interactive: ?boolean;
_showTileBoundaries: ?boolean;
_showCollisionBoxes: ?boolean;
_showPadding: ?boolean;
_showOverdrawInspector: boolean;
_repaint: ?boolean;
_vertices: ?boolean;
_canvas: HTMLCanvasElement;
_maxTileCacheSize: number;
_frame: ?Cancelable;
_styleDirty: ?boolean;
_sourcesDirty: ?boolean;
_placementDirty: ?boolean;
_loaded: boolean;
// accounts for placement finishing as well
_fullyLoaded: boolean;
_trackResize: boolean;
_preserveDrawingBuffer: boolean;
_failIfMajorPerformanceCaveat: boolean;
_antialias: boolean;
_refreshExpiredTiles: boolean;
_hash: Hash;
_delegatedListeners: any;
_fadeDuration: number;
_crossSourceCollisions: boolean;
_crossFadingFactor: number;
_collectResourceTiming: boolean;
_renderTaskQueue: TaskQueue;
_controls: Array<IControl>;
_mapId: number;
_localIdeographFontFamily: string;
_requestManager: RequestManager;
_locale: Object;
_removed: boolean;
_clickTolerance: number;
/**
* The map's {@link ScrollZoomHandler}, which implements zooming in and out with a scroll wheel or trackpad.
* Find more details and examples using `scrollZoom` in the {@link ScrollZoomHandler} section.
*/
scrollZoom: ScrollZoomHandler;
/**
* The map's {@link BoxZoomHandler}, which implements zooming using a drag gesture with the Shift key pressed.
* Find more details and examples using `boxZoom` in the {@link BoxZoomHandler} section.
*/
boxZoom: BoxZoomHandler;
/**
* The map's {@link DragRotateHandler}, which implements rotating the map while dragging with the right
* mouse button or with the Control key pressed. Find more details and examples using `dragRotate`
* in the {@link DragRotateHandler} section.
*/
dragRotate: DragRotateHandler;
/**
* The map's {@link DragPanHandler}, which implements dragging the map with a mouse or touch gesture.
* Find more details and examples using `dragPan` in the {@link DragPanHandler} section.
*/
dragPan: DragPanHandler;
/**
* The map's {@link KeyboardHandler}, which allows the user to zoom, rotate, and pan the map using keyboard
* shortcuts. Find more details and examples using `keyboard` in the {@link KeyboardHandler} section.
*/
keyboard: KeyboardHandler;
/**
* The map's {@link DoubleClickZoomHandler}, which allows the user to zoom by double clicking.
* Find more details and examples using `doubleClickZoom` in the {@link DoubleClickZoomHandler} section.
*/
doubleClickZoom: DoubleClickZoomHandler;
/**
* The map's {@link TouchZoomRotateHandler}, which allows the user to zoom or rotate the map with touch gestures.
* Find more details and examples using `touchZoomRotate` in the {@link TouchZoomRotateHandler} section.
*/
touchZoomRotate: TouchZoomRotateHandler;
/**
* The map's {@link TouchPitchHandler}, which allows the user to pitch the map with touch gestures.
* Find more details and examples using `touchPitch` in the {@link TouchPitchHandler} section.
*/
touchPitch: TouchPitchHandler;
constructor(options: MapOptions) {
PerformanceUtils.mark(PerformanceMarkers.create);
options = extend({}, defaultOptions, options);
if (options.minZoom != null && options.maxZoom != null && options.minZoom > options.maxZoom) {
throw new Error(`maxZoom must be greater than or equal to minZoom`);
}
if (options.minPitch != null && options.maxPitch != null && options.minPitch > options.maxPitch) {
throw new Error(`maxPitch must be greater than or equal to minPitch`);
}
if (options.minPitch != null && options.minPitch < defaultMinPitch) {
throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
}
if (options.maxPitch != null && options.maxPitch > defaultMaxPitch) {
throw new Error(`maxPitch must be less than or equal to ${defaultMaxPitch}`);
}
const transform = new Transform(options.minZoom, options.maxZoom, options.minPitch, options.maxPitch, options.renderWorldCopies);
super(transform, options);
this._interactive = options.interactive;
this._maxTileCacheSize = options.maxTileCacheSize;
this._failIfMajorPerformanceCaveat = options.failIfMajorPerformanceCaveat;
this._preserveDrawingBuffer = options.preserveDrawingBuffer;
this._antialias = options.antialias;
this._trackResize = options.trackResize;
this._bearingSnap = options.bearingSnap;
this._refreshExpiredTiles = options.refreshExpiredTiles;
this._fadeDuration = options.fadeDuration;
this._crossSourceCollisions = options.crossSourceCollisions;
this._crossFadingFactor = 1;
this._collectResourceTiming = options.collectResourceTiming;
this._renderTaskQueue = new TaskQueue();
this._controls = [];
this._mapId = uniqueId();
this._locale = extend({}, defaultLocale, options.locale);
this._clickTolerance = options.clickTolerance;
this._requestManager = new RequestManager(options.transformRequest, options.accessToken);
if (typeof options.container === 'string') {
this._container = window.document.getElementById(options.container);
if (!this._container) {
throw new Error(`Container '${options.container}' not found.`);
}
} else if (options.container instanceof HTMLElement) {
this._container = options.container;
} else {
throw new Error(`Invalid type: 'container' must be a String or HTMLElement.`);
}
if (options.maxBounds) {
this.setMaxBounds(options.maxBounds);
}
bindAll([
'_onWindowOnline',
'_onWindowResize',
'_onMapScroll',
'_contextLost',
'_contextRestored'
], this);
this._setupContainer();
this._setupPainter();
if (this.painter === undefined) {
throw new Error(`Failed to initialize WebGL.`);
}
this.on('move', () => this._update(false));
this.on('moveend', () => this._update(false));
this.on('zoom', () => this._update(true));
if (typeof window !== 'undefined') {
window.addEventListener('online', this._onWindowOnline, false);
window.addEventListener('resize', this._onWindowResize, false);
window.addEventListener('orientationchange', this._onWindowResize, false);
}
this.handlers = new HandlerManager(this, options);
const hashName = (typeof options.hash === 'string' && options.hash) || undefined;
this._hash = options.hash && (new Hash(hashName)).addTo(this);
// don't set position from options if set through hash
if (!this._hash || !this._hash._onHashChange()) {
this.jumpTo({
center: options.center,
zoom: options.zoom,
bearing: options.bearing,
pitch: options.pitch
});
if (options.bounds) {
this.resize();
this.fitBounds(options.bounds, extend({}, options.fitBoundsOptions, {duration: 0}));
}
}
this.resize();
this._localIdeographFontFamily = options.localIdeographFontFamily;
if (options.style) this.setStyle(options.style, {localIdeographFontFamily: options.localIdeographFontFamily});
if (options.attributionControl)
this.addControl(new AttributionControl({customAttribution: options.customAttribution}));
this.addControl(new LogoControl(), options.logoPosition);
this.on('style.load', () => {
if (this.transform.unmodified) {
this.jumpTo((this.style.stylesheet: any));
}
});
this.on('data', (event: MapDataEvent) => {
this._update(event.dataType === 'style');
this.fire(new Event(`${event.dataType}data`, event));
});
this.on('dataloading', (event: MapDataEvent) => {
this.fire(new Event(`${event.dataType}dataloading`, event));
});
}
/*
* Returns a unique number for this map instance which is used for the MapLoadEvent
* to make sure we only fire one event per instantiated map object.
* @private
* @returns {number}
*/
_getMapId() {
return this._mapId;
}
/**
* Adds an {@link IControl} to the map, calling `control.onAdd(this)`.
*
* @param {IControl} control The {@link IControl} to add.
* @param {string} [position] position on the map to which the control will be added.
* Valid values are `'top-left'`, `'top-right'`, `'bottom-left'`, and `'bottom-right'`. Defaults to `'top-right'`.
* @returns {Map} `this`
* @example
* // Add zoom and rotation controls to the map.
* map.addControl(new mapboxgl.NavigationControl());
* @see [Display map navigation controls](https://www.mapbox.com/mapbox-gl-js/example/navigation/)
*/
addControl(control: IControl, position?: ControlPosition) {
if (position === undefined) {
if (control.getDefaultPosition) {
position = control.getDefaultPosition();
} else {
position = 'top-right';
}
}
if (!control || !control.onAdd) {
return this.fire(new ErrorEvent(new Error(
'Invalid argument to map.addControl(). Argument must be a control with onAdd and onRemove methods.')));
}
const controlElement = control.onAdd(this);
this._controls.push(control);
const positionContainer = this._controlPositions[position];
if (position.indexOf('bottom') !== -1) {
positionContainer.insertBefore(controlElement, positionContainer.firstChild);
} else {
positionContainer.appendChild(controlElement);
}
return this;
}
/**
* Removes the control from the map.
*
* @param {IControl} control The {@link IControl} to remove.
* @returns {Map} `this`
* @example
* // Define a new navigation control.
* var navigation = new mapboxgl.NavigationControl();
* // Add zoom and rotation controls to the map.
* map.addControl(navigation);
* // Remove zoom and rotation controls from the map.
* map.removeControl(navigation);
*/
removeControl(control: IControl) {
if (!control || !control.onRemove) {
return this.fire(new ErrorEvent(new Error(
'Invalid argument to map.removeControl(). Argument must be a control with onAdd and onRemove methods.')));
}
const ci = this._controls.indexOf(control);
if (ci > -1) this._controls.splice(ci, 1);
control.onRemove(this);
return this;
}
/**
* Checks if a control exists on the map.
*
* @param {IControl} control The {@link IControl} to check.
* @returns {boolean} True if map contains control.
* @example
* // Define a new navigation control.
* var navigation = new mapboxgl.NavigationControl();
* // Add zoom and rotation controls to the map.
* map.addControl(navigation);
* // Check that the navigation control exists on the map.
* map.hasControl(navigation);
*/
hasControl(control: IControl) {
return this._controls.indexOf(control) > -1;
}
/**
* Resizes the map according to the dimensions of its
* `container` element.
*
* Checks if the map container size changed and updates the map if it has changed.
* This method must be called after the map's `container` is resized programmatically
* or when the map is shown after being initially hidden with CSS.
*
* @param eventData Additional properties to be passed to `movestart`, `move`, `resize`, and `moveend`
* events that get triggered as a result of resize. This can be useful for differentiating the
* source of an event (for example, user-initiated or programmatically-triggered events).
* @returns {Map} `this`
* @example
* // Resize the map when the map container is shown
* // after being initially hidden with CSS.
* var mapDiv = document.getElementById('map');
* if (mapDiv.style.visibility === true) map.resize();
*/
resize(eventData?: Object) {
const [width, height] = this._containerDimensions();
// do nothing if container remained the same size
if (width === this.transform.width && height === this.transform.height) return this;
this._resizeCanvas(width, height);
this.transform.resize(width, height);
this.painter.resize(width, height);
const fireMoving = !this._moving;
if (fireMoving) {
this.fire(new Event('movestart', eventData))
.fire(new Event('move', eventData));
}
this.fire(new Event('resize', eventData));
if (fireMoving) this.fire(new Event('moveend', eventData));
return this;
}
/**
* Returns the map's geographical bounds. When the bearing or pitch is non-zero, the visible region is not
* an axis-aligned rectangle, and the result is the smallest bounds that encompasses the visible region.
* @returns {LngLatBounds} The geographical bounds of the map as {@link LngLatBounds}.
* @example
* var bounds = map.getBounds();
*/
getBounds(): LngLatBounds {
return this.transform.getBounds();
}
/**
* Returns the maximum geographical bounds the map is constrained to, or `null` if none set.
* @returns The map object.
* @example
* var maxBounds = map.getMaxBounds();
*/
getMaxBounds(): LngLatBounds | null {
return this.transform.getMaxBounds();
}
/**
* Sets or clears the map's geographical bounds.
*
* Pan and zoom operations are constrained within these bounds.
* If a pan or zoom is performed that would
* display regions outside these bounds, the map will
* instead display a position and zoom level
* as close as possible to the operation's request while still
* remaining within the bounds.
*
* @param {LngLatBoundsLike | null | undefined} bounds The maximum bounds to set. If `null` or `undefined` is provided, the function removes the map's maximum bounds.
* @returns {Map} `this`
* @example
* // Define bounds that conform to the `LngLatBoundsLike` object.
* var bounds = [
* [-74.04728, 40.68392], // [west, south]
* [-73.91058, 40.87764] // [east, north]
* ];
* // Set the map's max bounds.
* map.setMaxBounds(bounds);
*/
setMaxBounds(bounds: LngLatBoundsLike) {
this.transform.setMaxBounds(LngLatBounds.convert(bounds));
return this._update();
}
/**
* Sets or clears the map's minimum zoom level.
* If the map's current zoom level is lower than the new minimum,
* the map will zoom to the new minimum.
*
* It is not always possible to zoom out and reach the set `minZoom`.
* Other factors such as map height may restrict zooming. For example,
* if the map is 512px tall it will not be possible to zoom below zoom 0
* no matter what the `minZoom` is set to.
*
* @param {number | null | undefined} minZoom The minimum zoom level to set (-2 - 24).
* If `null` or `undefined` is provided, the function removes the current minimum zoom (i.e. sets it to -2).
* @returns {Map} `this`
* @example
* map.setMinZoom(12.25);
*/
setMinZoom(minZoom?: ?number) {
minZoom = minZoom === null || minZoom === undefined ? defaultMinZoom : minZoom;
if (minZoom >= defaultMinZoom && minZoom <= this.transform.maxZoom) {
this.transform.minZoom = minZoom;
this._update();
if (this.getZoom() < minZoom) this.setZoom(minZoom);
return this;
} else throw new Error(`minZoom must be between ${defaultMinZoom} and the current maxZoom, inclusive`);
}
/**
* Returns the map's minimum allowable zoom level.
*
* @returns {number} minZoom
* @example
* var minZoom = map.getMinZoom();
*/
getMinZoom() { return this.transform.minZoom; }
/**
* Sets or clears the map's maximum zoom level.
* If the map's current zoom level is higher than the new maximum,
* the map will zoom to the new maximum.
*
* @param {number | null | undefined} maxZoom The maximum zoom level to set.
* If `null` or `undefined` is provided, the function removes the current maximum zoom (sets it to 22).
* @returns {Map} `this`
* @example
* map.setMaxZoom(18.75);
*/
setMaxZoom(maxZoom?: ?number) {
maxZoom = maxZoom === null || maxZoom === undefined ? defaultMaxZoom : maxZoom;
if (maxZoom >= this.transform.minZoom) {
this.transform.maxZoom = maxZoom;
this._update();
if (this.getZoom() > maxZoom) this.setZoom(maxZoom);
return this;
} else throw new Error(`maxZoom must be greater than the current minZoom`);
}
/**
* Returns the map's maximum allowable zoom level.
*
* @returns {number} maxZoom
* @example
* var maxZoom = map.getMaxZoom();
*/
getMaxZoom() { return this.transform.maxZoom; }
/**
* Sets or clears the map's minimum pitch.
* If the map's current pitch is lower than the new minimum,
* the map will pitch to the new minimum.
*
* @param {number | null | undefined} minPitch The minimum pitch to set (0-60).
* If `null` or `undefined` is provided, the function removes the current minimum pitch (i.e. sets it to 0).
* @returns {Map} `this`
*/
setMinPitch(minPitch?: ?number) {
minPitch = minPitch === null || minPitch === undefined ? defaultMinPitch : minPitch;
if (minPitch < defaultMinPitch) {
throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`);
}
if (minPitch >= defaultMinPitch && minPitch <= this.transform.maxPitch) {
this.transform.minPitch = minPitch;
this._update();
if (this.getPitch() < minPitch) this.setPitch(minPitch);
return this;
} else throw new Error(`minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive`);
}
/**
* Returns the map's minimum allowable pitch.
*
* @returns {number} minPitch
*/
getMinPitch() { return this.transform.minPitch; }
/**
* Sets or clears the map's maximum pitch.
* If the map's current pitch is higher than the new maximum,
* the map will pitch to the new maximum.
*
* @param {number | null | undefined} maxPitch The maximum pitch to set.
* If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 60).
* @returns {Map} `this`
*/
setMaxPitch(maxPitch?: ?number) {
maxPitch = maxPitch === null || maxPitch === undefined ? defaultMaxPitch : maxPitch;
if (maxPitch > defaultMaxPitch) {
throw new Error(`maxPitch must be less than or equal to ${defaultMaxPitch}`);
}
if (maxPitch >= this.transform.minPitch) {
this.transform.maxPitch = maxPitch;
this._update();
if (this.getPitch() > maxPitch) this.setPitch(maxPitch);
return this;
} else throw new Error(`maxPitch must be greater than the current minPitch`);
}
/**
* Returns the map's maximum allowable pitch.
*
* @returns {number} maxPitch
*/
getMaxPitch() { return this.transform.maxPitch; }
/**
* 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`:
* - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
* container, there will be blank space beyond 180 and -180 degrees longitude.
* - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
* map and the other on the left edge of the map) at every zoom level.
* @returns {boolean} renderWorldCopies
* @example
* var worldCopiesRendered = map.getRenderWorldCopies();
* @see [Render world copies](https://docs.mapbox.com/mapbox-gl-js/example/render-world-copies/)
*/
getRenderWorldCopies() { return this.transform.renderWorldCopies; }
/**
* Sets the state of `renderWorldCopies`.
*
* @param {boolean} renderWorldCopies If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`:
* - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire
* container, there will be blank space beyond 180 and -180 degrees longitude.
* - Features that cross 180 and -180 degrees longitude will be cut in two (with one portion on the right edge of the
* map and the other on the left edge of the map) at every zoom level.
*
* `undefined` is treated as `true`, `null` is treated as `false`.
* @returns {Map} `this`
* @example
* map.setRenderWorldCopies(true);
* @see [Render world copies](https://docs.mapbox.com/mapbox-gl-js/example/render-world-copies/)
*/
setRenderWorldCopies(renderWorldCopies?: ?boolean) {
this.transform.renderWorldCopies = renderWorldCopies;
return this._update();
}
/**
* Returns a {@link Point} representing pixel coordinates, relative to the map's `container`,
* that correspond to the specified geographical location.
*
* @param {LngLatLike} lnglat The geographical location to project.
* @returns {Point} The {@link Point} corresponding to `lnglat`, relative to the map's `container`.
* @example
* var coordinate = [-122.420679, 37.772537];
* var point = map.project(coordinate);
*/
project(lnglat: LngLatLike) {
return this.transform.locationPoint(LngLat.convert(lnglat));
}
/**
* Returns a {@link LngLat} representing geographical coordinates that correspond
* to the specified pixel coordinates.
*
* @param {PointLike} point The pixel coordinates to unproject.
* @returns {LngLat} The {@link LngLat} corresponding to `point`.
* @example
* map.on('click', function(e) {
* // When the map is clicked, get the geographic coordinate.
* var coordinate = map.unproject(e.point);
* });
*/
unproject(point: PointLike) {
return this.transform.pointLocation(Point.convert(point));
}
/**
* Returns true if the map is panning, zooming, rotating, or pitching due to a camera animation or user gesture.
* @returns {boolean} True if the map is moving.
* @example
* var isMoving = map.isMoving();
*/
isMoving(): boolean {
return this._moving || this.handlers.isMoving();
}
/**
* Returns true if the map is zooming due to a camera animation or user gesture.
* @returns {boolean} True if the map is zooming.
* @example
* var isZooming = map.isZooming();
*/
isZooming(): boolean {
return this._zooming || this.handlers.isZooming();
}
/**
* Returns true if the map is rotating due to a camera animation or user gesture.
* @returns {boolean} True if the map is rotating.
* @example
* map.isRotating();
*/
isRotating(): boolean {
return this._rotating || this.handlers.isRotating();
}
_createDelegatedListener(type: MapEvent, layerId: any, listener: any) {
if (type === 'mouseenter' || type === 'mouseover') {
let mousein = false;
const mousemove = (e) => {
const features = this.getLayer(layerId) ? this.queryRenderedFeatures(e.point, {layers: [layerId]}) : [];
if (!features.length) {
mousein = false;
} else if (!mousein) {
mousein = true;
listener.call(this, new MapMouseEvent(type, this, e.originalEvent, {features}));
}
};
const mouseout = () => {
mousein = false;
};
return {layer: layerId, listener, delegates: {mousemove, mouseout}};
} else if (type === 'mouseleave' || type === 'mouseout') {
let mousein = false;
const mousemove = (e) => {
const features = this.getLayer(layerId) ? this.queryRenderedFeatures(e.point, {layers: [layerId]}) : [];
if (features.length) {
mousein = true;
} else if (mousein) {
mousein = false;
listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
}
};
const mouseout = (e) => {
if (mousein) {
mousein = false;
listener.call(this, new MapMouseEvent(type, this, e.originalEvent));
}
};
return {layer: layerId, listener, delegates: {mousemove, mouseout}};
} else {
const delegate = (e) => {
const features = this.getLayer(layerId) ? this.queryRenderedFeatures(e.point, {layers: [layerId]}) : [];
if (features.length) {
// Here we need to mutate the original event, so that preventDefault works as expected.
e.features = features;
listener.call(this, e);
delete e.features;
}
};
return {layer: layerId, listener, delegates: {[type]: delegate}};
}
}
/**
* Adds a listener for events of a specified type, optionally limited to features in a specified style layer.
*
* @param {string} type The event type to listen for. Events compatible with the optional `layerId` parameter are triggered
* when the cursor enters a visible portion of the specified layer from outside that layer or outside the map canvas.
*
* | Event | Compatible with `layerId` |
* |-----------------------------------------------------------|---------------------------|
* | [`mousedown`](#map.event:mousedown) | yes |
* | [`mouseup`](#map.event:mouseup) | yes |
* | [`mouseover`](#map.event:mouseover) | yes |
* | [`mouseout`](#map.event:mouseout) | yes |
* | [`mousemove`](#map.event:mousemove) | yes |
* | [`mouseenter`](#map.event:mouseenter) | yes (required) |
* | [`mouseleave`](#map.event:mouseleave) | yes (required) |
* | [`click`](#map.event:click) | yes |
* | [`dblclick`](#map.event:dblclick) | yes |
* | [`contextmenu`](#map.event:contextmenu) | yes |
* | [`touchstart`](#map.event:touchstart) | yes |
* | [`touchend`](#map.event:touchend) | yes |
* | [`touchcancel`](#map.event:touchcancel) | yes |
* | [`wheel`](#map.event:wheel) | |
* | [`resize`](#map.event:resize) | |
* | [`remove`](#map.event:remove) | |
* | [`touchmove`](#map.event:touchmove) | |
* | [`movestart`](#map.event:movestart) | |
* | [`move`](#map.event:move) | |
* | [`moveend`](#map.event:moveend) | |
* | [`dragstart`](#map.event:dragstart) | |
* | [`drag`](#map.event:drag) | |
* | [`dragend`](#map.event:dragend) | |
* | [`zoomstart`](#map.event:zoomstart) | |
* | [`zoom`](#map.event:zoom) | |
* | [`zoomend`](#map.event:zoomend) | |
* | [`rotatestart`](#map.event:rotatestart) | |
* | [`rotate`](#map.event:rotate) | |
* | [`rotateend`](#map.event:rotateend) | |
* | [`pitchstart`](#map.event:pitchstart) | |
* | [`pitch`](#map.event:pitch) | |
* | [`pitchend`](#map.event:pitchend) | |
* | [`boxzoomstart`](#map.event:boxzoomstart) | |
* | [`boxzoomend`](#map.event:boxzoomend) | |
* | [`boxzoomcancel`](#map.event:boxzoomcancel) | |
* | [`webglcontextlost`](#map.event:webglcontextlost) | |
* | [`webglcontextrestored`](#map.event:webglcontextrestored) | |
* | [`load`](#map.event:load) | |
* | [`render`](#map.event:render) | |
* | [`idle`](#map.event:idle) | |
* | [`error`](#map.event:error) | |
* | [`data`](#map.event:data) | |
* | [`styledata`](#map.event:styledata) | |
* | [`sourcedata`](#map.event:sourcedata) | |
* | [`dataloading`](#map.event:dataloading) | |
* | [`styledataloading`](#map.event:styledataloading) | |
* | [`sourcedataloading`](#map.event:sourcedataloading) | |
* | [`styleimagemissing`](#map.event:styleimagemissing) | |
*
* @param {string} layerId (optional) The ID of a style layer. Event will only be triggered if its location
* is within a visible feature in this layer. The event will have a `features` property containing
* an array of the matching features. If `layerId` is not supplied, the event will not have a `features` property.
* Please note that many event types are not compatible with the optional `layerId` parameter.
* @param {Function} listener The function to be called when the event is fired.
* @returns {Map} `this`
* @example
* // Set an event listener that will fire
* // when the map has finished loading
* map.on('load', function() {
* // Once the map has finished loading,
* // add a new layer
* map.addLayer({
* id: 'points-of-interest',
* source: {
* type: 'vector',
* url: 'mapbox://mapbox.mapbox-streets-v8'
* },
* 'source-layer': 'poi_label',
* type: 'circle',
* paint: {
* // Mapbox Style Specification paint properties
* },
* layout: {
* // Mapbox Style Specification layout properties
* }
* });
* });
* @example
* // Set an event listener that will fire
* // when a feature on the countries layer of the map is clicked
* map.on('click', 'countries', function(e) {
* new mapboxgl.Popup()
* .setLngLat(e.lngLat)
* .setHTML(`Country name: ${e.features[0].properties.name}`)
* .addTo(map);
* });
* @see [Display popup on click](https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/)
* @see [Center the map on a clicked symbol](https://docs.mapbox.com/mapbox-gl-js/example/center-on-symbol/)
* @see [Create a hover effect](https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/)
* @see [Create a draggable marker](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/)
*/
on(type: MapEvent, layerId: any, listener: any) {
if (listener === undefined) {
return super.on(type, layerId);
}
const delegatedListener = this._createDelegatedListener(type, layerId, listener);
this._delegatedListeners = this._delegatedListeners || {};
this._delegatedListeners[type] = this._delegatedListeners[type] || [];
this._delegatedListeners[type].push(delegatedListener);
for (const event in delegatedListener.delegates) {
this.on((event: any), delegatedListener.delegates[event]);
}
return this;
}
/**
* Adds a listener that will be called only once to a specified event type.
*
* @method
* @name once
* @memberof Map
* @instance
* @param {string} type The event type to add a listener for.
* @param {Function} listener The function to be called when the event is fired.
* The listener function is called with the data object passed to `fire`,
* extended with `target` and `type` properties.
* @returns {Map} `this`
*/
/**
* Adds a listener that will be called only once to a specified event type occurring on features in a specified style layer.
*
* @param {string} type The event type to listen for; one of `'mousedown'`, `'mouseup'`, `'click'`, `'dblclick'`,
* `'mousemove'`, `'mouseenter'`, `'mouseleave'`, `'mouseover'`, `'mouseout'`, `'contextmenu'`, `'touchstart'`,
* `'touchend'`, or `'touchcancel'`. `mouseenter` and `mouseover` events are triggered when the cursor enters
* a visible portion of the specified layer from outside that layer or outside the map canvas. `mouseleave`
* and `mouseout` events are triggered when the cursor leaves a visible portion of the specified layer, or leaves
* the map canvas.
* @param {string} layerId The ID of a style layer. Only events whose location is within a visible
* feature in this layer will trigger the listener. The event will have a `features` property containing
* an array of the matching features.
* @param {Function} listener The function to be called when the event is fired.
* @returns {Map} `this`
*/
once(type: MapEvent, layerId: any, listener: any) {
if (listener === undefined) {
return super.once(type, layerId);
}
const delegatedListener = this._createDelegatedListener(type, layerId, listener);
for (const event in delegatedListener.delegates) {
this.once((event: any), delegatedListener.delegates[event]);
}
return this;
}
/**
* Removes an event listener previously added with `Map#on`.
*
* @method
* @name off
* @memberof Map
* @instance
* @param {string} type The event type previously used to install the listener.
* @param {Function} listener The function previously installed as a listener.
* @returns {Map} `this`
*/
/**
* Removes an event listener for layer-specific events previously added with `Map#on`.
*
* @param {string} type The event type previously used to install the listener.
* @param {string} layerId The layer ID previously used to install the listener.
* @param {Function} listener The function previously installed as a listener.
* @returns {Map} `this`
*/
off(type: MapEvent, layerId: any, listener: any) {
if (listener === undefined) {
return super.off(type, layerId);
}
const removeDelegatedListener = (delegatedListeners) => {
const listeners = delegatedListeners[type];
for (let i = 0; i < listeners.length; i++) {
const delegatedListener = listeners[i];
if (delegatedListener.layer === layerId && delegatedListener.listener === listener) {
for (const event in delegatedListener.delegates) {
this.off((event: any), delegatedListener.delegates[event]);
}
listeners.splice(i, 1);
return this;
}
}
};
if (this._delegatedListeners && this._delegatedListeners[type]) {
removeDelegatedListener(this._delegatedListeners);
}
return this;
}
/**
* Returns an array of [GeoJSON](http://geojson.org/)
* [Feature objects](https://tools.ietf.org/html/rfc7946#section-3.2)
* representing visible features that satisfy the query parameters.
*
* @param {PointLike|Array<PointLike>} [geometry] - The geometry of the query region:
* either a single point or southwest and northeast points describing a bounding box.
* Omitting this parameter (i.e. calling {@link Map#queryRenderedFeatures} with zero arguments,
* or with only a `options` argument) is equivalent to passing a bounding box encompassing the entire
* map viewport.
* @param {Object} [options] Options object.
* @param {Array<string>} [options.layers] An array of [style layer IDs](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layer-id) for the query to inspect.
* Only features within these layers will be returned. If this parameter is undefined, all layers will be checked.
* @param {Array} [options.filter] A [filter](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter)
* to limit query results.
* @param {boolean} [options.validate=true] Whether to check if the [options.filter] conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function.
*
* @returns {Array<Object>} An array of [GeoJSON](http://geojson.org/)
* [feature objects](https://tools.ietf.org/html/rfc7946#section-3.2).
*
* The `properties` value of each returned feature object contains the properties of its source feature. For GeoJSON sources, only
* string and numeric property values are supported (i.e. `null`, `Array`, and `Object` values are not supported).
*
* Each feature includes top-level `layer`, `source`, and `sourceLayer` properties. The `layer` property is an object
* representing the style layer to which the feature belongs. Layout and paint properties in this object contain values
* which are fully evaluated for the given zoom level and feature.
*
* Only features that are currently rendered are included. Some features will **not** be included, like:
*
* - Features from layers whose `visibility` property is `"none"`.
* - Features from layers whose zoom range excludes the current zoom level.
* - Symbol features that have been hidden due to text or icon collision.
*
* Features from all other layers are included, including features that may have no visible
* contribution to the rendered result; for example, because the layer's opacity or color alpha component is set to
* 0.
*
* The topmost rendered feature appears first in the returned array, and subsequent features are sorted by
* descending z-order. Features that are rendered multiple times (due to wrapping across the antimeridian at low
* zoom levels) are returned only once (though subject to the following caveat).
*
* Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
* geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
* times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
* The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
* rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
* will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
* tiles due to tile buffering.
*
* @example
* // Find all features at a point
* var features = map.queryRenderedFeatures(
* [20, 35],
* { layers: ['my-layer-name'] }
* );
*
* @example
* // Find all features within a static bounding box
* var features = map.queryRenderedFeatures(
* [[10, 20], [30, 50]],
* { layers: ['my-layer-name'] }
* );
*
* @example
* // Find all features within a bounding box around a point
* var width = 10;
* var height = 20;
* var features = map.queryRenderedFeatures([
* [point.x - width / 2, point.y - height / 2],
* [point.x + width / 2, point.y + height / 2]
* ], { layers: ['my-layer-name'] });
*
* @example
* // Query all rendered features from a single layer
* var features = map.queryRenderedFeatures({ layers: ['my-layer-name'] });
* @see [Get features under the mouse pointer](https://www.mapbox.com/mapbox-gl-js/example/queryrenderedfeatures/)
* @see [Highlight features within a bounding box](https://www.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/)
* @see [Filter features within map view](https://www.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view/)
*/
queryRenderedFeatures(geometry?: PointLike | [PointLike, PointLike], options?: Object) {
// The first parameter can be omitted entirely, making this effectively an overloaded method
// with two signatures:
//
// queryRenderedFeatures(geometry: PointLike | [PointLike, PointLike], options?: Object)
// queryRenderedFeatures(options?: Object)
//
// There no way to express that in a way that's compatible with both flow and documentation.js.
// Related: https://github.com/facebook/flow/issues/1556
if (!this.style) {
return [];
}
if (options === undefined && geometry !== undefined && !(geometry instanceof Point) && !Array.isArray(geometry)) {
options = (geometry: Object);
geometry = undefined;
}
options = options || {};
geometry = geometry || [[0, 0], [this.transform.width, this.transform.height]];
let queryGeometry;
if (geometry instanceof Point || typeof geometry[0] === 'number') {
queryGeometry = [Point.convert(geometry)];
} else {
const tl = Point.convert(geometry[0]);
const br = Point.convert(geometry[1]);
queryGeometry = [tl, new Point(br.x, tl.y), br, new Point(tl.x, br.y), tl];
}
return this.style.queryRenderedFeatures(queryGeometry, options, this.transform);
}
/**
* Returns an array of [GeoJSON](http://geojson.org/)
* [Feature objects](https://tools.ietf.org/html/rfc7946#section-3.2)
* representing features within the specified vector tile or GeoJSON source that satisfy the query parameters.
*
* @param {string} sourceId The ID of the vector tile or GeoJSON source to query.
* @param {Object} [parameters] Options object.
* @param {string} [parameters.sourceLayer] The name of the [source layer](https://docs.mapbox.com/help/glossary/source-layer/)
* to query. *For vector tile sources, this parameter is required.* For GeoJSON sources, it is ignored.
* @param {Array} [parameters.filter] A [filter](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter)
* to limit query results.
* @param {boolean} [parameters.validate=true] Whether to check if the [parameters.filter] conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function.
*
* @returns {Array<Object>} An array of [GeoJSON](http://geojson.org/)
* [Feature objects](https://tools.ietf.org/html/rfc7946#section-3.2).
*
* In contrast to {@link Map#queryRenderedFeatures}, this function returns all features matching the query parameters,
* whether or not they are rendered by the current style (i.e. visible). The domain of the query includes all currently-loaded
* vector tiles and GeoJSON source tiles: this function does not check tiles outside the currently
* visible viewport.
*
* Because features come from tiled vector data or GeoJSON data that is converted to tiles internally, feature
* geometries may be split or duplicated across tile boundaries and, as a result, features may appear multiple
* times in query results. For example, suppose there is a highway running through the bounding rectangle of a query.
* The results of the query will be those parts of the highway that lie within the map tiles covering the bounding
* rectangle, even if the highway extends into other tiles, and the portion of the highway within each map tile
* will be returned as a separate feature. Similarly, a point feature near a tile boundary may appear in multiple
* tiles due to tile buffering.
*
* @example
* // Find all features in one source layer in a vector source
* var features = map.querySourceFeatures('your-source-id', {
* sourceLayer: 'your-source-layer'
* });
*
* @see [Highlight features containing similar data](https://www.mapbox.com/mapbox-gl-js/example/query-similar-features/)
*/
querySourceFeatures(sourceId: string, parameters: ?{sourceLayer: ?string, filter: ?Array<any>, validate?: boolean}) {
return this.style.querySourceFeatures(sourceId, parameters);
}
/**
* Updates the map's Mapbox style object with a new value.
*
* 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
* against the map's current state and perform only the changes necessary to make the map style match the desired state. Changes in sprites
* (images used for icons and patterns) and glyphs (fonts for label text) **cannot** be diffed. If the sprites or fonts used in the current
* style and the given style are different in any way, the map renderer will force a full update, removing the current style and building
* the given one from scratch.
*
*
* @param style A JSON object conforming to the schema described in the
* [Mapbox Style Specification](https://mapbox.com/mapbox-gl-style-spec/), or a URL to such JSON.
* @param {Object} [options] Options object.
* @param {boolean} [options.diff=true] If false, force a 'full' update, removing the current style
* and building the given one instead of attempting a diff-based update.
* @param {string} [options.localIdeographFontFamily='sans-serif'] Defines a CSS
* font-family for locally overriding generation of glyphs in the 'CJK Unified Ideographs', 'Hiragana', 'Katakana' and 'Hangul Syllables' ranges.
* In these ranges, font settings from the map's style will be ignored, except for font-weight keywords (light/regular/medium/bold).
* Set to `false`, to enable font settings from the map's style for these glyph ranges.
* Forces a full update.
* @returns {Map} `this`
*
* @example
* map.setStyle("mapbox://styles/mapbox/streets-v11");
*
* @see [Change a map's style](https://www.mapbox.com/mapbox-gl-js/example/setstyle/)
*/
setStyle(style: StyleSpecification | string | null, options?: {diff?: boolean} & StyleOptions) {
options = extend({}, {localIdeographFontFamily: this._localIdeographFontFamily}, options);
if ((options.diff !== false && options.localIdeographFontFamily === this._localIdeographFontFamily) && this.style && style) {
this._diffStyle(style, options);
return this;
} else {
this._localIdeographFontFamily = options.localIdeographFontFamily;
return this._updateStyle(style, options);
}
}
_getUIString(key: string) {
const str = this._locale[key];
if (str == null) {
throw new Error(`Missing UI string '${key}'`);
}
return str;
}
_updateStyle(style: StyleSpecification | string | null, options?: {diff?: boolean} & StyleOptions) {
if (this.style) {
this.style.setEventedParent(null);
this.style._remove();
}
if (!style) {
delete this.style;
return this;
} else {
this.style = new Style(this, options || {});
}
this.style.setEventedParent(this, {style: this.style});
if (typeof style === 'string') {
this.style.loadURL(style);
} else {
this.style.loadJSON(style);
}
return this;
}
_lazyInitEmptyStyle() {
if (!this.style) {
this.style = new Style(this, {});
this.style.setEventedParent(this, {style: this.style});
this.style.loadEmpty();
}
}
_diffStyle(style: StyleSpecification | string, options?: {diff?: boolean} & StyleOptions) {
if (typeof style === 'string') {
const url = this._requestManager.normalizeStyleURL(style);
const request = this._requestManager.transformRequest(url, ResourceType.Style);
getJSON(request, (error: ?Error, json: ?Object) => {
if (error) {
this.fire(new ErrorEvent(error));
} else if (json) {
this._updateDiff(json, options);
}
});
} else if (typeof style === 'object') {
this._updateDiff(style, options);
}
}
_updateDiff(style: StyleSpecification, options?: {diff?: boolean} & StyleOptions) {
try {
if (this.style.setState(style)) {
this._update(true);
}
} catch (e) {
warnOnce(
`Unable to perform style diff: ${e.message || e.error || e}. Rebuilding the style from scratch.`
);
this._updateStyle(style, options);
}
}
/**
* Returns the map's Mapbox [style](https://docs.mapbox.com/help/glossary/style/) object, a JSON object which can be used to recreate the map's style.
*
* @returns {Object} The map's style JSON object.
*
* @example
* var styleJson = map.getStyle();
*
*/
getStyle() {
if (this.style) {
return this.style.serialize();
}
}
/**
* Returns a Boolean indicating whether the map's style is fully loaded.
*
* @returns {boolean} A Boolean indicating whether the style is fully loaded.
*
* @example
* var styleLoadStatus = map.isStyleLoaded();
*/
isStyleLoaded() {
if (!this.style) return warnOnce('There is no style added to the map.');
return this.style.loaded();
}
/**
* Adds a source to the map's style.
*
* @param {string} id The ID of the source to add. Must not conflict with existing sources.
* @param {Object} source The source object, conforming to the
* Mapbox Style Specification's [source definition](https://www.mapbox.com/mapbox-gl-style-spec/#sources) or
* {@link CanvasSourceOptions}.
* @fires source.add
* @returns {Map} `this`
* @example
* map.addSource('my-data', {
* type: 'vector',
* url: 'mapbox://myusername.tilesetid'
* });
* @example
* map.addSource('my-data', {
* "type": "geojson",
* "data": {
* "type": "Feature",
* "geometry": {
* "type": "Point",
* "coordinates": [-77.0323, 38.9131]
* },
* "properties": {
* "title": "Mapbox DC",
* "marker-symbol": "monument"
* }
* }
* });
* @see Vector source: [Show and hide layers](https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/)
* @see GeoJSON source: [Add live realtime data](https://docs.mapbox.com/mapbox-gl-js/example/live-geojson/)
* @see Raster DEM source: [Add hillshading](https://docs.mapbox.com/mapbox-gl-js/example/hillshade/)
*/
addSource(id: string, source: SourceSpecification) {
this._lazyInitEmptyStyle();
this.style.addSource(id, source);
return this._update(true);
}
/**
* Returns a Boolean indicating whether the source is loaded. Returns `true` if the source with
* the given ID in the map's style has no outstanding network requests, otherwise `false`.
*
* @param {string} id The ID of the source to be checked.
* @returns {boolean} A Boolean indicating whether the source is loaded.
* @example
* var sourceLoaded = map.isSourceLoaded('bathymetry-data');
*/
isSourceLoaded(id: string) {
const source = this.style && this.style.sourceCaches[id];
if (source === undefined) {
this.fire(new ErrorEvent(new Error(`There is no source with ID '${id}'`)));
return;
}
return source.loaded();
}
/**
* Returns a Boolean indicating whether all tiles in the viewport from all sources on
* the style are loaded.
*
* @returns {boolean} A Boolean indicating whether all tiles are loaded.
* @example
* var tilesLoaded = map.areTilesLoaded();
*/
areTilesLoaded() {
const sources = this.style && this.style.sourceCaches;
for (const id in sources) {
const source = sources[id];
const tiles = source._tiles;
for (const t in tiles) {
const tile = tiles[t];
if (!(tile.state === 'loaded' || tile.state === 'errored')) return false;
}
}
return true;
}
/**
* Adds a [custom source type](#Custom Sources), making it available for use with
* {@link Map#addSource}.
* @private
* @param {string} name The name of the source type; source definition objects use this name in the `{type: ...}` field.
* @param {Function} SourceType A {@link Source} constructor.
* @param {Function} callback Called when the source type is ready or with an error argument if there is an error.
*/
addSourceType(name: string, SourceType: any, callback: Function) {
this._lazyInitEmptyStyle();
return this.style.addSourceType(name, SourceType, callback);
}
/**
* Removes a source from the map's style.
*
* @param {string} id The ID of the source to remove.
* @returns {Map} `this`
* @example
* map.removeSource('bathymetry-data');
*/
removeSource(id: string) {
this.style.removeSource(id);
return this._update(true);
}
/**
* Returns the source with the specified ID in the map's style.
*
* This method is often used to update a source using the instance members for the relevant
* source type as defined in [Sources](#sources).
* For example, setting the `data` for a GeoJSON source or updating the `url` and `coordinates`
* of an image source.
*
* @param {string} id The ID of the source to get.
* @returns {?Object} The style source with the specified ID or `undefined` if the ID
* corresponds to no existing sources.
* The shape of the object varies by source type.
* A list of options for each source type is available on the Mapbox Style Specification's
* [Sources](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/) page.
* @example
* var sourceObject = map.getSource('points');
* @see [Create a draggable point](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-point/)
* @see [Animate a point](https://docs.mapbox.com/mapbox-gl-js/example/animate-point-along-line/)
* @see [Add live realtime data](https://docs.mapbox.com/mapbox-gl-js/example/live-geojson/)
*/
getSource(id: string) {
return this.style.getSource(id);
}
// eslint-disable-next-line jsdoc/require-returns
/**
* Add an image to the style. This image can be displayed on the map like any other icon in the style's
* [sprite](https://docs.mapbox.com/help/glossary/sprite/) using the image's ID with
* [`icon-image`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-icon-image),
* [`background-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-background-background-pattern),
* [`fill-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-fill-fill-pattern),
* or [`line-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-line-line-pattern).
* A {@link Map.event:error} event will be fired if there is not enough space in the sprite to add this image.
*
* @param id The ID of the image.
* @param image The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
* properties with the same format as `ImageData`.
* @param options Options object.
* @param options.pixelRatio The ratio of pixels in the image to physical pixels on the screen
* @param options.sdf Whether the image should be interpreted as an SDF image
* @param options.content `[x1, y1, x2, y2]` If `icon-text-fit` is used in a layer with this image, this option defines the part of the image that can be covered by the content in `text-field`.
* @param options.stretchX `[[x1, x2], ...]` If `icon-text-fit` is used in a layer with this image, this option defines the part(s) of the image that can be stretched horizontally.
* @param options.stretchY `[[y1, y2], ...]` If `icon-text-fit` is used in a layer with this image, this option defines the part(s) of the image that can be stretched vertically.
*
* @example
* // If the style's sprite does not already contain an image with ID 'cat',
* // add the image 'cat-icon.png' to the style's sprite with the ID 'cat'.
* map.loadImage('https://upload.wikimedia.org/wikipedia/commons/thumb/6/60/Cat_silhouette.svg/400px-Cat_silhouette.svg.png', function(error, image) {
* if (error) throw error;
* if (!map.hasImage('cat')) map.addImage('cat', image);
* });
*
*
* // Add a stretchable image that can be used with `icon-text-fit`
* // In this example, the image is 600px wide by 400px high.
* map.loadImage('https://upload.wikimedia.org/wikipedia/commons/8/89/Black_and_White_Boxed_%28bordered%29.png', function(error, image) {
* if (error) throw error;
* if (!map.hasImage('border-image')) {
* map.addImage('border-image', image, {
* content: [16, 16, 300, 384], // place text over left half of image, avoiding the 16px border
* stretchX: [[16, 584]], // stretch everything horizontally except the 16px border
* stretchY: [[16, 384]], // stretch everything vertically except the 16px border
* });
* }
* });
*
*
* @see Use `HTMLImageElement`: [Add an icon to the map](https://www.mapbox.com/mapbox-gl-js/example/add-image/)
* @see Use `ImageData`: [Add a generated icon to the map](https://www.mapbox.com/mapbox-gl-js/example/add-image-generated/)
*/
addImage(id: string,
image: HTMLImageElement | ImageBitmap | ImageData | {width: number, height: number, data: Uint8Array | Uint8ClampedArray} | StyleImageInterface,
{pixelRatio = 1, sdf = false, stretchX, stretchY, content}: $Shape<StyleImageMetadata> = {}) {
this._lazyInitEmptyStyle();
const version = 0;
if (image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap)) {
const {width, height, data} = browser.getImageData(image);
this.style.addImage(id, {data: new RGBAImage({width, height}, data), pixelRatio, stretchX, stretchY, content, sdf, version});
} else if (image.width === undefined || image.height === undefined) {
return this.fire(new ErrorEvent(new Error(
'Invalid arguments to map.addImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' +
'or object with `width`, `height`, and `data` properties with the same format as `ImageData`')));
} else {
const {width, height, data} = image;
const userImage = ((image: any): StyleImageInterface);
this.style.addImage(id, {
data: new RGBAImage({width, height}, new Uint8Array(data)),
pixelRatio,
stretchX,
stretchY,
content,
sdf,
version,
userImage
});
if (userImage.onAdd) {
userImage.onAdd(this, id);
}
}
}
// eslint-disable-next-line jsdoc/require-returns
/**
* Update an existing image in a style. This image can be displayed on the map like any other icon in the style's
* [sprite](https://docs.mapbox.com/help/glossary/sprite/) using the image's ID with
* [`icon-image`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layout-symbol-icon-image),
* [`background-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-background-background-pattern),
* [`fill-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-fill-fill-pattern),
* or [`line-pattern`](https://docs.mapbox.com/mapbox-gl-js/style-spec/#paint-line-line-pattern).
*
* @param id The ID of the image.
* @param image The image as an `HTMLImageElement`, `ImageData`, `ImageBitmap` or object with `width`, `height`, and `data`
* properties with the same format as `ImageData`.
*
* @example
* // If an image with the ID 'cat' already exists in the style's sprite,
* // replace that image with a new image, 'other-cat-icon.png'.
* if (map.hasImage('cat')) map.updateImage('cat', './other-cat-icon.png');
*/
updateImage(id: string,
image: HTMLImageElement | ImageBitmap | ImageData | {width: number, height: number, data: Uint8Array | Uint8ClampedArray} | StyleImageInterface) {
const existingImage = this.style.getImage(id);
if (!existingImage) {
return this.fire(new ErrorEvent(new Error(
'The map has no image with that id. If you are adding a new image use `map.addImage(...)` instead.')));
}
const imageData = (image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap)) ? browser.getImageData(image) : image;
const {width, height, data} = imageData;
if (width === undefined || height === undefined) {
return this.fire(new ErrorEvent(new Error(
'Invalid arguments to map.updateImage(). The second argument must be an `HTMLImageElement`, `ImageData`, `ImageBitmap`, ' +
'or object with `width`, `height`, and `data` properties with the same format as `ImageData`')));
}
if (width !== existingImage.data.width || height !== existingImage.data.height) {
return this.fire(new ErrorEvent(new Error(
'The width and height of the updated image must be that same as the previous version of the image')));
}
const copy = !(image instanceof HTMLImageElement || (ImageBitmap && image instanceof ImageBitmap));
existingImage.data.replace(data, copy);
this.style.updateImage(id, existingImage);
}
/**
* Check whether or not an image with a specific ID exists in the style. This checks both images
* in the style's original [sprite](https://docs.mapbox.com/help/glossary/sprite/) and any images
* that have been added at runtime using {@link Map#addImage}.
*
* @param id The ID of the image.
*
* @returns {boolean} A Boolean indicating whether the image exists.
* @example
* // Check if an image with the ID 'cat' exists in
* // the style's sprite.
* var catIconExists = map.hasImage('cat');
*/
hasImage(id: string): boolean {
if (!id) {
this.fire(new ErrorEvent(new Error('Missing required image id')));
return false;
}
return !!this.style.getImage(id);
}
/**
* Remove an image from a style. This can be an image from the style's original
* [sprite](https://docs.mapbox.com/help/glossary/sprite/) or any images
* that have been added at runtime using {@link Map#addImage}.
*
* @param id The ID of the image.
*
* @example
* // If an image with the ID 'cat' exists in
* // the style's sprite, remove it.
* if (map.hasImage('cat')) map.removeImage('cat');
*/
removeImage(id: string) {
this.style.removeImage(id);
}
/**
* Load an image from an external URL to be used with {@link Map#addImage}. External
* domains must support [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS).
*
* @param {string} url The URL of the image file. Image file must be in png, webp, or jpg format.
* @param {Function} callback Expecting `callback(error, data)`. Called when the image has loaded or with an error argument if there is an error.
*
* @example
* // Load an image from an external URL.
* map.loadImage('http://placekitten.com/50/50', function(error, image) {
* if (error) throw error;
* // Add the loaded image to the style's sprite with the ID 'kitten'.
* map.addImage('kitten', image);
* });
*
* @see [Add an icon to the map](https://www.mapbox.com/mapbox-gl-js/example/add-image/)
*/
loadImage(url: string, callback: Function) {
getImage(this._requestManager.transformRequest(url, ResourceType.Image), callback);
}
/**
* Returns an Array of strings containing the IDs of all images currently available in the map.
* This includes both images from the style's original [sprite](https://docs.mapbox.com/help/glossary/sprite/)
* and any images that have been added at runtime using {@link Map#addImage}.
*
* @returns {Array<string>} An Array of strings containing the names of all sprites/images currently available in the map.
*
* @example
* var allImages = map.listImages();
*
*/
listImages() {
return this.style.listImages();
}
/**
* Adds a [Mapbox style layer](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers)
* to the map's style.
*
* A layer defines how data from a specified source will be styled. Read more about layer types
* and available paint and layout properties in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers).
*
* @param {Object | CustomLayerInterface} layer The layer to add, conforming to either the Mapbox Style Specification's [layer definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layers) or, less commonly, the {@link CustomLayerInterface} specification.
* The Mapbox Style Specification's layer definition is appropriate for most layers.
*
* @param {string} layer.id A unique idenfier that you define.
* @param {string} layer.type The type of layer (for example `fill` or `symbol`).
* A list of layer types is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#type).
*
* (This can also be `custom`. For more information, see {@link CustomLayerInterface}.)
* @param {string | Object} [layer.source] The data source for the layer.
* Reference a source that has _already been defined_ using the source's unique id.
* Reference a _new source_ using a source object (as defined in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/)) directly.
* This is **required** for all `layer.type` options _except_ for `custom`.
* @param {string} [layer.sourceLayer] (optional) The name of the [source layer](https://docs.mapbox.com/help/glossary/source-layer/) within the specified `layer.source` to use for this style layer.
* This is only applicable for vector tile sources and is **required** when `layer.source` is of the type `vector`.
* @param {array} [layer.filter] (optional) An expression specifying conditions on source features.
* Only features that match the filter are displayed.
* The Mapbox Style Specification includes more information on the limitations of the [`filter`](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter) parameter
* and a complete list of available [expressions](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/).
* If no filter is provided, all features in the source (or source layer for vector tilesets) will be displayed.
* @param {Object} [layer.paint] (optional) Paint properties for the layer.
* Available paint properties vary by `layer.type`.
* A full list of paint properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/).
* If no paint properties are specified, default values will be used.
* @param {Object} [layer.layout] (optional) Layout properties for the layer.
* Available layout properties vary by `layer.type`.
* A full list of layout properties for each layer type is available in the [Mapbox Style Specification](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/).
* If no layout properties are specified, default values will be used.
* @param {number} [layer.maxzoom] (optional) The maximum zoom level for the layer.
* At zoom levels equal to or greater than the maxzoom, the layer will be hidden.
* The value can be any number between `0` and `24` (inclusive).
* If no maxzoom is provided, the layer will be visible at all zoom levels for which there are tiles available.
* @param {number} [layer.minzoom] (optional) The minimum zoom level for the layer.
* At zoom levels less than the minzoom, the layer will be hidden.
* The value can be any number between `0` and `24` (inclusive).
* If no minzoom is provided, the layer will be visible at all zoom levels for which there are tiles available.
* @param {Object} [layer.metadata] (optional) Arbitrary properties useful to track with the layer, but do not influence rendering.
* @param {string} [layer.renderingMode] This is only applicable for layers with the type `custom`.
* See {@link CustomLayerInterface} for more information.
* @param {string} [beforeId] The ID of an existing layer to insert the new layer before,
* resulting in the new layer appearing visually beneath the existing layer.
* If this argument is not specified, the layer will be appended to the end of the layers array
* and appear visually above all other layers.
*
* @returns {Map} `this`
*
* @example
* // Add a circle layer with a vector source
* map.addLayer({
* id: 'points-of-interest',
* source: {
* type: 'vector',
* url: 'mapbox://mapbox.mapbox-streets-v8'
* },
* 'source-layer': 'poi_label',
* type: 'circle',
* paint: {
* // Mapbox Style Specification paint properties
* },
* layout: {
* // Mapbox Style Specification layout properties
* }
* });
*
* @example
* // Define a source before using it to create a new layer
* map.addSource('state-data', {
* type: 'geojson',
* data: 'path/to/data.geojson'
* });
*
* map.addLayer({
* id: 'states',
* // References the GeoJSON source defined above
* // and does not require a `source-layer`
* source: 'state-data',
* type: 'symbol',
* layout: {
* // Set the label content to the
* // feature's `name` property
* text-field: ['get', 'name']
* }
* });
*
* @example
* // Add a new symbol layer before an existing layer
* map.addLayer({
* id: 'states',
* // References a source that's already been defined
* source: 'state-data',
* type: 'symbol',
* layout: {
* // Set the label content to the
* // feature's `name` property
* text-field: ['get', 'name']
* }
* // Add the layer before the existing `cities` layer
* }, 'cities');
*
* @see [Create and style clusters](https://docs.mapbox.com/mapbox-gl-js/example/cluster/)
* @see [Add a vector tile source](https://docs.mapbox.com/mapbox-gl-js/example/vector-source/)
* @see [Add a WMS source](https://docs.mapbox.com/mapbox-gl-js/example/wms/)
*/
addLayer(layer: LayerSpecification | CustomLayerInterface, beforeId?: string) {
this._lazyInitEmptyStyle();
this.style.addLayer(layer, beforeId);
return this._update(true);
}
/**
* Moves a layer to a different z-position.
*
* @param {string} id The ID of the layer to move.
* @param {string} [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.
* @returns {Map} `this`
*
* @example
* // 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.
* map.moveLayer('polygon', 'country-label');
*/
moveLayer(id: string, beforeId?: string) {
this.style.moveLayer(id, beforeId);
return this._update(true);
}
// eslint-disable-next-line jsdoc/require-returns
/**
* Removes the layer with the given ID from the map's style.
*
* If no such layer exists, an `error` event is fired.
*
* @param {string} id id of the layer to remove
* @fires error
*
* @example
* // If a layer with ID 'state-data' exists, remove it.
* if (map.getLayer('state-data')) map.removeLayer('state-data');
*/
removeLayer(id: string) {
this.style.removeLayer(id);
return this._update(true);
}
/**
* Returns the layer with the specified ID in the map's style.
*
* @param {string} id The ID of the layer to get.
* @returns {?Object} The layer with the specified ID, or `undefined`
* if the ID corresponds to no existing layers.
*
* @example
* var stateDataLayer = map.getLayer('state-data');
*
* @see [Filter symbols by toggling a list](https://www.mapbox.com/mapbox-gl-js/example/filter-markers/)
* @see [Filter symbols by text input](https://www.mapbox.com/mapbox-gl-js/example/filter-markers-by-input/)
*/
getLayer(id: string) {
return this.style.getLayer(id);
}
/**
* Sets the zoom extent for the specified style layer. The zoom extent includes the
* [minimum zoom level](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layer-minzoom)
* and [maximum zoom level](https://docs.mapbox.com/mapbox-gl-js/style-spec/#layer-maxzoom))
* at which the layer will be rendered.
*
* Note: For style layers using vector sources, style layers cannot be rendered at zoom levels lower than the
* minimum zoom level of the _source layer_ because the data does not exist at those zoom levels. If the minimum
* zoom level of the source layer is higher than the minimum zoom level defined in the style layer, the style
* layer will not be rendered at all zoom levels in the zoom range.
*
* @param {string} layerId The ID of the layer to which the zoom extent will be applied.
* @param {number} minzoom The minimum zoom to set (0-24).
* @param {number} maxzoom The maximum zoom to set (0-24).
* @returns {Map} `this`
*
* @example
* map.setLayerZoomRange('my-layer', 2, 5);
*
*/
setLayerZoomRange(layerId: string, minzoom: number, maxzoom: number) {
this.style.setLayerZoomRange(layerId, minzoom, maxzoom);
return this._update(true);
}
/**
* Sets the filter for the specified style layer.
*
* Filters control which features a style layer renders from its source.
* Any feature for which the filter expression evaluates to `true` will be
* rendered on the map. Those that are false will be hidden.
*
* Use `setFilter` to show a subset of your source data.
*
* To clear the filter, pass `null` or `undefined` as the second parameter.
*
* @param {string} layerId The ID of the layer to which the filter will be applied.
* @param {Array | null | undefined} filter The filter, conforming to the Mapbox Style Specification's
* [filter definition](https://docs.mapbox.com/mapbox-gl-js/style-spec/layers/#filter). If `null` or `undefined` is provided, the function removes any existing filter from the layer.
* @param {Object} [options] Options object.
* @param {boolean} [options.validate=true] Whether to check if the filter conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function.
* @returns {Map} `this`
*
* @example
* // display only features with the 'name' property 'USA'
* map.setFilter('my-layer', ['==', ['get', 'name'], 'USA']);
* @example
* // display only features with five or more 'available-spots'
* map.setFilter('bike-docks', ['>=', ['get', 'available-spots'], 5]);
* @example
* // remove the filter for the 'bike-docks' style layer
* map.setFilter('bike-docks', null);
*
* @see [Filter features within map view](https://www.mapbox.com/mapbox-gl-js/example/filter-features-within-map-view/)
* @see [Highlight features containing similar data](https://www.mapbox.com/mapbox-gl-js/example/query-similar-features/)
* @see [Create a timeline animation](https://www.mapbox.com/mapbox-gl-js/example/timeline-animation/)
* @see Tutorial: [Show changes over time](https://docs.mapbox.com/help/tutorials/show-changes-over-time/)
*/
setFilter(layerId: string, filter: ?FilterSpecification, options: StyleSetterOptions = {}) {
this.style.setFilter(layerId, filter, options);
return this._update(true);
}
/**
* Returns the filter applied to the specified style layer.
*
* @param {string} layerId The ID of the style layer whose filter to get.
* @returns {Array} The layer's filter.
*/
getFilter(layerId: string) {
return this.style.getFilter(layerId);
}
/**
* Sets the value of a paint property in the specified style layer.
*
* @param {string} layerId The ID of the layer to set the paint property in.
* @param {string} name The name of the paint property to set.
* @param {*} value The value of the paint property to set.
* Must be of a type appropriate for the property, as defined in the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/).
* @param {Object} [options] Options object.
* @param {boolean} [options.validate=true] Whether to check if `value` conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function.
* @returns {Map} `this`
* @example
* map.setPaintProperty('my-layer', 'fill-color', '#faafee');
* @see [Change a layer's color with buttons](https://www.mapbox.com/mapbox-gl-js/example/color-switcher/)
* @see [Adjust a layer's opacity](https://www.mapbox.com/mapbox-gl-js/example/adjust-layer-opacity/)
* @see [Create a draggable point](https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/)
*/
setPaintProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}) {
this.style.setPaintProperty(layerId, name, value, options);
return this._update(true);
}
/**
* Returns the value of a paint property in the specified style layer.
*
* @param {string} layerId The ID of the layer to get the paint property from.
* @param {string} name The name of a paint property to get.
* @returns {*} The value of the specified paint property.
*/
getPaintProperty(layerId: string, name: string) {
return this.style.getPaintProperty(layerId, name);
}
/**
* Sets the value of a layout property in the specified style layer.
*
* @param {string} layerId The ID of the layer to set the layout property in.
* @param {string} name The name of the layout property to set.
* @param {*} value The value of the layout property. Must be of a type appropriate for the property, as defined in the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/).
* @param {Object} [options] Options object.
* @param {boolean} [options.validate=true] Whether to check if `value` conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function.
* @returns {Map} `this`
* @example
* map.setLayoutProperty('my-layer', 'visibility', 'none');
* @see [Show and hide layers](https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/)
*/
setLayoutProperty(layerId: string, name: string, value: any, options: StyleSetterOptions = {}) {
this.style.setLayoutProperty(layerId, name, value, options);
return this._update(true);
}
/**
* Returns the value of a layout property in the specified style layer.
*
* @param {string} layerId The ID of the layer to get the layout property from.
* @param {string} name The name of the layout property to get.
* @returns {*} The value of the specified layout property.
*/
getLayoutProperty(layerId: string, name: string) {
return this.style.getLayoutProperty(layerId, name);
}
/**
* Sets the any combination of light values.
*
* @param light Light properties to set. Must conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/#light).
* @param {Object} [options] Options object.
* @param {boolean} [options.validate=true] Whether to check if the filter conforms to the Mapbox GL Style Specification. Disabling validation is a performance optimization that should only be used if you have previously validated the values you will be passing to this function.
* @returns {Map} `this`
* @example
* var layerVisibility = map.getLayoutProperty('my-layer', 'visibility');
* @see [Show and hide layers](https://docs.mapbox.com/mapbox-gl-js/example/toggle-layers/)
*/
setLight(light: LightSpecification, options: StyleSetterOptions = {}) {
this._lazyInitEmptyStyle();
this.style.setLight(light, options);
return this._update(true);
}
/**
* Returns the value of the light object.
*
* @returns {Object} light Light properties of the style.
*/
getLight() {
return this.style.getLight();
}
// eslint-disable-next-line jsdoc/require-returns
/**
* Sets the `state` of a feature.
* A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
* When using this method, the `state` object is merged with any existing key-value pairs in the feature's state.
* Features are identified by their `feature.id` attribute, which can be any number or string.
*
* This method can only be used with sources that have a `feature.id` attribute. The `feature.id` attribute can be defined in three ways:
* - For vector or GeoJSON sources, including an `id` attribute in the original data file.
* - For vector or GeoJSON sources, using the [`promoteId`](https://docs.mapbox.com/mapbox-gl-js/style-spec/sources/#vector-promoteId) option at the time the source is defined.
* - For GeoJSON sources, using the [`generateId`](https://docs.mapbox.com/mapbox-gl-js/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.
*
* _Note: You can use the [`feature-state` expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#feature-state) to access the values in a feature's state object for the purposes of styling._
*
* @param {Object} feature Feature identifier. Feature objects returned from
* {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers.
* @param {string | number} feature.id Unique id of the feature.
* @param {string} feature.source The id of the vector or GeoJSON source for the feature.
* @param {string} [feature.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required.*
* @param {Object} state A set of key-value pairs. The values should be valid JSON types.
*
* @example
* // When the mouse moves over the `my-layer` layer, update
* // the feature state for the feature under the mouse
* map.on('mousemove', 'my-layer', function(e) {
* if (e.features.length > 0) {
* map.setFeatureState({
* source: 'my-source',
* sourceLayer: 'my-source-layer',
* id: e.features[0].id,
* }, {
* hover: true
* });
* }
* });
*
* @see [Create a hover effect](https://docs.mapbox.com/mapbox-gl-js/example/hover-styles/)
* @see Tutorial: [Create interactive hover effects with Mapbox GL JS](https://docs.mapbox.com/help/tutorials/create-interactive-hover-effects-with-mapbox-gl-js/)
*/
setFeatureState(feature: { source: string; sourceLayer?: string; id: string | number; }, state: Object) {
this.style.setFeatureState(feature, state);
return this._update();
}
// eslint-disable-next-line jsdoc/require-returns
/**
* Removes the `state` of a feature, setting it back to the default behavior.
* If only a `target.source` is specified, it will remove the state for all features from that source.
* If `target.id` is also specified, it will remove all keys for that feature's state.
* If `key` is also specified, it removes only that key from that feature's state.
* Features are identified by their `feature.id` attribute, which can be any number or string.
*
* @param {Object} target Identifier of where to remove state. It can be a source, a feature, or a specific key of feature.
* Feature objects returned from {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers.
* @param {string | number} target.id (optional) Unique id of the feature. Optional if key is not specified.
* @param {string} target.source The id of the vector or GeoJSON source for the feature.
* @param {string} [target.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required.*
* @param {string} key (optional) The key in the feature state to reset.
*
* @example
* // Reset the entire state object for all features
* // in the `my-source` source
* map.removeFeatureState({
* source: 'my-source'
* });
*
* @example
* // When the mouse leaves the `my-layer` layer,
* // reset the entire state object for the
* // feature under the mouse
* map.on('mouseleave', 'my-layer', function(e) {
* map.removeFeatureState({
* source: 'my-source',
* sourceLayer: 'my-source-layer',
* id: e.features[0].id
* });
* });
*
* @example
* // When the mouse leaves the `my-layer` layer,
* // reset only the `hover` key-value pair in the
* // state for the feature under the mouse
* map.on('mouseleave', 'my-layer', function(e) {
* map.removeFeatureState({
* source: 'my-source',
* sourceLayer: 'my-source-layer',
* id: e.features[0].id
* }, 'hover');
* });
*
*/
removeFeatureState(target: { source: string; sourceLayer?: string; id?: string | number; }, key?: string) {
this.style.removeFeatureState(target, key);
return this._update();
}
/**
* Gets the `state` of a feature.
* A feature's `state` is a set of user-defined key-value pairs that are assigned to a feature at runtime.
* Features are identified by their `feature.id` attribute, which can be any number or string.
*
* _Note: To access the values in a feature's state object for the purposes of styling the feature, use the [`feature-state` expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#feature-state)._
*
* @param {Object} feature Feature identifier. Feature objects returned from
* {@link Map#queryRenderedFeatures} or event handlers can be used as feature identifiers.
* @param {string | number} feature.id Unique id of the feature.
* @param {string} feature.source The id of the vector or GeoJSON source for the feature.
* @param {string} [feature.sourceLayer] (optional) *For vector tile sources, `sourceLayer` is required.*
*
* @returns {Object} The state of the feature: a set of key-value pairs that was assigned to the feature at runtime.
*
* @example
* // When the mouse moves over the `my-layer` layer,
* // get the feature state for the feature under the mouse
* map.on('mousemove', 'my-layer', function(e) {
* if (e.features.length > 0) {
* map.getFeatureState({
* source: 'my-source',
* sourceLayer: 'my-source-layer',
* id: e.features[0].id
* });
* }
* });
*
*/
getFeatureState(feature: { source: string; sourceLayer?: string; id: string | number; }): any {
return this.style.getFeatureState(feature);
}
/**
* Returns the map's containing HTML element.
*
* @returns {HTMLElement} The map's container.
*/
getContainer() {
return this._container;
}
/**
* Returns the HTML element containing the map's `<canvas>` element.
*
* If you want to add non-GL overlays to the map, you should append them to this element.
*
* This is the element to which event bindings for map interactivity (such as panning and zooming) are
* attached. It will receive bubbled events from child elements such as the `<canvas>`, but not from
* map controls.
*
* @returns {HTMLElement} The container of the map's `<canvas>`.
* @see [Create a draggable point](https://www.mapbox.com/mapbox-gl-js/example/drag-a-point/)
* @see [Highlight features within a bounding box](https://www.mapbox.com/mapbox-gl-js/example/using-box-queryrenderedfeatures/)
*/
getCanvasContainer() {
return this._canvasContainer;
}
/**
* Returns the map's `<canvas>` element.
*
* @returns {HTMLCanvasElement} The map's `<canvas>` element.
* @see [Measure distances](https://www.mapbox.com/mapbox-gl-js/example/measure/)
* @see [Display a popup on hover](https://www.mapbox.com/mapbox-gl-js/example/popup-on-hover/)
* @see [Center the map on a clicked symbol](https://www.mapbox.com/mapbox-gl-js/example/center-on-symbol/)
*/
getCanvas() {
return this._canvas;
}
_containerDimensions() {
let width = 0;
let height = 0;
if (this._container) {
width = this._container.clientWidth || 400;
height = this._container.clientHeight || 300;
}
return [width, height];
}
_detectMissingCSS(): void {
const computedColor = window.getComputedStyle(this._missingCSSCanary).getPropertyValue('background-color');
if (computedColor !== 'rgb(250, 128, 114)') {
warnOnce('This page appears to be missing CSS declarations for ' +
'Mapbox GL JS, which may cause the map to display incorrectly. ' +
'Please ensure your page includes mapbox-gl.css, as described ' +
'in https://www.mapbox.com/mapbox-gl-js/api/.');
}
}
_setupContainer() {
const container = this._container;
container.classList.add('mapboxgl-map');
const missingCSSCanary = this._missingCSSCanary = DOM.create('div', 'mapboxgl-canary', container);
missingCSSCanary.style.visibility = 'hidden';
this._detectMissingCSS();
const canvasContainer = this._canvasContainer = DOM.create('div', 'mapboxgl-canvas-container', container);
if (this._interactive) {
canvasContainer.classList.add('mapboxgl-interactive');
}
this._canvas = DOM.create('canvas', 'mapboxgl-canvas', canvasContainer);
this._canvas.addEventListener('webglcontextlost', this._contextLost, false);
this._canvas.addEventListener('webglcontextrestored', this._contextRestored, false);
this._canvas.setAttribute('tabindex', '0');
this._canvas.setAttribute('aria-label', 'Map');
this._canvas.setAttribute('role', 'region');
const dimensions = this._containerDimensions();
this._resizeCanvas(dimensions[0], dimensions[1]);
const controlContainer = this._controlContainer = DOM.create('div', 'mapboxgl-control-container', container);
const positions = this._controlPositions = {};
['top-left', 'top-right', 'bottom-left', 'bottom-right'].forEach((positionName) => {
positions[positionName] = DOM.create('div', `mapboxgl-ctrl-${positionName}`, controlContainer);
});
this._container.addEventListener('scroll', this._onMapScroll, false);
}
_resizeCanvas(width: number, height: number) {
const pixelRatio = browser.devicePixelRatio || 1;
// Request the required canvas size taking the pixelratio into account.
this._canvas.width = pixelRatio * width;
this._canvas.height = pixelRatio * height;
// Maintain the same canvas size, potentially downscaling it for HiDPI displays
this._canvas.style.width = `${width}px`;
this._canvas.style.height = `${height}px`;
}
_setupPainter() {
const attributes = extend({}, isSupported.webGLContextAttributes, {
failIfMajorPerformanceCaveat: this._failIfMajorPerformanceCaveat,
preserveDrawingBuffer: this._preserveDrawingBuffer,
antialias: this._antialias || false
});
const gl = this._canvas.getContext('webgl', attributes) ||
this._canvas.getContext('experimental-webgl', attributes);
if (!gl) {
this.fire(new ErrorEvent(new Error('Failed to initialize WebGL')));
return;
}
this.painter = new Painter(gl, this.transform);
webpSupported.testSupport(gl);
}
_contextLost(event: *) {
event.preventDefault();
if (this._frame) {
this._frame.cancel();
this._frame = null;
}
this.fire(new Event('webglcontextlost', {originalEvent: event}));
}
_contextRestored(event: *) {
this._setupPainter();
this.resize();
this._update();
this.fire(new Event('webglcontextrestored', {originalEvent: event}));
}
_onMapScroll(event: *) {
if (event.target !== this._container) return;
// Revert any scroll which would move the canvas outside of the view
this._container.scrollTop = 0;
this._container.scrollLeft = 0;
return false;
}
/**
* Returns a Boolean indicating whether the map is fully loaded.
*
* Returns `false` if the style is not yet fully loaded,
* or if there has been a change to the sources or style that
* has not yet fully loaded.
*
* @returns {boolean} A Boolean indicating whether the map is fully loaded.
*/
loaded() {
return !this._styleDirty && !this._sourcesDirty && !!this.style && this.style.loaded();
}
/**
* Update this map's style and sources, and re-render the map.
*
* @param {boolean} updateStyle mark the map's style for reprocessing as
* well as its sources
* @returns {Map} this
* @private
*/
_update(updateStyle?: boolean) {
if (!this.style) return this;
this._styleDirty = this._styleDirty || updateStyle;
this._sourcesDirty = true;
this.triggerRepaint();
return this;
}
/**
* Request that the given callback be executed during the next render
* frame. Schedule a render frame if one is not already scheduled.
* @returns An id that can be used to cancel the callback
* @private
*/
_requestRenderFrame(callback: () => void): TaskID {
this._update();
return this._renderTaskQueue.add(callback);
}
_cancelRenderFrame(id: TaskID) {
this._renderTaskQueue.remove(id);
}
/**
* Call when a (re-)render of the map is required:
* - The style has changed (`setPaintProperty()`, etc.)
* - Source data has changed (e.g. tiles have finished loading)
* - The map has is moving (or just finished moving)
* - A transition is in progress
*
* @param {number} paintStartTimeStamp The time when the animation frame began executing.
*
* @returns {Map} this
* @private
*/
_render(paintStartTimeStamp: number) {
let gpuTimer, frameStartTime = 0;
const extTimerQuery = this.painter.context.extTimerQuery;
if (this.listens('gpu-timing-frame')) {
gpuTimer = extTimerQuery.createQueryEXT();
extTimerQuery.beginQueryEXT(extTimerQuery.TIME_ELAPSED_EXT, gpuTimer);
frameStartTime = browser.now();
}
// A custom layer may have used the context asynchronously. Mark the state as dirty.
this.painter.context.setDirty();
this.painter.setBaseState();
this._renderTaskQueue.run(paintStartTimeStamp);
// A task queue callback may have fired a user event which may have removed the map
if (this._removed) return;
let crossFading = false;
// If the style has changed, the map is being zoomed, or a transition or fade is in progress:
// - Apply style changes (in a batch)
// - Recalculate paint properties.
if (this.style && this._styleDirty) {
this._styleDirty = false;
const zoom = this.transform.zoom;
const now = browser.now();
this.style.zoomHistory.update(zoom, now);
const parameters = new EvaluationParameters(zoom, {
now,
fadeDuration: this._fadeDuration,
zoomHistory: this.style.zoomHistory,
transition: this.style.getTransition()
});
const factor = parameters.crossFadingFactor();
if (factor !== 1 || factor !== this._crossFadingFactor) {
crossFading = true;
this._crossFadingFactor = factor;
}
this.style.update(parameters);
}
// If we are in _render for any reason other than an in-progress paint
// transition, update source caches to check for and load any tiles we
// need for the current transform
if (this.style && this._sourcesDirty) {
this._sourcesDirty = false;
this.style._updateSources(this.transform);
}
this._placementDirty = this.style && this.style._updatePlacement(this.painter.transform, this.showCollisionBoxes, this._fadeDuration, this._crossSourceCollisions);
// Actually draw
this.painter.render(this.style, {
showTileBoundaries: this.showTileBoundaries,
showOverdrawInspector: this._showOverdrawInspector,
rotating: this.isRotating(),
zooming: this.isZooming(),
moving: this.isMoving(),
fadeDuration: this._fadeDuration,
showPadding: this.showPadding,
gpuTiming: !!this.listens('gpu-timing-layer'),
});
this.fire(new Event('render'));
if (this.loaded() && !this._loaded) {
this._loaded = true;
PerformanceUtils.mark(PerformanceMarkers.load);
this.fire(new Event('load'));
}
if (this.style && (this.style.hasTransitions() || crossFading)) {
this._styleDirty = true;
}
if (this.style && !this._placementDirty) {
// Since no fade operations are in progress, we can release
// all tiles held for fading. If we didn't do this, the tiles
// would just sit in the SourceCaches until the next render
this.style._releaseSymbolFadeTiles();
}
if (this.listens('gpu-timing-frame')) {
const renderCPUTime = browser.now() - frameStartTime;
extTimerQuery.endQueryEXT(extTimerQuery.TIME_ELAPSED_EXT, gpuTimer);
setTimeout(() => {
const renderGPUTime = extTimerQuery.getQueryObjectEXT(gpuTimer, extTimerQuery.QUERY_RESULT_EXT) / (1000 * 1000);
extTimerQuery.deleteQueryEXT(gpuTimer);
this.fire(new Event('gpu-timing-frame', {
cpuTime: renderCPUTime,
gpuTime: renderGPUTime
}));
}, 50); // Wait 50ms to give time for all GPU calls to finish before querying
}
if (this.listens('gpu-timing-layer')) {
// Resetting the Painter's per-layer timing queries here allows us to isolate
// the queries to individual frames.
const frameLayerQueries = this.painter.collectGpuTimers();
setTimeout(() => {
const renderedLayerTimes = this.painter.queryGpuTimers(frameLayerQueries);
this.fire(new Event('gpu-timing-layer', {
layerTimes: renderedLayerTimes
}));
}, 50); // Wait 50ms to give time for all GPU calls to finish before querying
}
// Schedule another render frame if it's needed.
//
// Even though `_styleDirty` and `_sourcesDirty` are reset in this
// method, synchronous events fired during Style#update or
// Style#_updateSources could have caused them to be set again.
const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty;
if (somethingDirty || this._repaint) {
this.triggerRepaint();
} else if (!this.isMoving() && this.loaded()) {
this.fire(new Event('idle'));
}
if (this._loaded && !this._fullyLoaded && !somethingDirty) {
this._fullyLoaded = true;
PerformanceUtils.mark(PerformanceMarkers.fullLoad);
}
return this;
}
/**
* Clean up and release all internal resources associated with this map.
*
* This includes DOM elements, event bindings, web workers, and WebGL resources.
*
* Use this method when you are done using the map and wish to ensure that it no
* longer consumes browser resources. Afterwards, you must not call any other
* methods on the map.
*/
remove() {
if (this._hash) this._hash.remove();
for (const control of this._controls) control.onRemove(this);
this._controls = [];
if (this._frame) {
this._frame.cancel();
this._frame = null;
}
this._renderTaskQueue.clear();
this.painter.destroy();
this.handlers.destroy();
delete this.handlers;
this.setStyle(null);
if (typeof window !== 'undefined') {
window.removeEventListener('resize', this._onWindowResize, false);
window.removeEventListener('orientationchange', this._onWindowResize, false);
window.removeEventListener('online', this._onWindowOnline, false);
}
const extension = this.painter.context.gl.getExtension('WEBGL_lose_context');
if (extension && extension.loseContext) extension.loseContext();
removeNode(this._canvasContainer);
removeNode(this._controlContainer);
removeNode(this._missingCSSCanary);
this._container.classList.remove('mapboxgl-map');
PerformanceUtils.clearMetrics();
this._removed = true;
this.fire(new Event('remove'));
}
/**
* Trigger the rendering of a single frame. Use this method with custom layers to
* repaint the map when the layer changes. Calling this multiple times before the
* next frame is rendered will still result in only a single frame being rendered.
* @example
* map.triggerRepaint();
* @see [Add a 3D model](https://docs.mapbox.com/mapbox-gl-js/example/add-3d-model/)
* @see [Add an animated icon to the map](https://docs.mapbox.com/mapbox-gl-js/example/add-image-animated/)
*/
triggerRepaint() {
if (this.style && !this._frame) {
this._frame = browser.frame((paintStartTimeStamp: number) => {
PerformanceUtils.frame(paintStartTimeStamp);
this._frame = null;
this._render(paintStartTimeStamp);
});
}
}
_onWindowOnline() {
this._update();
}
_onWindowResize(event: Event) {
if (this._trackResize) {
this.resize({originalEvent: event})._update();
}
}
/**
* Gets and sets a Boolean indicating whether the map will render an outline
* around each tile and the tile ID. These tile boundaries are useful for
* debugging.
*
* The uncompressed file size of the first vector source is drawn in the top left
* corner of each tile, next to the tile ID.
*
* @name showTileBoundaries
* @type {boolean}
* @instance
* @memberof Map
* @example
* map.showTileBoundaries = true;
*/
get showTileBoundaries(): boolean { return !!this._showTileBoundaries; }
set showTileBoundaries(value: boolean) {
if (this._showTileBoundaries === value) return;
this._showTileBoundaries = value;
this._update();
}
/**
* Gets and sets a Boolean indicating whether the map will visualize
* the padding offsets.
*
* @name showPadding
* @type {boolean}
* @instance
* @memberof Map
*/
get showPadding(): boolean { return !!this._showPadding; }
set showPadding(value: boolean) {
if (this._showPadding === value) return;
this._showPadding = value;
this._update();
}
/**
* Gets and sets a Boolean indicating whether the map will render boxes
* around all symbols in the data source, revealing which symbols
* were rendered or which were hidden due to collisions.
* This information is useful for debugging.
*
* @name showCollisionBoxes
* @type {boolean}
* @instance
* @memberof Map
*/
get showCollisionBoxes(): boolean { return !!this._showCollisionBoxes; }
set showCollisionBoxes(value: boolean) {
if (this._showCollisionBoxes === value) return;
this._showCollisionBoxes = value;
if (value) {
// When we turn collision boxes on we have to generate them for existing tiles
// When we turn them off, there's no cost to leaving existing boxes in place
this.style._generateCollisionBoxes();
} else {
// Otherwise, call an update to remove collision boxes
this._update();
}
}
/*
* Gets and sets a Boolean indicating whether the map should color-code
* each fragment to show how many times it has been shaded.
* White fragments have been shaded 8 or more times.
* Black fragments have been shaded 0 times.
* This information is useful for debugging.
*
* @name showOverdraw
* @type {boolean}
* @instance
* @memberof Map
*/
get showOverdrawInspector(): boolean { return !!this._showOverdrawInspector; }
set showOverdrawInspector(value: boolean) {
if (this._showOverdrawInspector === value) return;
this._showOverdrawInspector = value;
this._update();
}
/**
* Gets and sets a Boolean indicating whether the map will
* continuously repaint. This information is useful for analyzing performance.
*
* @name repaint
* @type {boolean}
* @instance
* @memberof Map
*/
get repaint(): boolean { return !!this._repaint; }
set repaint(value: boolean) {
if (this._repaint !== value) {
this._repaint = value;
this.triggerRepaint();
}
}
// show vertices
get vertices(): boolean { return !!this._vertices; }
set vertices(value: boolean) { this._vertices = value; this._update(); }
// for cache browser tests
_setCacheLimits(limit: number, checkThreshold: number) {
setCacheLimits(limit, checkThreshold);
}
/**
* The version of Mapbox GL JS in use as specified in package.json, CHANGELOG.md, and the GitHub release.
*
* @name version
* @instance
* @memberof Map
* @var {string} version
*/
get version(): string { return version; }
}
export default Map;
function removeNode(node) {
if (node.parentNode) {
node.parentNode.removeChild(node);
}
}
/**
* Interface for interactive controls added to the map. This is a
* specification for implementers to model: it is not
* an exported method or class.
*
* Controls must implement `onAdd` and `onRemove`, and must own an
* element, which is often a `div` element. To use Mapbox GL JS's
* default control styling, add the `mapboxgl-ctrl` class to your control's
* node.
*
* @interface IControl
* @example
* // Control implemented as ES6 class
* class HelloWorldControl {
* onAdd(map) {
* this._map = map;
* this._container = document.createElement('div');
* this._container.className = 'mapboxgl-ctrl';
* this._container.textContent = 'Hello, world';
* return this._container;
* }
*
* onRemove() {
* this._container.parentNode.removeChild(this._container);
* this._map = undefined;
* }
* }
*
* // Control implemented as ES5 prototypical class
* function HelloWorldControl() { }
*
* HelloWorldControl.prototype.onAdd = function(map) {
* this._map = map;
* this._container = document.createElement('div');
* this._container.className = 'mapboxgl-ctrl';
* this._container.textContent = 'Hello, world';
* return this._container;
* };
*
* HelloWorldControl.prototype.onRemove = function () {
* this._container.parentNode.removeChild(this._container);
* this._map = undefined;
* };
*/
/**
* Register a control on the map and give it a chance to register event listeners
* and resources. This method is called by {@link Map#addControl}
* internally.
*
* @function
* @memberof IControl
* @instance
* @name onAdd
* @param {Map} map the Map this control will be added to
* @returns {HTMLElement} The control's container element. This should
* be created by the control and returned by onAdd without being attached
* to the DOM: the map will insert the control's element into the DOM
* as necessary.
*/
/**
* Unregister a control on the map and give it a chance to detach event listeners
* and resources. This method is called by {@link Map#removeControl}
* internally.
*
* @function
* @memberof IControl
* @instance
* @name onRemove
* @param {Map} map the Map this control will be removed from
* @returns {undefined} there is no required return value for this method
*/
/**
* Optionally provide a default position for this control. If this method
* is implemented and {@link Map#addControl} is called without the `position`
* parameter, the value returned by getDefaultPosition will be used as the
* control's position.
*
* @function
* @memberof IControl
* @instance
* @name getDefaultPosition
* @returns {string} a control position, one of the values valid in addControl.
*/
/**
* A [`Point` geometry](https://github.com/mapbox/point-geometry) object, which has
* `x` and `y` properties representing screen coordinates in pixels.
*
* @typedef {Object} Point
* @example
* var point = new mapboxgl.Point(-77, 38);
*/
/**
* A {@link Point} or an array of two numbers representing `x` and `y` screen coordinates in pixels.
*
* @typedef {(Point | Array<number>)} PointLike
* @example
* var p1 = new mapboxgl.Point(-77, 38); // a PointLike which is a Point
* var p2 = [-77, 38]; // a PointLike which is an array of two numbers
*/
|