Codebase list icingaweb2-module-map / d907e15
Import upstream version 1.1.0+git20201016.1.7f2ef16 Debian Janitor 2 years ago
27 changed file(s) with 15255 addition(s) and 13903 deletion(s). Raw diff Collapse all Expand all
22 namespace Icinga\Module\Map\Controllers;
33
44 use Icinga\Module\Map\Forms\Config\GeneralConfigForm;
5 use Icinga\Module\Map\Forms\Config\DirectorConfigForm;
56 use Icinga\Web\Controller;
67
78 class ConfigController extends Controller
89 {
910 public function init()
1011 {
11 $this->assertPermission('config/modules');
12
1213 }
1314
1415 public function indexAction()
1516 {
17 $this->assertPermission('config/modules');
1618 $form = new GeneralConfigForm();
1719 $form->setIniConfig($this->Config());
1820 $form->handleRequest();
2022 $this->view->form = $form;
2123 $this->view->tabs = $this->Module()->getConfigTabs()->activate('config');
2224 }
25
26 public function directorAction()
27 {
28 $this->assertPermission('map/director/configuration');
29 $form = new DirectorConfigForm();
30 $form->setIniConfig($this->Config());
31 $form->handleRequest();
32
33 $this->view->form = $form;
34 $this->view->tabs = $this->Module()->getConfigTabs()->activate('director');
35 }
36
37
38 public function fetchAction()
39 {
40 $type = strtolower($this->params->shift('type', ""));
41 $moduleConfig = $this->Config();
42 $config = [];
43
44 // TODO: Reuse the default values in config form?
45 $defaults = array(
46 "default_zoom" => "4",
47 "default_long" => '13.377485',
48 "default_lat" => '52.515855',
49 "min_zoom" => "2",
50 "max_zoom" => "19",
51 "max_native_zoom" => "19",
52 "cluster_problem_count" => 0,
53 "popup_mouseover" => 0,
54 "tile_url" => "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
55 );
56
57 $defaults['disable_cluster_at_zoom'] = $defaults['max_zoom'] - 1; // should be by default: max_zoom - 1
58
59 /*
60 * Override module config with user's config
61 */
62 $userPreferences = $this->Auth()->getUser()->getPreferences();
63 if ($userPreferences->has("map")) {
64 $moduleConfig->getSection("map")->merge($userPreferences->get("map"));
65 }
66
67 if ($type === "director") {
68 $moduleConfig->getSection("map")->merge($moduleConfig->getSection("director"));
69
70 if($userPreferences->has("map-director")) {
71 $moduleConfig->getSection("map")->merge($userPreferences->get("map-director"));
72 }
73 }
74
75
76 foreach ($defaults as $parameter => $default) {
77 $config[$parameter] = $moduleConfig->get("map", $parameter, $default);
78 }
79
80 print json_encode($config);
81 exit();
82
83 }
2384 }
2485
4040 "max_native_zoom" => "19",
4141 "disable_cluster_at_zoom" => null, // should be by default: max_zoom - 1
4242 "cluster_problem_count" => 0,
43 "popup_mouseover" => 0,
4344 "tile_url" => "//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
45 "opencage_apikey" => "",
4446 );
4547
4648 /*
0 <?php
1
2 namespace Icinga\Module\Map\Controllers;
3
4 use Icinga\Data\Filter\Filter;
5 use Icinga\Module\Monitoring\Controller;
6 use Icinga\Module\Monitoring\DataView\DataView;
7 use OpenCage\Geocoder\Geocoder;
8
9 class SearchController extends Controller
10 {
11 private $geocoder;
12 private $limit;
13
14 public function init()
15 {
16 parent::init();
17
18 $apiKey = $this->config()->get("map", "opencage_apikey", "");
19 if ($apiKey != "") {
20 $this->geocoder = $geocoder = new Geocoder($apiKey);
21 }
22 }
23
24 protected function filterQuery(DataView $dataView)
25 {
26 $this->setupFilterControl($dataView, null, null,
27 ['stateType', 'objectType', 'problems', 'jsonp', 'q', 'proximity', 'lite']);
28 return $dataView;
29 }
30
31 private function opencageSearch($query)
32 {
33 $results = [];
34
35 if (!$this->geocoder) {
36 return $results;
37 }
38
39 $result = $this->geocoder->geocode($query, ["limit" => $this->limit]);
40
41 if ($result && $result['total_results'] > 0) {
42 foreach ($result['results'] as $el) {
43 $results[] = [
44 "name" => $el['formatted'],
45 "center" => [
46 "lat" => $el['geometry']['lat'],
47 "lng" => $el['geometry']['lng']
48 ],
49 "icon" => "globe"
50 ];
51 }
52 }
53
54 return $results;
55 }
56
57 private function serviceSearch($query)
58 {
59 $results = [];
60
61 if (empty($query)) {
62 return $results;
63 }
64 $filter = sprintf('(service=*%s*|service_display_name=*%s*)', $query, $query);
65
66 $hostQuery = $this->backend
67 ->select()
68 ->from('servicestatus', array(
69 'service_display_name',
70 'service',
71 'host_name',
72 'coordinates' => '_service_geolocation',
73 ))
74 ->applyFilter(Filter::fromQueryString('_service_geolocation >'))
75 ->applyFilter(Filter::fromQueryString($filter))
76 ->limit($this->limit);
77 $this->filterQuery($hostQuery);
78 $this->applyRestriction('monitoring/filter/objects', $hostQuery);
79
80 if ($hostQuery->count() > 0) {
81 foreach ($hostQuery as $el) {
82 // @TODO: Move to library
83 // check for broken coordinates
84 $coordinate_pattern = '/^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/';
85
86 if (!preg_match($coordinate_pattern, $el->coordinates)) {
87 continue;
88 }
89 $coordinates = explode(",", $el->coordinates);
90
91 $results[] = [
92 "id" => sprintf("%s!%s", $el->host_name, $el->service),
93 "name" => sprintf("%s (%s)", $el->service, $el->host_name),
94 "center" => [
95 "lat" => $coordinates[0],
96 "lng" => $coordinates[1]
97 ],
98 "icon" => "service"
99 ];
100 }
101 }
102
103 return $results;
104 }
105
106 private function hostSearch($query)
107 {
108 $results = [];
109
110 if (empty($query)) {
111 return $results;
112 }
113 $filter = sprintf('(host=*%s*|host_display_name=*%s*)', $query, $query);
114
115 $hostQuery = $this->backend
116 ->select()
117 ->from('hoststatus', array(
118 'host_display_name',
119 'host_name',
120 'coordinates' => '_host_geolocation',
121 ))
122 ->applyFilter(Filter::fromQueryString('_host_geolocation >'))
123 ->applyFilter(Filter::fromQueryString($filter))
124 ->limit($this->limit);
125 $this->filterQuery($hostQuery);
126 $this->applyRestriction('monitoring/filter/objects', $hostQuery);
127
128 if ($hostQuery->count() > 0) {
129 foreach ($hostQuery as $el) {
130 // @TODO: Move to library
131 // check for broken coordinates
132 $coordinate_pattern = '/^(\-?\d+(\.\d+)?),\s*(\-?\d+(\.\d+)?)$/';
133
134 if (!preg_match($coordinate_pattern, $el->coordinates)) {
135 continue;
136 }
137 $coordinates = explode(",", $el->coordinates);
138
139 $results[] = [
140 "id" => $el->host_name,
141 "name" => $el->host_display_name,
142 "center" => [
143 "lat" => $coordinates[0],
144 "lng" => $coordinates[1]
145 ],
146 "icon" => "host"
147 ];
148 }
149 }
150
151 return $results;
152 }
153
154 public function indexAction()
155 {
156 $config = $this->config();
157 $query = strtolower($this->params->shift('q', ''));
158 $callback = strtolower($this->params->shift('jsonp', ''));
159 $this->limit = $this->params->shift('limit', 5);
160 $lite = boolval($this->params->shift('lite', 0));
161
162 $results = [
163 "ocg" => [],
164 "hosts" => [],
165 "services" => []
166 ];
167
168 $results["ocg"] = $this->opencageSearch($query);
169 if (!$lite) {
170 $results["hosts"] = $this->hostSearch($query);
171 $results["services"] = $this->serviceSearch($query);
172 }
173 print $callback . "(" . json_encode($results) . ");";
174 exit();
175 }
176 }
0 <?php
1
2 namespace Icinga\Module\Map\Forms\Config;
3
4 use Icinga\Forms\ConfigForm;
5
6 class DirectorConfigForm extends ConfigForm
7 {
8 /**
9 * Initialize this form
10 */
11 public function init()
12 {
13 $this->setName('form_config_director');
14 $this->setSubmitLabel($this->translate('Save Changes'));
15 }
16
17 /**
18 * {@inheritdoc}
19 */
20 public function createElements(array $formData)
21 {
22 $this->addElement(
23 'text',
24 'director_default_lat',
25 array(
26 'placeholder' => '(use map modules configuration)',
27 'label' => $this->translate('Default latitude (WGS84)'),
28 'description' => $this->translate('Default map position (latitude)'),
29 'required' => false
30 )
31 );
32 $this->addElement(
33 'text',
34 'director_default_long',
35 array(
36 'placeholder' => '(use map modules configuration)',
37 'label' => $this->translate('Default longitude (WGS84)'),
38 'description' => $this->translate('Default map position (longitude)'),
39 'required' => false
40 )
41 );
42 $this->addElement(
43 'text',
44 'director_default_zoom',
45 array(
46 'placeholder' => '(use map modules configuration)',
47 'label' => $this->translate('Default zoom level'),
48 'description' => $this->translate('Default zoom level of the map'),
49 'required' => false
50 )
51 );
52 $this->addElement(
53 'text',
54 'director_max_zoom',
55 array(
56 'placeholder' => '(use map modules configuration)',
57 'label' => $this->translate('Maximum zoom level'),
58 'description' => $this->translate('Maximum zoom level of the map'),
59 'required' => false
60 )
61 );
62 $this->addElement(
63 'text',
64 'director_max_native_zoom',
65 array(
66 'placeholder' => '(use map modules configuration)',
67 'label' => $this->translate('Maximum native zoom level '),
68 'description' => $this->translate('Maximum zoom level natively supported by the map'),
69 'required' => false
70 )
71 );
72 $this->addElement(
73 'text',
74 'director_min_zoom',
75 array(
76 'placeholder' => '(use map modules configuration)',
77 'label' => $this->translate('Minimal zoom level'),
78 'description' => $this->translate('Minimal zoom level of the map'),
79 'required' => false
80 )
81 );
82 $this->addElement(
83 'text',
84 'director_tile_url',
85 array(
86 'placeholder' => '(use map modules configuration)',
87 'label' => $this->translate('URL for tile server'),
88 'description' => $this->translate('Escaped server url, for leaflet tilelayer'),
89 'required' => false,
90 )
91 );
92 }
93 }
94
9292 );
9393 $this->addElement(
9494 'text',
95 'map_opencage_apikey',
96 array(
97 'placeholder' => 'OpenCage Geocoder API KEY',
98 'label' => $this->translate('OpenCage API key'),
99 'description' => $this->translate('Your personal OpenCage Geocoder API key'),
100 'required' => false,
101 )
102 );
103 $this->addElement(
104 'text',
95105 'map_dashlet_height',
96106 array(
97107 'placeholder' => '300',
133143 )
134144 );
135145
146
147 $this->addElement(
148 'checkbox',
149 'map_popup_mouseover',
150 array(
151 'label' => $this->translate('Show popup on mouseover'),
152 'description' => $this->translate('Show popup when hovering the object'),
153 'required' => false,
154 'default' => false,
155 )
156 );
157
136158 }
137159 }
138160
8989 #: ../../../../modules/map/application/views/scripts/index/index.phtml:14
9090 msgctxt "icinga.state"
9191 msgid "OK"
92 msgstr ""
92 msgstr "OK"
9393
9494 #: ../../../../modules/map/application/views/scripts/index/index.phtml:18
9595 msgctxt "icinga.state"
116116
117117 #: ../../../../modules/map/library/Map/ProvidedHook/CubeLinks.php:32
118118 msgid "This shows all matching hosts and their current state on the map module"
119 msgstr ""
119 msgstr "Hier werden alle passenden Hosts und deren aktueller Status im Map-Modul angezeigt"
120120
121121 #: ../../../../modules/map/application/views/scripts/index/index.phtml:17
122122 msgctxt "icinga.state"
0 <div class="controls">
1 <?= $tabs; ?>
2 </div>
3 <div class="content">
4 <?= $form; ?>
5 </div>
11 ?>
22 <script type="text/javascript">
33 <?php
4 echo 'var map_default_zoom = ' . (!empty($this->default_zoom) ? $this->default_zoom : "null") . ";\n";
5 echo 'var map_default_long = ' . (!empty($this->default_long) ? $this->default_long : "null") . ";\n";
6 echo 'var map_default_lat = ' . (!empty($this->default_lat) ? $this->default_lat : "null") . ";\n";
7 echo 'var map_max_zoom = ' . $this->max_zoom . ";\n";
8 echo 'var map_max_native_zoom = ' . $this->max_native_zoom . ";\n";
9 echo 'var map_min_zoom = ' . $this->min_zoom . ";\n";
10 echo 'var disable_cluster_at_zoom = ' . $this->disable_cluster_at_zoom . ";\n";
11 echo "var tile_url = '" . $this->tile_url . "';\n";
12 echo "var cluster_problem_count = " . $this->cluster_problem_count . ";\n";
13 echo 'var map_show_host = "' . $this->host . "\";\n";
4 echo 'var map_default_zoom = ' . (!empty($this->default_zoom) ? intval($this->default_zoom) : "null") . ";\n";
5 echo 'var map_default_long = ' . (!empty($this->default_long) ? preg_replace("/[^0-9\.\,\-]/", "", $this->default_long) : "null") . ";\n";
6 echo 'var map_default_lat = ' . (!empty($this->default_lat) ? preg_replace("/[^0-9\.\,\-]/", "", $this->default_lat) : "null") . ";\n";
7 echo 'var map_max_zoom = ' . intval($this->max_zoom) . ";\n";
8 echo 'var map_max_native_zoom = ' . intval($this->max_native_zoom) . ";\n";
9 echo 'var map_min_zoom = ' . intval($this->min_zoom) . ";\n";
10 echo 'var disable_cluster_at_zoom = ' . intval($this->disable_cluster_at_zoom) . ";\n";
11 echo "var tile_url = '" . preg_replace("/[\'\;]/", "", $this->tile_url) . "';\n";
12 echo "var cluster_problem_count = " . intval($this->cluster_problem_count) . ";\n";
13 echo "var popup_mouseover = " . intval($this->popup_mouseover) . ";\n";
14 echo 'var map_show_host = "' . preg_replace("/[\'\;]/", "", $this->host) . "\";\n";
1415 ?>
1516 var service_status = {};
1617 service_status[0] = ['<?= $this->translate('OK', 'icinga.state') ?>', 'OK'];
1515 $this->provideCssFile('vendor/easy-button.css');
1616 $this->provideCssFile('vendor/leaflet.awesome-markers.css');
1717 $this->provideCssFile('vendor/leaflet.modal.css');
18 $this->provideCssFile('vendor/L.Control.OpenCageData.Search.min.css');
1819
1920 // javascript libraries
2021 $this->provideJsFile('vendor/spin.js');
2526 $this->provideJsFile('vendor/easy-button.js');
2627 $this->provideJsFile('vendor/leaflet.awesome-markers.js');
2728 $this->provideJsFile('vendor/Leaflet.Modal.js');
29 $this->provideJsFile('vendor/L.Control.OpenCageSearch.js');
2830
2931 // configuration menu
3032 $this->provideConfigTab('config', array(
3234 'label' => $this->translate('Configuration'),
3335 'url' => 'config'
3436 ));
37
38 if ($this->app->getModuleManager()->hasEnabled("mapDatatype") && $this->app->getModuleManager()->hasEnabled("director")) {
39
40 $this->provideConfigTab('director', array(
41 'title' => $this->translate('Configure the director map datatype'),
42 'label' => $this->translate('Director'),
43 'url' => 'config/director'
44 ));
45
46 }
4040
4141 ## Using the Icinga Director
4242
43 If you're using the [Icinga Director](https://github.com/icinga/icingaweb2-module-director), you could use the new [mapDatatype](https://github.com/nbuchwitz/icingaweb2-module-mapDatatype) plugin. This plugin enables you to directly selected your coordinates on a map.
43 If you're using the [Icinga Director](https://github.com/icinga/icingaweb2-module-director), you could use the new [mapDatatype](https://github.com/nbuchwitz/icingaweb2-module-mapDatatype) plugin. This plugin enables you to directly select the coordinates on a map.
4444
4545 ![Using the Icinga Director](screenshot/director-plugin.png)
4646
4747
4848 ## What's next?
4949
50 After adding the first markers to your map, you wan't to [explore the map](03-Exploring-the-map.md).
50 After adding the first markers to your map, you want to [explore the map](03-Exploring-the-map.md).
3737 | hostgroup_name=customer1&_host_environment=production | Show all hosts of hostgroup `customer1` of where the custom variable environment is equal to `production` |
3838 | _host_customer=(max-corp\|icinga) | Show all hosts where the custom variable `customer` is set to `max-corp` or `icinga` |
3939 | host_in_downtime=0&host_acknowledged=0 | Don't show acknowledged or objects in downtime |
40 |problems&host_state=1 | Show all hosts in state DOWN `1` (`0` = UP, `1`= DOWN) |
4041
4142
4243 ## Dashboard integration
4849
4950 Besides the global map configuration in the module settings, it is possible to override these settings per user or per each map.
5051
51 The following parameters could be overriden:
52 The following parameters can be overridden:
5253
5354 | Parameter |
5455 | --- |
9596
9697 ### Show number of problems instead of totals
9798
98 Set ``cluster_problem_count`` to ``1`` to show the number of problems in the cluster instead of the marker count.
99 Set ``cluster_problem_count`` to ``1`` to show the number of problems in the cluster instead of the marker count.
00 # Filter Map Object
11
22 The map module allows you to filter objects by using the Icinga Web 2 filter syntax.
3
4 For more details have look into the [filter section](https://icinga.com/docs/icingaweb2/latest/doc/06-Security/#restrictions) of the offical Icinga Web 2 documentation.
35
46 ## Special Filters
57
0 <?php
1
2 namespace OpenCage\Geocoder;
3
4 abstract class AbstractGeocoder
5 {
6 const TIMEOUT = 10;
7 const URL = 'https://api.opencagedata.com/geocode/v1/json/?';
8
9 protected $key;
10 protected $timeout;
11 protected $url;
12
13 public function __construct($key = null)
14 {
15 if (isset($key) && !empty($key)) {
16 $this->setKey($key);
17 }
18 $this->setTimeout(self::TIMEOUT);
19 }
20
21 public function setKey($key)
22 {
23 $this->key = $key;
24 }
25
26 public function setTimeout($timeout)
27 {
28 $this->timeout = $timeout;
29 }
30
31 protected function getJSON($query)
32 {
33 if (function_exists('curl_version')) {
34 $ret = $this->getJSONByCurl($query);
35 return $ret;
36 } elseif (ini_get('allow_url_fopen')) {
37 $ret = $this->getJSONByFopen($query);
38 return $ret;
39 } else {
40 throw new \Exception('PHP is not compiled with CURL support and allow_url_fopen is disabled; giving up');
41 }
42 }
43
44 protected function getJSONByFopen($query)
45 {
46 $context = stream_context_create(
47 [
48 'http' => [
49 'timeout' => $this->timeout
50 ]
51 ]
52 );
53
54 $ret = file_get_contents($query);
55 return $ret;
56 }
57
58 protected function getJSONByCurl($query)
59 {
60 $ch = curl_init();
61 $options = [
62 CURLOPT_TIMEOUT => $this->timeout,
63 CURLOPT_URL => $query,
64 CURLOPT_RETURNTRANSFER => 1
65 ];
66 curl_setopt_array($ch, $options);
67
68 $ret = curl_exec($ch);
69 return $ret;
70 }
71
72 abstract public function geocode($query);
73 }
0 <?php
1
2 namespace OpenCage\Geocoder;
3
4 use OpenCage\Geocoder\AbstractGeocoder;
5
6 class Geocoder extends AbstractGeocoder
7 {
8 public function geocode($query, $optParams = [])
9 {
10 $url = self::URL . 'q=' . urlencode($query);
11
12 if(is_array($optParams) && !empty($optParams))
13 {
14 foreach($optParams as $param => $paramValue)
15 {
16 $url .= '&'.$param.'=' . urlencode($paramValue);
17 }
18 }
19
20 if (empty($this->key)) {
21 throw new \Exception('Missing API key');
22 }
23 $url .= '&key=' . $this->key;
24
25 $ret = json_decode($this->getJSON($url), true);
26 return $ret;
27 }
28 }
0 <?php
1
2 namespace OpenCage\Loader;
3
4 use Icinga\Application\ApplicationBootstrap;
5
6 class CompatLoader
7 {
8 public static function delegateLoadingToIcingaWeb(ApplicationBootstrap $app)
9 {
10 $app->getLoader()->registerNamespace(
11 'OpenCage',
12 dirname(__DIR__)
13 );
14 }
15 }
0 /*
1 * OpenCage Data Search Control v1.2.0 - 2018-09-30
2 * Copyright (c) 2018, OpenCage Data
3 * info@opencagedata.com
4 * https://opencagedata.com
5 *
6 * Licensed under the BSD license.
7 * Demo: https://opencagedata.com
8 * Source: git@github.com:opencagedata/leaflet-opencage-search.git
9 */
10
11 .leaflet-control-ocd-search {
12 background: #fff;
13 box-shadow: 0 1px 7px rgba(0, 0, 0, .65);
14 -webkit-border-radius: 4px;
15 border-radius: 4px;
16 }
17
18 .leaflet-touch .leaflet-control-ocd-search {
19 box-shadow: none;
20 border: 2px solid rgba(0, 0, 0, .2);
21 /*background-clip: padding-box;*/
22 line-height: 30px;
23 }
24
25 .leaflet-control-ocd-search-form {
26 display: inline;
27 /*padding: 2px 0;*/
28 margin-top: 0 !important;
29 margin-bottom: 0 !important;
30 height: 0;
31 }
32
33 .leaflet-control-ocd-search-error, .leaflet-control-ocd-search-form input, .leaflet-control-ocd-search-form ul {
34 border: 0;
35 color: transparent;
36 background: #fff
37 }
38
39 .leaflet-control-ocd-search-form input {
40 font-size: 14px;
41 width: 0;
42 transition: width .125s ease-in;
43 min-width: unset;
44 max-width: unset;
45 line-height: 30px;
46 }
47
48 .leaflet-touch .leaflet-control-ocd-search-form input {
49 font-size: 14px;
50 padding:0;
51 height: unset;
52 }
53 .leaflet-control-ocd-search-icon span {
54 display: inline;
55 vertical-align: middle;
56 }
57 .leaflet-control-ocd-search-icon span:before {
58 content: '\e85c';
59 font-family: 'ifont';
60 font-size: 14px;
61 }
62
63 .leaflet-control-ocd-search-icon {
64 width: 24px;
65 height: 24px;
66 float: right;
67 cursor: pointer;
68 text-align: center;
69
70 margin:0;
71 padding:0;
72 }
73
74 .leaflet-touch .leaflet-control-ocd-search-icon {
75 width: 30px;
76 }
77
78 .leaflet-control-ocd-search-spinner .leaflet-control-ocd-search-icon {
79 background-image: url(../images/spinner.gif)
80 }
81
82 .leaflet-control-ocd-search-error, .leaflet-control-ocd-search-expanded input {
83 width: 226px;
84 margin-left: 4px;
85 padding-left: 4px;
86 padding-right: 0;
87 color: #000;
88 line-height: unset;
89 height: unset;
90 }
91
92 .leaflet-control-ocd-search-form input:focus {
93 outline: 0
94 }
95
96 .leaflet-control-ocd-search-form button, .leaflet-control-ocd-search-form-no-error {
97 display: none
98 }
99
100 .leaflet-control-ocd-search-error {
101 margin-top: 8px;
102 display: block;
103 color: #444
104 }
105
106 ul.leaflet-control-ocd-search-alternatives {
107 width: 260px;
108 overflow: hidden;
109 text-overflow: ellipsis;
110 white-space: nowrap;
111 list-style: none;
112 padding: 0;
113 transition: height .125s ease-in
114 }
115
116 .leaflet-control-ocd-search-alternatives-minimized {
117 width: 0 !important;
118 height: 0;
119 overflow: hidden;
120 margin: 0;
121 padding: 0
122 }
123
124 .leaflet-control-ocd-search-alternatives li {
125 width: 100%;
126 overflow: hidden;
127 text-overflow: ellipsis;
128 border-bottom: 1px solid #eee;
129 padding: 0;
130 margin: auto;
131 line-height: 1.7em;
132 }
133
134 .leaflet-search-result-globe:before {
135 content: '\e819';
136 }
137
138 .leaflet-search-result-host:before {
139 content: '\e866';
140 }
141
142 .leaflet-search-result-service:before {
143 content: '\e83f';
144 }
145
146 .leaflet-control-ocd-search-alternatives li:before {
147 font-family: 'ifont';
148 color: @icinga-blue;
149 padding: 0px 6px;
150 }
151
152 .leaflet-control-ocd-search-alternatives li:last-child {
153 border-bottom: none
154 }
155
156 .leaflet-control-ocd-search-alternatives a {
157 text-decoration: none;
158 color: #000;
159 padding: 0px 8px 8px 6px;
160 font-size: 14px;
161 width: 100%;
162 }
163
164 .leaflet-touch .leaflet-control-ocd-search-alternatives a {
165 font-size: 12px
166 }
167
168 .leaflet-control-ocd-search-alternatives li:hover, .leaflet-control-ocd-search-selected {
169 background-color: @tr-active-color;
170 }
2626 (-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5),(min-resolution: 1.5dppx) {
2727 .awesome-marker {
2828 background-image: url('../img/map/markers-soft@2x.png');
29 background-size: 720px 92px;
29 background-size: 720px 46px;
3030 }
3131 .awesome-marker-shadow {
3232 background-image: url('../img/map/markers-shadow@2x.png');
121121 .awesome-marker-icon-black {
122122 background-position: -682px 0;
123123 }
124
125 .awesome-marker-square {
126 background-position-y: -46px;
127 }
2424 user-select: none;
2525 -webkit-user-drag: none;
2626 }
27 /* Prevents IE11 from highlighting tiles in blue */
28 .leaflet-tile::selection {
29 background: transparent;
30 }
2731 /* Safari renders non-retina tile on retina better with this, but Chrome is worse */
2832 .leaflet-safari .leaflet-tile {
2933 image-rendering: -webkit-optimize-contrast;
4448 .leaflet-container .leaflet-marker-pane img,
4549 .leaflet-container .leaflet-shadow-pane img,
4650 .leaflet-container .leaflet-tile-pane img,
47 .leaflet-container img.leaflet-image-layer {
51 .leaflet-container img.leaflet-image-layer,
52 .leaflet-container .leaflet-tile {
4853 max-width: none !important;
4954 max-height: none !important;
5055 }
167172 opacity: 0;
168173 -webkit-transition: opacity 0.2s linear;
169174 -moz-transition: opacity 0.2s linear;
170 -o-transition: opacity 0.2s linear;
171175 transition: opacity 0.2s linear;
172176 }
173177 .leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
184188 .leaflet-zoom-anim .leaflet-zoom-animated {
185189 -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
186190 -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
187 -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
188191 transition: transform 0.25s cubic-bezier(0,0,0.25,1);
189192 }
190193 .leaflet-zoom-anim .leaflet-tile,
191194 .leaflet-pan-anim .leaflet-tile {
192195 -webkit-transition: none;
193196 -moz-transition: none;
194 -o-transition: none;
195197 transition: none;
196198 }
197199
208210 .leaflet-grab {
209211 cursor: -webkit-grab;
210212 cursor: -moz-grab;
213 cursor: grab;
211214 }
212215 .leaflet-crosshair,
213216 .leaflet-crosshair .leaflet-interactive {
223226 cursor: move;
224227 cursor: -webkit-grabbing;
225228 cursor: -moz-grabbing;
229 cursor: grabbing;
226230 }
227231
228232 /* marker & overlays interactivity */
236240
237241 .leaflet-marker-icon.leaflet-interactive,
238242 .leaflet-image-layer.leaflet-interactive,
239 .leaflet-pane > svg path.leaflet-interactive {
243 .leaflet-pane > svg path.leaflet-interactive,
244 svg.leaflet-image-layer.leaflet-interactive path {
240245 pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
241246 pointer-events: auto;
242247 }
493498 -webkit-transform: rotate(45deg);
494499 -moz-transform: rotate(45deg);
495500 -ms-transform: rotate(45deg);
496 -o-transform: rotate(45deg);
497501 transform: rotate(45deg);
498502 }
499503 .leaflet-popup-content-wrapper,
527531 }
528532
529533 .leaflet-oldie .leaflet-popup-content-wrapper {
530 zoom: 1;
534 -ms-zoom: 1;
531535 }
532536 .leaflet-oldie .leaflet-popup-tip {
533537 width: 24px;
0 <svg width="218" height="216" viewBox="0 0 218 216" fill="none" xmlns="http://www.w3.org/2000/svg">
1 <path d="M9.81498 208.749L8.32636 207.988C7.26342 207.445 6.83732 206.99 6.83594 206.396C6.83495 205.939 7.52353 201.39 8.36648 196.288L9.89883 187.012L8.30974 178.905C7.43584 174.447 6.82796 170.519 6.95918 170.176C7.20341 169.539 10.6723 167.731 11.6756 167.719C11.9827 167.715 12.8802 166.706 13.6703 165.478C15.3508 162.864 15.1719 162.898 20.8298 164.119C23.0611 164.601 27.9468 165.64 31.6871 166.429L38.4875 167.863L41.8277 166.222L45.1679 164.582L48.9827 166.141L52.7975 167.7L54.8105 165.688L56.8237 163.676L58.3919 154.264L59.9603 144.852L58.7302 142.769C58.0538 141.623 56.0216 138.504 54.2143 135.838L50.9281 130.99L43.6756 128.61C39.6868 127.301 36.3001 126.043 36.1497 125.814C35.9993 125.585 35.8737 124.184 35.8706 122.702C35.8638 119.464 35.4232 119.848 43.0682 116.441C47.3273 114.543 48.6946 114.08 49.0866 114.406C49.3655 114.638 49.8005 115.754 50.0535 116.887L50.5133 118.945L52.0275 118.707C54.4745 118.322 56.3328 117.75 56.3328 117.382C56.3328 117.194 55.7331 114.84 55.0002 112.151C54.267 109.463 53.7594 107.022 53.872 106.729C54.0181 106.347 54.7134 106.194 56.3023 106.194H58.5279L60.2541 108.618L61.9802 111.042H64.3488H66.7173L66.828 108.711L66.9388 106.381L71.6607 104.765L76.3826 103.149L77.0261 99.4655C77.38 97.4396 77.7776 95.6069 77.9098 95.3928C78.136 95.026 85.3564 91.6815 87.7788 90.8216C88.3928 90.6037 89.0761 90.2849 89.2971 90.1133C89.5181 89.9419 91.4993 86.1909 93.6996 81.7779L97.7001 73.7542H99.3449H100.99V76.9236V80.093H102.118C102.77 80.093 103.335 79.8603 103.457 79.5418C103.573 79.2386 103.31 76.8633 102.871 74.2632C102.433 71.6632 102.21 69.3995 102.376 69.2331C102.542 69.0665 104.83 68.8831 107.461 68.8253L112.243 68.7203L112.962 66.3708C113.358 65.0785 113.902 63.9364 114.172 63.8328C114.441 63.7292 116.294 64.325 118.289 65.157C120.284 65.9889 121.978 66.6696 122.054 66.6696C122.129 66.6696 122.685 65.6887 123.289 64.49L124.387 62.3105L123.097 61.694C121.152 60.7648 121.129 60.1349 122.972 58.2537L124.587 56.6048L123.158 52.2056L121.728 47.8065L120.104 47.1336L118.481 46.4605V39.9648V33.4689L120.159 30.1206L121.837 26.7721L128.811 23.2839C132.647 21.3652 135.995 19.8691 136.25 19.9591C136.866 20.1754 136.842 22.2447 136.16 27.7186C135.855 30.1718 135.604 32.3784 135.602 32.6224C135.601 32.8665 136.227 33.1181 136.995 33.1817L138.39 33.2975L138.485 35.7211L138.58 38.1448L135.934 39.8221C134.092 40.9895 133.071 41.9245 132.575 42.8982L131.863 44.2973H133.191C133.922 44.2973 134.972 44.5275 135.525 44.8092C136.077 45.0908 136.909 45.4385 137.372 45.5818C138.138 45.8186 138.205 45.7329 138.116 44.6413C138.029 43.571 138.2 43.3345 139.693 42.4674C140.614 41.9323 143.16 40.6571 145.35 39.6333L149.332 37.772L149.351 36.2933C149.366 35.0255 149.124 34.566 147.655 33.0717C146.321 31.715 145.795 30.8036 145.277 28.9573C144.912 27.6529 143.483 22.7265 142.102 18.0097C140.72 13.2929 139.412 8.6958 139.194 7.79428C138.942 6.74931 138.556 6.09131 138.129 5.97961C137.761 5.88311 137.46 5.51762 137.46 5.16703C137.46 4.81664 137.046 4.1399 136.539 3.66318C135.664 2.83906 135.608 2.8314 135.393 3.50494C134.677 5.7474 134.534 5.89126 133.019 5.89126C131.681 5.89126 131.141 6.23871 127.368 9.52674C125.073 11.5263 122.069 14.1297 120.692 15.312L118.187 17.4616L114.705 16.6244C111.823 15.9314 110.439 15.3164 106.665 13.052C104.158 11.5477 102.106 10.1012 102.106 9.83756C102.106 9.57391 102.52 8.57814 103.026 7.62481C103.532 6.67132 103.864 5.89126 103.763 5.89126C103.662 5.89126 103.92 5.22295 104.336 4.40623L105.092 2.92133L103.487 3.14186L101.883 3.36214M204.626 214.124L205.984 212.463L204.936 212.457C204.359 212.454 203.701 212.328 203.473 212.178C203.246 212.027 200.441 207.768 197.24 202.714L191.421 193.524V188.105V182.686L189.095 180.915C185.573 178.234 176.292 171.15 173.528 169.034C172.181 168.003 169.467 165.929 167.496 164.426C165.526 162.923 163.84 161.412 163.749 161.07C163.659 160.727 163.28 158.868 162.906 156.937L162.226 153.427L160.542 152.351C158.649 151.142 159.067 150.935 156.787 154.21C155.829 155.586 155.578 155.75 154.942 155.409C154.285 155.056 154.206 154.618 154.206 151.299V147.583H152.887C152.162 147.583 150.495 147.853 149.183 148.183C146.914 148.754 146.807 148.831 146.986 149.768C147.089 150.31 147.387 152.549 147.648 154.745L148.123 158.737L152.217 162.645L156.312 166.553L157.771 170.52L159.23 174.488L162.126 176.79L165.022 179.091L168.638 179.277C172.005 179.451 172.263 179.52 172.371 180.277C172.434 180.725 172.224 181.6 171.903 182.222L171.319 183.354L174.578 185.629C176.371 186.88 179.931 189.368 182.489 191.157L187.141 194.41V196.813C187.141 198.802 187.029 199.237 186.492 199.341C186.135 199.41 183.839 198.446 181.39 197.199L176.937 194.932L176.522 196.477C176.113 197.998 176.13 198.046 177.623 199.57C179.067 201.044 179.14 201.229 179.14 203.425V205.731L177.3 206.523C175.786 207.176 175.016 207.904 172.951 210.635C171.064 213.131 170.228 213.954 169.581 213.954C169.104 213.954 168.72 214.18 168.72 214.46M146.935 214.527C145.576 212.886 145.763 212.316 148.051 211.116L150.083 210.05L154.347 210.914C158.448 211.746 158.723 211.757 161.526 211.199C163.129 210.88 165.328 210.507 166.413 210.371C168.325 210.132 168.415 210.068 169.319 208.281L170.251 206.438L168.734 201.879C167.265 197.462 167.176 197.305 165.875 196.833C165.014 196.522 162.743 194.573 159.557 191.411L154.583 186.476L151.392 185.631L148.201 184.787L145.006 180.727C141.823 176.682 141.805 176.667 140.202 176.667C138.213 176.667 138.332 176.839 134.663 168.656C133.102 165.172 131.643 162.21 131.421 162.073C131.2 161.936 129.194 161.542 126.964 161.198C122.314 160.48 123.219 160.168 117.922 164.322C111.832 169.099 110.132 170.209 107.642 171.033L105.177 171.849L101.13 170.188C98.9037 169.274 96.4598 168.26 95.6987 167.936C94.9379 167.611 94.0948 167.345 93.8255 167.345C93.5561 167.345 92.2109 168.194 90.8363 169.231L88.3371 171.116V176.084V181.052L81.8737 185.021C78.3189 187.205 75.1587 189.28 74.8511 189.634C74.0637 190.539 67.4971 201.308 67.4971 201.694C67.4971 201.869 68.2048 203.441 69.0698 205.189L70.6427 208.367L69.349 209.615M215.108 21.0962C214.444 20.0964 214.308 20.0605 211.194 20.0605H207.968L205.462 25.0978L202.957 30.1351L202.949 36.75C202.942 42.6774 202.846 43.6917 202.021 46.508C201.031 49.8877 201.495 49.2908 193.387 57.611C192.353 58.6721 191.94 58.8392 190.354 58.8392H188.518L188.077 56.8818C187.285 53.3624 186.927 52.5003 186.258 52.5003C185.906 52.5003 179.865 54.6713 172.832 57.3248L160.045 62.1489L158.335 61.2746C156.721 60.4494 156.566 60.2306 155.574 57.3826C154.712 54.9071 154.383 54.3647 153.746 54.3647C153.318 54.3647 151.028 55.5687 148.657 57.0401C146.285 58.5117 143.433 60.2735 142.32 60.9553C141.206 61.6372 139.908 62.1952 139.436 62.1952C138.688 62.1952 138.576 62.0099 138.576 60.7721C138.576 59.4278 138.443 59.2636 136.157 57.8038C134.729 56.8916 133.738 55.9984 133.738 55.6231C133.738 55.2735 132.722 53.9266 131.479 52.63C129.164 50.2137 129.05 49.8527 130.037 48.026C130.288 47.5605 130.384 47.5918 130.605 48.2124C131.805 51.5763 131.634 51.4286 134.192 51.3067L136.529 51.1954L137.531 49.3354C138.572 47.401 138.634 47.4212 139.233 49.8902C139.642 51.575 140.185 51.6311 145.643 50.5505C150.515 49.5857 150.905 49.1985 148.923 47.2899C147.757 46.1664 147.73 46.0831 148.405 45.6674C148.797 45.4264 149.405 44.956 149.756 44.6217C150.355 44.0528 150.416 44.1357 150.719 45.9269L151.043 47.8396H155.137H159.23L159.34 45.4157C159.449 43.0212 159.476 42.9665 161.561 40.8482L163.673 38.7041H166.895C169.03 38.7041 170.241 38.5467 170.483 38.2379C170.684 37.9817 171.202 36.2617 171.634 34.4161C175.777 16.7208 177.378 10.1471 177.589 9.96659C177.725 9.84959 179.805 8.1921 182.21 6.28319C184.615 4.37427 186.583 2.66618 186.583 2.48752M214.383 13.5435C212.173 14.8719 210.214 15.9588 210.029 15.9588C209.51 15.9588 207.795 13.5444 207.795 12.8135C207.795 12.2017 210.566 7.85415 211.291 7.32716C211.782 6.97099 211.26 5.89126 210.598 5.89126C209.779 5.89126 209.892 5.36481 210.991 4.05547C212.366 2.41843 213.467 2.53261 214.307 4.39972C214.739 5.35918 215.224 5.89126 215.666 5.89126C216.447 5.89126 216.574 6.64008 215.908 7.3082C215.662 7.55429 215.537 7.75553 215.631 7.75553M2.76316 87.6904C4.22646 90.0744 5.63967 92.0249 5.90364 92.0249C6.16745 92.0249 8.36965 91.282 10.7972 90.3739C13.2248 89.466 17.9741 87.6984 21.3513 86.446C24.7285 85.1936 27.6826 84.0489 27.9159 83.9021C28.1491 83.7554 28.7452 81.2863 29.2408 78.4152L30.1415 73.1948L29.3523 68.5956L28.5629 63.9966L30.9114 62.3173C32.203 61.3938 33.2599 60.5105 33.2599 60.3546C33.2599 59.7306 28.4344 49.9919 27.9085 49.5546C27.5986 49.2969 25.4384 48.7339 23.108 48.3037L18.8711 47.521L16.2968 48.4261C13.794 49.3058 13.6681 49.4123 11.7697 52.2473C9.585 55.5101 9.58438 56.3332 11.7656 56.802C13.5152 57.1779 13.6588 57.3399 12.9447 58.1306C12.3932 58.7412 11.7589 58.84 8.36166 58.8449C6.19307 58.8481 4.22008 58.9773 3.97712 59.1321C3.47211 59.4538 2 62.7334 2 63.5366C2 63.8302 2.49959 64.3748 3.11023 64.747L4.22034 65.4234L3.11023 66.4891C2.49959 67.0752 2 67.7925 2 68.0829C2 69.0192 5.36193 72.4365 6.47911 72.6355L7.52537 72.822L5.65078 77.371L3.77606 81.9199L2.05409 82.032M46.6732 4.30653C49.1994 6.00499 50.1618 6.24904 50.5312 5.28471C50.8406 4.47694 50.5104 4.13065 47.3983 2M121.955 205.629L120.032 203.647L119.257 197.259C118.83 193.746 118.481 190.125 118.481 189.214V187.556L121.281 186.533C124.365 185.406 124.331 185.536 122.112 183.271C121.573 182.722 121.458 181.925 121.458 178.77V174.936L124.23 172.259C126.903 169.679 128.323 168.986 128.735 170.06C128.835 170.323 128.579 173.182 128.165 176.413C127.286 183.276 127.242 184.495 127.877 184.504C128.133 184.508 129.231 185.339 130.316 186.351C132.095 188.009 132.271 188.312 132.095 189.42C131.988 190.097 131.637 193.251 131.316 196.429C130.994 199.608 130.649 202.596 130.548 203.069C130.427 203.638 129.394 204.551 127.493 205.772C125.913 206.786 124.454 207.614 124.249 207.613C124.044 207.612 123.012 206.719 121.955 205.629ZM83.9434 202.37C82.3871 201.271 81.2663 200.211 81.2663 199.838C81.2663 199.429 82.0141 198.827 83.3213 198.185C85.0292 197.346 85.8736 197.175 88.3202 197.175C92.1934 197.175 92.3792 197.628 89.9482 201.147C88.1305 203.777 87.6892 204.244 87.0135 204.254C86.7973 204.257 85.4158 203.409 83.9434 202.37ZM185.075 27.8544C184.24 26.2928 183.231 22.8926 183.433 22.3226C183.744 21.4411 190.664 16.6201 191.279 16.8564C192.21 17.2146 192.294 18.197 191.468 19.0781C190.791 19.7999 190.675 20.3777 190.671 23.0675C190.667 24.7974 190.542 26.3988 190.391 26.6261C190.074 27.1061 186.724 28.6365 185.99 28.6365C185.716 28.6365 185.304 28.2846 185.075 27.8544ZM33.0739 105.904C31.646 105.123 31.2131 104.679 31.2131 103.998C31.2131 103.387 32.7811 101.334 36.2371 97.4208C40.6868 92.3824 41.4098 91.7168 42.5634 91.5988L43.866 91.4656L41.8242 90.0675C39.9321 88.7717 39.5866 88.6691 37.1125 88.6691C34.588 88.6691 34.3901 88.6066 33.4791 87.5219C32.9492 86.8908 32.5157 86.2276 32.5157 86.0478C32.5157 85.6119 33.5434 84.9021 37.5396 82.5784C40.8685 80.6426 40.8889 80.6216 40.8889 79.15V77.6693L38.5444 77.5579C35.4927 77.4128 35.2456 76.8383 37.3233 74.7171L38.8826 73.1252L38.264 71.5761C37.2432 69.0199 37.4307 68.9068 42.6869 68.9068H47.2993L47.7518 66.017C48.0008 64.4277 48.2931 62.7498 48.4014 62.2884C48.5629 61.6007 48.4402 61.4484 47.7208 61.4437C47.238 61.4406 46.6676 61.3147 46.4531 61.164C45.6362 60.5902 44.052 57.352 44.052 56.2559C44.052 54.9075 44.0826 54.9106 39.9585 55.8511L36.7953 56.5725L35.3892 55.1731L33.9833 53.7737L34.9201 49.8013C36.1675 44.5115 35.7964 43.3045 33.8646 46.3686C33.0637 47.6386 32.5955 48.037 32.0037 47.9524C31.3231 47.8551 31.1873 47.5281 31.0272 45.6022L30.8409 43.3651L28.6082 43.1786C26.7336 43.022 26.3584 42.8724 26.2697 42.2464C26.1897 41.6817 26.7512 41.1385 28.5823 40.0091C30.6029 38.7631 31.0027 38.3483 31.0137 37.4879C31.0266 36.4939 30.9755 36.4664 29.5386 36.6967C28.0906 36.9286 28.0501 36.9058 28.0565 35.862C28.0601 35.2719 28.5625 33.2032 29.173 31.2651C29.7833 29.3269 30.2827 27.607 30.2827 27.4432C30.2827 27.2792 29.4683 27.145 28.4729 27.145C26.7835 27.145 26.5484 27.0221 24.9375 25.2966C23.9886 24.2799 23.2121 23.1214 23.2121 22.722C23.2121 22.251 23.8261 21.6351 24.9584 20.9702C26.5423 20.0402 26.8453 19.9827 28.2146 20.3515C29.0452 20.5752 29.824 20.6431 29.9456 20.5026C30.1865 20.224 31.0483 17.9906 32.9218 12.7894C33.5866 10.9438 34.2933 9.21996 34.4921 8.95895C34.7656 8.5999 36.4053 8.50905 41.2207 8.5861L47.5874 8.68785L48.6066 10.7386C49.1672 11.8666 49.6277 12.9773 49.6301 13.2067C49.6323 13.4362 48.6252 14.2579 47.392 15.0325C45.0054 16.5319 42.9357 18.6133 42.9357 19.5142C42.9357 19.9727 43.9557 20.0605 49.2803 20.0605H55.6249L56.3429 21.6452C57.4099 24.0006 57.2628 24.8498 55.2912 27.7171C50.0343 35.3625 47.3322 39.505 47.4955 39.6685C47.596 39.7693 49.4614 40.156 51.6405 40.528C53.8198 40.9 55.665 41.2709 55.7409 41.3526C55.8169 41.4341 57.3288 44.5209 59.1006 48.2124C61.4409 53.0879 62.7631 55.3832 63.9336 56.6021C65.396 58.1249 65.6936 58.7793 67.1551 63.6865C68.5612 68.4083 68.9679 69.3286 70.364 70.9513C71.2433 71.9731 71.9627 73.0275 71.9627 73.2943C71.9627 73.561 71.3766 74.1258 70.6604 74.5493C69.3897 75.3004 68.9565 75.9914 69.7559 75.9914C69.9749 75.9914 70.8842 75.4879 71.7766 74.8727C72.6691 74.2574 73.7169 73.7542 74.1051 73.7542C74.5709 73.7542 75.7193 75.0097 77.4804 77.4447C78.9486 79.4743 80.1498 81.2387 80.1498 81.3653C80.1498 81.4919 78.4786 83.5577 76.4358 85.956L72.7217 90.3166L74.6674 91.1003C75.7376 91.5312 76.66 92.1283 76.7175 92.4272C76.775 92.7261 75.9878 93.9086 74.968 95.055L73.114 97.1392L70.2125 97.3823C68.6166 97.516 65.3852 97.8137 63.0314 98.0441L58.7518 98.4628L57.0643 100.091C56.1362 100.987 55.1495 101.72 54.8716 101.72C54.5937 101.72 53.3211 101.058 52.0434 100.25L49.7202 98.7794L47.8467 99.4107C46.1788 99.9725 45.8885 100.236 45.2003 101.813C44.3035 103.868 43.9246 103.954 41.0573 102.76C39.9698 102.308 39.0263 102.098 38.961 102.295C38.6632 103.19 35.7597 106.939 35.3703 106.931C35.1305 106.926 34.0973 106.464 33.0739 105.904ZM20.2676 21.7777C19.9389 21.381 19.9889 20.4829 20.4792 17.9828C21.0537 15.0538 21.3059 14.488 22.8649 12.6299C25.775 9.1617 26.8603 8.2713 27.5983 8.74724C28.3564 9.23607 28.3505 9.64223 27.5283 13.5352C26.9069 16.4785 26.8603 16.5566 24.0405 19.4078C22.4687 20.9972 21.0737 22.2976 20.9404 22.2976C20.8073 22.2976 20.5043 22.0637 20.2676 21.7777Z" stroke="black" stroke-width="3"/>
2 <path fill-rule="evenodd" clip-rule="evenodd" d="M96.0648 116.507C101.153 128.734 81.7284 147.108 81.7284 147.108C81.7284 147.108 63.0031 128.734 68.0914 116.507C73.2976 103.996 90.8586 103.996 96.0648 116.507ZM82.2148 127.124C85.5286 127.124 88.2148 124.437 88.2148 121.124C88.2148 117.81 85.5286 115.124 82.2148 115.124C78.9011 115.124 76.2148 117.81 76.2148 121.124C76.2148 124.437 78.9011 127.124 82.2148 127.124Z" fill="#EB902E"/>
3 <path fill-rule="evenodd" clip-rule="evenodd" d="M180.065 104.507C185.153 116.734 165.728 135.108 165.728 135.108C165.728 135.108 147.003 116.734 152.091 104.507C157.298 91.996 174.859 91.996 180.065 104.507ZM166.215 115.124C169.529 115.124 172.215 112.437 172.215 109.124C172.215 105.81 169.529 103.124 166.215 103.124C162.901 103.124 160.215 105.81 160.215 109.124C160.215 112.437 162.901 115.124 166.215 115.124Z" fill="#35A1D1"/>
4 <path fill-rule="evenodd" clip-rule="evenodd" d="M72.0648 61.5066C77.1531 73.734 57.7284 92.108 57.7284 92.108C57.7284 92.108 39.0031 73.734 44.0914 61.5066C49.2976 48.996 66.8586 48.996 72.0648 61.5066ZM58.2148 72.1236C61.5286 72.1236 64.2148 69.4373 64.2148 66.1236C64.2148 62.8099 61.5286 60.1236 58.2148 60.1236C54.9011 60.1236 52.2148 62.8099 52.2148 66.1236C52.2148 69.4373 54.9011 72.1236 58.2148 72.1236Z" fill="#CD3B28"/>
5 <path fill-rule="evenodd" clip-rule="evenodd" d="M134.065 76.5066C139.153 88.734 119.728 107.108 119.728 107.108C119.728 107.108 101.003 88.734 106.091 76.5066C111.298 63.996 128.859 63.996 134.065 76.5066ZM120.215 87.1236C123.529 87.1236 126.215 84.4373 126.215 81.1236C126.215 77.8099 123.529 75.1236 120.215 75.1236C116.901 75.1236 114.215 77.8099 114.215 81.1236C114.215 84.4373 116.901 87.1236 120.215 87.1236Z" fill="#72B026"/>
6 </svg>
0 <svg width="218" height="216" viewBox="0 0 218 216" fill="none" xmlns="http://www.w3.org/2000/svg">
1 <path d="M9.81498 208.749L8.32636 207.988C7.26342 207.445 6.83732 206.99 6.83594 206.396C6.83495 205.939 7.52353 201.39 8.36648 196.288L9.89883 187.012L8.30974 178.905C7.43584 174.447 6.82796 170.519 6.95918 170.176C7.20341 169.539 10.6723 167.731 11.6756 167.719C11.9827 167.715 12.8802 166.706 13.6703 165.478C15.3508 162.864 15.1719 162.898 20.8298 164.119C23.0611 164.601 27.9468 165.64 31.6871 166.429L38.4875 167.863L41.8277 166.222L45.1679 164.582L48.9827 166.141L52.7975 167.7L54.8105 165.688L56.8237 163.676L58.3919 154.264L59.9603 144.852L58.7302 142.769C58.0538 141.623 56.0216 138.504 54.2143 135.838L50.9281 130.99L43.6756 128.61C39.6868 127.301 36.3001 126.043 36.1497 125.814C35.9993 125.585 35.8737 124.184 35.8706 122.702C35.8638 119.464 35.4232 119.848 43.0682 116.441C47.3273 114.543 48.6946 114.08 49.0866 114.406C49.3655 114.638 49.8005 115.754 50.0535 116.887L50.5133 118.945L52.0275 118.707C54.4745 118.322 56.3328 117.75 56.3328 117.382C56.3328 117.194 55.7331 114.84 55.0002 112.151C54.267 109.463 53.7594 107.022 53.872 106.729C54.0181 106.347 54.7134 106.194 56.3023 106.194H58.5279L60.2541 108.618L61.9802 111.042H64.3488H66.7173L66.828 108.711L66.9388 106.381L71.6607 104.765L76.3826 103.149L77.0261 99.4655C77.38 97.4396 77.7776 95.6069 77.9098 95.3928C78.136 95.026 85.3564 91.6815 87.7788 90.8216C88.3928 90.6037 89.0761 90.2849 89.2971 90.1133C89.5181 89.9419 91.4993 86.1909 93.6996 81.7779L97.7001 73.7542H99.3449H100.99V76.9236V80.093H102.118C102.77 80.093 103.335 79.8603 103.457 79.5418C103.573 79.2386 103.31 76.8633 102.871 74.2632C102.433 71.6632 102.21 69.3995 102.376 69.2331C102.542 69.0665 104.83 68.8831 107.461 68.8253L112.243 68.7203L112.962 66.3708C113.358 65.0785 113.902 63.9364 114.172 63.8328C114.441 63.7292 116.294 64.325 118.289 65.157C120.284 65.9889 121.978 66.6696 122.054 66.6696C122.129 66.6696 122.685 65.6887 123.289 64.49L124.387 62.3105L123.097 61.694C121.152 60.7648 121.129 60.1349 122.972 58.2537L124.587 56.6048L123.158 52.2056L121.728 47.8065L120.104 47.1336L118.481 46.4605V39.9648V33.4689L120.159 30.1206L121.837 26.7721L128.811 23.2839C132.647 21.3652 135.995 19.8691 136.25 19.9591C136.866 20.1754 136.842 22.2447 136.16 27.7186C135.855 30.1718 135.604 32.3784 135.602 32.6224C135.601 32.8665 136.227 33.1181 136.995 33.1817L138.39 33.2975L138.485 35.7211L138.58 38.1448L135.934 39.8221C134.092 40.9895 133.071 41.9245 132.575 42.8982L131.863 44.2973H133.191C133.922 44.2973 134.972 44.5275 135.525 44.8092C136.077 45.0908 136.909 45.4385 137.372 45.5818C138.138 45.8186 138.205 45.7329 138.116 44.6413C138.029 43.571 138.2 43.3345 139.693 42.4674C140.614 41.9323 143.16 40.6571 145.35 39.6333L149.332 37.772L149.351 36.2933C149.366 35.0255 149.124 34.566 147.655 33.0717C146.321 31.715 145.795 30.8036 145.277 28.9573C144.912 27.6529 143.483 22.7265 142.102 18.0097C140.72 13.2929 139.412 8.6958 139.194 7.79428C138.942 6.74931 138.556 6.09131 138.129 5.97961C137.761 5.88311 137.46 5.51762 137.46 5.16703C137.46 4.81664 137.046 4.1399 136.539 3.66318C135.664 2.83906 135.608 2.8314 135.393 3.50494C134.677 5.7474 134.534 5.89126 133.019 5.89126C131.681 5.89126 131.141 6.23871 127.368 9.52674C125.073 11.5263 122.069 14.1297 120.692 15.312L118.187 17.4616L114.705 16.6244C111.823 15.9314 110.439 15.3164 106.665 13.052C104.158 11.5477 102.106 10.1012 102.106 9.83756C102.106 9.57391 102.52 8.57814 103.026 7.62481C103.532 6.67132 103.864 5.89126 103.763 5.89126C103.662 5.89126 103.92 5.22295 104.336 4.40623L105.092 2.92133L103.487 3.14186L101.883 3.36214M204.626 214.124L205.984 212.463L204.936 212.457C204.359 212.454 203.701 212.328 203.473 212.178C203.246 212.027 200.441 207.768 197.24 202.714L191.421 193.524V188.105V182.686L189.095 180.915C185.573 178.234 176.292 171.15 173.528 169.034C172.181 168.003 169.467 165.929 167.496 164.426C165.526 162.923 163.84 161.412 163.749 161.07C163.659 160.727 163.28 158.868 162.906 156.937L162.226 153.427L160.542 152.351C158.649 151.142 159.067 150.935 156.787 154.21C155.829 155.586 155.578 155.75 154.942 155.409C154.285 155.056 154.206 154.618 154.206 151.299V147.583H152.887C152.162 147.583 150.495 147.853 149.183 148.183C146.914 148.754 146.807 148.831 146.986 149.768C147.089 150.31 147.387 152.549 147.648 154.745L148.123 158.737L152.217 162.645L156.312 166.553L157.771 170.52L159.23 174.488L162.126 176.79L165.022 179.091L168.638 179.277C172.005 179.451 172.263 179.52 172.371 180.277C172.434 180.725 172.224 181.6 171.903 182.222L171.319 183.354L174.578 185.629C176.371 186.88 179.931 189.368 182.489 191.157L187.141 194.41V196.813C187.141 198.802 187.029 199.237 186.492 199.341C186.135 199.41 183.839 198.446 181.39 197.199L176.937 194.932L176.522 196.477C176.113 197.998 176.13 198.046 177.623 199.57C179.067 201.044 179.14 201.229 179.14 203.425V205.731L177.3 206.523C175.786 207.176 175.016 207.904 172.951 210.635C171.064 213.131 170.228 213.954 169.581 213.954C169.104 213.954 168.72 214.18 168.72 214.46M146.935 214.527C145.576 212.886 145.763 212.316 148.051 211.116L150.083 210.05L154.347 210.914C158.448 211.746 158.723 211.757 161.526 211.199C163.129 210.88 165.328 210.507 166.413 210.371C168.325 210.132 168.415 210.068 169.319 208.281L170.251 206.438L168.734 201.879C167.265 197.462 167.176 197.305 165.875 196.833C165.014 196.522 162.743 194.573 159.557 191.411L154.583 186.476L151.392 185.631L148.201 184.787L145.006 180.727C141.823 176.682 141.805 176.667 140.202 176.667C138.213 176.667 138.332 176.839 134.663 168.656C133.102 165.172 131.643 162.21 131.421 162.073C131.2 161.936 129.194 161.542 126.964 161.198C122.314 160.48 123.219 160.168 117.922 164.322C111.832 169.099 110.132 170.209 107.642 171.033L105.177 171.849L101.13 170.188C98.9037 169.274 96.4598 168.26 95.6987 167.936C94.9379 167.611 94.0948 167.345 93.8255 167.345C93.5561 167.345 92.2109 168.194 90.8363 169.231L88.3371 171.116V176.084V181.052L81.8737 185.021C78.3189 187.205 75.1587 189.28 74.8511 189.634C74.0637 190.539 67.4971 201.308 67.4971 201.694C67.4971 201.869 68.2048 203.441 69.0698 205.189L70.6427 208.367L69.349 209.615M215.108 21.0962C214.444 20.0964 214.308 20.0605 211.194 20.0605H207.968L205.462 25.0978L202.957 30.1351L202.949 36.75C202.942 42.6774 202.846 43.6917 202.021 46.508C201.031 49.8877 201.495 49.2908 193.387 57.611C192.353 58.6721 191.94 58.8392 190.354 58.8392H188.518L188.077 56.8818C187.285 53.3624 186.927 52.5003 186.258 52.5003C185.906 52.5003 179.865 54.6713 172.832 57.3248L160.045 62.1489L158.335 61.2746C156.721 60.4494 156.566 60.2306 155.574 57.3826C154.712 54.9071 154.383 54.3647 153.746 54.3647C153.318 54.3647 151.028 55.5687 148.657 57.0401C146.285 58.5117 143.433 60.2735 142.32 60.9553C141.206 61.6372 139.908 62.1952 139.436 62.1952C138.688 62.1952 138.576 62.0099 138.576 60.7721C138.576 59.4278 138.443 59.2636 136.157 57.8038C134.729 56.8916 133.738 55.9984 133.738 55.6231C133.738 55.2735 132.722 53.9266 131.479 52.63C129.164 50.2137 129.05 49.8527 130.037 48.026C130.288 47.5605 130.384 47.5918 130.605 48.2124C131.805 51.5763 131.634 51.4286 134.192 51.3067L136.529 51.1954L137.531 49.3354C138.572 47.401 138.634 47.4212 139.233 49.8902C139.642 51.575 140.185 51.6311 145.643 50.5505C150.515 49.5857 150.905 49.1985 148.923 47.2899C147.757 46.1664 147.73 46.0831 148.405 45.6674C148.797 45.4264 149.405 44.956 149.756 44.6217C150.355 44.0528 150.416 44.1357 150.719 45.9269L151.043 47.8396H155.137H159.23L159.34 45.4157C159.449 43.0212 159.476 42.9665 161.561 40.8482L163.673 38.7041H166.895C169.03 38.7041 170.241 38.5467 170.483 38.2379C170.684 37.9817 171.202 36.2617 171.634 34.4161C175.777 16.7208 177.378 10.1471 177.589 9.96659C177.725 9.84959 179.805 8.1921 182.21 6.28319C184.615 4.37427 186.583 2.66618 186.583 2.48752M214.383 13.5435C212.173 14.8719 210.214 15.9588 210.029 15.9588C209.51 15.9588 207.795 13.5444 207.795 12.8135C207.795 12.2017 210.566 7.85415 211.291 7.32716C211.782 6.97099 211.26 5.89126 210.598 5.89126C209.779 5.89126 209.892 5.36481 210.991 4.05547C212.366 2.41843 213.467 2.53261 214.307 4.39972C214.739 5.35918 215.224 5.89126 215.666 5.89126C216.447 5.89126 216.574 6.64008 215.908 7.3082C215.662 7.55429 215.537 7.75553 215.631 7.75553M2.76316 87.6904C4.22646 90.0744 5.63967 92.0249 5.90364 92.0249C6.16745 92.0249 8.36965 91.282 10.7972 90.3739C13.2248 89.466 17.9741 87.6984 21.3513 86.446C24.7285 85.1936 27.6826 84.0489 27.9159 83.9021C28.1491 83.7554 28.7452 81.2863 29.2408 78.4152L30.1415 73.1948L29.3523 68.5956L28.5629 63.9966L30.9114 62.3173C32.203 61.3938 33.2599 60.5105 33.2599 60.3546C33.2599 59.7306 28.4344 49.9919 27.9085 49.5546C27.5986 49.2969 25.4384 48.7339 23.108 48.3037L18.8711 47.521L16.2968 48.4261C13.794 49.3058 13.6681 49.4123 11.7697 52.2473C9.585 55.5101 9.58438 56.3332 11.7656 56.802C13.5152 57.1779 13.6588 57.3399 12.9447 58.1306C12.3932 58.7412 11.7589 58.84 8.36166 58.8449C6.19307 58.8481 4.22008 58.9773 3.97712 59.1321C3.47211 59.4538 2 62.7334 2 63.5366C2 63.8302 2.49959 64.3748 3.11023 64.747L4.22034 65.4234L3.11023 66.4891C2.49959 67.0752 2 67.7925 2 68.0829C2 69.0192 5.36193 72.4365 6.47911 72.6355L7.52537 72.822L5.65078 77.371L3.77606 81.9199L2.05409 82.032M46.6732 4.30653C49.1994 6.00499 50.1618 6.24904 50.5312 5.28471C50.8406 4.47694 50.5104 4.13065 47.3983 2M121.955 205.629L120.032 203.647L119.257 197.259C118.83 193.746 118.481 190.125 118.481 189.214V187.556L121.281 186.533C124.365 185.406 124.331 185.536 122.112 183.271C121.573 182.722 121.458 181.925 121.458 178.77V174.936L124.23 172.259C126.903 169.679 128.323 168.986 128.735 170.06C128.835 170.323 128.579 173.182 128.165 176.413C127.286 183.276 127.242 184.495 127.877 184.504C128.133 184.508 129.231 185.339 130.316 186.351C132.095 188.009 132.271 188.312 132.095 189.42C131.988 190.097 131.637 193.251 131.316 196.429C130.994 199.608 130.649 202.596 130.548 203.069C130.427 203.638 129.394 204.551 127.493 205.772C125.913 206.786 124.454 207.614 124.249 207.613C124.044 207.612 123.012 206.719 121.955 205.629ZM83.9434 202.37C82.3871 201.271 81.2663 200.211 81.2663 199.838C81.2663 199.429 82.0141 198.827 83.3213 198.185C85.0292 197.346 85.8736 197.175 88.3202 197.175C92.1934 197.175 92.3792 197.628 89.9482 201.147C88.1305 203.777 87.6892 204.244 87.0135 204.254C86.7973 204.257 85.4158 203.409 83.9434 202.37ZM185.075 27.8544C184.24 26.2928 183.231 22.8926 183.433 22.3226C183.744 21.4411 190.664 16.6201 191.279 16.8564C192.21 17.2146 192.294 18.197 191.468 19.0781C190.791 19.7999 190.675 20.3777 190.671 23.0675C190.667 24.7974 190.542 26.3988 190.391 26.6261C190.074 27.1061 186.724 28.6365 185.99 28.6365C185.716 28.6365 185.304 28.2846 185.075 27.8544ZM33.0739 105.904C31.646 105.123 31.2131 104.679 31.2131 103.998C31.2131 103.387 32.7811 101.334 36.2371 97.4208C40.6868 92.3824 41.4098 91.7168 42.5634 91.5988L43.866 91.4656L41.8242 90.0675C39.9321 88.7717 39.5866 88.6691 37.1125 88.6691C34.588 88.6691 34.3901 88.6066 33.4791 87.5219C32.9492 86.8908 32.5157 86.2276 32.5157 86.0478C32.5157 85.6119 33.5434 84.9021 37.5396 82.5784C40.8685 80.6426 40.8889 80.6216 40.8889 79.15V77.6693L38.5444 77.5579C35.4927 77.4128 35.2456 76.8383 37.3233 74.7171L38.8826 73.1252L38.264 71.5761C37.2432 69.0199 37.4307 68.9068 42.6869 68.9068H47.2993L47.7518 66.017C48.0008 64.4277 48.2931 62.7498 48.4014 62.2884C48.5629 61.6007 48.4402 61.4484 47.7208 61.4437C47.238 61.4406 46.6676 61.3147 46.4531 61.164C45.6362 60.5902 44.052 57.352 44.052 56.2559C44.052 54.9075 44.0826 54.9106 39.9585 55.8511L36.7953 56.5725L35.3892 55.1731L33.9833 53.7737L34.9201 49.8013C36.1675 44.5115 35.7964 43.3045 33.8646 46.3686C33.0637 47.6386 32.5955 48.037 32.0037 47.9524C31.3231 47.8551 31.1873 47.5281 31.0272 45.6022L30.8409 43.3651L28.6082 43.1786C26.7336 43.022 26.3584 42.8724 26.2697 42.2464C26.1897 41.6817 26.7512 41.1385 28.5823 40.0091C30.6029 38.7631 31.0027 38.3483 31.0137 37.4879C31.0266 36.4939 30.9755 36.4664 29.5386 36.6967C28.0906 36.9286 28.0501 36.9058 28.0565 35.862C28.0601 35.2719 28.5625 33.2032 29.173 31.2651C29.7833 29.3269 30.2827 27.607 30.2827 27.4432C30.2827 27.2792 29.4683 27.145 28.4729 27.145C26.7835 27.145 26.5484 27.0221 24.9375 25.2966C23.9886 24.2799 23.2121 23.1214 23.2121 22.722C23.2121 22.251 23.8261 21.6351 24.9584 20.9702C26.5423 20.0402 26.8453 19.9827 28.2146 20.3515C29.0452 20.5752 29.824 20.6431 29.9456 20.5026C30.1865 20.224 31.0483 17.9906 32.9218 12.7894C33.5866 10.9438 34.2933 9.21996 34.4921 8.95895C34.7656 8.5999 36.4053 8.50905 41.2207 8.5861L47.5874 8.68785L48.6066 10.7386C49.1672 11.8666 49.6277 12.9773 49.6301 13.2067C49.6323 13.4362 48.6252 14.2579 47.392 15.0325C45.0054 16.5319 42.9357 18.6133 42.9357 19.5142C42.9357 19.9727 43.9557 20.0605 49.2803 20.0605H55.6249L56.3429 21.6452C57.4099 24.0006 57.2628 24.8498 55.2912 27.7171C50.0343 35.3625 47.3322 39.505 47.4955 39.6685C47.596 39.7693 49.4614 40.156 51.6405 40.528C53.8198 40.9 55.665 41.2709 55.7409 41.3526C55.8169 41.4341 57.3288 44.5209 59.1006 48.2124C61.4409 53.0879 62.7631 55.3832 63.9336 56.6021C65.396 58.1249 65.6936 58.7793 67.1551 63.6865C68.5612 68.4083 68.9679 69.3286 70.364 70.9513C71.2433 71.9731 71.9627 73.0275 71.9627 73.2943C71.9627 73.561 71.3766 74.1258 70.6604 74.5493C69.3897 75.3004 68.9565 75.9914 69.7559 75.9914C69.9749 75.9914 70.8842 75.4879 71.7766 74.8727C72.6691 74.2574 73.7169 73.7542 74.1051 73.7542C74.5709 73.7542 75.7193 75.0097 77.4804 77.4447C78.9486 79.4743 80.1498 81.2387 80.1498 81.3653C80.1498 81.4919 78.4786 83.5577 76.4358 85.956L72.7217 90.3166L74.6674 91.1003C75.7376 91.5312 76.66 92.1283 76.7175 92.4272C76.775 92.7261 75.9878 93.9086 74.968 95.055L73.114 97.1392L70.2125 97.3823C68.6166 97.516 65.3852 97.8137 63.0314 98.0441L58.7518 98.4628L57.0643 100.091C56.1362 100.987 55.1495 101.72 54.8716 101.72C54.5937 101.72 53.3211 101.058 52.0434 100.25L49.7202 98.7794L47.8467 99.4107C46.1788 99.9725 45.8885 100.236 45.2003 101.813C44.3035 103.868 43.9246 103.954 41.0573 102.76C39.9698 102.308 39.0263 102.098 38.961 102.295C38.6632 103.19 35.7597 106.939 35.3703 106.931C35.1305 106.926 34.0973 106.464 33.0739 105.904ZM20.2676 21.7777C19.9389 21.381 19.9889 20.4829 20.4792 17.9828C21.0537 15.0538 21.3059 14.488 22.8649 12.6299C25.775 9.1617 26.8603 8.2713 27.5983 8.74724C28.3564 9.23607 28.3505 9.64223 27.5283 13.5352C26.9069 16.4785 26.8603 16.5566 24.0405 19.4078C22.4687 20.9972 21.0737 22.2976 20.9404 22.2976C20.8073 22.2976 20.5043 22.0637 20.2676 21.7777Z" stroke="white" stroke-width="3"/>
2 <path fill-rule="evenodd" clip-rule="evenodd" d="M96.0648 116.507C101.153 128.734 81.7284 147.108 81.7284 147.108C81.7284 147.108 63.0031 128.734 68.0914 116.507C73.2976 103.996 90.8586 103.996 96.0648 116.507ZM82.2148 127.124C85.5286 127.124 88.2148 124.437 88.2148 121.124C88.2148 117.81 85.5286 115.124 82.2148 115.124C78.9011 115.124 76.2148 117.81 76.2148 121.124C76.2148 124.437 78.9011 127.124 82.2148 127.124Z" fill="#EB902E"/>
3 <path fill-rule="evenodd" clip-rule="evenodd" d="M180.065 104.507C185.153 116.734 165.728 135.108 165.728 135.108C165.728 135.108 147.003 116.734 152.091 104.507C157.298 91.996 174.859 91.996 180.065 104.507ZM166.215 115.124C169.529 115.124 172.215 112.437 172.215 109.124C172.215 105.81 169.529 103.124 166.215 103.124C162.901 103.124 160.215 105.81 160.215 109.124C160.215 112.437 162.901 115.124 166.215 115.124Z" fill="#35A1D1"/>
4 <path fill-rule="evenodd" clip-rule="evenodd" d="M72.0648 61.5066C77.1531 73.734 57.7284 92.108 57.7284 92.108C57.7284 92.108 39.0031 73.734 44.0914 61.5066C49.2976 48.996 66.8586 48.996 72.0648 61.5066ZM58.2148 72.1236C61.5286 72.1236 64.2148 69.4373 64.2148 66.1236C64.2148 62.8099 61.5286 60.1236 58.2148 60.1236C54.9011 60.1236 52.2148 62.8099 52.2148 66.1236C52.2148 69.4373 54.9011 72.1236 58.2148 72.1236Z" fill="#CD3B28"/>
5 <path fill-rule="evenodd" clip-rule="evenodd" d="M134.065 76.5066C139.153 88.734 119.728 107.108 119.728 107.108C119.728 107.108 101.003 88.734 106.091 76.5066C111.298 63.996 128.859 63.996 134.065 76.5066ZM120.215 87.1236C123.529 87.1236 126.215 84.4373 126.215 81.1236C126.215 77.8099 123.529 75.1236 120.215 75.1236C116.901 75.1236 114.215 77.8099 114.215 81.1236C114.215 84.4373 116.901 87.1236 120.215 87.1236Z" fill="#72B026"/>
6 </svg>
6868 // Protect Icinga filter syntax
6969 if (isFilterParameter(sURLVariables[i])) {
7070 params.push(sURLVariables[i]);
71 continue;
71
7272 }
7373 }
7474
7575 return params.join("&")
76 }
77
78 function showHost(hostname) {
79 if (cache[id].hostMarkers[hostname]) {
80 var el = cache[id].hostMarkers[hostname];
81 cache[id].markers.zoomToShowLayer(el, function () {
82 el.openPopup();
83 })
84 }
7685 }
7786
7887 function showDefaultView() {
216225 this.module = module;
217226 this.initialize();
218227 this.timer;
219 this.module.icinga.logger.debug('Map module loaded');
228 // this.module.icinga.logger.debug('Map module loaded');
220229 };
221230
222231 Map.prototype = {
252261 var _this = this;
253262
254263 if (cache.length == 0) {
255 this.removeTimer(id)
264 this.removeTimer(id);
256265 return this
257266 }
258267
269278 var id = parameters.id;
270279 var show_host = parameters.show_host;
271280 var $that = this;
272
273 function showHost(hostname) {
274 if (cache[id].hostMarkers[hostname]) {
275 var el = cache[id].hostMarkers[hostname];
276 cache[id].markers.zoomToShowLayer(el, function () {
277 el.openPopup();
278 })
279 }
280 }
281281
282282 function removeOldMarkers(id, data) {
283283 // remove old markers
313313 $.each(json, function (type, element) {
314314 $.each(element, function (identifier, data) {
315315 if (data.length < 1 || data['coordinates'] == "") {
316 console.log('found empty coordinates: ' + data)
316 console.log('found empty coordinates: ' + data);
317317 return true
318318 }
319319
363363 + service['service_name']
364364 + '">';
365365 services += service_display_name;
366 services += '</a>'
367 services += '</div>'
366 services += '</a>';
367 services += '</div>';
368368 services += '</td>';
369369
370370 services += '</tr>';
435435 }
436436
437437 marker.bindPopup(info);
438
439 if (popup_mouseover) {
440 marker.on('mouseover', function (e) {
441 this.openPopup();
442 });
443 marker.on('mouseout', function (e) {
444 // this.closePopup();
445 });
446 }
438447 })
439448 });
440449
479488 });
480489 osm.addTo(cache[id].map);
481490
491 var options = {
492 limit: 10,
493 filter: function () {
494 return filterParams(id, cache[id].parameters);
495 }
496 };
497 var control = L.Control.openCageSearch(options).addTo(cache[id].map);
498
499 control.setMarker(function (el) {
500 if (el['id'] && cache[id].hostMarkers[el.id]) {
501 showHost(el.id)
502 } else {
503 var geocodeMarker = new L.Marker(el.center, {
504 icon: L.AwesomeMarkers.icon({
505 icon: 'globe',
506 markerColor: 'blue',
507 className: 'awesome-marker'
508 })
509 })
510 .bindPopup(el.name)
511 .addTo(cache[id].map)
512 .openPopup();
513
514 cache[id].map.setView(geocodeMarker.getLatLng(), map_max_zoom);
515
516 geocodeMarker.on('popupclose', function (evt) {
517 cache[id].map.removeLayer(evt.target);
518 });
519 }
520 });
521
482522 cache[id].markers = new L.MarkerClusterGroup({
483523 iconCreateFunction: function (cluster) {
484 var childCount = cluster_problem_count ? 0 : cluster.getChildCount();
524 var childCount = cluster.getChildCount();
525 var childProblem = 0;
485526
486527 var states = [];
487528 $.each(cluster.getAllChildMarkers(), function (id, el) {
488529 states.push(el.options.state);
489530
490 if (cluster_problem_count && el.options.state > 0) {
491 childCount++;
531 if (el.options.state > 0) {
532 childProblem++;
492533 }
493534 });
494535
495536 var worstState = getWorstState(states);
496537 var c = ' marker-cluster-' + worstState;
497
538 var clusterLabel = childProblem + '/' + childCount;
539
540 if (cluster_problem_count) {
541 clusterLabel = childProblem;
542 }
543
498544 return new L.DivIcon({
499 html: '<div><span>' + childCount + '</span></div>',
545 html: '<div><span>' + clusterLabel + '</span></div>',
500546 className: 'marker-cluster' + c,
501547 iconSize: new L.Point(40, 40)
502548 });
523569
524570 L.control.zoom({
525571 zoomInTitle: translation['btn-zoom-in'],
526 zoomOutTitle: translation['btn-zoom-in']
572 zoomOutTitle: translation['btn-zoom-out']
527573 }
528574 ).addTo(cache[id].map);
529575
604650 marker.addTo(cache[id].markers);
605651
606652 marker.on('popupclose', function (evt) {
607 cache[id].markers.removeLayer(marker);
653 cache[id].markers.removeLayer(evt.target);
608654 });
609655
610656 cache[id].markers.zoomToShowLayer(marker, function () {
0 (function (factory) {
1 // Packaging/modules magic dance
2 var L;
3 if (typeof define === 'function' && define.amd) {
4 // AMD
5 define(['leaflet'], factory);
6 } else if (typeof module !== 'undefined') {
7 // Node/CommonJS
8 L = require('leaflet');
9 module.exports = factory(L);
10 } else {
11 // Browser globals
12 if (typeof window.L === 'undefined') {
13 throw 'Leaflet must be loaded first';
14 }
15 factory(window.L);
16 }
17 }(function (L) {
18 'use strict';
19 L.Control.OpenCageSearch = L.Control.extend({
20 options: {
21 showResultIcons: false,
22 collapsed: true,
23 expand: 'click',
24 position: 'topright',
25 placeholder: 'Search...',
26 errorMessage: 'Nothing found.',
27 limit: 5
28 },
29
30 _callbackId: 0,
31
32 initialize: function (options) {
33 L.Util.setOptions(this, options);
34 if (!this.options.geocoder) {
35 this.options.geocoder = new L.Control.OpenCageSearch.Geocoder(this.options);
36 }
37 },
38
39 setMarker: function (f) {
40 this._setMarkerFunction = f;
41 },
42
43 onAdd: function (map) {
44 var className = 'leaflet-control-ocd-search';
45 var container = L.DomUtil.create('div', className);
46 var icon_container = L.DomUtil.create('div', 'leaflet-control-ocd-search-icon', container);
47 var icon = L.DomUtil.create('span', '', icon_container);
48 var form = this._form = L.DomUtil.create('form', className + '-form', container);
49 var input;
50
51 this._map = map;
52 this._container = container;
53 input = this._input = L.DomUtil.create('input');
54 input.type = 'text';
55 input.placeholder = this.options.placeholder;
56
57 L.DomEvent.addListener(input, 'keydown', this._keydown, this);
58 L.DomEvent.addListener(window, 'keydown', this._hotkeySearch, this);
59
60 this._errorElement = document.createElement('div');
61 this._errorElement.className = className + '-form-no-error';
62 this._errorElement.innerHTML = this.options.errorMessage;
63
64 this._alts = L.DomUtil.create('ul', className + '-alternatives leaflet-control-ocd-search-alternatives-minimized');
65
66 form.appendChild(input);
67 form.appendChild(this._errorElement);
68 container.appendChild(this._alts);
69
70 L.DomEvent.addListener(form, 'submit', this._geocode, this);
71
72 if (this.options.collapsed) {
73 if (this.options.expand === 'click') {
74 L.DomEvent.addListener(icon, 'click', function (e) {
75 // TODO: touch
76 if (e.button === 0 && e.detail !== 2) {
77 this._toggle();
78 }
79 }, this);
80 } else {
81 L.DomEvent.addListener(icon, 'mouseover', this._expand, this);
82 L.DomEvent.addListener(icon, 'mouseout', this._collapse, this);
83 this._map.on('movestart', this._collapse, this);
84 }
85 } else {
86 this._expand();
87 }
88
89 L.DomEvent.disableClickPropagation(container);
90
91 return container;
92 },
93
94 _geocodeResult: function (results) {
95 L.DomUtil.removeClass(this._container, 'leaflet-control-ocd-search-spinner');
96 if (results.length === 1) {
97 this._geocodeResultSelected(results[0]);
98 } else if (results.length > 0) {
99 this._alts.innerHTML = '';
100 this._results = results;
101 L.DomUtil.removeClass(this._alts, 'leaflet-control-ocd-search-alternatives-minimized');
102 for (var i = 0; i < results.length; i++) {
103 this._alts.appendChild(this._createAlt(results[i], i));
104 }
105 } else {
106 L.DomUtil.addClass(this._errorElement, 'leaflet-control-ocd-search-error');
107 }
108 },
109
110 markGeocode: function (result) {
111 if (result.bounds) {
112 this._map.fitBounds(result.bounds);
113 } else {
114 this._map.panTo(result.center);
115 }
116
117 if (this._setMarkerFunction) {
118 this._setMarkerFunction(result);
119 } else {
120 if (this._geocodeMarker) {
121 this._map.removeLayer(this._geocodeMarker);
122 }
123
124 this._geocodeMarker = new L.Marker(result.center, {
125 icon: L.AwesomeMarkers.icon({
126 icon: 'globe',
127 markerColor: 'blue',
128 className: 'awesome-marker'
129 })
130 })
131 .bindPopup(result.name)
132 .addTo(this._map)
133 .openPopup();
134
135 this._geocodeMarker.on('popupclose', function (evt) {
136 this._map.removeLayer(evt.target);
137 });
138 }
139
140 return this;
141 },
142
143 getMarker: function () {
144 return this._geocodeMarker;
145 },
146
147 _geocode: function (event) {
148 L.DomEvent.preventDefault(event);
149 L.DomEvent.stopPropagation(event);
150
151 // L.DomUtil.addClass(this._container, 'leaflet-control-ocd-search-spinner');
152 this._clearResults();
153 this.options.geocoder.geocode(this._input.value, this._geocodeResult, this);
154
155 return false;
156 },
157
158 _geocodeResultSelected: function (result) {
159 if (this.options.collapsed) {
160 this._collapse();
161 } else {
162 this._clearResults();
163 }
164
165 this.markGeocode(result);
166 },
167
168 _toggle: function () {
169 if (this._container.className.indexOf('leaflet-control-ocd-search-expanded') >= 0) {
170 this._collapse();
171 } else {
172 this._expand();
173 }
174 },
175
176 _expand: function () {
177 L.DomUtil.addClass(this._container, 'leaflet-control-ocd-search-expanded');
178 this._input.select();
179 },
180
181 _collapse: function () {
182 this._container.className = this._container.className.replace(' leaflet-control-ocd-search-expanded', '');
183 L.DomUtil.addClass(this._alts, 'leaflet-control-ocd-search-alternatives-minimized');
184 L.DomUtil.removeClass(this._errorElement, 'leaflet-control-ocd-search-error');
185 },
186
187 _clearResults: function () {
188 L.DomUtil.addClass(this._alts, 'leaflet-control-ocd-search-alternatives-minimized');
189 this._selection = null;
190 L.DomUtil.removeClass(this._errorElement, 'leaflet-control-ocd-search-error');
191 },
192
193 _createAlt: function (result, index) {
194 var icon = result['icon'] || "globe";
195 var li = document.createElement('li');
196 li.classList.add("leaflet-search-result-" + icon);
197 li.innerHTML = '<a href="#" data-result-index="' + index + '">' +
198 (this.options.showResultIcons && result.icon ?
199 '<img src="' + result.icon + '"/>' :
200 '') +
201 result.name + '</a>';
202 L.DomEvent.addListener(li, 'click', function clickHandler() {
203 this._geocodeResultSelected(result);
204 }, this);
205
206 return li;
207 },
208
209 _hotkeySearch: function (e) {
210 var key = e.which;
211
212 // ignore hotkey if map is not visible
213 if (document.querySelectorAll('.module-map').length == 0) {
214 return;
215 }
216
217 if (key === 114 || (e.ctrlKey && key === 70)) {
218 e.preventDefault();
219 this._toggle();
220 }
221 },
222
223 _keydown: function (e) {
224 var key = e.which;
225 var _this = this;
226
227 var select = function select(dir) {
228 if (_this._selection) {
229 L.DomUtil.removeClass(_this._selection.firstChild.parentElement, 'leaflet-control-ocd-search-selected');
230 _this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
231 }
232
233 if (!_this._selection) {
234 _this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
235 }
236
237 if (_this._selection) {
238 L.DomUtil.addClass(_this._selection.firstChild.parentElement, 'leaflet-control-ocd-search-selected');
239 }
240 };
241
242 switch (key) {
243 // Up
244 case 38:
245 select(-1);
246 L.DomEvent.preventDefault(e);
247 break;
248 // Down
249 case 40:
250 select(1);
251 L.DomEvent.preventDefault(e);
252 break;
253 // Enter
254 case 13:
255 if (this._selection) {
256 var index = parseInt(this._selection.firstChild.getAttribute('data-result-index'), 10);
257 this._geocodeResultSelected(this._results[index]);
258 this._clearResults();
259 L.DomEvent.preventDefault(e);
260 }
261 }
262 return true;
263 }
264 });
265
266 L.Control.openCageSearch = function (id, options) {
267 return new L.Control.OpenCageSearch(id, options);
268 };
269
270 L.Control.OpenCageSearch.callbackId = 0;
271 L.Control.OpenCageSearch.jsonp = function (url, params, callback, context, jsonpParam) {
272 var callbackId = '_ocd_geocoder_' + (L.Control.OpenCageSearch.callbackId++);
273
274 params[jsonpParam || 'callback'] = callbackId;
275 window[callbackId] = L.Util.bind(callback, context);
276 var script = document.createElement('script');
277 script.type = 'text/javascript';
278 script.src = url + L.Util.getParamString(params);
279
280 if ("filter" in context.options) {
281 script.src += '&' + context.options.filter();
282 }
283
284 script.id = callbackId;
285 script.addEventListener('error', function () {
286 callback({results: []});
287 });
288 script.addEventListener('abort', function () {
289 callback({results: []});
290 });
291 document.getElementsByTagName('head')[0].appendChild(script);
292 };
293
294 L.Control.OpenCageSearch.Geocoder = L.Class.extend({
295 options: {
296 serviceUrl: 'https://api.opencagedata.com/geocode/v1/',
297 geocodingQueryParams: {},
298 reverseQueryParams: {},
299 limit: 5,
300 lite: 0,
301 },
302
303 initialize: function (options) {
304 L.Util.setOptions(this, options);
305 },
306
307 geocode: function (query, cb, context) {
308 var proximity = {};
309 if (context && context._map && context._map.getCenter()) {
310 var center = context._map.getCenter();
311 proximity.proximity = center.lat + "," + center.lng;
312 }
313
314 L.Control.OpenCageSearch.jsonp(icinga.config.baseUrl + '/map/search', L.extend({
315 q: query,
316 limit: this.options.limit,
317 lite: this.options.lite,
318 }, proximity, this.options.geocodingQueryParams),
319 function (data) {
320 var results = [];
321
322 if (data.hosts.length) {
323 results = results.concat(data.hosts)
324 }
325
326 if (data.services.length) {
327 results = results.concat(data.services)
328 }
329
330 if (data.ocg.length) {
331 results = results.concat(data.ocg)
332 }
333
334 cb.call(context, results);
335 }, this, 'jsonp');
336 },
337
338 reverse: function (location, scale, cb, context) {
339 this.geocode(location, cb, context);
340 }
341 });
342
343 L.Control.OpenCageSearch.geocoder = function (options) {
344 return new L.Control.OpenCageSearch.Geocoder(options);
345 };
346
347 return L.Control.OpenCageSearch;
348 }));
00 /* @preserve
1 * Leaflet 1.3.1+Detached: ba6f97fff8647e724e4dfe66d2ed7da11f908989.ba6f97f, a JS library for interactive maps. http://leafletjs.com
2 * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade
1 * Leaflet 1.7.1, a JS library for interactive maps. http://leafletjs.com
2 * (c) 2010-2019 Vladimir Agafonkin, (c) 2010-2011 CloudMade
33 */
44
55 (function (global, factory) {
6 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
7 typeof define === 'function' && define.amd ? define(['exports'], factory) :
8 (factory((global.L = {})));
6 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
7 typeof define === 'function' && define.amd ? define(['exports'], factory) :
8 (factory((global.L = {})));
99 }(this, (function (exports) { 'use strict';
1010
11 var version = "1.3.1+HEAD.ba6f97f";
12
13 /*
14 * @namespace Util
15 *
16 * Various utility functions, used by Leaflet internally.
17 */
18
19 var freeze = Object.freeze;
20 Object.freeze = function (obj) { return obj; };
21
22 // @function extend(dest: Object, src?: Object): Object
23 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
24 function extend(dest) {
25 var i, j, len, src;
26
27 for (j = 1, len = arguments.length; j < len; j++) {
28 src = arguments[j];
29 for (i in src) {
30 dest[i] = src[i];
31 }
32 }
33 return dest;
34 }
35
36 // @function create(proto: Object, properties?: Object): Object
37 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
38 var create = Object.create || (function () {
39 function F() {}
40 return function (proto) {
41 F.prototype = proto;
42 return new F();
43 };
44 })();
45
46 // @function bind(fn: Function, …): Function
47 // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
48 // Has a `L.bind()` shortcut.
49 function bind(fn, obj) {
50 var slice = Array.prototype.slice;
51
52 if (fn.bind) {
53 return fn.bind.apply(fn, slice.call(arguments, 1));
54 }
55
56 var args = slice.call(arguments, 2);
57
58 return function () {
59 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
60 };
61 }
62
63 // @property lastId: Number
64 // Last unique ID used by [`stamp()`](#util-stamp)
65 var lastId = 0;
66
67 // @function stamp(obj: Object): Number
68 // Returns the unique ID of an object, assigning it one if it doesn't have it.
69 function stamp(obj) {
70 /*eslint-disable */
71 obj._leaflet_id = obj._leaflet_id || ++lastId;
72 return obj._leaflet_id;
73 /* eslint-enable */
74 }
75
76 // @function throttle(fn: Function, time: Number, context: Object): Function
77 // Returns a function which executes function `fn` with the given scope `context`
78 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
79 // `fn` will be called no more than one time per given amount of `time`. The arguments
80 // received by the bound function will be any arguments passed when binding the
81 // function, followed by any arguments passed when invoking the bound function.
82 // Has an `L.throttle` shortcut.
83 function throttle(fn, time, context) {
84 var lock, args, wrapperFn, later;
85
86 later = function () {
87 // reset lock and call if queued
88 lock = false;
89 if (args) {
90 wrapperFn.apply(context, args);
91 args = false;
92 }
93 };
94
95 wrapperFn = function () {
96 if (lock) {
97 // called too soon, queue to call later
98 args = arguments;
99
100 } else {
101 // call and lock until later
102 fn.apply(context, arguments);
103 setTimeout(later, time);
104 lock = true;
105 }
106 };
107
108 return wrapperFn;
109 }
110
111 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
112 // Returns the number `num` modulo `range` in such a way so it lies within
113 // `range[0]` and `range[1]`. The returned value will be always smaller than
114 // `range[1]` unless `includeMax` is set to `true`.
115 function wrapNum(x, range, includeMax) {
116 var max = range[1],
117 min = range[0],
118 d = max - min;
119 return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
120 }
121
122 // @function falseFn(): Function
123 // Returns a function which always returns `false`.
124 function falseFn() { return false; }
125
126 // @function formatNum(num: Number, digits?: Number): Number
127 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
128 function formatNum(num, digits) {
129 var pow = Math.pow(10, (digits === undefined ? 6 : digits));
130 return Math.round(num * pow) / pow;
131 }
132
133 // @function trim(str: String): String
134 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
135 function trim(str) {
136 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
137 }
138
139 // @function splitWords(str: String): String[]
140 // Trims and splits the string on whitespace and returns the array of parts.
141 function splitWords(str) {
142 return trim(str).split(/\s+/);
143 }
144
145 // @function setOptions(obj: Object, options: Object): Object
146 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
147 function setOptions(obj, options) {
148 if (!obj.hasOwnProperty('options')) {
149 obj.options = obj.options ? create(obj.options) : {};
150 }
151 for (var i in options) {
152 obj.options[i] = options[i];
153 }
154 return obj.options;
155 }
156
157 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
158 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
159 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
160 // be appended at the end. If `uppercase` is `true`, the parameter names will
161 // be uppercased (e.g. `'?A=foo&B=bar'`)
162 function getParamString(obj, existingUrl, uppercase) {
163 var params = [];
164 for (var i in obj) {
165 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
166 }
167 return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
168 }
169
170 var templateRe = /\{ *([\w_-]+) *\}/g;
171
172 // @function template(str: String, data: Object): String
173 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
174 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
175 // `('Hello foo, bar')`. You can also specify functions instead of strings for
176 // data values — they will be evaluated passing `data` as an argument.
177 function template(str, data) {
178 return str.replace(templateRe, function (str, key) {
179 var value = data[key];
180
181 if (value === undefined) {
182 throw new Error('No value provided for variable ' + str);
183
184 } else if (typeof value === 'function') {
185 value = value(data);
186 }
187 return value;
188 });
189 }
190
191 // @function isArray(obj): Boolean
192 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
193 var isArray = Array.isArray || function (obj) {
194 return (Object.prototype.toString.call(obj) === '[object Array]');
195 };
196
197 // @function indexOf(array: Array, el: Object): Number
198 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
199 function indexOf(array, el) {
200 for (var i = 0; i < array.length; i++) {
201 if (array[i] === el) { return i; }
202 }
203 return -1;
204 }
205
206 // @property emptyImageUrl: String
207 // Data URI string containing a base64-encoded empty GIF image.
208 // Used as a hack to free memory from unused images on WebKit-powered
209 // mobile devices (by setting image `src` to this string).
210 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
211
212 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
213
214 function getPrefixed(name) {
215 return window['webkit' + name] || window['moz' + name] || window['ms' + name];
216 }
217
218 var lastTime = 0;
219
220 // fallback for IE 7-8
221 function timeoutDefer(fn) {
222 var time = +new Date(),
223 timeToCall = Math.max(0, 16 - (time - lastTime));
224
225 lastTime = time + timeToCall;
226 return window.setTimeout(fn, timeToCall);
227 }
228
229 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
230 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
231 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
232
233 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
234 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
235 // `context` if given. When `immediate` is set, `fn` is called immediately if
236 // the browser doesn't have native support for
237 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
238 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
239 function requestAnimFrame(fn, context, immediate) {
240 if (immediate && requestFn === timeoutDefer) {
241 fn.call(context);
242 } else {
243 return requestFn.call(window, bind(fn, context));
244 }
245 }
246
247 // @function cancelAnimFrame(id: Number): undefined
248 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
249 function cancelAnimFrame(id) {
250 if (id) {
251 cancelFn.call(window, id);
252 }
253 }
254
255
256 var Util = (Object.freeze || Object)({
257 freeze: freeze,
258 extend: extend,
259 create: create,
260 bind: bind,
261 lastId: lastId,
262 stamp: stamp,
263 throttle: throttle,
264 wrapNum: wrapNum,
265 falseFn: falseFn,
266 formatNum: formatNum,
267 trim: trim,
268 splitWords: splitWords,
269 setOptions: setOptions,
270 getParamString: getParamString,
271 template: template,
272 isArray: isArray,
273 indexOf: indexOf,
274 emptyImageUrl: emptyImageUrl,
275 requestFn: requestFn,
276 cancelFn: cancelFn,
277 requestAnimFrame: requestAnimFrame,
278 cancelAnimFrame: cancelAnimFrame
279 });
280
281 // @class Class
282 // @aka L.Class
283
284 // @section
285 // @uninheritable
286
287 // Thanks to John Resig and Dean Edwards for inspiration!
288
289 function Class() {}
290
291 Class.extend = function (props) {
292
293 // @function extend(props: Object): Function
294 // [Extends the current class](#class-inheritance) given the properties to be included.
295 // Returns a Javascript function that is a class constructor (to be called with `new`).
296 var NewClass = function () {
297
298 // call the constructor
299 if (this.initialize) {
300 this.initialize.apply(this, arguments);
301 }
302
303 // call all constructor hooks
304 this.callInitHooks();
305 };
306
307 var parentProto = NewClass.__super__ = this.prototype;
308
309 var proto = create(parentProto);
310 proto.constructor = NewClass;
311
312 NewClass.prototype = proto;
313
314 // inherit parent's statics
315 for (var i in this) {
316 if (this.hasOwnProperty(i) && i !== 'prototype' && i !== '__super__') {
317 NewClass[i] = this[i];
318 }
319 }
320
321 // mix static properties into the class
322 if (props.statics) {
323 extend(NewClass, props.statics);
324 delete props.statics;
325 }
326
327 // mix includes into the prototype
328 if (props.includes) {
329 checkDeprecatedMixinEvents(props.includes);
330 extend.apply(null, [proto].concat(props.includes));
331 delete props.includes;
332 }
333
334 // merge options
335 if (proto.options) {
336 props.options = extend(create(proto.options), props.options);
337 }
338
339 // mix given properties into the prototype
340 extend(proto, props);
341
342 proto._initHooks = [];
343
344 // add method for calling all hooks
345 proto.callInitHooks = function () {
346
347 if (this._initHooksCalled) { return; }
348
349 if (parentProto.callInitHooks) {
350 parentProto.callInitHooks.call(this);
351 }
352
353 this._initHooksCalled = true;
354
355 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
356 proto._initHooks[i].call(this);
357 }
358 };
359
360 return NewClass;
361 };
362
363
364 // @function include(properties: Object): this
365 // [Includes a mixin](#class-includes) into the current class.
366 Class.include = function (props) {
367 extend(this.prototype, props);
368 return this;
369 };
370
371 // @function mergeOptions(options: Object): this
372 // [Merges `options`](#class-options) into the defaults of the class.
373 Class.mergeOptions = function (options) {
374 extend(this.prototype.options, options);
375 return this;
376 };
377
378 // @function addInitHook(fn: Function): this
379 // Adds a [constructor hook](#class-constructor-hooks) to the class.
380 Class.addInitHook = function (fn) { // (Function) || (String, args...)
381 var args = Array.prototype.slice.call(arguments, 1);
382
383 var init = typeof fn === 'function' ? fn : function () {
384 this[fn].apply(this, args);
385 };
386
387 this.prototype._initHooks = this.prototype._initHooks || [];
388 this.prototype._initHooks.push(init);
389 return this;
390 };
391
392 function checkDeprecatedMixinEvents(includes) {
393 if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
394
395 includes = isArray(includes) ? includes : [includes];
396
397 for (var i = 0; i < includes.length; i++) {
398 if (includes[i] === L.Mixin.Events) {
399 console.warn('Deprecated include of L.Mixin.Events: ' +
400 'this property will be removed in future releases, ' +
401 'please inherit from L.Evented instead.', new Error().stack);
402 }
403 }
404 }
405
406 /*
407 * @class Evented
408 * @aka L.Evented
409 * @inherits Class
410 *
411 * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
412 *
413 * @example
414 *
415 * ```js
416 * map.on('click', function(e) {
417 * alert(e.latlng);
418 * } );
419 * ```
420 *
421 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
422 *
423 * ```js
424 * function onClick(e) { ... }
425 *
426 * map.on('click', onClick);
427 * map.off('click', onClick);
428 * ```
429 */
430
431 var Events = {
432 /* @method on(type: String, fn: Function, context?: Object): this
433 * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
434 *
435 * @alternative
436 * @method on(eventMap: Object): this
437 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
438 */
439 on: function (types, fn, context) {
440
441 // types can be a map of types/handlers
442 if (typeof types === 'object') {
443 for (var type in types) {
444 // we don't process space-separated events here for performance;
445 // it's a hot path since Layer uses the on(obj) syntax
446 this._on(type, types[type], fn);
447 }
448
449 } else {
450 // types can be a string of space-separated words
451 types = splitWords(types);
452
453 for (var i = 0, len = types.length; i < len; i++) {
454 this._on(types[i], fn, context);
455 }
456 }
457
458 return this;
459 },
460
461 /* @method off(type: String, fn?: Function, context?: Object): this
462 * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
463 *
464 * @alternative
465 * @method off(eventMap: Object): this
466 * Removes a set of type/listener pairs.
467 *
468 * @alternative
469 * @method off: this
470 * Removes all listeners to all events on the object.
471 */
472 off: function (types, fn, context) {
473
474 if (!types) {
475 // clear all listeners if called without arguments
476 delete this._events;
477
478 } else if (typeof types === 'object') {
479 for (var type in types) {
480 this._off(type, types[type], fn);
481 }
482
483 } else {
484 types = splitWords(types);
485
486 for (var i = 0, len = types.length; i < len; i++) {
487 this._off(types[i], fn, context);
488 }
489 }
490
491 return this;
492 },
493
494 // attach listener (without syntactic sugar now)
495 _on: function (type, fn, context) {
496 this._events = this._events || {};
497
498 /* get/init listeners for type */
499 var typeListeners = this._events[type];
500 if (!typeListeners) {
501 typeListeners = [];
502 this._events[type] = typeListeners;
503 }
504
505 if (context === this) {
506 // Less memory footprint.
507 context = undefined;
508 }
509 var newListener = {fn: fn, ctx: context},
510 listeners = typeListeners;
511
512 // check if fn already there
513 for (var i = 0, len = listeners.length; i < len; i++) {
514 if (listeners[i].fn === fn && listeners[i].ctx === context) {
515 return;
516 }
517 }
518
519 listeners.push(newListener);
520 },
521
522 _off: function (type, fn, context) {
523 var listeners,
524 i,
525 len;
526
527 if (!this._events) { return; }
528
529 listeners = this._events[type];
530
531 if (!listeners) {
532 return;
533 }
534
535 if (!fn) {
536 // Set all removed listeners to noop so they are not called if remove happens in fire
537 for (i = 0, len = listeners.length; i < len; i++) {
538 listeners[i].fn = falseFn;
539 }
540 // clear all listeners for a type if function isn't specified
541 delete this._events[type];
542 return;
543 }
544
545 if (context === this) {
546 context = undefined;
547 }
548
549 if (listeners) {
550
551 // find fn and remove it
552 for (i = 0, len = listeners.length; i < len; i++) {
553 var l = listeners[i];
554 if (l.ctx !== context) { continue; }
555 if (l.fn === fn) {
556
557 // set the removed listener to noop so that's not called if remove happens in fire
558 l.fn = falseFn;
559
560 if (this._firingCount) {
561 /* copy array in case events are being fired */
562 this._events[type] = listeners = listeners.slice();
563 }
564 listeners.splice(i, 1);
565
566 return;
567 }
568 }
569 }
570 },
571
572 // @method fire(type: String, data?: Object, propagate?: Boolean): this
573 // Fires an event of the specified type. You can optionally provide an data
574 // object — the first argument of the listener function will contain its
575 // properties. The event can optionally be propagated to event parents.
576 fire: function (type, data, propagate) {
577 if (!this.listens(type, propagate)) { return this; }
578
579 var event = extend({}, data, {
580 type: type,
581 target: this,
582 sourceTarget: data && data.sourceTarget || this
583 });
584
585 if (this._events) {
586 var listeners = this._events[type];
587
588 if (listeners) {
589 this._firingCount = (this._firingCount + 1) || 1;
590 for (var i = 0, len = listeners.length; i < len; i++) {
591 var l = listeners[i];
592 l.fn.call(l.ctx || this, event);
593 }
594
595 this._firingCount--;
596 }
597 }
598
599 if (propagate) {
600 // propagate the event to parents (set with addEventParent)
601 this._propagateEvent(event);
602 }
603
604 return this;
605 },
606
607 // @method listens(type: String): Boolean
608 // Returns `true` if a particular event type has any listeners attached to it.
609 listens: function (type, propagate) {
610 var listeners = this._events && this._events[type];
611 if (listeners && listeners.length) { return true; }
612
613 if (propagate) {
614 // also check parents for listeners if event propagates
615 for (var id in this._eventParents) {
616 if (this._eventParents[id].listens(type, propagate)) { return true; }
617 }
618 }
619 return false;
620 },
621
622 // @method once(…): this
623 // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
624 once: function (types, fn, context) {
625
626 if (typeof types === 'object') {
627 for (var type in types) {
628 this.once(type, types[type], fn);
629 }
630 return this;
631 }
632
633 var handler = bind(function () {
634 this
635 .off(types, fn, context)
636 .off(types, handler, context);
637 }, this);
638
639 // add a listener that's executed once and removed after that
640 return this
641 .on(types, fn, context)
642 .on(types, handler, context);
643 },
644
645 // @method addEventParent(obj: Evented): this
646 // Adds an event parent - an `Evented` that will receive propagated events
647 addEventParent: function (obj) {
648 this._eventParents = this._eventParents || {};
649 this._eventParents[stamp(obj)] = obj;
650 return this;
651 },
652
653 // @method removeEventParent(obj: Evented): this
654 // Removes an event parent, so it will stop receiving propagated events
655 removeEventParent: function (obj) {
656 if (this._eventParents) {
657 delete this._eventParents[stamp(obj)];
658 }
659 return this;
660 },
661
662 _propagateEvent: function (e) {
663 for (var id in this._eventParents) {
664 this._eventParents[id].fire(e.type, extend({
665 layer: e.target,
666 propagatedFrom: e.target
667 }, e), true);
668 }
669 }
670 };
671
672 // aliases; we should ditch those eventually
673
674 // @method addEventListener(…): this
675 // Alias to [`on(…)`](#evented-on)
676 Events.addEventListener = Events.on;
677
678 // @method removeEventListener(…): this
679 // Alias to [`off(…)`](#evented-off)
680
681 // @method clearAllEventListeners(…): this
682 // Alias to [`off()`](#evented-off)
683 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
684
685 // @method addOneTimeEventListener(…): this
686 // Alias to [`once(…)`](#evented-once)
687 Events.addOneTimeEventListener = Events.once;
688
689 // @method fireEvent(…): this
690 // Alias to [`fire(…)`](#evented-fire)
691 Events.fireEvent = Events.fire;
692
693 // @method hasEventListeners(…): Boolean
694 // Alias to [`listens(…)`](#evented-listens)
695 Events.hasEventListeners = Events.listens;
696
697 var Evented = Class.extend(Events);
698
699 /*
700 * @class Point
701 * @aka L.Point
702 *
703 * Represents a point with `x` and `y` coordinates in pixels.
704 *
705 * @example
706 *
707 * ```js
708 * var point = L.point(200, 300);
709 * ```
710 *
711 * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
712 *
713 * ```js
714 * map.panBy([200, 300]);
715 * map.panBy(L.point(200, 300));
716 * ```
717 *
718 * Note that `Point` does not inherit from Leafet's `Class` object,
719 * which means new classes can't inherit from it, and new methods
720 * can't be added to it with the `include` function.
721 */
722
723 function Point(x, y, round) {
724 // @property x: Number; The `x` coordinate of the point
725 this.x = (round ? Math.round(x) : x);
726 // @property y: Number; The `y` coordinate of the point
727 this.y = (round ? Math.round(y) : y);
728 }
729
730 var trunc = Math.trunc || function (v) {
731 return v > 0 ? Math.floor(v) : Math.ceil(v);
732 };
733
734 Point.prototype = {
735
736 // @method clone(): Point
737 // Returns a copy of the current point.
738 clone: function () {
739 return new Point(this.x, this.y);
740 },
741
742 // @method add(otherPoint: Point): Point
743 // Returns the result of addition of the current and the given points.
744 add: function (point) {
745 // non-destructive, returns a new point
746 return this.clone()._add(toPoint(point));
747 },
748
749 _add: function (point) {
750 // destructive, used directly for performance in situations where it's safe to modify existing point
751 this.x += point.x;
752 this.y += point.y;
753 return this;
754 },
755
756 // @method subtract(otherPoint: Point): Point
757 // Returns the result of subtraction of the given point from the current.
758 subtract: function (point) {
759 return this.clone()._subtract(toPoint(point));
760 },
761
762 _subtract: function (point) {
763 this.x -= point.x;
764 this.y -= point.y;
765 return this;
766 },
767
768 // @method divideBy(num: Number): Point
769 // Returns the result of division of the current point by the given number.
770 divideBy: function (num) {
771 return this.clone()._divideBy(num);
772 },
773
774 _divideBy: function (num) {
775 this.x /= num;
776 this.y /= num;
777 return this;
778 },
779
780 // @method multiplyBy(num: Number): Point
781 // Returns the result of multiplication of the current point by the given number.
782 multiplyBy: function (num) {
783 return this.clone()._multiplyBy(num);
784 },
785
786 _multiplyBy: function (num) {
787 this.x *= num;
788 this.y *= num;
789 return this;
790 },
791
792 // @method scaleBy(scale: Point): Point
793 // Multiply each coordinate of the current point by each coordinate of
794 // `scale`. In linear algebra terms, multiply the point by the
795 // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
796 // defined by `scale`.
797 scaleBy: function (point) {
798 return new Point(this.x * point.x, this.y * point.y);
799 },
800
801 // @method unscaleBy(scale: Point): Point
802 // Inverse of `scaleBy`. Divide each coordinate of the current point by
803 // each coordinate of `scale`.
804 unscaleBy: function (point) {
805 return new Point(this.x / point.x, this.y / point.y);
806 },
807
808 // @method round(): Point
809 // Returns a copy of the current point with rounded coordinates.
810 round: function () {
811 return this.clone()._round();
812 },
813
814 _round: function () {
815 this.x = Math.round(this.x);
816 this.y = Math.round(this.y);
817 return this;
818 },
819
820 // @method floor(): Point
821 // Returns a copy of the current point with floored coordinates (rounded down).
822 floor: function () {
823 return this.clone()._floor();
824 },
825
826 _floor: function () {
827 this.x = Math.floor(this.x);
828 this.y = Math.floor(this.y);
829 return this;
830 },
831
832 // @method ceil(): Point
833 // Returns a copy of the current point with ceiled coordinates (rounded up).
834 ceil: function () {
835 return this.clone()._ceil();
836 },
837
838 _ceil: function () {
839 this.x = Math.ceil(this.x);
840 this.y = Math.ceil(this.y);
841 return this;
842 },
843
844 // @method trunc(): Point
845 // Returns a copy of the current point with truncated coordinates (rounded towards zero).
846 trunc: function () {
847 return this.clone()._trunc();
848 },
849
850 _trunc: function () {
851 this.x = trunc(this.x);
852 this.y = trunc(this.y);
853 return this;
854 },
855
856 // @method distanceTo(otherPoint: Point): Number
857 // Returns the cartesian distance between the current and the given points.
858 distanceTo: function (point) {
859 point = toPoint(point);
860
861 var x = point.x - this.x,
862 y = point.y - this.y;
863
864 return Math.sqrt(x * x + y * y);
865 },
866
867 // @method equals(otherPoint: Point): Boolean
868 // Returns `true` if the given point has the same coordinates.
869 equals: function (point) {
870 point = toPoint(point);
871
872 return point.x === this.x &&
873 point.y === this.y;
874 },
875
876 // @method contains(otherPoint: Point): Boolean
877 // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
878 contains: function (point) {
879 point = toPoint(point);
880
881 return Math.abs(point.x) <= Math.abs(this.x) &&
882 Math.abs(point.y) <= Math.abs(this.y);
883 },
884
885 // @method toString(): String
886 // Returns a string representation of the point for debugging purposes.
887 toString: function () {
888 return 'Point(' +
889 formatNum(this.x) + ', ' +
890 formatNum(this.y) + ')';
891 }
892 };
893
894 // @factory L.point(x: Number, y: Number, round?: Boolean)
895 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
896
897 // @alternative
898 // @factory L.point(coords: Number[])
899 // Expects an array of the form `[x, y]` instead.
900
901 // @alternative
902 // @factory L.point(coords: Object)
903 // Expects a plain object of the form `{x: Number, y: Number}` instead.
904 function toPoint(x, y, round) {
905 if (x instanceof Point) {
906 return x;
907 }
908 if (isArray(x)) {
909 return new Point(x[0], x[1]);
910 }
911 if (x === undefined || x === null) {
912 return x;
913 }
914 if (typeof x === 'object' && 'x' in x && 'y' in x) {
915 return new Point(x.x, x.y);
916 }
917 return new Point(x, y, round);
918 }
919
920 /*
921 * @class Bounds
922 * @aka L.Bounds
923 *
924 * Represents a rectangular area in pixel coordinates.
925 *
926 * @example
927 *
928 * ```js
929 * var p1 = L.point(10, 10),
930 * p2 = L.point(40, 60),
931 * bounds = L.bounds(p1, p2);
932 * ```
933 *
934 * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
935 *
936 * ```js
937 * otherBounds.intersects([[10, 10], [40, 60]]);
938 * ```
939 *
940 * Note that `Bounds` does not inherit from Leafet's `Class` object,
941 * which means new classes can't inherit from it, and new methods
942 * can't be added to it with the `include` function.
943 */
944
945 function Bounds(a, b) {
946 if (!a) { return; }
947
948 var points = b ? [a, b] : a;
949
950 for (var i = 0, len = points.length; i < len; i++) {
951 this.extend(points[i]);
952 }
953 }
954
955 Bounds.prototype = {
956 // @method extend(point: Point): this
957 // Extends the bounds to contain the given point.
958 extend: function (point) { // (Point)
959 point = toPoint(point);
960
961 // @property min: Point
962 // The top left corner of the rectangle.
963 // @property max: Point
964 // The bottom right corner of the rectangle.
965 if (!this.min && !this.max) {
966 this.min = point.clone();
967 this.max = point.clone();
968 } else {
969 this.min.x = Math.min(point.x, this.min.x);
970 this.max.x = Math.max(point.x, this.max.x);
971 this.min.y = Math.min(point.y, this.min.y);
972 this.max.y = Math.max(point.y, this.max.y);
973 }
974 return this;
975 },
976
977 // @method getCenter(round?: Boolean): Point
978 // Returns the center point of the bounds.
979 getCenter: function (round) {
980 return new Point(
981 (this.min.x + this.max.x) / 2,
982 (this.min.y + this.max.y) / 2, round);
983 },
984
985 // @method getBottomLeft(): Point
986 // Returns the bottom-left point of the bounds.
987 getBottomLeft: function () {
988 return new Point(this.min.x, this.max.y);
989 },
990
991 // @method getTopRight(): Point
992 // Returns the top-right point of the bounds.
993 getTopRight: function () { // -> Point
994 return new Point(this.max.x, this.min.y);
995 },
996
997 // @method getTopLeft(): Point
998 // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
999 getTopLeft: function () {
1000 return this.min; // left, top
1001 },
1002
1003 // @method getBottomRight(): Point
1004 // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1005 getBottomRight: function () {
1006 return this.max; // right, bottom
1007 },
1008
1009 // @method getSize(): Point
1010 // Returns the size of the given bounds
1011 getSize: function () {
1012 return this.max.subtract(this.min);
1013 },
1014
1015 // @method contains(otherBounds: Bounds): Boolean
1016 // Returns `true` if the rectangle contains the given one.
1017 // @alternative
1018 // @method contains(point: Point): Boolean
1019 // Returns `true` if the rectangle contains the given point.
1020 contains: function (obj) {
1021 var min, max;
1022
1023 if (typeof obj[0] === 'number' || obj instanceof Point) {
1024 obj = toPoint(obj);
1025 } else {
1026 obj = toBounds(obj);
1027 }
1028
1029 if (obj instanceof Bounds) {
1030 min = obj.min;
1031 max = obj.max;
1032 } else {
1033 min = max = obj;
1034 }
1035
1036 return (min.x >= this.min.x) &&
1037 (max.x <= this.max.x) &&
1038 (min.y >= this.min.y) &&
1039 (max.y <= this.max.y);
1040 },
1041
1042 // @method intersects(otherBounds: Bounds): Boolean
1043 // Returns `true` if the rectangle intersects the given bounds. Two bounds
1044 // intersect if they have at least one point in common.
1045 intersects: function (bounds) { // (Bounds) -> Boolean
1046 bounds = toBounds(bounds);
1047
1048 var min = this.min,
1049 max = this.max,
1050 min2 = bounds.min,
1051 max2 = bounds.max,
1052 xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1053 yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1054
1055 return xIntersects && yIntersects;
1056 },
1057
1058 // @method overlaps(otherBounds: Bounds): Boolean
1059 // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1060 // overlap if their intersection is an area.
1061 overlaps: function (bounds) { // (Bounds) -> Boolean
1062 bounds = toBounds(bounds);
1063
1064 var min = this.min,
1065 max = this.max,
1066 min2 = bounds.min,
1067 max2 = bounds.max,
1068 xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1069 yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1070
1071 return xOverlaps && yOverlaps;
1072 },
1073
1074 isValid: function () {
1075 return !!(this.min && this.max);
1076 }
1077 };
1078
1079
1080 // @factory L.bounds(corner1: Point, corner2: Point)
1081 // Creates a Bounds object from two corners coordinate pairs.
1082 // @alternative
1083 // @factory L.bounds(points: Point[])
1084 // Creates a Bounds object from the given array of points.
1085 function toBounds(a, b) {
1086 if (!a || a instanceof Bounds) {
1087 return a;
1088 }
1089 return new Bounds(a, b);
1090 }
1091
1092 /*
1093 * @class LatLngBounds
1094 * @aka L.LatLngBounds
1095 *
1096 * Represents a rectangular geographical area on a map.
1097 *
1098 * @example
1099 *
1100 * ```js
1101 * var corner1 = L.latLng(40.712, -74.227),
1102 * corner2 = L.latLng(40.774, -74.125),
1103 * bounds = L.latLngBounds(corner1, corner2);
1104 * ```
1105 *
1106 * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
1107 *
1108 * ```js
1109 * map.fitBounds([
1110 * [40.712, -74.227],
1111 * [40.774, -74.125]
1112 * ]);
1113 * ```
1114 *
1115 * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
1116 *
1117 * Note that `LatLngBounds` does not inherit from Leafet's `Class` object,
1118 * which means new classes can't inherit from it, and new methods
1119 * can't be added to it with the `include` function.
1120 */
1121
1122 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1123 if (!corner1) { return; }
1124
1125 var latlngs = corner2 ? [corner1, corner2] : corner1;
1126
1127 for (var i = 0, len = latlngs.length; i < len; i++) {
1128 this.extend(latlngs[i]);
1129 }
1130 }
1131
1132 LatLngBounds.prototype = {
1133
1134 // @method extend(latlng: LatLng): this
1135 // Extend the bounds to contain the given point
1136
1137 // @alternative
1138 // @method extend(otherBounds: LatLngBounds): this
1139 // Extend the bounds to contain the given bounds
1140 extend: function (obj) {
1141 var sw = this._southWest,
1142 ne = this._northEast,
1143 sw2, ne2;
1144
1145 if (obj instanceof LatLng) {
1146 sw2 = obj;
1147 ne2 = obj;
1148
1149 } else if (obj instanceof LatLngBounds) {
1150 sw2 = obj._southWest;
1151 ne2 = obj._northEast;
1152
1153 if (!sw2 || !ne2) { return this; }
1154
1155 } else {
1156 return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1157 }
1158
1159 if (!sw && !ne) {
1160 this._southWest = new LatLng(sw2.lat, sw2.lng);
1161 this._northEast = new LatLng(ne2.lat, ne2.lng);
1162 } else {
1163 sw.lat = Math.min(sw2.lat, sw.lat);
1164 sw.lng = Math.min(sw2.lng, sw.lng);
1165 ne.lat = Math.max(ne2.lat, ne.lat);
1166 ne.lng = Math.max(ne2.lng, ne.lng);
1167 }
1168
1169 return this;
1170 },
1171
1172 // @method pad(bufferRatio: Number): LatLngBounds
1173 // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1174 // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1175 // Negative values will retract the bounds.
1176 pad: function (bufferRatio) {
1177 var sw = this._southWest,
1178 ne = this._northEast,
1179 heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1180 widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1181
1182 return new LatLngBounds(
1183 new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1184 new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1185 },
1186
1187 // @method getCenter(): LatLng
1188 // Returns the center point of the bounds.
1189 getCenter: function () {
1190 return new LatLng(
1191 (this._southWest.lat + this._northEast.lat) / 2,
1192 (this._southWest.lng + this._northEast.lng) / 2);
1193 },
1194
1195 // @method getSouthWest(): LatLng
1196 // Returns the south-west point of the bounds.
1197 getSouthWest: function () {
1198 return this._southWest;
1199 },
1200
1201 // @method getNorthEast(): LatLng
1202 // Returns the north-east point of the bounds.
1203 getNorthEast: function () {
1204 return this._northEast;
1205 },
1206
1207 // @method getNorthWest(): LatLng
1208 // Returns the north-west point of the bounds.
1209 getNorthWest: function () {
1210 return new LatLng(this.getNorth(), this.getWest());
1211 },
1212
1213 // @method getSouthEast(): LatLng
1214 // Returns the south-east point of the bounds.
1215 getSouthEast: function () {
1216 return new LatLng(this.getSouth(), this.getEast());
1217 },
1218
1219 // @method getWest(): Number
1220 // Returns the west longitude of the bounds
1221 getWest: function () {
1222 return this._southWest.lng;
1223 },
1224
1225 // @method getSouth(): Number
1226 // Returns the south latitude of the bounds
1227 getSouth: function () {
1228 return this._southWest.lat;
1229 },
1230
1231 // @method getEast(): Number
1232 // Returns the east longitude of the bounds
1233 getEast: function () {
1234 return this._northEast.lng;
1235 },
1236
1237 // @method getNorth(): Number
1238 // Returns the north latitude of the bounds
1239 getNorth: function () {
1240 return this._northEast.lat;
1241 },
1242
1243 // @method contains(otherBounds: LatLngBounds): Boolean
1244 // Returns `true` if the rectangle contains the given one.
1245
1246 // @alternative
1247 // @method contains (latlng: LatLng): Boolean
1248 // Returns `true` if the rectangle contains the given point.
1249 contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1250 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1251 obj = toLatLng(obj);
1252 } else {
1253 obj = toLatLngBounds(obj);
1254 }
1255
1256 var sw = this._southWest,
1257 ne = this._northEast,
1258 sw2, ne2;
1259
1260 if (obj instanceof LatLngBounds) {
1261 sw2 = obj.getSouthWest();
1262 ne2 = obj.getNorthEast();
1263 } else {
1264 sw2 = ne2 = obj;
1265 }
1266
1267 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1268 (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1269 },
1270
1271 // @method intersects(otherBounds: LatLngBounds): Boolean
1272 // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1273 intersects: function (bounds) {
1274 bounds = toLatLngBounds(bounds);
1275
1276 var sw = this._southWest,
1277 ne = this._northEast,
1278 sw2 = bounds.getSouthWest(),
1279 ne2 = bounds.getNorthEast(),
1280
1281 latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1282 lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1283
1284 return latIntersects && lngIntersects;
1285 },
1286
1287 // @method overlaps(otherBounds: Bounds): Boolean
1288 // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1289 overlaps: function (bounds) {
1290 bounds = toLatLngBounds(bounds);
1291
1292 var sw = this._southWest,
1293 ne = this._northEast,
1294 sw2 = bounds.getSouthWest(),
1295 ne2 = bounds.getNorthEast(),
1296
1297 latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1298 lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1299
1300 return latOverlaps && lngOverlaps;
1301 },
1302
1303 // @method toBBoxString(): String
1304 // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
1305 toBBoxString: function () {
1306 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1307 },
1308
1309 // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1310 // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
1311 equals: function (bounds, maxMargin) {
1312 if (!bounds) { return false; }
1313
1314 bounds = toLatLngBounds(bounds);
1315
1316 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1317 this._northEast.equals(bounds.getNorthEast(), maxMargin);
1318 },
1319
1320 // @method isValid(): Boolean
1321 // Returns `true` if the bounds are properly initialized.
1322 isValid: function () {
1323 return !!(this._southWest && this._northEast);
1324 }
1325 };
1326
1327 // TODO International date line?
1328
1329 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1330 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1331
1332 // @alternative
1333 // @factory L.latLngBounds(latlngs: LatLng[])
1334 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
1335 function toLatLngBounds(a, b) {
1336 if (a instanceof LatLngBounds) {
1337 return a;
1338 }
1339 return new LatLngBounds(a, b);
1340 }
1341
1342 /* @class LatLng
1343 * @aka L.LatLng
1344 *
1345 * Represents a geographical point with a certain latitude and longitude.
1346 *
1347 * @example
1348 *
1349 * ```
1350 * var latlng = L.latLng(50.5, 30.5);
1351 * ```
1352 *
1353 * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
1354 *
1355 * ```
1356 * map.panTo([50, 30]);
1357 * map.panTo({lon: 30, lat: 50});
1358 * map.panTo({lat: 50, lng: 30});
1359 * map.panTo(L.latLng(50, 30));
1360 * ```
1361 *
1362 * Note that `LatLng` does not inherit from Leafet's `Class` object,
1363 * which means new classes can't inherit from it, and new methods
1364 * can't be added to it with the `include` function.
1365 */
1366
1367 function LatLng(lat, lng, alt) {
1368 if (isNaN(lat) || isNaN(lng)) {
1369 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1370 }
1371
1372 // @property lat: Number
1373 // Latitude in degrees
1374 this.lat = +lat;
1375
1376 // @property lng: Number
1377 // Longitude in degrees
1378 this.lng = +lng;
1379
1380 // @property alt: Number
1381 // Altitude in meters (optional)
1382 if (alt !== undefined) {
1383 this.alt = +alt;
1384 }
1385 }
1386
1387 LatLng.prototype = {
1388 // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1389 // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
1390 equals: function (obj, maxMargin) {
1391 if (!obj) { return false; }
1392
1393 obj = toLatLng(obj);
1394
1395 var margin = Math.max(
1396 Math.abs(this.lat - obj.lat),
1397 Math.abs(this.lng - obj.lng));
1398
1399 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1400 },
1401
1402 // @method toString(): String
1403 // Returns a string representation of the point (for debugging purposes).
1404 toString: function (precision) {
1405 return 'LatLng(' +
1406 formatNum(this.lat, precision) + ', ' +
1407 formatNum(this.lng, precision) + ')';
1408 },
1409
1410 // @method distanceTo(otherLatLng: LatLng): Number
1411 // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
1412 distanceTo: function (other) {
1413 return Earth.distance(this, toLatLng(other));
1414 },
1415
1416 // @method wrap(): LatLng
1417 // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1418 wrap: function () {
1419 return Earth.wrapLatLng(this);
1420 },
1421
1422 // @method toBounds(sizeInMeters: Number): LatLngBounds
1423 // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1424 toBounds: function (sizeInMeters) {
1425 var latAccuracy = 180 * sizeInMeters / 40075017,
1426 lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1427
1428 return toLatLngBounds(
1429 [this.lat - latAccuracy, this.lng - lngAccuracy],
1430 [this.lat + latAccuracy, this.lng + lngAccuracy]);
1431 },
1432
1433 clone: function () {
1434 return new LatLng(this.lat, this.lng, this.alt);
1435 }
1436 };
1437
1438
1439
1440 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1441 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1442
1443 // @alternative
1444 // @factory L.latLng(coords: Array): LatLng
1445 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1446
1447 // @alternative
1448 // @factory L.latLng(coords: Object): LatLng
1449 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1450
1451 function toLatLng(a, b, c) {
1452 if (a instanceof LatLng) {
1453 return a;
1454 }
1455 if (isArray(a) && typeof a[0] !== 'object') {
1456 if (a.length === 3) {
1457 return new LatLng(a[0], a[1], a[2]);
1458 }
1459 if (a.length === 2) {
1460 return new LatLng(a[0], a[1]);
1461 }
1462 return null;
1463 }
1464 if (a === undefined || a === null) {
1465 return a;
1466 }
1467 if (typeof a === 'object' && 'lat' in a) {
1468 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1469 }
1470 if (b === undefined) {
1471 return null;
1472 }
1473 return new LatLng(a, b, c);
1474 }
1475
1476 /*
1477 * @namespace CRS
1478 * @crs L.CRS.Base
1479 * Object that defines coordinate reference systems for projecting
1480 * geographical points into pixel (screen) coordinates and back (and to
1481 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1482 * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1483 *
1484 * Leaflet defines the most usual CRSs by default. If you want to use a
1485 * CRS not defined by default, take a look at the
1486 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1487 *
1488 * Note that the CRS instances do not inherit from Leafet's `Class` object,
1489 * and can't be instantiated. Also, new classes can't inherit from them,
1490 * and methods can't be added to them with the `include` function.
1491 */
1492
1493 var CRS = {
1494 // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1495 // Projects geographical coordinates into pixel coordinates for a given zoom.
1496 latLngToPoint: function (latlng, zoom) {
1497 var projectedPoint = this.projection.project(latlng),
1498 scale = this.scale(zoom);
1499
1500 return this.transformation._transform(projectedPoint, scale);
1501 },
1502
1503 // @method pointToLatLng(point: Point, zoom: Number): LatLng
1504 // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1505 // zoom into geographical coordinates.
1506 pointToLatLng: function (point, zoom) {
1507 var scale = this.scale(zoom),
1508 untransformedPoint = this.transformation.untransform(point, scale);
1509
1510 return this.projection.unproject(untransformedPoint);
1511 },
1512
1513 // @method project(latlng: LatLng): Point
1514 // Projects geographical coordinates into coordinates in units accepted for
1515 // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1516 project: function (latlng) {
1517 return this.projection.project(latlng);
1518 },
1519
1520 // @method unproject(point: Point): LatLng
1521 // Given a projected coordinate returns the corresponding LatLng.
1522 // The inverse of `project`.
1523 unproject: function (point) {
1524 return this.projection.unproject(point);
1525 },
1526
1527 // @method scale(zoom: Number): Number
1528 // Returns the scale used when transforming projected coordinates into
1529 // pixel coordinates for a particular zoom. For example, it returns
1530 // `256 * 2^zoom` for Mercator-based CRS.
1531 scale: function (zoom) {
1532 return 256 * Math.pow(2, zoom);
1533 },
1534
1535 // @method zoom(scale: Number): Number
1536 // Inverse of `scale()`, returns the zoom level corresponding to a scale
1537 // factor of `scale`.
1538 zoom: function (scale) {
1539 return Math.log(scale / 256) / Math.LN2;
1540 },
1541
1542 // @method getProjectedBounds(zoom: Number): Bounds
1543 // Returns the projection's bounds scaled and transformed for the provided `zoom`.
1544 getProjectedBounds: function (zoom) {
1545 if (this.infinite) { return null; }
1546
1547 var b = this.projection.bounds,
1548 s = this.scale(zoom),
1549 min = this.transformation.transform(b.min, s),
1550 max = this.transformation.transform(b.max, s);
1551
1552 return new Bounds(min, max);
1553 },
1554
1555 // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1556 // Returns the distance between two geographical coordinates.
1557
1558 // @property code: String
1559 // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1560 //
1561 // @property wrapLng: Number[]
1562 // An array of two numbers defining whether the longitude (horizontal) coordinate
1563 // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1564 // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1565 //
1566 // @property wrapLat: Number[]
1567 // Like `wrapLng`, but for the latitude (vertical) axis.
1568
1569 // wrapLng: [min, max],
1570 // wrapLat: [min, max],
1571
1572 // @property infinite: Boolean
1573 // If true, the coordinate space will be unbounded (infinite in both axes)
1574 infinite: false,
1575
1576 // @method wrapLatLng(latlng: LatLng): LatLng
1577 // Returns a `LatLng` where lat and lng has been wrapped according to the
1578 // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1579 wrapLatLng: function (latlng) {
1580 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1581 lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1582 alt = latlng.alt;
1583
1584 return new LatLng(lat, lng, alt);
1585 },
1586
1587 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1588 // Returns a `LatLngBounds` with the same size as the given one, ensuring
1589 // that its center is within the CRS's bounds.
1590 // Only accepts actual `L.LatLngBounds` instances, not arrays.
1591 wrapLatLngBounds: function (bounds) {
1592 var center = bounds.getCenter(),
1593 newCenter = this.wrapLatLng(center),
1594 latShift = center.lat - newCenter.lat,
1595 lngShift = center.lng - newCenter.lng;
1596
1597 if (latShift === 0 && lngShift === 0) {
1598 return bounds;
1599 }
1600
1601 var sw = bounds.getSouthWest(),
1602 ne = bounds.getNorthEast(),
1603 newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1604 newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1605
1606 return new LatLngBounds(newSw, newNe);
1607 }
1608 };
1609
1610 /*
1611 * @namespace CRS
1612 * @crs L.CRS.Earth
1613 *
1614 * Serves as the base for CRS that are global such that they cover the earth.
1615 * Can only be used as the base for other CRS and cannot be used directly,
1616 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1617 * meters.
1618 */
1619
1620 var Earth = extend({}, CRS, {
1621 wrapLng: [-180, 180],
1622
1623 // Mean Earth Radius, as recommended for use by
1624 // the International Union of Geodesy and Geophysics,
1625 // see http://rosettacode.org/wiki/Haversine_formula
1626 R: 6371000,
1627
1628 // distance between two geographical points using spherical law of cosines approximation
1629 distance: function (latlng1, latlng2) {
1630 var rad = Math.PI / 180,
1631 lat1 = latlng1.lat * rad,
1632 lat2 = latlng2.lat * rad,
1633 sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1634 sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1635 a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1636 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1637 return this.R * c;
1638 }
1639 });
1640
1641 /*
1642 * @namespace Projection
1643 * @projection L.Projection.SphericalMercator
1644 *
1645 * Spherical Mercator projection — the most common projection for online maps,
1646 * used by almost all free and commercial tile providers. Assumes that Earth is
1647 * a sphere. Used by the `EPSG:3857` CRS.
1648 */
1649
1650 var SphericalMercator = {
1651
1652 R: 6378137,
1653 MAX_LATITUDE: 85.0511287798,
1654
1655 project: function (latlng) {
1656 var d = Math.PI / 180,
1657 max = this.MAX_LATITUDE,
1658 lat = Math.max(Math.min(max, latlng.lat), -max),
1659 sin = Math.sin(lat * d);
1660
1661 return new Point(
1662 this.R * latlng.lng * d,
1663 this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1664 },
1665
1666 unproject: function (point) {
1667 var d = 180 / Math.PI;
1668
1669 return new LatLng(
1670 (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1671 point.x * d / this.R);
1672 },
1673
1674 bounds: (function () {
1675 var d = 6378137 * Math.PI;
1676 return new Bounds([-d, -d], [d, d]);
1677 })()
1678 };
1679
1680 /*
1681 * @class Transformation
1682 * @aka L.Transformation
1683 *
1684 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1685 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1686 * the reverse. Used by Leaflet in its projections code.
1687 *
1688 * @example
1689 *
1690 * ```js
1691 * var transformation = L.transformation(2, 5, -1, 10),
1692 * p = L.point(1, 2),
1693 * p2 = transformation.transform(p), // L.point(7, 8)
1694 * p3 = transformation.untransform(p2); // L.point(1, 2)
1695 * ```
1696 */
1697
1698
1699 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1700 // Creates a `Transformation` object with the given coefficients.
1701 function Transformation(a, b, c, d) {
1702 if (isArray(a)) {
1703 // use array properties
1704 this._a = a[0];
1705 this._b = a[1];
1706 this._c = a[2];
1707 this._d = a[3];
1708 return;
1709 }
1710 this._a = a;
1711 this._b = b;
1712 this._c = c;
1713 this._d = d;
1714 }
1715
1716 Transformation.prototype = {
1717 // @method transform(point: Point, scale?: Number): Point
1718 // Returns a transformed point, optionally multiplied by the given scale.
1719 // Only accepts actual `L.Point` instances, not arrays.
1720 transform: function (point, scale) { // (Point, Number) -> Point
1721 return this._transform(point.clone(), scale);
1722 },
1723
1724 // destructive transform (faster)
1725 _transform: function (point, scale) {
1726 scale = scale || 1;
1727 point.x = scale * (this._a * point.x + this._b);
1728 point.y = scale * (this._c * point.y + this._d);
1729 return point;
1730 },
1731
1732 // @method untransform(point: Point, scale?: Number): Point
1733 // Returns the reverse transformation of the given point, optionally divided
1734 // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1735 untransform: function (point, scale) {
1736 scale = scale || 1;
1737 return new Point(
1738 (point.x / scale - this._b) / this._a,
1739 (point.y / scale - this._d) / this._c);
1740 }
1741 };
1742
1743 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1744
1745 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1746 // Instantiates a Transformation object with the given coefficients.
1747
1748 // @alternative
1749 // @factory L.transformation(coefficients: Array): Transformation
1750 // Expects an coefficients array of the form
1751 // `[a: Number, b: Number, c: Number, d: Number]`.
1752
1753 function toTransformation(a, b, c, d) {
1754 return new Transformation(a, b, c, d);
1755 }
1756
1757 /*
1758 * @namespace CRS
1759 * @crs L.CRS.EPSG3857
1760 *
1761 * The most common CRS for online maps, used by almost all free and commercial
1762 * tile providers. Uses Spherical Mercator projection. Set in by default in
1763 * Map's `crs` option.
1764 */
1765
1766 var EPSG3857 = extend({}, Earth, {
1767 code: 'EPSG:3857',
1768 projection: SphericalMercator,
1769
1770 transformation: (function () {
1771 var scale = 0.5 / (Math.PI * SphericalMercator.R);
1772 return toTransformation(scale, 0.5, -scale, 0.5);
1773 }())
1774 });
1775
1776 var EPSG900913 = extend({}, EPSG3857, {
1777 code: 'EPSG:900913'
1778 });
1779
1780 // @namespace SVG; @section
1781 // There are several static functions which can be called without instantiating L.SVG:
1782
1783 // @function create(name: String): SVGElement
1784 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1785 // corresponding to the class name passed. For example, using 'line' will return
1786 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1787 function svgCreate(name) {
1788 return document.createElementNS('http://www.w3.org/2000/svg', name);
1789 }
1790
1791 // @function pointsToPath(rings: Point[], closed: Boolean): String
1792 // Generates a SVG path string for multiple rings, with each ring turning
1793 // into "M..L..L.." instructions
1794 function pointsToPath(rings, closed) {
1795 var str = '',
1796 i, j, len, len2, points, p;
1797
1798 for (i = 0, len = rings.length; i < len; i++) {
1799 points = rings[i];
1800
1801 for (j = 0, len2 = points.length; j < len2; j++) {
1802 p = points[j];
1803 str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1804 }
1805
1806 // closes the ring for polygons; "x" is VML syntax
1807 str += closed ? (svg ? 'z' : 'x') : '';
1808 }
1809
1810 // SVG complains about empty path strings
1811 return str || 'M0 0';
1812 }
1813
1814 /*
1815 * @namespace Browser
1816 * @aka L.Browser
1817 *
1818 * A namespace with static properties for browser/feature detection used by Leaflet internally.
1819 *
1820 * @example
1821 *
1822 * ```js
1823 * if (L.Browser.ielt9) {
1824 * alert('Upgrade your browser, dude!');
1825 * }
1826 * ```
1827 */
1828
1829 var style$1 = document.documentElement.style;
1830
1831 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1832 var ie = 'ActiveXObject' in window;
1833
1834 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1835 var ielt9 = ie && !document.addEventListener;
1836
1837 // @property edge: Boolean; `true` for the Edge web browser.
1838 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1839
1840 // @property webkit: Boolean;
1841 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1842 var webkit = userAgentContains('webkit');
1843
1844 // @property android: Boolean
1845 // `true` for any browser running on an Android platform.
1846 var android = userAgentContains('android');
1847
1848 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1849 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1850
1851 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1852 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1853 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1854 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1855
1856 // @property opera: Boolean; `true` for the Opera browser
1857 var opera = !!window.opera;
1858
1859 // @property chrome: Boolean; `true` for the Chrome browser.
1860 var chrome = userAgentContains('chrome');
1861
1862 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1863 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1864
1865 // @property safari: Boolean; `true` for the Safari browser.
1866 var safari = !chrome && userAgentContains('safari');
1867
1868 var phantom = userAgentContains('phantom');
1869
1870 // @property opera12: Boolean
1871 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1872 var opera12 = 'OTransition' in style$1;
1873
1874 // @property win: Boolean; `true` when the browser is running in a Windows platform
1875 var win = navigator.platform.indexOf('Win') === 0;
1876
1877 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1878 var ie3d = ie && ('transition' in style$1);
1879
1880 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1881 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1882
1883 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1884 var gecko3d = 'MozPerspective' in style$1;
1885
1886 // @property any3d: Boolean
1887 // `true` for all browsers supporting CSS transforms.
1888 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1889
1890 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1891 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1892
1893 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1894 var mobileWebkit = mobile && webkit;
1895
1896 // @property mobileWebkit3d: Boolean
1897 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1898 var mobileWebkit3d = mobile && webkit3d;
1899
1900 // @property msPointer: Boolean
1901 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
1902 var msPointer = !window.PointerEvent && window.MSPointerEvent;
1903
1904 // @property pointer: Boolean
1905 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1906 var pointer = !!(window.PointerEvent || msPointer);
1907
1908 // @property touch: Boolean
1909 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1910 // This does not necessarily mean that the browser is running in a computer with
1911 // a touchscreen, it only means that the browser is capable of understanding
1912 // touch events.
1913 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1914 (window.DocumentTouch && document instanceof window.DocumentTouch));
1915
1916 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1917 var mobileOpera = mobile && opera;
1918
1919 // @property mobileGecko: Boolean
1920 // `true` for gecko-based browsers running in a mobile device.
1921 var mobileGecko = mobile && gecko;
1922
1923 // @property retina: Boolean
1924 // `true` for browsers on a high-resolution "retina" screen.
1925 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1926
1927
1928 // @property canvas: Boolean
1929 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1930 var canvas = (function () {
1931 return !!document.createElement('canvas').getContext;
1932 }());
1933
1934 // @property svg: Boolean
1935 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1936 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1937
1938 // @property vml: Boolean
1939 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1940 var vml = !svg && (function () {
1941 try {
1942 var div = document.createElement('div');
1943 div.innerHTML = '<v:shape adj="1"/>';
1944
1945 var shape = div.firstChild;
1946 shape.style.behavior = 'url(#default#VML)';
1947
1948 return shape && (typeof shape.adj === 'object');
1949
1950 } catch (e) {
1951 return false;
1952 }
1953 }());
1954
1955
1956 function userAgentContains(str) {
1957 return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1958 }
1959
1960
1961 var Browser = (Object.freeze || Object)({
1962 ie: ie,
1963 ielt9: ielt9,
1964 edge: edge,
1965 webkit: webkit,
1966 android: android,
1967 android23: android23,
1968 androidStock: androidStock,
1969 opera: opera,
1970 chrome: chrome,
1971 gecko: gecko,
1972 safari: safari,
1973 phantom: phantom,
1974 opera12: opera12,
1975 win: win,
1976 ie3d: ie3d,
1977 webkit3d: webkit3d,
1978 gecko3d: gecko3d,
1979 any3d: any3d,
1980 mobile: mobile,
1981 mobileWebkit: mobileWebkit,
1982 mobileWebkit3d: mobileWebkit3d,
1983 msPointer: msPointer,
1984 pointer: pointer,
1985 touch: touch,
1986 mobileOpera: mobileOpera,
1987 mobileGecko: mobileGecko,
1988 retina: retina,
1989 canvas: canvas,
1990 svg: svg,
1991 vml: vml
1992 });
1993
1994 /*
1995 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
1996 */
1997
1998
1999 var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown';
2000 var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove';
2001 var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup';
2002 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2003 var TAG_WHITE_LIST = ['INPUT', 'SELECT', 'OPTION'];
2004
2005 var _pointers = {};
2006 var _pointerDocListener = false;
2007
2008 // DomEvent.DoubleTap needs to know about this
2009 var _pointersCount = 0;
2010
2011 // Provides a touch events wrapper for (ms)pointer events.
2012 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2013
2014 function addPointerListener(obj, type, handler, id) {
2015 if (type === 'touchstart') {
2016 _addPointerStart(obj, handler, id);
2017
2018 } else if (type === 'touchmove') {
2019 _addPointerMove(obj, handler, id);
2020
2021 } else if (type === 'touchend') {
2022 _addPointerEnd(obj, handler, id);
2023 }
2024
2025 return this;
2026 }
2027
2028 function removePointerListener(obj, type, id) {
2029 var handler = obj['_leaflet_' + type + id];
2030
2031 if (type === 'touchstart') {
2032 obj.removeEventListener(POINTER_DOWN, handler, false);
2033
2034 } else if (type === 'touchmove') {
2035 obj.removeEventListener(POINTER_MOVE, handler, false);
2036
2037 } else if (type === 'touchend') {
2038 obj.removeEventListener(POINTER_UP, handler, false);
2039 obj.removeEventListener(POINTER_CANCEL, handler, false);
2040 }
2041
2042 return this;
2043 }
2044
2045 function _addPointerStart(obj, handler, id) {
2046 var onDown = bind(function (e) {
2047 if (e.pointerType !== 'mouse' && e.MSPOINTER_TYPE_MOUSE && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) {
2048 // In IE11, some touch events needs to fire for form controls, or
2049 // the controls will stop working. We keep a whitelist of tag names that
2050 // need these events. For other target tags, we prevent default on the event.
2051 if (TAG_WHITE_LIST.indexOf(e.target.tagName) < 0) {
2052 preventDefault(e);
2053 } else {
2054 return;
2055 }
2056 }
2057
2058 _handlePointer(e, handler);
2059 });
2060
2061 obj['_leaflet_touchstart' + id] = onDown;
2062 obj.addEventListener(POINTER_DOWN, onDown, false);
2063
2064 // need to keep track of what pointers and how many are active to provide e.touches emulation
2065 if (!_pointerDocListener) {
2066 // we listen documentElement as any drags that end by moving the touch off the screen get fired there
2067 document.documentElement.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2068 document.documentElement.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2069 document.documentElement.addEventListener(POINTER_UP, _globalPointerUp, true);
2070 document.documentElement.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2071
2072 _pointerDocListener = true;
2073 }
2074 }
2075
2076 function _globalPointerDown(e) {
2077 _pointers[e.pointerId] = e;
2078 _pointersCount++;
2079 }
2080
2081 function _globalPointerMove(e) {
2082 if (_pointers[e.pointerId]) {
2083 _pointers[e.pointerId] = e;
2084 }
2085 }
2086
2087 function _globalPointerUp(e) {
2088 delete _pointers[e.pointerId];
2089 _pointersCount--;
2090 }
2091
2092 function _handlePointer(e, handler) {
2093 e.touches = [];
2094 for (var i in _pointers) {
2095 e.touches.push(_pointers[i]);
2096 }
2097 e.changedTouches = [e];
2098
2099 handler(e);
2100 }
2101
2102 function _addPointerMove(obj, handler, id) {
2103 var onMove = function (e) {
2104 // don't fire touch moves when mouse isn't down
2105 if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; }
2106
2107 _handlePointer(e, handler);
2108 };
2109
2110 obj['_leaflet_touchmove' + id] = onMove;
2111 obj.addEventListener(POINTER_MOVE, onMove, false);
2112 }
2113
2114 function _addPointerEnd(obj, handler, id) {
2115 var onUp = function (e) {
2116 _handlePointer(e, handler);
2117 };
2118
2119 obj['_leaflet_touchend' + id] = onUp;
2120 obj.addEventListener(POINTER_UP, onUp, false);
2121 obj.addEventListener(POINTER_CANCEL, onUp, false);
2122 }
2123
2124 /*
2125 * Extends the event handling code with double tap support for mobile browsers.
2126 */
2127
2128 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2129 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2130 var _pre = '_leaflet_';
2131
2132 // inspired by Zepto touch code by Thomas Fuchs
2133 function addDoubleTapListener(obj, handler, id) {
2134 var last, touch$$1,
2135 doubleTap = false,
2136 delay = 250;
2137
2138 function onTouchStart(e) {
2139 var count;
2140
2141 if (pointer) {
2142 if ((!edge) || e.pointerType === 'mouse') { return; }
2143 count = _pointersCount;
2144 } else {
2145 count = e.touches.length;
2146 }
2147
2148 if (count > 1) { return; }
2149
2150 var now = Date.now(),
2151 delta = now - (last || now);
2152
2153 touch$$1 = e.touches ? e.touches[0] : e;
2154 doubleTap = (delta > 0 && delta <= delay);
2155 last = now;
2156 }
2157
2158 function onTouchEnd(e) {
2159 if (doubleTap && !touch$$1.cancelBubble) {
2160 if (pointer) {
2161 if ((!edge) || e.pointerType === 'mouse') { return; }
2162 // work around .type being readonly with MSPointer* events
2163 var newTouch = {},
2164 prop, i;
2165
2166 for (i in touch$$1) {
2167 prop = touch$$1[i];
2168 newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2169 }
2170 touch$$1 = newTouch;
2171 }
2172 touch$$1.type = 'dblclick';
2173 handler(touch$$1);
2174 last = null;
2175 }
2176 }
2177
2178 obj[_pre + _touchstart + id] = onTouchStart;
2179 obj[_pre + _touchend + id] = onTouchEnd;
2180 obj[_pre + 'dblclick' + id] = handler;
2181
2182 obj.addEventListener(_touchstart, onTouchStart, false);
2183 obj.addEventListener(_touchend, onTouchEnd, false);
2184
2185 // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2186 // the browser doesn't fire touchend/pointerup events but does fire
2187 // native dblclicks. See #4127.
2188 // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2189 obj.addEventListener('dblclick', handler, false);
2190
2191 return this;
2192 }
2193
2194 function removeDoubleTapListener(obj, id) {
2195 var touchstart = obj[_pre + _touchstart + id],
2196 touchend = obj[_pre + _touchend + id],
2197 dblclick = obj[_pre + 'dblclick' + id];
2198
2199 obj.removeEventListener(_touchstart, touchstart, false);
2200 obj.removeEventListener(_touchend, touchend, false);
2201 if (!edge) {
2202 obj.removeEventListener('dblclick', dblclick, false);
2203 }
2204
2205 return this;
2206 }
2207
2208 /*
2209 * @namespace DomEvent
2210 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2211 */
2212
2213 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2214
2215 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2216 // Adds a listener function (`fn`) to a particular DOM event type of the
2217 // element `el`. You can optionally specify the context of the listener
2218 // (object the `this` keyword will point to). You can also pass several
2219 // space-separated types (e.g. `'click dblclick'`).
2220
2221 // @alternative
2222 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2223 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2224 function on(obj, types, fn, context) {
2225
2226 if (typeof types === 'object') {
2227 for (var type in types) {
2228 addOne(obj, type, types[type], fn);
2229 }
2230 } else {
2231 types = splitWords(types);
2232
2233 for (var i = 0, len = types.length; i < len; i++) {
2234 addOne(obj, types[i], fn, context);
2235 }
2236 }
2237
2238 return this;
2239 }
2240
2241 var eventsKey = '_leaflet_events';
2242
2243 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2244 // Removes a previously added listener function.
2245 // Note that if you passed a custom context to on, you must pass the same
2246 // context to `off` in order to remove the listener.
2247
2248 // @alternative
2249 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2250 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2251 function off(obj, types, fn, context) {
2252
2253 if (typeof types === 'object') {
2254 for (var type in types) {
2255 removeOne(obj, type, types[type], fn);
2256 }
2257 } else if (types) {
2258 types = splitWords(types);
2259
2260 for (var i = 0, len = types.length; i < len; i++) {
2261 removeOne(obj, types[i], fn, context);
2262 }
2263 } else {
2264 for (var j in obj[eventsKey]) {
2265 removeOne(obj, j, obj[eventsKey][j]);
2266 }
2267 delete obj[eventsKey];
2268 }
2269
2270 return this;
2271 }
2272
2273 function addOne(obj, type, fn, context) {
2274 var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2275
2276 if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2277
2278 var handler = function (e) {
2279 return fn.call(context || obj, e || window.event);
2280 };
2281
2282 var originalHandler = handler;
2283
2284 if (pointer && type.indexOf('touch') === 0) {
2285 // Needs DomEvent.Pointer.js
2286 addPointerListener(obj, type, handler, id);
2287
2288 } else if (touch && (type === 'dblclick') && addDoubleTapListener &&
2289 !(pointer && chrome)) {
2290 // Chrome >55 does not need the synthetic dblclicks from addDoubleTapListener
2291 // See #5180
2292 addDoubleTapListener(obj, handler, id);
2293
2294 } else if ('addEventListener' in obj) {
2295
2296 if (type === 'mousewheel') {
2297 obj.addEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2298
2299 } else if ((type === 'mouseenter') || (type === 'mouseleave')) {
2300 handler = function (e) {
2301 e = e || window.event;
2302 if (isExternalTarget(obj, e)) {
2303 originalHandler(e);
2304 }
2305 };
2306 obj.addEventListener(type === 'mouseenter' ? 'mouseover' : 'mouseout', handler, false);
2307
2308 } else {
2309 if (type === 'click' && android) {
2310 handler = function (e) {
2311 filterClick(e, originalHandler);
2312 };
2313 }
2314 obj.addEventListener(type, handler, false);
2315 }
2316
2317 } else if ('attachEvent' in obj) {
2318 obj.attachEvent('on' + type, handler);
2319 }
2320
2321 obj[eventsKey] = obj[eventsKey] || {};
2322 obj[eventsKey][id] = handler;
2323 }
2324
2325 function removeOne(obj, type, fn, context) {
2326
2327 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2328 handler = obj[eventsKey] && obj[eventsKey][id];
2329
2330 if (!handler) { return this; }
2331
2332 if (pointer && type.indexOf('touch') === 0) {
2333 removePointerListener(obj, type, id);
2334
2335 } else if (touch && (type === 'dblclick') && removeDoubleTapListener &&
2336 !(pointer && chrome)) {
2337 removeDoubleTapListener(obj, id);
2338
2339 } else if ('removeEventListener' in obj) {
2340
2341 if (type === 'mousewheel') {
2342 obj.removeEventListener('onwheel' in obj ? 'wheel' : 'mousewheel', handler, false);
2343
2344 } else {
2345 obj.removeEventListener(
2346 type === 'mouseenter' ? 'mouseover' :
2347 type === 'mouseleave' ? 'mouseout' : type, handler, false);
2348 }
2349
2350 } else if ('detachEvent' in obj) {
2351 obj.detachEvent('on' + type, handler);
2352 }
2353
2354 obj[eventsKey][id] = null;
2355 }
2356
2357 // @function stopPropagation(ev: DOMEvent): this
2358 // Stop the given event from propagation to parent elements. Used inside the listener functions:
2359 // ```js
2360 // L.DomEvent.on(div, 'click', function (ev) {
2361 // L.DomEvent.stopPropagation(ev);
2362 // });
2363 // ```
2364 function stopPropagation(e) {
2365
2366 if (e.stopPropagation) {
2367 e.stopPropagation();
2368 } else if (e.originalEvent) { // In case of Leaflet event.
2369 e.originalEvent._stopped = true;
2370 } else {
2371 e.cancelBubble = true;
2372 }
2373 skipped(e);
2374
2375 return this;
2376 }
2377
2378 // @function disableScrollPropagation(el: HTMLElement): this
2379 // Adds `stopPropagation` to the element's `'mousewheel'` events (plus browser variants).
2380 function disableScrollPropagation(el) {
2381 addOne(el, 'mousewheel', stopPropagation);
2382 return this;
2383 }
2384
2385 // @function disableClickPropagation(el: HTMLElement): this
2386 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2387 // `'mousedown'` and `'touchstart'` events (plus browser variants).
2388 function disableClickPropagation(el) {
2389 on(el, 'mousedown touchstart dblclick', stopPropagation);
2390 addOne(el, 'click', fakeStop);
2391 return this;
2392 }
2393
2394 // @function preventDefault(ev: DOMEvent): this
2395 // Prevents the default action of the DOM Event `ev` from happening (such as
2396 // following a link in the href of the a element, or doing a POST request
2397 // with page reload when a `<form>` is submitted).
2398 // Use it inside listener functions.
2399 function preventDefault(e) {
2400 if (e.preventDefault) {
2401 e.preventDefault();
2402 } else {
2403 e.returnValue = false;
2404 }
2405 return this;
2406 }
2407
2408 // @function stop(ev: DOMEvent): this
2409 // Does `stopPropagation` and `preventDefault` at the same time.
2410 function stop(e) {
2411 preventDefault(e);
2412 stopPropagation(e);
2413 return this;
2414 }
2415
2416 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2417 // Gets normalized mouse position from a DOM event relative to the
2418 // `container` or to the whole page if not specified.
2419 function getMousePosition(e, container) {
2420 if (!container) {
2421 return new Point(e.clientX, e.clientY);
2422 }
2423
2424 var rect = container.getBoundingClientRect();
2425
2426 var scaleX = rect.width / container.offsetWidth || 1;
2427 var scaleY = rect.height / container.offsetHeight || 1;
2428 return new Point(
2429 e.clientX / scaleX - rect.left - container.clientLeft,
2430 e.clientY / scaleY - rect.top - container.clientTop);
2431 }
2432
2433 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2434 // and Firefox scrolls device pixels, not CSS pixels
2435 var wheelPxFactor =
2436 (win && chrome) ? 2 * window.devicePixelRatio :
2437 gecko ? window.devicePixelRatio : 1;
2438
2439 // @function getWheelDelta(ev: DOMEvent): Number
2440 // Gets normalized wheel delta from a mousewheel DOM event, in vertical
2441 // pixels scrolled (negative if scrolling down).
2442 // Events from pointing devices without precise scrolling are mapped to
2443 // a best guess of 60 pixels.
2444 function getWheelDelta(e) {
2445 return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2446 (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2447 (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2448 (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2449 (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events
2450 e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2451 (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2452 e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2453 0;
2454 }
2455
2456 var skipEvents = {};
2457
2458 function fakeStop(e) {
2459 // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2460 skipEvents[e.type] = true;
2461 }
2462
2463 function skipped(e) {
2464 var events = skipEvents[e.type];
2465 // reset when checking, as it's only used in map container and propagates outside of the map
2466 skipEvents[e.type] = false;
2467 return events;
2468 }
2469
2470 // check if element really left/entered the event target (for mouseenter/mouseleave)
2471 function isExternalTarget(el, e) {
2472
2473 var related = e.relatedTarget;
2474
2475 if (!related) { return true; }
2476
2477 try {
2478 while (related && (related !== el)) {
2479 related = related.parentNode;
2480 }
2481 } catch (err) {
2482 return false;
2483 }
2484 return (related !== el);
2485 }
2486
2487 var lastClick;
2488
2489 // this is a horrible workaround for a bug in Android where a single touch triggers two click events
2490 function filterClick(e, handler) {
2491 var timeStamp = (e.timeStamp || (e.originalEvent && e.originalEvent.timeStamp)),
2492 elapsed = lastClick && (timeStamp - lastClick);
2493
2494 // are they closer together than 500ms yet more than 100ms?
2495 // Android typically triggers them ~300ms apart while multiple listeners
2496 // on the same event should be triggered far faster;
2497 // or check if click is simulated on the element, and if it is, reject any non-simulated events
2498
2499 if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) {
2500 stop(e);
2501 return;
2502 }
2503 lastClick = timeStamp;
2504
2505 handler(e);
2506 }
2507
2508
2509
2510
2511 var DomEvent = (Object.freeze || Object)({
2512 on: on,
2513 off: off,
2514 stopPropagation: stopPropagation,
2515 disableScrollPropagation: disableScrollPropagation,
2516 disableClickPropagation: disableClickPropagation,
2517 preventDefault: preventDefault,
2518 stop: stop,
2519 getMousePosition: getMousePosition,
2520 getWheelDelta: getWheelDelta,
2521 fakeStop: fakeStop,
2522 skipped: skipped,
2523 isExternalTarget: isExternalTarget,
2524 addListener: on,
2525 removeListener: off
2526 });
2527
2528 /*
2529 * @namespace DomUtil
2530 *
2531 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2532 * tree, used by Leaflet internally.
2533 *
2534 * Most functions expecting or returning a `HTMLElement` also work for
2535 * SVG elements. The only difference is that classes refer to CSS classes
2536 * in HTML and SVG classes in SVG.
2537 */
2538
2539
2540 // @property TRANSFORM: String
2541 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2542 var TRANSFORM = testProp(
2543 ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2544
2545 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2546 // the same for the transitionend event, in particular the Android 4.1 stock browser
2547
2548 // @property TRANSITION: String
2549 // Vendor-prefixed transition style name.
2550 var TRANSITION = testProp(
2551 ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2552
2553 // @property TRANSITION_END: String
2554 // Vendor-prefixed transitionend event name.
2555 var TRANSITION_END =
2556 TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2557
2558
2559 // @function get(id: String|HTMLElement): HTMLElement
2560 // Returns an element given its DOM id, or returns the element itself
2561 // if it was passed directly.
2562 function get(id) {
2563 return typeof id === 'string' ? document.getElementById(id) : id;
2564 }
2565
2566 // @function getStyle(el: HTMLElement, styleAttrib: String): String
2567 // Returns the value for a certain style attribute on an element,
2568 // including computed values or values set through CSS.
2569 function getStyle(el, style) {
2570 var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2571
2572 if ((!value || value === 'auto') && document.defaultView) {
2573 var css = document.defaultView.getComputedStyle(el, null);
2574 value = css ? css[style] : null;
2575 }
2576 return value === 'auto' ? null : value;
2577 }
2578
2579 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2580 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2581 function create$1(tagName, className, container) {
2582 var el = document.createElement(tagName);
2583 el.className = className || '';
2584
2585 if (container) {
2586 container.appendChild(el);
2587 }
2588 return el;
2589 }
2590
2591 // @function remove(el: HTMLElement)
2592 // Removes `el` from its parent element
2593 function remove(el) {
2594 var parent = el.parentNode;
2595 if (parent) {
2596 parent.removeChild(el);
2597 }
2598 }
2599
2600 // @function empty(el: HTMLElement)
2601 // Removes all of `el`'s children elements from `el`
2602 function empty(el) {
2603 while (el.firstChild) {
2604 el.removeChild(el.firstChild);
2605 }
2606 }
2607
2608 // @function toFront(el: HTMLElement)
2609 // Makes `el` the last child of its parent, so it renders in front of the other children.
2610 function toFront(el) {
2611 var parent = el.parentNode;
2612 if (parent.lastChild !== el) {
2613 parent.appendChild(el);
2614 }
2615 }
2616
2617 // @function toBack(el: HTMLElement)
2618 // Makes `el` the first child of its parent, so it renders behind the other children.
2619 function toBack(el) {
2620 var parent = el.parentNode;
2621 if (parent.firstChild !== el) {
2622 parent.insertBefore(el, parent.firstChild);
2623 }
2624 }
2625
2626 // @function hasClass(el: HTMLElement, name: String): Boolean
2627 // Returns `true` if the element's class attribute contains `name`.
2628 function hasClass(el, name) {
2629 if (el.classList !== undefined) {
2630 return el.classList.contains(name);
2631 }
2632 var className = getClass(el);
2633 return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2634 }
2635
2636 // @function addClass(el: HTMLElement, name: String)
2637 // Adds `name` to the element's class attribute.
2638 function addClass(el, name) {
2639 if (el.classList !== undefined) {
2640 var classes = splitWords(name);
2641 for (var i = 0, len = classes.length; i < len; i++) {
2642 el.classList.add(classes[i]);
2643 }
2644 } else if (!hasClass(el, name)) {
2645 var className = getClass(el);
2646 setClass(el, (className ? className + ' ' : '') + name);
2647 }
2648 }
2649
2650 // @function removeClass(el: HTMLElement, name: String)
2651 // Removes `name` from the element's class attribute.
2652 function removeClass(el, name) {
2653 if (el.classList !== undefined) {
2654 el.classList.remove(name);
2655 } else {
2656 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2657 }
2658 }
2659
2660 // @function setClass(el: HTMLElement, name: String)
2661 // Sets the element's class.
2662 function setClass(el, name) {
2663 if (el.className.baseVal === undefined) {
2664 el.className = name;
2665 } else {
2666 // in case of SVG element
2667 el.className.baseVal = name;
2668 }
2669 }
2670
2671 // @function getClass(el: HTMLElement): String
2672 // Returns the element's class.
2673 function getClass(el) {
2674 return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2675 }
2676
2677 // @function setOpacity(el: HTMLElement, opacity: Number)
2678 // Set the opacity of an element (including old IE support).
2679 // `opacity` must be a number from `0` to `1`.
2680 function setOpacity(el, value) {
2681 if ('opacity' in el.style) {
2682 el.style.opacity = value;
2683 } else if ('filter' in el.style) {
2684 _setOpacityIE(el, value);
2685 }
2686 }
2687
2688 function _setOpacityIE(el, value) {
2689 var filter = false,
2690 filterName = 'DXImageTransform.Microsoft.Alpha';
2691
2692 // filters collection throws an error if we try to retrieve a filter that doesn't exist
2693 try {
2694 filter = el.filters.item(filterName);
2695 } catch (e) {
2696 // don't set opacity to 1 if we haven't already set an opacity,
2697 // it isn't needed and breaks transparent pngs.
2698 if (value === 1) { return; }
2699 }
2700
2701 value = Math.round(value * 100);
2702
2703 if (filter) {
2704 filter.Enabled = (value !== 100);
2705 filter.Opacity = value;
2706 } else {
2707 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2708 }
2709 }
2710
2711 // @function testProp(props: String[]): String|false
2712 // Goes through the array of style names and returns the first name
2713 // that is a valid style name for an element. If no such name is found,
2714 // it returns false. Useful for vendor-prefixed styles like `transform`.
2715 function testProp(props) {
2716 var style = document.documentElement.style;
2717
2718 for (var i = 0; i < props.length; i++) {
2719 if (props[i] in style) {
2720 return props[i];
2721 }
2722 }
2723 return false;
2724 }
2725
2726 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2727 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2728 // and optionally scaled by `scale`. Does not have an effect if the
2729 // browser doesn't support 3D CSS transforms.
2730 function setTransform(el, offset, scale) {
2731 var pos = offset || new Point(0, 0);
2732
2733 el.style[TRANSFORM] =
2734 (ie3d ?
2735 'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2736 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2737 (scale ? ' scale(' + scale + ')' : '');
2738 }
2739
2740 // @function setPosition(el: HTMLElement, position: Point)
2741 // Sets the position of `el` to coordinates specified by `position`,
2742 // using CSS translate or top/left positioning depending on the browser
2743 // (used by Leaflet internally to position its layers).
2744 function setPosition(el, point) {
2745
2746 /*eslint-disable */
2747 el._leaflet_pos = point;
2748 /* eslint-enable */
2749
2750 if (any3d) {
2751 setTransform(el, point);
2752 } else {
2753 el.style.left = point.x + 'px';
2754 el.style.top = point.y + 'px';
2755 }
2756 }
2757
2758 // @function getPosition(el: HTMLElement): Point
2759 // Returns the coordinates of an element previously positioned with setPosition.
2760 function getPosition(el) {
2761 // this method is only used for elements previously positioned using setPosition,
2762 // so it's safe to cache the position for performance
2763
2764 return el._leaflet_pos || new Point(0, 0);
2765 }
2766
2767 // @function disableTextSelection()
2768 // Prevents the user from generating `selectstart` DOM events, usually generated
2769 // when the user drags the mouse through a page with text. Used internally
2770 // by Leaflet to override the behaviour of any click-and-drag interaction on
2771 // the map. Affects drag interactions on the whole document.
2772
2773 // @function enableTextSelection()
2774 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2775 var disableTextSelection;
2776 var enableTextSelection;
2777 var _userSelect;
2778 if ('onselectstart' in document) {
2779 disableTextSelection = function () {
2780 on(window, 'selectstart', preventDefault);
2781 };
2782 enableTextSelection = function () {
2783 off(window, 'selectstart', preventDefault);
2784 };
2785 } else {
2786 var userSelectProperty = testProp(
2787 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2788
2789 disableTextSelection = function () {
2790 if (userSelectProperty) {
2791 var style = document.documentElement.style;
2792 _userSelect = style[userSelectProperty];
2793 style[userSelectProperty] = 'none';
2794 }
2795 };
2796 enableTextSelection = function () {
2797 if (userSelectProperty) {
2798 document.documentElement.style[userSelectProperty] = _userSelect;
2799 _userSelect = undefined;
2800 }
2801 };
2802 }
2803
2804 // @function disableImageDrag()
2805 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2806 // for `dragstart` DOM events, usually generated when the user drags an image.
2807 function disableImageDrag() {
2808 on(window, 'dragstart', preventDefault);
2809 }
2810
2811 // @function enableImageDrag()
2812 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2813 function enableImageDrag() {
2814 off(window, 'dragstart', preventDefault);
2815 }
2816
2817 var _outlineElement;
2818 var _outlineStyle;
2819 // @function preventOutline(el: HTMLElement)
2820 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2821 // of the element `el` invisible. Used internally by Leaflet to prevent
2822 // focusable elements from displaying an outline when the user performs a
2823 // drag interaction on them.
2824 function preventOutline(element) {
2825 while (element.tabIndex === -1) {
2826 element = element.parentNode;
2827 }
2828 if (!element.style) { return; }
2829 restoreOutline();
2830 _outlineElement = element;
2831 _outlineStyle = element.style.outline;
2832 element.style.outline = 'none';
2833 on(window, 'keydown', restoreOutline);
2834 }
2835
2836 // @function restoreOutline()
2837 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2838 function restoreOutline() {
2839 if (!_outlineElement) { return; }
2840 _outlineElement.style.outline = _outlineStyle;
2841 _outlineElement = undefined;
2842 _outlineStyle = undefined;
2843 off(window, 'keydown', restoreOutline);
2844 }
2845
2846
2847 var DomUtil = (Object.freeze || Object)({
2848 TRANSFORM: TRANSFORM,
2849 TRANSITION: TRANSITION,
2850 TRANSITION_END: TRANSITION_END,
2851 get: get,
2852 getStyle: getStyle,
2853 create: create$1,
2854 remove: remove,
2855 empty: empty,
2856 toFront: toFront,
2857 toBack: toBack,
2858 hasClass: hasClass,
2859 addClass: addClass,
2860 removeClass: removeClass,
2861 setClass: setClass,
2862 getClass: getClass,
2863 setOpacity: setOpacity,
2864 testProp: testProp,
2865 setTransform: setTransform,
2866 setPosition: setPosition,
2867 getPosition: getPosition,
2868 disableTextSelection: disableTextSelection,
2869 enableTextSelection: enableTextSelection,
2870 disableImageDrag: disableImageDrag,
2871 enableImageDrag: enableImageDrag,
2872 preventOutline: preventOutline,
2873 restoreOutline: restoreOutline
2874 });
2875
2876 /*
2877 * @class PosAnimation
2878 * @aka L.PosAnimation
2879 * @inherits Evented
2880 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2881 *
2882 * @example
2883 * ```js
2884 * var fx = new L.PosAnimation();
2885 * fx.run(el, [300, 500], 0.5);
2886 * ```
2887 *
2888 * @constructor L.PosAnimation()
2889 * Creates a `PosAnimation` object.
2890 *
2891 */
2892
2893 var PosAnimation = Evented.extend({
2894
2895 // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2896 // Run an animation of a given element to a new position, optionally setting
2897 // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2898 // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2899 // `0.5` by default).
2900 run: function (el, newPos, duration, easeLinearity) {
2901 this.stop();
2902
2903 this._el = el;
2904 this._inProgress = true;
2905 this._duration = duration || 0.25;
2906 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2907
2908 this._startPos = getPosition(el);
2909 this._offset = newPos.subtract(this._startPos);
2910 this._startTime = +new Date();
2911
2912 // @event start: Event
2913 // Fired when the animation starts
2914 this.fire('start');
2915
2916 this._animate();
2917 },
2918
2919 // @method stop()
2920 // Stops the animation (if currently running).
2921 stop: function () {
2922 if (!this._inProgress) { return; }
2923
2924 this._step(true);
2925 this._complete();
2926 },
2927
2928 _animate: function () {
2929 // animation loop
2930 this._animId = requestAnimFrame(this._animate, this);
2931 this._step();
2932 },
2933
2934 _step: function (round) {
2935 var elapsed = (+new Date()) - this._startTime,
2936 duration = this._duration * 1000;
2937
2938 if (elapsed < duration) {
2939 this._runFrame(this._easeOut(elapsed / duration), round);
2940 } else {
2941 this._runFrame(1);
2942 this._complete();
2943 }
2944 },
2945
2946 _runFrame: function (progress, round) {
2947 var pos = this._startPos.add(this._offset.multiplyBy(progress));
2948 if (round) {
2949 pos._round();
2950 }
2951 setPosition(this._el, pos);
2952
2953 // @event step: Event
2954 // Fired continuously during the animation.
2955 this.fire('step');
2956 },
2957
2958 _complete: function () {
2959 cancelAnimFrame(this._animId);
2960
2961 this._inProgress = false;
2962 // @event end: Event
2963 // Fired when the animation ends.
2964 this.fire('end');
2965 },
2966
2967 _easeOut: function (t) {
2968 return 1 - Math.pow(1 - t, this._easeOutPower);
2969 }
2970 });
2971
2972 /*
2973 * @class Map
2974 * @aka L.Map
2975 * @inherits Evented
2976 *
2977 * The central class of the API — it is used to create a map on a page and manipulate it.
2978 *
2979 * @example
2980 *
2981 * ```js
2982 * // initialize the map on the "map" div with a given center and zoom
2983 * var map = L.map('map', {
2984 * center: [51.505, -0.09],
2985 * zoom: 13
2986 * });
2987 * ```
2988 *
2989 */
2990
2991 var Map = Evented.extend({
2992
2993 options: {
2994 // @section Map State Options
2995 // @option crs: CRS = L.CRS.EPSG3857
2996 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
2997 // sure what it means.
2998 crs: EPSG3857,
2999
3000 // @option center: LatLng = undefined
3001 // Initial geographic center of the map
3002 center: undefined,
3003
3004 // @option zoom: Number = undefined
3005 // Initial map zoom level
3006 zoom: undefined,
3007
3008 // @option minZoom: Number = *
3009 // Minimum zoom level of the map.
3010 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3011 // the lowest of their `minZoom` options will be used instead.
3012 minZoom: undefined,
3013
3014 // @option maxZoom: Number = *
3015 // Maximum zoom level of the map.
3016 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3017 // the highest of their `maxZoom` options will be used instead.
3018 maxZoom: undefined,
3019
3020 // @option layers: Layer[] = []
3021 // Array of layers that will be added to the map initially
3022 layers: [],
3023
3024 // @option maxBounds: LatLngBounds = null
3025 // When this option is set, the map restricts the view to the given
3026 // geographical bounds, bouncing the user back if the user tries to pan
3027 // outside the view. To set the restriction dynamically, use
3028 // [`setMaxBounds`](#map-setmaxbounds) method.
3029 maxBounds: undefined,
3030
3031 // @option renderer: Renderer = *
3032 // The default method for drawing vector layers on the map. `L.SVG`
3033 // or `L.Canvas` by default depending on browser support.
3034 renderer: undefined,
3035
3036
3037 // @section Animation Options
3038 // @option zoomAnimation: Boolean = true
3039 // Whether the map zoom animation is enabled. By default it's enabled
3040 // in all browsers that support CSS3 Transitions except Android.
3041 zoomAnimation: true,
3042
3043 // @option zoomAnimationThreshold: Number = 4
3044 // Won't animate zoom if the zoom difference exceeds this value.
3045 zoomAnimationThreshold: 4,
3046
3047 // @option fadeAnimation: Boolean = true
3048 // Whether the tile fade animation is enabled. By default it's enabled
3049 // in all browsers that support CSS3 Transitions except Android.
3050 fadeAnimation: true,
3051
3052 // @option markerZoomAnimation: Boolean = true
3053 // Whether markers animate their zoom with the zoom animation, if disabled
3054 // they will disappear for the length of the animation. By default it's
3055 // enabled in all browsers that support CSS3 Transitions except Android.
3056 markerZoomAnimation: true,
3057
3058 // @option transform3DLimit: Number = 2^23
3059 // Defines the maximum size of a CSS translation transform. The default
3060 // value should not be changed unless a web browser positions layers in
3061 // the wrong place after doing a large `panBy`.
3062 transform3DLimit: 8388608, // Precision limit of a 32-bit float
3063
3064 // @section Interaction Options
3065 // @option zoomSnap: Number = 1
3066 // Forces the map's zoom level to always be a multiple of this, particularly
3067 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3068 // By default, the zoom level snaps to the nearest integer; lower values
3069 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3070 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3071 zoomSnap: 1,
3072
3073 // @option zoomDelta: Number = 1
3074 // Controls how much the map's zoom level will change after a
3075 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3076 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3077 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3078 zoomDelta: 1,
3079
3080 // @option trackResize: Boolean = true
3081 // Whether the map automatically handles browser window resize to update itself.
3082 trackResize: true
3083 },
3084
3085 initialize: function (id, options) { // (HTMLElement or String, Object)
3086 options = setOptions(this, options);
3087
3088 this._initContainer(id);
3089 this._initLayout();
3090
3091 // hack for https://github.com/Leaflet/Leaflet/issues/1980
3092 this._onResize = bind(this._onResize, this);
3093
3094 this._initEvents();
3095
3096 if (options.maxBounds) {
3097 this.setMaxBounds(options.maxBounds);
3098 }
3099
3100 if (options.zoom !== undefined) {
3101 this._zoom = this._limitZoom(options.zoom);
3102 }
3103
3104 if (options.center && options.zoom !== undefined) {
3105 this.setView(toLatLng(options.center), options.zoom, {reset: true});
3106 }
3107
3108 this._handlers = [];
3109 this._layers = {};
3110 this._zoomBoundLayers = {};
3111 this._sizeChanged = true;
3112
3113 this.callInitHooks();
3114
3115 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3116 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3117 this.options.zoomAnimation;
3118
3119 // zoom transitions run with the same duration for all layers, so if one of transitionend events
3120 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3121 if (this._zoomAnimated) {
3122 this._createAnimProxy();
3123 on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3124 }
3125
3126 this._addLayers(this.options.layers);
3127 },
3128
3129
3130 // @section Methods for modifying map state
3131
3132 // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3133 // Sets the view of the map (geographical center and zoom) with the given
3134 // animation options.
3135 setView: function (center, zoom, options) {
3136
3137 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3138 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3139 options = options || {};
3140
3141 this._stop();
3142
3143 if (this._loaded && !options.reset && options !== true) {
3144
3145 if (options.animate !== undefined) {
3146 options.zoom = extend({animate: options.animate}, options.zoom);
3147 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3148 }
3149
3150 // try animating pan or zoom
3151 var moved = (this._zoom !== zoom) ?
3152 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3153 this._tryAnimatedPan(center, options.pan);
3154
3155 if (moved) {
3156 // prevent resize handler call, the view will refresh after animation anyway
3157 clearTimeout(this._sizeTimer);
3158 return this;
3159 }
3160 }
3161
3162 // animation didn't start, just reset the map view
3163 this._resetView(center, zoom);
3164
3165 return this;
3166 },
3167
3168 // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3169 // Sets the zoom of the map.
3170 setZoom: function (zoom, options) {
3171 if (!this._loaded) {
3172 this._zoom = zoom;
3173 return this;
3174 }
3175 return this.setView(this.getCenter(), zoom, {zoom: options});
3176 },
3177
3178 // @method zoomIn(delta?: Number, options?: Zoom options): this
3179 // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3180 zoomIn: function (delta, options) {
3181 delta = delta || (any3d ? this.options.zoomDelta : 1);
3182 return this.setZoom(this._zoom + delta, options);
3183 },
3184
3185 // @method zoomOut(delta?: Number, options?: Zoom options): this
3186 // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3187 zoomOut: function (delta, options) {
3188 delta = delta || (any3d ? this.options.zoomDelta : 1);
3189 return this.setZoom(this._zoom - delta, options);
3190 },
3191
3192 // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3193 // Zooms the map while keeping a specified geographical point on the map
3194 // stationary (e.g. used internally for scroll zoom and double-click zoom).
3195 // @alternative
3196 // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3197 // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3198 setZoomAround: function (latlng, zoom, options) {
3199 var scale = this.getZoomScale(zoom),
3200 viewHalf = this.getSize().divideBy(2),
3201 containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3202
3203 centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3204 newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3205
3206 return this.setView(newCenter, zoom, {zoom: options});
3207 },
3208
3209 _getBoundsCenterZoom: function (bounds, options) {
3210
3211 options = options || {};
3212 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3213
3214 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3215 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3216
3217 zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3218
3219 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3220
3221 if (zoom === Infinity) {
3222 return {
3223 center: bounds.getCenter(),
3224 zoom: zoom
3225 };
3226 }
3227
3228 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3229
3230 swPoint = this.project(bounds.getSouthWest(), zoom),
3231 nePoint = this.project(bounds.getNorthEast(), zoom),
3232 center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3233
3234 return {
3235 center: center,
3236 zoom: zoom
3237 };
3238 },
3239
3240 // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3241 // Sets a map view that contains the given geographical bounds with the
3242 // maximum zoom level possible.
3243 fitBounds: function (bounds, options) {
3244
3245 bounds = toLatLngBounds(bounds);
3246
3247 if (!bounds.isValid()) {
3248 throw new Error('Bounds are not valid.');
3249 }
3250
3251 var target = this._getBoundsCenterZoom(bounds, options);
3252 return this.setView(target.center, target.zoom, options);
3253 },
3254
3255 // @method fitWorld(options?: fitBounds options): this
3256 // Sets a map view that mostly contains the whole world with the maximum
3257 // zoom level possible.
3258 fitWorld: function (options) {
3259 return this.fitBounds([[-90, -180], [90, 180]], options);
3260 },
3261
3262 // @method panTo(latlng: LatLng, options?: Pan options): this
3263 // Pans the map to a given center.
3264 panTo: function (center, options) { // (LatLng)
3265 return this.setView(center, this._zoom, {pan: options});
3266 },
3267
3268 // @method panBy(offset: Point, options?: Pan options): this
3269 // Pans the map by a given number of pixels (animated).
3270 panBy: function (offset, options) {
3271 offset = toPoint(offset).round();
3272 options = options || {};
3273
3274 if (!offset.x && !offset.y) {
3275 return this.fire('moveend');
3276 }
3277 // If we pan too far, Chrome gets issues with tiles
3278 // and makes them disappear or appear in the wrong place (slightly offset) #2602
3279 if (options.animate !== true && !this.getSize().contains(offset)) {
3280 this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3281 return this;
3282 }
3283
3284 if (!this._panAnim) {
3285 this._panAnim = new PosAnimation();
3286
3287 this._panAnim.on({
3288 'step': this._onPanTransitionStep,
3289 'end': this._onPanTransitionEnd
3290 }, this);
3291 }
3292
3293 // don't fire movestart if animating inertia
3294 if (!options.noMoveStart) {
3295 this.fire('movestart');
3296 }
3297
3298 // animate pan unless animate: false specified
3299 if (options.animate !== false) {
3300 addClass(this._mapPane, 'leaflet-pan-anim');
3301
3302 var newPos = this._getMapPanePos().subtract(offset).round();
3303 this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3304 } else {
3305 this._rawPanBy(offset);
3306 this.fire('move').fire('moveend');
3307 }
3308
3309 return this;
3310 },
3311
3312 // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3313 // Sets the view of the map (geographical center and zoom) performing a smooth
3314 // pan-zoom animation.
3315 flyTo: function (targetCenter, targetZoom, options) {
3316
3317 options = options || {};
3318 if (options.animate === false || !any3d) {
3319 return this.setView(targetCenter, targetZoom, options);
3320 }
3321
3322 this._stop();
3323
3324 var from = this.project(this.getCenter()),
3325 to = this.project(targetCenter),
3326 size = this.getSize(),
3327 startZoom = this._zoom;
3328
3329 targetCenter = toLatLng(targetCenter);
3330 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3331
3332 var w0 = Math.max(size.x, size.y),
3333 w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3334 u1 = (to.distanceTo(from)) || 1,
3335 rho = 1.42,
3336 rho2 = rho * rho;
3337
3338 function r(i) {
3339 var s1 = i ? -1 : 1,
3340 s2 = i ? w1 : w0,
3341 t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3342 b1 = 2 * s2 * rho2 * u1,
3343 b = t1 / b1,
3344 sq = Math.sqrt(b * b + 1) - b;
3345
3346 // workaround for floating point precision bug when sq = 0, log = -Infinite,
3347 // thus triggering an infinite loop in flyTo
3348 var log = sq < 0.000000001 ? -18 : Math.log(sq);
3349
3350 return log;
3351 }
3352
3353 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3354 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3355 function tanh(n) { return sinh(n) / cosh(n); }
3356
3357 var r0 = r(0);
3358
3359 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3360 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3361
3362 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3363
3364 var start = Date.now(),
3365 S = (r(1) - r0) / rho,
3366 duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3367
3368 function frame() {
3369 var t = (Date.now() - start) / duration,
3370 s = easeOut(t) * S;
3371
3372 if (t <= 1) {
3373 this._flyToFrame = requestAnimFrame(frame, this);
3374
3375 this._move(
3376 this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3377 this.getScaleZoom(w0 / w(s), startZoom),
3378 {flyTo: true});
3379
3380 } else {
3381 this
3382 ._move(targetCenter, targetZoom)
3383 ._moveEnd(true);
3384 }
3385 }
3386
3387 this._moveStart(true, options.noMoveStart);
3388
3389 frame.call(this);
3390 return this;
3391 },
3392
3393 // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3394 // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3395 // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3396 flyToBounds: function (bounds, options) {
3397 var target = this._getBoundsCenterZoom(bounds, options);
3398 return this.flyTo(target.center, target.zoom, options);
3399 },
3400
3401 // @method setMaxBounds(bounds: Bounds): this
3402 // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3403 setMaxBounds: function (bounds) {
3404 bounds = toLatLngBounds(bounds);
3405
3406 if (!bounds.isValid()) {
3407 this.options.maxBounds = null;
3408 return this.off('moveend', this._panInsideMaxBounds);
3409 } else if (this.options.maxBounds) {
3410 this.off('moveend', this._panInsideMaxBounds);
3411 }
3412
3413 this.options.maxBounds = bounds;
3414
3415 if (this._loaded) {
3416 this._panInsideMaxBounds();
3417 }
3418
3419 return this.on('moveend', this._panInsideMaxBounds);
3420 },
3421
3422 // @method setMinZoom(zoom: Number): this
3423 // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3424 setMinZoom: function (zoom) {
3425 var oldZoom = this.options.minZoom;
3426 this.options.minZoom = zoom;
3427
3428 if (this._loaded && oldZoom !== zoom) {
3429 this.fire('zoomlevelschange');
3430
3431 if (this.getZoom() < this.options.minZoom) {
3432 return this.setZoom(zoom);
3433 }
3434 }
3435
3436 return this;
3437 },
3438
3439 // @method setMaxZoom(zoom: Number): this
3440 // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3441 setMaxZoom: function (zoom) {
3442 var oldZoom = this.options.maxZoom;
3443 this.options.maxZoom = zoom;
3444
3445 if (this._loaded && oldZoom !== zoom) {
3446 this.fire('zoomlevelschange');
3447
3448 if (this.getZoom() > this.options.maxZoom) {
3449 return this.setZoom(zoom);
3450 }
3451 }
3452
3453 return this;
3454 },
3455
3456 // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3457 // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
3458 panInsideBounds: function (bounds, options) {
3459 this._enforcingBounds = true;
3460 var center = this.getCenter(),
3461 newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3462
3463 if (!center.equals(newCenter)) {
3464 this.panTo(newCenter, options);
3465 }
3466
3467 this._enforcingBounds = false;
3468 return this;
3469 },
3470
3471 // @method invalidateSize(options: Zoom/pan options): this
3472 // Checks if the map container size changed and updates the map if so —
3473 // call it after you've changed the map size dynamically, also animating
3474 // pan by default. If `options.pan` is `false`, panning will not occur.
3475 // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3476 // that it doesn't happen often even if the method is called many
3477 // times in a row.
3478
3479 // @alternative
3480 // @method invalidateSize(animate: Boolean): this
3481 // Checks if the map container size changed and updates the map if so —
3482 // call it after you've changed the map size dynamically, also animating
3483 // pan by default.
3484 invalidateSize: function (options) {
3485 if (!this._loaded) { return this; }
3486
3487 options = extend({
3488 animate: false,
3489 pan: true
3490 }, options === true ? {animate: true} : options);
3491
3492 var oldSize = this.getSize();
3493 this._sizeChanged = true;
3494 this._lastCenter = null;
3495
3496 var newSize = this.getSize(),
3497 oldCenter = oldSize.divideBy(2).round(),
3498 newCenter = newSize.divideBy(2).round(),
3499 offset = oldCenter.subtract(newCenter);
3500
3501 if (!offset.x && !offset.y) { return this; }
3502
3503 if (options.animate && options.pan) {
3504 this.panBy(offset);
3505
3506 } else {
3507 if (options.pan) {
3508 this._rawPanBy(offset);
3509 }
3510
3511 this.fire('move');
3512
3513 if (options.debounceMoveend) {
3514 clearTimeout(this._sizeTimer);
3515 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3516 } else {
3517 this.fire('moveend');
3518 }
3519 }
3520
3521 // @section Map state change events
3522 // @event resize: ResizeEvent
3523 // Fired when the map is resized.
3524 return this.fire('resize', {
3525 oldSize: oldSize,
3526 newSize: newSize
3527 });
3528 },
3529
3530 // @section Methods for modifying map state
3531 // @method stop(): this
3532 // Stops the currently running `panTo` or `flyTo` animation, if any.
3533 stop: function () {
3534 this.setZoom(this._limitZoom(this._zoom));
3535 if (!this.options.zoomSnap) {
3536 this.fire('viewreset');
3537 }
3538 return this._stop();
3539 },
3540
3541 // @section Geolocation methods
3542 // @method locate(options?: Locate options): this
3543 // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3544 // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3545 // and optionally sets the map view to the user's location with respect to
3546 // detection accuracy (or to the world view if geolocation failed).
3547 // Note that, if your page doesn't use HTTPS, this method will fail in
3548 // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3549 // See `Locate options` for more details.
3550 locate: function (options) {
3551
3552 options = this._locateOptions = extend({
3553 timeout: 10000,
3554 watch: false
3555 // setView: false
3556 // maxZoom: <Number>
3557 // maximumAge: 0
3558 // enableHighAccuracy: false
3559 }, options);
3560
3561 if (!('geolocation' in navigator)) {
3562 this._handleGeolocationError({
3563 code: 0,
3564 message: 'Geolocation not supported.'
3565 });
3566 return this;
3567 }
3568
3569 var onResponse = bind(this._handleGeolocationResponse, this),
3570 onError = bind(this._handleGeolocationError, this);
3571
3572 if (options.watch) {
3573 this._locationWatchId =
3574 navigator.geolocation.watchPosition(onResponse, onError, options);
3575 } else {
3576 navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3577 }
3578 return this;
3579 },
3580
3581 // @method stopLocate(): this
3582 // Stops watching location previously initiated by `map.locate({watch: true})`
3583 // and aborts resetting the map view if map.locate was called with
3584 // `{setView: true}`.
3585 stopLocate: function () {
3586 if (navigator.geolocation && navigator.geolocation.clearWatch) {
3587 navigator.geolocation.clearWatch(this._locationWatchId);
3588 }
3589 if (this._locateOptions) {
3590 this._locateOptions.setView = false;
3591 }
3592 return this;
3593 },
3594
3595 _handleGeolocationError: function (error) {
3596 var c = error.code,
3597 message = error.message ||
3598 (c === 1 ? 'permission denied' :
3599 (c === 2 ? 'position unavailable' : 'timeout'));
3600
3601 if (this._locateOptions.setView && !this._loaded) {
3602 this.fitWorld();
3603 }
3604
3605 // @section Location events
3606 // @event locationerror: ErrorEvent
3607 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3608 this.fire('locationerror', {
3609 code: c,
3610 message: 'Geolocation error: ' + message + '.'
3611 });
3612 },
3613
3614 _handleGeolocationResponse: function (pos) {
3615 var lat = pos.coords.latitude,
3616 lng = pos.coords.longitude,
3617 latlng = new LatLng(lat, lng),
3618 bounds = latlng.toBounds(pos.coords.accuracy),
3619 options = this._locateOptions;
3620
3621 if (options.setView) {
3622 var zoom = this.getBoundsZoom(bounds);
3623 this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3624 }
3625
3626 var data = {
3627 latlng: latlng,
3628 bounds: bounds,
3629 timestamp: pos.timestamp
3630 };
3631
3632 for (var i in pos.coords) {
3633 if (typeof pos.coords[i] === 'number') {
3634 data[i] = pos.coords[i];
3635 }
3636 }
3637
3638 // @event locationfound: LocationEvent
3639 // Fired when geolocation (using the [`locate`](#map-locate) method)
3640 // went successfully.
3641 this.fire('locationfound', data);
3642 },
3643
3644 // TODO Appropriate docs section?
3645 // @section Other Methods
3646 // @method addHandler(name: String, HandlerClass: Function): this
3647 // Adds a new `Handler` to the map, given its name and constructor function.
3648 addHandler: function (name, HandlerClass) {
3649 if (!HandlerClass) { return this; }
3650
3651 var handler = this[name] = new HandlerClass(this);
3652
3653 this._handlers.push(handler);
3654
3655 if (this.options[name]) {
3656 handler.enable();
3657 }
3658
3659 return this;
3660 },
3661
3662 // @method remove(): this
3663 // Destroys the map and clears all related event listeners.
3664 remove: function () {
3665
3666 this._initEvents(true);
3667
3668 if (this._containerId !== this._container._leaflet_id) {
3669 throw new Error('Map container is being reused by another instance');
3670 }
3671
3672 try {
3673 // throws error in IE6-8
3674 delete this._container._leaflet_id;
3675 delete this._containerId;
3676 } catch (e) {
3677 /*eslint-disable */
3678 this._container._leaflet_id = undefined;
3679 /* eslint-enable */
3680 this._containerId = undefined;
3681 }
3682
3683 if (this._locationWatchId !== undefined) {
3684 this.stopLocate();
3685 }
3686
3687 this._stop();
3688
3689 remove(this._mapPane);
3690
3691 if (this._clearControlPos) {
3692 this._clearControlPos();
3693 }
3694
3695 this._clearHandlers();
3696
3697 if (this._loaded) {
3698 // @section Map state change events
3699 // @event unload: Event
3700 // Fired when the map is destroyed with [remove](#map-remove) method.
3701 this.fire('unload');
3702 }
3703
3704 var i;
3705 for (i in this._layers) {
3706 this._layers[i].remove();
3707 }
3708 for (i in this._panes) {
3709 remove(this._panes[i]);
3710 }
3711
3712 this._layers = [];
3713 this._panes = [];
3714 delete this._mapPane;
3715 delete this._renderer;
3716
3717 return this;
3718 },
3719
3720 // @section Other Methods
3721 // @method createPane(name: String, container?: HTMLElement): HTMLElement
3722 // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3723 // then returns it. The pane is created as a child of `container`, or
3724 // as a child of the main map pane if not set.
3725 createPane: function (name, container) {
3726 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3727 pane = create$1('div', className, container || this._mapPane);
3728
3729 if (name) {
3730 this._panes[name] = pane;
3731 }
3732 return pane;
3733 },
3734
3735 // @section Methods for Getting Map State
3736
3737 // @method getCenter(): LatLng
3738 // Returns the geographical center of the map view
3739 getCenter: function () {
3740 this._checkIfLoaded();
3741
3742 if (this._lastCenter && !this._moved()) {
3743 return this._lastCenter;
3744 }
3745 return this.layerPointToLatLng(this._getCenterLayerPoint());
3746 },
3747
3748 // @method getZoom(): Number
3749 // Returns the current zoom level of the map view
3750 getZoom: function () {
3751 return this._zoom;
3752 },
3753
3754 // @method getBounds(): LatLngBounds
3755 // Returns the geographical bounds visible in the current map view
3756 getBounds: function () {
3757 var bounds = this.getPixelBounds(),
3758 sw = this.unproject(bounds.getBottomLeft()),
3759 ne = this.unproject(bounds.getTopRight());
3760
3761 return new LatLngBounds(sw, ne);
3762 },
3763
3764 // @method getMinZoom(): Number
3765 // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
3766 getMinZoom: function () {
3767 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3768 },
3769
3770 // @method getMaxZoom(): Number
3771 // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3772 getMaxZoom: function () {
3773 return this.options.maxZoom === undefined ?
3774 (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3775 this.options.maxZoom;
3776 },
3777
3778 // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean): Number
3779 // Returns the maximum zoom level on which the given bounds fit to the map
3780 // view in its entirety. If `inside` (optional) is set to `true`, the method
3781 // instead returns the minimum zoom level on which the map view fits into
3782 // the given bounds in its entirety.
3783 getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3784 bounds = toLatLngBounds(bounds);
3785 padding = toPoint(padding || [0, 0]);
3786
3787 var zoom = this.getZoom() || 0,
3788 min = this.getMinZoom(),
3789 max = this.getMaxZoom(),
3790 nw = bounds.getNorthWest(),
3791 se = bounds.getSouthEast(),
3792 size = this.getSize().subtract(padding),
3793 boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3794 snap = any3d ? this.options.zoomSnap : 1,
3795 scalex = size.x / boundsSize.x,
3796 scaley = size.y / boundsSize.y,
3797 scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3798
3799 zoom = this.getScaleZoom(scale, zoom);
3800
3801 if (snap) {
3802 zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3803 zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3804 }
3805
3806 return Math.max(min, Math.min(max, zoom));
3807 },
3808
3809 // @method getSize(): Point
3810 // Returns the current size of the map container (in pixels).
3811 getSize: function () {
3812 if (!this._size || this._sizeChanged) {
3813 this._size = new Point(
3814 this._container.clientWidth || 0,
3815 this._container.clientHeight || 0);
3816
3817 this._sizeChanged = false;
3818 }
3819 return this._size.clone();
3820 },
3821
3822 // @method getPixelBounds(): Bounds
3823 // Returns the bounds of the current map view in projected pixel
3824 // coordinates (sometimes useful in layer and overlay implementations).
3825 getPixelBounds: function (center, zoom) {
3826 var topLeftPoint = this._getTopLeftPoint(center, zoom);
3827 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3828 },
3829
3830 // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3831 // the map pane? "left point of the map layer" can be confusing, specially
3832 // since there can be negative offsets.
3833 // @method getPixelOrigin(): Point
3834 // Returns the projected pixel coordinates of the top left point of
3835 // the map layer (useful in custom layer and overlay implementations).
3836 getPixelOrigin: function () {
3837 this._checkIfLoaded();
3838 return this._pixelOrigin;
3839 },
3840
3841 // @method getPixelWorldBounds(zoom?: Number): Bounds
3842 // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3843 // If `zoom` is omitted, the map's current zoom level is used.
3844 getPixelWorldBounds: function (zoom) {
3845 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3846 },
3847
3848 // @section Other Methods
3849
3850 // @method getPane(pane: String|HTMLElement): HTMLElement
3851 // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3852 getPane: function (pane) {
3853 return typeof pane === 'string' ? this._panes[pane] : pane;
3854 },
3855
3856 // @method getPanes(): Object
3857 // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3858 // the panes as values.
3859 getPanes: function () {
3860 return this._panes;
3861 },
3862
3863 // @method getContainer: HTMLElement
3864 // Returns the HTML element that contains the map.
3865 getContainer: function () {
3866 return this._container;
3867 },
3868
3869
3870 // @section Conversion Methods
3871
3872 // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3873 // Returns the scale factor to be applied to a map transition from zoom level
3874 // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3875 getZoomScale: function (toZoom, fromZoom) {
3876 // TODO replace with universal implementation after refactoring projections
3877 var crs = this.options.crs;
3878 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3879 return crs.scale(toZoom) / crs.scale(fromZoom);
3880 },
3881
3882 // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3883 // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3884 // level and everything is scaled by a factor of `scale`. Inverse of
3885 // [`getZoomScale`](#map-getZoomScale).
3886 getScaleZoom: function (scale, fromZoom) {
3887 var crs = this.options.crs;
3888 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3889 var zoom = crs.zoom(scale * crs.scale(fromZoom));
3890 return isNaN(zoom) ? Infinity : zoom;
3891 },
3892
3893 // @method project(latlng: LatLng, zoom: Number): Point
3894 // Projects a geographical coordinate `LatLng` according to the projection
3895 // of the map's CRS, then scales it according to `zoom` and the CRS's
3896 // `Transformation`. The result is pixel coordinate relative to
3897 // the CRS origin.
3898 project: function (latlng, zoom) {
3899 zoom = zoom === undefined ? this._zoom : zoom;
3900 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3901 },
3902
3903 // @method unproject(point: Point, zoom: Number): LatLng
3904 // Inverse of [`project`](#map-project).
3905 unproject: function (point, zoom) {
3906 zoom = zoom === undefined ? this._zoom : zoom;
3907 return this.options.crs.pointToLatLng(toPoint(point), zoom);
3908 },
3909
3910 // @method layerPointToLatLng(point: Point): LatLng
3911 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3912 // returns the corresponding geographical coordinate (for the current zoom level).
3913 layerPointToLatLng: function (point) {
3914 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3915 return this.unproject(projectedPoint);
3916 },
3917
3918 // @method latLngToLayerPoint(latlng: LatLng): Point
3919 // Given a geographical coordinate, returns the corresponding pixel coordinate
3920 // relative to the [origin pixel](#map-getpixelorigin).
3921 latLngToLayerPoint: function (latlng) {
3922 var projectedPoint = this.project(toLatLng(latlng))._round();
3923 return projectedPoint._subtract(this.getPixelOrigin());
3924 },
3925
3926 // @method wrapLatLng(latlng: LatLng): LatLng
3927 // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3928 // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3929 // CRS's bounds.
3930 // By default this means longitude is wrapped around the dateline so its
3931 // value is between -180 and +180 degrees.
3932 wrapLatLng: function (latlng) {
3933 return this.options.crs.wrapLatLng(toLatLng(latlng));
3934 },
3935
3936 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3937 // Returns a `LatLngBounds` with the same size as the given one, ensuring that
3938 // its center is within the CRS's bounds.
3939 // By default this means the center longitude is wrapped around the dateline so its
3940 // value is between -180 and +180 degrees, and the majority of the bounds
3941 // overlaps the CRS's bounds.
3942 wrapLatLngBounds: function (latlng) {
3943 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
3944 },
3945
3946 // @method distance(latlng1: LatLng, latlng2: LatLng): Number
3947 // Returns the distance between two geographical coordinates according to
3948 // the map's CRS. By default this measures distance in meters.
3949 distance: function (latlng1, latlng2) {
3950 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
3951 },
3952
3953 // @method containerPointToLayerPoint(point: Point): Point
3954 // Given a pixel coordinate relative to the map container, returns the corresponding
3955 // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
3956 containerPointToLayerPoint: function (point) { // (Point)
3957 return toPoint(point).subtract(this._getMapPanePos());
3958 },
3959
3960 // @method layerPointToContainerPoint(point: Point): Point
3961 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3962 // returns the corresponding pixel coordinate relative to the map container.
3963 layerPointToContainerPoint: function (point) { // (Point)
3964 return toPoint(point).add(this._getMapPanePos());
3965 },
3966
3967 // @method containerPointToLatLng(point: Point): LatLng
3968 // Given a pixel coordinate relative to the map container, returns
3969 // the corresponding geographical coordinate (for the current zoom level).
3970 containerPointToLatLng: function (point) {
3971 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
3972 return this.layerPointToLatLng(layerPoint);
3973 },
3974
3975 // @method latLngToContainerPoint(latlng: LatLng): Point
3976 // Given a geographical coordinate, returns the corresponding pixel coordinate
3977 // relative to the map container.
3978 latLngToContainerPoint: function (latlng) {
3979 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
3980 },
3981
3982 // @method mouseEventToContainerPoint(ev: MouseEvent): Point
3983 // Given a MouseEvent object, returns the pixel coordinate relative to the
3984 // map container where the event took place.
3985 mouseEventToContainerPoint: function (e) {
3986 return getMousePosition(e, this._container);
3987 },
3988
3989 // @method mouseEventToLayerPoint(ev: MouseEvent): Point
3990 // Given a MouseEvent object, returns the pixel coordinate relative to
3991 // the [origin pixel](#map-getpixelorigin) where the event took place.
3992 mouseEventToLayerPoint: function (e) {
3993 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
3994 },
3995
3996 // @method mouseEventToLatLng(ev: MouseEvent): LatLng
3997 // Given a MouseEvent object, returns geographical coordinate where the
3998 // event took place.
3999 mouseEventToLatLng: function (e) { // (MouseEvent)
4000 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4001 },
4002
4003
4004 // map initialization methods
4005
4006 _initContainer: function (id) {
4007 var container = this._container = get(id);
4008
4009 if (!container) {
4010 throw new Error('Map container not found.');
4011 } else if (container._leaflet_id) {
4012 throw new Error('Map container is already initialized.');
4013 }
4014
4015 on(container, 'scroll', this._onScroll, this);
4016 this._containerId = stamp(container);
4017 },
4018
4019 _initLayout: function () {
4020 var container = this._container;
4021
4022 this._fadeAnimated = this.options.fadeAnimation && any3d;
4023
4024 addClass(container, 'leaflet-container' +
4025 (touch ? ' leaflet-touch' : '') +
4026 (retina ? ' leaflet-retina' : '') +
4027 (ielt9 ? ' leaflet-oldie' : '') +
4028 (safari ? ' leaflet-safari' : '') +
4029 (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4030
4031 var position = getStyle(container, 'position');
4032
4033 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4034 container.style.position = 'relative';
4035 }
4036
4037 this._initPanes();
4038
4039 if (this._initControlPos) {
4040 this._initControlPos();
4041 }
4042 },
4043
4044 _initPanes: function () {
4045 var panes = this._panes = {};
4046 this._paneRenderers = {};
4047
4048 // @section
4049 //
4050 // Panes are DOM elements used to control the ordering of layers on the map. You
4051 // can access panes with [`map.getPane`](#map-getpane) or
4052 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4053 // [`map.createPane`](#map-createpane) method.
4054 //
4055 // Every map has the following default panes that differ only in zIndex.
4056 //
4057 // @pane mapPane: HTMLElement = 'auto'
4058 // Pane that contains all other map panes
4059
4060 this._mapPane = this.createPane('mapPane', this._container);
4061 setPosition(this._mapPane, new Point(0, 0));
4062
4063 // @pane tilePane: HTMLElement = 200
4064 // Pane for `GridLayer`s and `TileLayer`s
4065 this.createPane('tilePane');
4066 // @pane overlayPane: HTMLElement = 400
4067 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4068 this.createPane('shadowPane');
4069 // @pane shadowPane: HTMLElement = 500
4070 // Pane for overlay shadows (e.g. `Marker` shadows)
4071 this.createPane('overlayPane');
4072 // @pane markerPane: HTMLElement = 600
4073 // Pane for `Icon`s of `Marker`s
4074 this.createPane('markerPane');
4075 // @pane tooltipPane: HTMLElement = 650
4076 // Pane for `Tooltip`s.
4077 this.createPane('tooltipPane');
4078 // @pane popupPane: HTMLElement = 700
4079 // Pane for `Popup`s.
4080 this.createPane('popupPane');
4081
4082 if (!this.options.markerZoomAnimation) {
4083 addClass(panes.markerPane, 'leaflet-zoom-hide');
4084 addClass(panes.shadowPane, 'leaflet-zoom-hide');
4085 }
4086 },
4087
4088
4089 // private methods that modify map state
4090
4091 // @section Map state change events
4092 _resetView: function (center, zoom) {
4093 setPosition(this._mapPane, new Point(0, 0));
4094
4095 var loading = !this._loaded;
4096 this._loaded = true;
4097 zoom = this._limitZoom(zoom);
4098
4099 this.fire('viewprereset');
4100
4101 var zoomChanged = this._zoom !== zoom;
4102 this
4103 ._moveStart(zoomChanged, false)
4104 ._move(center, zoom)
4105 ._moveEnd(zoomChanged);
4106
4107 // @event viewreset: Event
4108 // Fired when the map needs to redraw its content (this usually happens
4109 // on map zoom or load). Very useful for creating custom overlays.
4110 this.fire('viewreset');
4111
4112 // @event load: Event
4113 // Fired when the map is initialized (when its center and zoom are set
4114 // for the first time).
4115 if (loading) {
4116 this.fire('load');
4117 }
4118 },
4119
4120 _moveStart: function (zoomChanged, noMoveStart) {
4121 // @event zoomstart: Event
4122 // Fired when the map zoom is about to change (e.g. before zoom animation).
4123 // @event movestart: Event
4124 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
4125 if (zoomChanged) {
4126 this.fire('zoomstart');
4127 }
4128 if (!noMoveStart) {
4129 this.fire('movestart');
4130 }
4131 return this;
4132 },
4133
4134 _move: function (center, zoom, data) {
4135 if (zoom === undefined) {
4136 zoom = this._zoom;
4137 }
4138 var zoomChanged = this._zoom !== zoom;
4139
4140 this._zoom = zoom;
4141 this._lastCenter = center;
4142 this._pixelOrigin = this._getNewPixelOrigin(center);
4143
4144 // @event zoom: Event
4145 // Fired repeatedly during any change in zoom level, including zoom
4146 // and fly animations.
4147 if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530
4148 this.fire('zoom', data);
4149 }
4150
4151 // @event move: Event
4152 // Fired repeatedly during any movement of the map, including pan and
4153 // fly animations.
4154 return this.fire('move', data);
4155 },
4156
4157 _moveEnd: function (zoomChanged) {
4158 // @event zoomend: Event
4159 // Fired when the map has changed, after any animations.
4160 if (zoomChanged) {
4161 this.fire('zoomend');
4162 }
4163
4164 // @event moveend: Event
4165 // Fired when the center of the map stops changing (e.g. user stopped
4166 // dragging the map).
4167 return this.fire('moveend');
4168 },
4169
4170 _stop: function () {
4171 cancelAnimFrame(this._flyToFrame);
4172 if (this._panAnim) {
4173 this._panAnim.stop();
4174 }
4175 return this;
4176 },
4177
4178 _rawPanBy: function (offset) {
4179 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4180 },
4181
4182 _getZoomSpan: function () {
4183 return this.getMaxZoom() - this.getMinZoom();
4184 },
4185
4186 _panInsideMaxBounds: function () {
4187 if (!this._enforcingBounds) {
4188 this.panInsideBounds(this.options.maxBounds);
4189 }
4190 },
4191
4192 _checkIfLoaded: function () {
4193 if (!this._loaded) {
4194 throw new Error('Set map center and zoom first.');
4195 }
4196 },
4197
4198 // DOM event handling
4199
4200 // @section Interaction events
4201 _initEvents: function (remove$$1) {
4202 this._targets = {};
4203 this._targets[stamp(this._container)] = this;
4204
4205 var onOff = remove$$1 ? off : on;
4206
4207 // @event click: MouseEvent
4208 // Fired when the user clicks (or taps) the map.
4209 // @event dblclick: MouseEvent
4210 // Fired when the user double-clicks (or double-taps) the map.
4211 // @event mousedown: MouseEvent
4212 // Fired when the user pushes the mouse button on the map.
4213 // @event mouseup: MouseEvent
4214 // Fired when the user releases the mouse button on the map.
4215 // @event mouseover: MouseEvent
4216 // Fired when the mouse enters the map.
4217 // @event mouseout: MouseEvent
4218 // Fired when the mouse leaves the map.
4219 // @event mousemove: MouseEvent
4220 // Fired while the mouse moves over the map.
4221 // @event contextmenu: MouseEvent
4222 // Fired when the user pushes the right mouse button on the map, prevents
4223 // default browser context menu from showing if there are listeners on
4224 // this event. Also fired on mobile when the user holds a single touch
4225 // for a second (also called long press).
4226 // @event keypress: KeyboardEvent
4227 // Fired when the user presses a key from the keyboard while the map is focused.
4228 onOff(this._container, 'click dblclick mousedown mouseup ' +
4229 'mouseover mouseout mousemove contextmenu keypress', this._handleDOMEvent, this);
4230
4231 if (this.options.trackResize) {
4232 onOff(window, 'resize', this._onResize, this);
4233 }
4234
4235 if (any3d && this.options.transform3DLimit) {
4236 (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4237 }
4238 },
4239
4240 _onResize: function () {
4241 cancelAnimFrame(this._resizeRequest);
4242 this._resizeRequest = requestAnimFrame(
4243 function () { this.invalidateSize({debounceMoveend: true}); }, this);
4244 },
4245
4246 _onScroll: function () {
4247 this._container.scrollTop = 0;
4248 this._container.scrollLeft = 0;
4249 },
4250
4251 _onMoveEnd: function () {
4252 var pos = this._getMapPanePos();
4253 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4254 // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4255 // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4256 this._resetView(this.getCenter(), this.getZoom());
4257 }
4258 },
4259
4260 _findEventTargets: function (e, type) {
4261 var targets = [],
4262 target,
4263 isHover = type === 'mouseout' || type === 'mouseover',
4264 src = e.target || e.srcElement,
4265 dragging = false;
4266
4267 while (src) {
4268 target = this._targets[stamp(src)];
4269 if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4270 // Prevent firing click after you just dragged an object.
4271 dragging = true;
4272 break;
4273 }
4274 if (target && target.listens(type, true)) {
4275 if (isHover && !isExternalTarget(src, e)) { break; }
4276 targets.push(target);
4277 if (isHover) { break; }
4278 }
4279 if (src === this._container) { break; }
4280 src = src.parentNode;
4281 }
4282 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4283 targets = [this];
4284 }
4285 return targets;
4286 },
4287
4288 _handleDOMEvent: function (e) {
4289 if (!this._loaded || skipped(e)) { return; }
4290
4291 var type = e.type;
4292
4293 if (type === 'mousedown' || type === 'keypress') {
4294 // prevents outline when clicking on keyboard-focusable element
4295 preventOutline(e.target || e.srcElement);
4296 }
4297
4298 this._fireDOMEvent(e, type);
4299 },
4300
4301 _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4302
4303 _fireDOMEvent: function (e, type, targets) {
4304
4305 if (e.type === 'click') {
4306 // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4307 // @event preclick: MouseEvent
4308 // Fired before mouse click on the map (sometimes useful when you
4309 // want something to happen on click before any existing click
4310 // handlers start running).
4311 var synth = extend({}, e);
4312 synth.type = 'preclick';
4313 this._fireDOMEvent(synth, synth.type, targets);
4314 }
4315
4316 if (e._stopped) { return; }
4317
4318 // Find the layer the event is propagating from and its parents.
4319 targets = (targets || []).concat(this._findEventTargets(e, type));
4320
4321 if (!targets.length) { return; }
4322
4323 var target = targets[0];
4324 if (type === 'contextmenu' && target.listens(type, true)) {
4325 preventDefault(e);
4326 }
4327
4328 var data = {
4329 originalEvent: e
4330 };
4331
4332 if (e.type !== 'keypress') {
4333 var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4334 data.containerPoint = isMarker ?
4335 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4336 data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4337 data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4338 }
4339
4340 for (var i = 0; i < targets.length; i++) {
4341 targets[i].fire(type, data, true);
4342 if (data.originalEvent._stopped ||
4343 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4344 }
4345 },
4346
4347 _draggableMoved: function (obj) {
4348 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4349 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4350 },
4351
4352 _clearHandlers: function () {
4353 for (var i = 0, len = this._handlers.length; i < len; i++) {
4354 this._handlers[i].disable();
4355 }
4356 },
4357
4358 // @section Other Methods
4359
4360 // @method whenReady(fn: Function, context?: Object): this
4361 // Runs the given function `fn` when the map gets initialized with
4362 // a view (center and zoom) and at least one layer, or immediately
4363 // if it's already initialized, optionally passing a function context.
4364 whenReady: function (callback, context) {
4365 if (this._loaded) {
4366 callback.call(context || this, {target: this});
4367 } else {
4368 this.on('load', callback, context);
4369 }
4370 return this;
4371 },
4372
4373
4374 // private methods for getting map state
4375
4376 _getMapPanePos: function () {
4377 return getPosition(this._mapPane) || new Point(0, 0);
4378 },
4379
4380 _moved: function () {
4381 var pos = this._getMapPanePos();
4382 return pos && !pos.equals([0, 0]);
4383 },
4384
4385 _getTopLeftPoint: function (center, zoom) {
4386 var pixelOrigin = center && zoom !== undefined ?
4387 this._getNewPixelOrigin(center, zoom) :
4388 this.getPixelOrigin();
4389 return pixelOrigin.subtract(this._getMapPanePos());
4390 },
4391
4392 _getNewPixelOrigin: function (center, zoom) {
4393 var viewHalf = this.getSize()._divideBy(2);
4394 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4395 },
4396
4397 _latLngToNewLayerPoint: function (latlng, zoom, center) {
4398 var topLeft = this._getNewPixelOrigin(center, zoom);
4399 return this.project(latlng, zoom)._subtract(topLeft);
4400 },
4401
4402 _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4403 var topLeft = this._getNewPixelOrigin(center, zoom);
4404 return toBounds([
4405 this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4406 this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4407 this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4408 this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4409 ]);
4410 },
4411
4412 // layer point of the current center
4413 _getCenterLayerPoint: function () {
4414 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4415 },
4416
4417 // offset of the specified place to the current center in pixels
4418 _getCenterOffset: function (latlng) {
4419 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4420 },
4421
4422 // adjust center for view to get inside bounds
4423 _limitCenter: function (center, zoom, bounds) {
4424
4425 if (!bounds) { return center; }
4426
4427 var centerPoint = this.project(center, zoom),
4428 viewHalf = this.getSize().divideBy(2),
4429 viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4430 offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4431
4432 // If offset is less than a pixel, ignore.
4433 // This prevents unstable projections from getting into
4434 // an infinite loop of tiny offsets.
4435 if (offset.round().equals([0, 0])) {
4436 return center;
4437 }
4438
4439 return this.unproject(centerPoint.add(offset), zoom);
4440 },
4441
4442 // adjust offset for view to get inside bounds
4443 _limitOffset: function (offset, bounds) {
4444 if (!bounds) { return offset; }
4445
4446 var viewBounds = this.getPixelBounds(),
4447 newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4448
4449 return offset.add(this._getBoundsOffset(newBounds, bounds));
4450 },
4451
4452 // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4453 _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4454 var projectedMaxBounds = toBounds(
4455 this.project(maxBounds.getNorthEast(), zoom),
4456 this.project(maxBounds.getSouthWest(), zoom)
4457 ),
4458 minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4459 maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4460
4461 dx = this._rebound(minOffset.x, -maxOffset.x),
4462 dy = this._rebound(minOffset.y, -maxOffset.y);
4463
4464 return new Point(dx, dy);
4465 },
4466
4467 _rebound: function (left, right) {
4468 return left + right > 0 ?
4469 Math.round(left - right) / 2 :
4470 Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4471 },
4472
4473 _limitZoom: function (zoom) {
4474 var min = this.getMinZoom(),
4475 max = this.getMaxZoom(),
4476 snap = any3d ? this.options.zoomSnap : 1;
4477 if (snap) {
4478 zoom = Math.round(zoom / snap) * snap;
4479 }
4480 return Math.max(min, Math.min(max, zoom));
4481 },
4482
4483 _onPanTransitionStep: function () {
4484 this.fire('move');
4485 },
4486
4487 _onPanTransitionEnd: function () {
4488 removeClass(this._mapPane, 'leaflet-pan-anim');
4489 this.fire('moveend');
4490 },
4491
4492 _tryAnimatedPan: function (center, options) {
4493 // difference between the new and current centers in pixels
4494 var offset = this._getCenterOffset(center)._trunc();
4495
4496 // don't animate too far unless animate: true specified in options
4497 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4498
4499 this.panBy(offset, options);
4500
4501 return true;
4502 },
4503
4504 _createAnimProxy: function () {
4505
4506 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4507 this._panes.mapPane.appendChild(proxy);
4508
4509 this.on('zoomanim', function (e) {
4510 var prop = TRANSFORM,
4511 transform = this._proxy.style[prop];
4512
4513 setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4514
4515 // workaround for case when transform is the same and so transitionend event is not fired
4516 if (transform === this._proxy.style[prop] && this._animatingZoom) {
4517 this._onZoomTransitionEnd();
4518 }
4519 }, this);
4520
4521 this.on('load moveend', function () {
4522 var c = this.getCenter(),
4523 z = this.getZoom();
4524 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4525 }, this);
4526
4527 this._on('unload', this._destroyAnimProxy, this);
4528 },
4529
4530 _destroyAnimProxy: function () {
4531 remove(this._proxy);
4532 delete this._proxy;
4533 },
4534
4535 _catchTransitionEnd: function (e) {
4536 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4537 this._onZoomTransitionEnd();
4538 }
4539 },
4540
4541 _nothingToAnimate: function () {
4542 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4543 },
4544
4545 _tryAnimatedZoom: function (center, zoom, options) {
4546
4547 if (this._animatingZoom) { return true; }
4548
4549 options = options || {};
4550
4551 // don't animate if disabled, not supported or zoom difference is too large
4552 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4553 Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4554
4555 // offset is the pixel coords of the zoom origin relative to the current center
4556 var scale = this.getZoomScale(zoom),
4557 offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4558
4559 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
4560 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4561
4562 requestAnimFrame(function () {
4563 this
4564 ._moveStart(true, false)
4565 ._animateZoom(center, zoom, true);
4566 }, this);
4567
4568 return true;
4569 },
4570
4571 _animateZoom: function (center, zoom, startAnim, noUpdate) {
4572 if (!this._mapPane) { return; }
4573
4574 if (startAnim) {
4575 this._animatingZoom = true;
4576
4577 // remember what center/zoom to set after animation
4578 this._animateToCenter = center;
4579 this._animateToZoom = zoom;
4580
4581 addClass(this._mapPane, 'leaflet-zoom-anim');
4582 }
4583
4584 // @event zoomanim: ZoomAnimEvent
4585 // Fired on every frame of a zoom animation
4586 this.fire('zoomanim', {
4587 center: center,
4588 zoom: zoom,
4589 noUpdate: noUpdate
4590 });
4591
4592 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4593 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4594 },
4595
4596 _onZoomTransitionEnd: function () {
4597 if (!this._animatingZoom) { return; }
4598
4599 if (this._mapPane) {
4600 removeClass(this._mapPane, 'leaflet-zoom-anim');
4601 }
4602
4603 this._animatingZoom = false;
4604
4605 this._move(this._animateToCenter, this._animateToZoom);
4606
4607 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
4608 requestAnimFrame(function () {
4609 this._moveEnd(true);
4610 }, this);
4611 }
4612 });
4613
4614 // @section
4615
4616 // @factory L.map(id: String, options?: Map options)
4617 // Instantiates a map object given the DOM ID of a `<div>` element
4618 // and optionally an object literal with `Map options`.
4619 //
4620 // @alternative
4621 // @factory L.map(el: HTMLElement, options?: Map options)
4622 // Instantiates a map object given an instance of a `<div>` HTML element
4623 // and optionally an object literal with `Map options`.
4624 function createMap(id, options) {
4625 return new Map(id, options);
4626 }
4627
4628 /*
4629 * @class Control
4630 * @aka L.Control
4631 * @inherits Class
4632 *
4633 * L.Control is a base class for implementing map controls. Handles positioning.
4634 * All other controls extend from this class.
4635 */
4636
4637 var Control = Class.extend({
4638 // @section
4639 // @aka Control options
4640 options: {
4641 // @option position: String = 'topright'
4642 // The position of the control (one of the map corners). Possible values are `'topleft'`,
4643 // `'topright'`, `'bottomleft'` or `'bottomright'`
4644 position: 'topright'
4645 },
4646
4647 initialize: function (options) {
4648 setOptions(this, options);
4649 },
4650
4651 /* @section
4652 * Classes extending L.Control will inherit the following methods:
4653 *
4654 * @method getPosition: string
4655 * Returns the position of the control.
4656 */
4657 getPosition: function () {
4658 return this.options.position;
4659 },
4660
4661 // @method setPosition(position: string): this
4662 // Sets the position of the control.
4663 setPosition: function (position) {
4664 var map = this._map;
4665
4666 if (map) {
4667 map.removeControl(this);
4668 }
4669
4670 this.options.position = position;
4671
4672 if (map) {
4673 map.addControl(this);
4674 }
4675
4676 return this;
4677 },
4678
4679 // @method getContainer: HTMLElement
4680 // Returns the HTMLElement that contains the control.
4681 getContainer: function () {
4682 return this._container;
4683 },
4684
4685 // @method addTo(map: Map): this
4686 // Adds the control to the given map.
4687 addTo: function (map) {
4688 this.remove();
4689 this._map = map;
4690
4691 var container = this._container = this.onAdd(map),
4692 pos = this.getPosition(),
4693 corner = map._controlCorners[pos];
4694
4695 addClass(container, 'leaflet-control');
4696
4697 if (pos.indexOf('bottom') !== -1) {
4698 corner.insertBefore(container, corner.firstChild);
4699 } else {
4700 corner.appendChild(container);
4701 }
4702
4703 return this;
4704 },
4705
4706 // @method remove: this
4707 // Removes the control from the map it is currently active on.
4708 remove: function () {
4709 if (!this._map) {
4710 return this;
4711 }
4712
4713 remove(this._container);
4714
4715 if (this.onRemove) {
4716 this.onRemove(this._map);
4717 }
4718
4719 this._map = null;
4720
4721 return this;
4722 },
4723
4724 _refocusOnMap: function (e) {
4725 // if map exists and event is not a keyboard event
4726 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4727 this._map.getContainer().focus();
4728 }
4729 }
4730 });
4731
4732 var control = function (options) {
4733 return new Control(options);
4734 };
4735
4736 /* @section Extension methods
4737 * @uninheritable
4738 *
4739 * Every control should extend from `L.Control` and (re-)implement the following methods.
4740 *
4741 * @method onAdd(map: Map): HTMLElement
4742 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4743 *
4744 * @method onRemove(map: Map)
4745 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4746 */
4747
4748 /* @namespace Map
4749 * @section Methods for Layers and Controls
4750 */
4751 Map.include({
4752 // @method addControl(control: Control): this
4753 // Adds the given control to the map
4754 addControl: function (control) {
4755 control.addTo(this);
4756 return this;
4757 },
4758
4759 // @method removeControl(control: Control): this
4760 // Removes the given control from the map
4761 removeControl: function (control) {
4762 control.remove();
4763 return this;
4764 },
4765
4766 _initControlPos: function () {
4767 var corners = this._controlCorners = {},
4768 l = 'leaflet-',
4769 container = this._controlContainer =
4770 create$1('div', l + 'control-container', this._container);
4771
4772 function createCorner(vSide, hSide) {
4773 var className = l + vSide + ' ' + l + hSide;
4774
4775 corners[vSide + hSide] = create$1('div', className, container);
4776 }
4777
4778 createCorner('top', 'left');
4779 createCorner('top', 'right');
4780 createCorner('bottom', 'left');
4781 createCorner('bottom', 'right');
4782 },
4783
4784 _clearControlPos: function () {
4785 for (var i in this._controlCorners) {
4786 remove(this._controlCorners[i]);
4787 }
4788 remove(this._controlContainer);
4789 delete this._controlCorners;
4790 delete this._controlContainer;
4791 }
4792 });
4793
4794 /*
4795 * @class Control.Layers
4796 * @aka L.Control.Layers
4797 * @inherits Control
4798 *
4799 * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`.
4800 *
4801 * @example
4802 *
4803 * ```js
4804 * var baseLayers = {
4805 * "Mapbox": mapbox,
4806 * "OpenStreetMap": osm
4807 * };
4808 *
4809 * var overlays = {
4810 * "Marker": marker,
4811 * "Roads": roadsLayer
4812 * };
4813 *
4814 * L.control.layers(baseLayers, overlays).addTo(map);
4815 * ```
4816 *
4817 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4818 *
4819 * ```js
4820 * {
4821 * "<someName1>": layer1,
4822 * "<someName2>": layer2
4823 * }
4824 * ```
4825 *
4826 * The layer names can contain HTML, which allows you to add additional styling to the items:
4827 *
4828 * ```js
4829 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4830 * ```
4831 */
4832
4833 var Layers = Control.extend({
4834 // @section
4835 // @aka Control.Layers options
4836 options: {
4837 // @option collapsed: Boolean = true
4838 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4839 collapsed: true,
4840 position: 'topright',
4841
4842 // @option autoZIndex: Boolean = true
4843 // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
4844 autoZIndex: true,
4845
4846 // @option hideSingleBase: Boolean = false
4847 // If `true`, the base layers in the control will be hidden when there is only one.
4848 hideSingleBase: false,
4849
4850 // @option sortLayers: Boolean = false
4851 // Whether to sort the layers. When `false`, layers will keep the order
4852 // in which they were added to the control.
4853 sortLayers: false,
4854
4855 // @option sortFunction: Function = *
4856 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4857 // that will be used for sorting the layers, when `sortLayers` is `true`.
4858 // The function receives both the `L.Layer` instances and their names, as in
4859 // `sortFunction(layerA, layerB, nameA, nameB)`.
4860 // By default, it sorts layers alphabetically by their name.
4861 sortFunction: function (layerA, layerB, nameA, nameB) {
4862 return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4863 }
4864 },
4865
4866 initialize: function (baseLayers, overlays, options) {
4867 setOptions(this, options);
4868
4869 this._layerControlInputs = [];
4870 this._layers = [];
4871 this._lastZIndex = 0;
4872 this._handlingClick = false;
4873
4874 for (var i in baseLayers) {
4875 this._addLayer(baseLayers[i], i);
4876 }
4877
4878 for (i in overlays) {
4879 this._addLayer(overlays[i], i, true);
4880 }
4881 },
4882
4883 onAdd: function (map) {
4884 this._initLayout();
4885 this._update();
4886
4887 this._map = map;
4888 map.on('zoomend', this._checkDisabledLayers, this);
4889
4890 for (var i = 0; i < this._layers.length; i++) {
4891 this._layers[i].layer.on('add remove', this._onLayerChange, this);
4892 }
4893
4894 return this._container;
4895 },
4896
4897 addTo: function (map) {
4898 Control.prototype.addTo.call(this, map);
4899 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
4900 return this._expandIfNotCollapsed();
4901 },
4902
4903 onRemove: function () {
4904 this._map.off('zoomend', this._checkDisabledLayers, this);
4905
4906 for (var i = 0; i < this._layers.length; i++) {
4907 this._layers[i].layer.off('add remove', this._onLayerChange, this);
4908 }
4909 },
4910
4911 // @method addBaseLayer(layer: Layer, name: String): this
4912 // Adds a base layer (radio button entry) with the given name to the control.
4913 addBaseLayer: function (layer, name) {
4914 this._addLayer(layer, name);
4915 return (this._map) ? this._update() : this;
4916 },
4917
4918 // @method addOverlay(layer: Layer, name: String): this
4919 // Adds an overlay (checkbox entry) with the given name to the control.
4920 addOverlay: function (layer, name) {
4921 this._addLayer(layer, name, true);
4922 return (this._map) ? this._update() : this;
4923 },
4924
4925 // @method removeLayer(layer: Layer): this
4926 // Remove the given layer from the control.
4927 removeLayer: function (layer) {
4928 layer.off('add remove', this._onLayerChange, this);
4929
4930 var obj = this._getLayer(stamp(layer));
4931 if (obj) {
4932 this._layers.splice(this._layers.indexOf(obj), 1);
4933 }
4934 return (this._map) ? this._update() : this;
4935 },
4936
4937 // @method expand(): this
4938 // Expand the control container if collapsed.
4939 expand: function () {
4940 addClass(this._container, 'leaflet-control-layers-expanded');
4941 this._form.style.height = null;
4942 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
4943 if (acceptableHeight < this._form.clientHeight) {
4944 addClass(this._form, 'leaflet-control-layers-scrollbar');
4945 this._form.style.height = acceptableHeight + 'px';
4946 } else {
4947 removeClass(this._form, 'leaflet-control-layers-scrollbar');
4948 }
4949 this._checkDisabledLayers();
4950 return this;
4951 },
4952
4953 // @method collapse(): this
4954 // Collapse the control container if expanded.
4955 collapse: function () {
4956 removeClass(this._container, 'leaflet-control-layers-expanded');
4957 return this;
4958 },
4959
4960 _initLayout: function () {
4961 var className = 'leaflet-control-layers',
4962 container = this._container = create$1('div', className),
4963 collapsed = this.options.collapsed;
4964
4965 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
4966 container.setAttribute('aria-haspopup', true);
4967
4968 disableClickPropagation(container);
4969 disableScrollPropagation(container);
4970
4971 var form = this._form = create$1('form', className + '-list');
4972
4973 if (collapsed) {
4974 this._map.on('click', this.collapse, this);
4975
4976 if (!android) {
4977 on(container, {
4978 mouseenter: this.expand,
4979 mouseleave: this.collapse
4980 }, this);
4981 }
4982 }
4983
4984 var link = this._layersLink = create$1('a', className + '-toggle', container);
4985 link.href = '#';
4986 link.title = 'Layers';
4987
4988 if (touch) {
4989 on(link, 'click', stop);
4990 on(link, 'click', this.expand, this);
4991 } else {
4992 on(link, 'focus', this.expand, this);
4993 }
4994
4995 if (!collapsed) {
4996 this.expand();
4997 }
4998
4999 this._baseLayersList = create$1('div', className + '-base', form);
5000 this._separator = create$1('div', className + '-separator', form);
5001 this._overlaysList = create$1('div', className + '-overlays', form);
5002
5003 container.appendChild(form);
5004 },
5005
5006 _getLayer: function (id) {
5007 for (var i = 0; i < this._layers.length; i++) {
5008
5009 if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5010 return this._layers[i];
5011 }
5012 }
5013 },
5014
5015 _addLayer: function (layer, name, overlay) {
5016 if (this._map) {
5017 layer.on('add remove', this._onLayerChange, this);
5018 }
5019
5020 this._layers.push({
5021 layer: layer,
5022 name: name,
5023 overlay: overlay
5024 });
5025
5026 if (this.options.sortLayers) {
5027 this._layers.sort(bind(function (a, b) {
5028 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5029 }, this));
5030 }
5031
5032 if (this.options.autoZIndex && layer.setZIndex) {
5033 this._lastZIndex++;
5034 layer.setZIndex(this._lastZIndex);
5035 }
5036
5037 this._expandIfNotCollapsed();
5038 },
5039
5040 _update: function () {
5041 if (!this._container) { return this; }
5042
5043 empty(this._baseLayersList);
5044 empty(this._overlaysList);
5045
5046 this._layerControlInputs = [];
5047 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5048
5049 for (i = 0; i < this._layers.length; i++) {
5050 obj = this._layers[i];
5051 this._addItem(obj);
5052 overlaysPresent = overlaysPresent || obj.overlay;
5053 baseLayersPresent = baseLayersPresent || !obj.overlay;
5054 baseLayersCount += !obj.overlay ? 1 : 0;
5055 }
5056
5057 // Hide base layers section if there's only one layer.
5058 if (this.options.hideSingleBase) {
5059 baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5060 this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5061 }
5062
5063 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5064
5065 return this;
5066 },
5067
5068 _onLayerChange: function (e) {
5069 if (!this._handlingClick) {
5070 this._update();
5071 }
5072
5073 var obj = this._getLayer(stamp(e.target));
5074
5075 // @namespace Map
5076 // @section Layer events
5077 // @event baselayerchange: LayersControlEvent
5078 // Fired when the base layer is changed through the [layer control](#control-layers).
5079 // @event overlayadd: LayersControlEvent
5080 // Fired when an overlay is selected through the [layer control](#control-layers).
5081 // @event overlayremove: LayersControlEvent
5082 // Fired when an overlay is deselected through the [layer control](#control-layers).
5083 // @namespace Control.Layers
5084 var type = obj.overlay ?
5085 (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5086 (e.type === 'add' ? 'baselayerchange' : null);
5087
5088 if (type) {
5089 this._map.fire(type, obj);
5090 }
5091 },
5092
5093 // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5094 _createRadioElement: function (name, checked) {
5095
5096 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5097 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5098
5099 var radioFragment = document.createElement('div');
5100 radioFragment.innerHTML = radioHtml;
5101
5102 return radioFragment.firstChild;
5103 },
5104
5105 _addItem: function (obj) {
5106 var label = document.createElement('label'),
5107 checked = this._map.hasLayer(obj.layer),
5108 input;
5109
5110 if (obj.overlay) {
5111 input = document.createElement('input');
5112 input.type = 'checkbox';
5113 input.className = 'leaflet-control-layers-selector';
5114 input.defaultChecked = checked;
5115 } else {
5116 input = this._createRadioElement('leaflet-base-layers', checked);
5117 }
5118
5119 this._layerControlInputs.push(input);
5120 input.layerId = stamp(obj.layer);
5121
5122 on(input, 'click', this._onInputClick, this);
5123
5124 var name = document.createElement('span');
5125 name.innerHTML = ' ' + obj.name;
5126
5127 // Helps from preventing layer control flicker when checkboxes are disabled
5128 // https://github.com/Leaflet/Leaflet/issues/2771
5129 var holder = document.createElement('div');
5130
5131 label.appendChild(holder);
5132 holder.appendChild(input);
5133 holder.appendChild(name);
5134
5135 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5136 container.appendChild(label);
5137
5138 this._checkDisabledLayers();
5139 return label;
5140 },
5141
5142 _onInputClick: function () {
5143 var inputs = this._layerControlInputs,
5144 input, layer;
5145 var addedLayers = [],
5146 removedLayers = [];
5147
5148 this._handlingClick = true;
5149
5150 for (var i = inputs.length - 1; i >= 0; i--) {
5151 input = inputs[i];
5152 layer = this._getLayer(input.layerId).layer;
5153
5154 if (input.checked) {
5155 addedLayers.push(layer);
5156 } else if (!input.checked) {
5157 removedLayers.push(layer);
5158 }
5159 }
5160
5161 // Bugfix issue 2318: Should remove all old layers before readding new ones
5162 for (i = 0; i < removedLayers.length; i++) {
5163 if (this._map.hasLayer(removedLayers[i])) {
5164 this._map.removeLayer(removedLayers[i]);
5165 }
5166 }
5167 for (i = 0; i < addedLayers.length; i++) {
5168 if (!this._map.hasLayer(addedLayers[i])) {
5169 this._map.addLayer(addedLayers[i]);
5170 }
5171 }
5172
5173 this._handlingClick = false;
5174
5175 this._refocusOnMap();
5176 },
5177
5178 _checkDisabledLayers: function () {
5179 var inputs = this._layerControlInputs,
5180 input,
5181 layer,
5182 zoom = this._map.getZoom();
5183
5184 for (var i = inputs.length - 1; i >= 0; i--) {
5185 input = inputs[i];
5186 layer = this._getLayer(input.layerId).layer;
5187 input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5188 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5189
5190 }
5191 },
5192
5193 _expandIfNotCollapsed: function () {
5194 if (this._map && !this.options.collapsed) {
5195 this.expand();
5196 }
5197 return this;
5198 },
5199
5200 _expand: function () {
5201 // Backward compatibility, remove me in 1.1.
5202 return this.expand();
5203 },
5204
5205 _collapse: function () {
5206 // Backward compatibility, remove me in 1.1.
5207 return this.collapse();
5208 }
5209
5210 });
5211
5212
5213 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5214 // Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
5215 var layers = function (baseLayers, overlays, options) {
5216 return new Layers(baseLayers, overlays, options);
5217 };
5218
5219 /*
5220 * @class Control.Zoom
5221 * @aka L.Control.Zoom
5222 * @inherits Control
5223 *
5224 * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
5225 */
5226
5227 var Zoom = Control.extend({
5228 // @section
5229 // @aka Control.Zoom options
5230 options: {
5231 position: 'topleft',
5232
5233 // @option zoomInText: String = '+'
5234 // The text set on the 'zoom in' button.
5235 zoomInText: '+',
5236
5237 // @option zoomInTitle: String = 'Zoom in'
5238 // The title set on the 'zoom in' button.
5239 zoomInTitle: 'Zoom in',
5240
5241 // @option zoomOutText: String = '&#x2212;'
5242 // The text set on the 'zoom out' button.
5243 zoomOutText: '&#x2212;',
5244
5245 // @option zoomOutTitle: String = 'Zoom out'
5246 // The title set on the 'zoom out' button.
5247 zoomOutTitle: 'Zoom out'
5248 },
5249
5250 onAdd: function (map) {
5251 var zoomName = 'leaflet-control-zoom',
5252 container = create$1('div', zoomName + ' leaflet-bar'),
5253 options = this.options;
5254
5255 this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle,
5256 zoomName + '-in', container, this._zoomIn);
5257 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5258 zoomName + '-out', container, this._zoomOut);
5259
5260 this._updateDisabled();
5261 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5262
5263 return container;
5264 },
5265
5266 onRemove: function (map) {
5267 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5268 },
5269
5270 disable: function () {
5271 this._disabled = true;
5272 this._updateDisabled();
5273 return this;
5274 },
5275
5276 enable: function () {
5277 this._disabled = false;
5278 this._updateDisabled();
5279 return this;
5280 },
5281
5282 _zoomIn: function (e) {
5283 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5284 this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5285 }
5286 },
5287
5288 _zoomOut: function (e) {
5289 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5290 this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5291 }
5292 },
5293
5294 _createButton: function (html, title, className, container, fn) {
5295 var link = create$1('a', className, container);
5296 link.innerHTML = html;
5297 link.href = '#';
5298 link.title = title;
5299
5300 /*
5301 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5302 */
5303 link.setAttribute('role', 'button');
5304 link.setAttribute('aria-label', title);
5305
5306 disableClickPropagation(link);
5307 on(link, 'click', stop);
5308 on(link, 'click', fn, this);
5309 on(link, 'click', this._refocusOnMap, this);
5310
5311 return link;
5312 },
5313
5314 _updateDisabled: function () {
5315 var map = this._map,
5316 className = 'leaflet-disabled';
5317
5318 removeClass(this._zoomInButton, className);
5319 removeClass(this._zoomOutButton, className);
5320
5321 if (this._disabled || map._zoom === map.getMinZoom()) {
5322 addClass(this._zoomOutButton, className);
5323 }
5324 if (this._disabled || map._zoom === map.getMaxZoom()) {
5325 addClass(this._zoomInButton, className);
5326 }
5327 }
5328 });
5329
5330 // @namespace Map
5331 // @section Control options
5332 // @option zoomControl: Boolean = true
5333 // Whether a [zoom control](#control-zoom) is added to the map by default.
5334 Map.mergeOptions({
5335 zoomControl: true
5336 });
5337
5338 Map.addInitHook(function () {
5339 if (this.options.zoomControl) {
5340 this.zoomControl = new Zoom();
5341 this.addControl(this.zoomControl);
5342 }
5343 });
5344
5345 // @namespace Control.Zoom
5346 // @factory L.control.zoom(options: Control.Zoom options)
5347 // Creates a zoom control
5348 var zoom = function (options) {
5349 return new Zoom(options);
5350 };
5351
5352 /*
5353 * @class Control.Scale
5354 * @aka L.Control.Scale
5355 * @inherits Control
5356 *
5357 * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
5358 *
5359 * @example
5360 *
5361 * ```js
5362 * L.control.scale().addTo(map);
5363 * ```
5364 */
5365
5366 var Scale = Control.extend({
5367 // @section
5368 // @aka Control.Scale options
5369 options: {
5370 position: 'bottomleft',
5371
5372 // @option maxWidth: Number = 100
5373 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5374 maxWidth: 100,
5375
5376 // @option metric: Boolean = True
5377 // Whether to show the metric scale line (m/km).
5378 metric: true,
5379
5380 // @option imperial: Boolean = True
5381 // Whether to show the imperial scale line (mi/ft).
5382 imperial: true
5383
5384 // @option updateWhenIdle: Boolean = false
5385 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5386 },
5387
5388 onAdd: function (map) {
5389 var className = 'leaflet-control-scale',
5390 container = create$1('div', className),
5391 options = this.options;
5392
5393 this._addScales(options, className + '-line', container);
5394
5395 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5396 map.whenReady(this._update, this);
5397
5398 return container;
5399 },
5400
5401 onRemove: function (map) {
5402 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5403 },
5404
5405 _addScales: function (options, className, container) {
5406 if (options.metric) {
5407 this._mScale = create$1('div', className, container);
5408 }
5409 if (options.imperial) {
5410 this._iScale = create$1('div', className, container);
5411 }
5412 },
5413
5414 _update: function () {
5415 var map = this._map,
5416 y = map.getSize().y / 2;
5417
5418 var maxMeters = map.distance(
5419 map.containerPointToLatLng([0, y]),
5420 map.containerPointToLatLng([this.options.maxWidth, y]));
5421
5422 this._updateScales(maxMeters);
5423 },
5424
5425 _updateScales: function (maxMeters) {
5426 if (this.options.metric && maxMeters) {
5427 this._updateMetric(maxMeters);
5428 }
5429 if (this.options.imperial && maxMeters) {
5430 this._updateImperial(maxMeters);
5431 }
5432 },
5433
5434 _updateMetric: function (maxMeters) {
5435 var meters = this._getRoundNum(maxMeters),
5436 label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5437
5438 this._updateScale(this._mScale, label, meters / maxMeters);
5439 },
5440
5441 _updateImperial: function (maxMeters) {
5442 var maxFeet = maxMeters * 3.2808399,
5443 maxMiles, miles, feet;
5444
5445 if (maxFeet > 5280) {
5446 maxMiles = maxFeet / 5280;
5447 miles = this._getRoundNum(maxMiles);
5448 this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5449
5450 } else {
5451 feet = this._getRoundNum(maxFeet);
5452 this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5453 }
5454 },
5455
5456 _updateScale: function (scale, text, ratio) {
5457 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5458 scale.innerHTML = text;
5459 },
5460
5461 _getRoundNum: function (num) {
5462 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5463 d = num / pow10;
5464
5465 d = d >= 10 ? 10 :
5466 d >= 5 ? 5 :
5467 d >= 3 ? 3 :
5468 d >= 2 ? 2 : 1;
5469
5470 return pow10 * d;
5471 }
5472 });
5473
5474
5475 // @factory L.control.scale(options?: Control.Scale options)
5476 // Creates an scale control with the given options.
5477 var scale = function (options) {
5478 return new Scale(options);
5479 };
5480
5481 /*
5482 * @class Control.Attribution
5483 * @aka L.Control.Attribution
5484 * @inherits Control
5485 *
5486 * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
5487 */
5488
5489 var Attribution = Control.extend({
5490 // @section
5491 // @aka Control.Attribution options
5492 options: {
5493 position: 'bottomright',
5494
5495 // @option prefix: String = 'Leaflet'
5496 // The HTML text shown before the attributions. Pass `false` to disable.
5497 prefix: '<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5498 },
5499
5500 initialize: function (options) {
5501 setOptions(this, options);
5502
5503 this._attributions = {};
5504 },
5505
5506 onAdd: function (map) {
5507 map.attributionControl = this;
5508 this._container = create$1('div', 'leaflet-control-attribution');
5509 disableClickPropagation(this._container);
5510
5511 // TODO ugly, refactor
5512 for (var i in map._layers) {
5513 if (map._layers[i].getAttribution) {
5514 this.addAttribution(map._layers[i].getAttribution());
5515 }
5516 }
5517
5518 this._update();
5519
5520 return this._container;
5521 },
5522
5523 // @method setPrefix(prefix: String): this
5524 // Sets the text before the attributions.
5525 setPrefix: function (prefix) {
5526 this.options.prefix = prefix;
5527 this._update();
5528 return this;
5529 },
5530
5531 // @method addAttribution(text: String): this
5532 // Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5533 addAttribution: function (text) {
5534 if (!text) { return this; }
5535
5536 if (!this._attributions[text]) {
5537 this._attributions[text] = 0;
5538 }
5539 this._attributions[text]++;
5540
5541 this._update();
5542
5543 return this;
5544 },
5545
5546 // @method removeAttribution(text: String): this
5547 // Removes an attribution text.
5548 removeAttribution: function (text) {
5549 if (!text) { return this; }
5550
5551 if (this._attributions[text]) {
5552 this._attributions[text]--;
5553 this._update();
5554 }
5555
5556 return this;
5557 },
5558
5559 _update: function () {
5560 if (!this._map) { return; }
5561
5562 var attribs = [];
5563
5564 for (var i in this._attributions) {
5565 if (this._attributions[i]) {
5566 attribs.push(i);
5567 }
5568 }
5569
5570 var prefixAndAttribs = [];
5571
5572 if (this.options.prefix) {
5573 prefixAndAttribs.push(this.options.prefix);
5574 }
5575 if (attribs.length) {
5576 prefixAndAttribs.push(attribs.join(', '));
5577 }
5578
5579 this._container.innerHTML = prefixAndAttribs.join(' | ');
5580 }
5581 });
5582
5583 // @namespace Map
5584 // @section Control options
5585 // @option attributionControl: Boolean = true
5586 // Whether a [attribution control](#control-attribution) is added to the map by default.
5587 Map.mergeOptions({
5588 attributionControl: true
5589 });
5590
5591 Map.addInitHook(function () {
5592 if (this.options.attributionControl) {
5593 new Attribution().addTo(this);
5594 }
5595 });
5596
5597 // @namespace Control.Attribution
5598 // @factory L.control.attribution(options: Control.Attribution options)
5599 // Creates an attribution control.
5600 var attribution = function (options) {
5601 return new Attribution(options);
5602 };
5603
5604 Control.Layers = Layers;
5605 Control.Zoom = Zoom;
5606 Control.Scale = Scale;
5607 Control.Attribution = Attribution;
5608
5609 control.layers = layers;
5610 control.zoom = zoom;
5611 control.scale = scale;
5612 control.attribution = attribution;
5613
5614 /*
5615 L.Handler is a base class for handler classes that are used internally to inject
5616 interaction features like dragging to classes like Map and Marker.
5617 */
5618
5619 // @class Handler
5620 // @aka L.Handler
5621 // Abstract class for map interaction handlers
5622
5623 var Handler = Class.extend({
5624 initialize: function (map) {
5625 this._map = map;
5626 },
5627
5628 // @method enable(): this
5629 // Enables the handler
5630 enable: function () {
5631 if (this._enabled) { return this; }
5632
5633 this._enabled = true;
5634 this.addHooks();
5635 return this;
5636 },
5637
5638 // @method disable(): this
5639 // Disables the handler
5640 disable: function () {
5641 if (!this._enabled) { return this; }
5642
5643 this._enabled = false;
5644 this.removeHooks();
5645 return this;
5646 },
5647
5648 // @method enabled(): Boolean
5649 // Returns `true` if the handler is enabled
5650 enabled: function () {
5651 return !!this._enabled;
5652 }
5653
5654 // @section Extension methods
5655 // Classes inheriting from `Handler` must implement the two following methods:
5656 // @method addHooks()
5657 // Called when the handler is enabled, should add event hooks.
5658 // @method removeHooks()
5659 // Called when the handler is disabled, should remove the event hooks added previously.
5660 });
5661
5662 // @section There is static function which can be called without instantiating L.Handler:
5663 // @function addTo(map: Map, name: String): this
5664 // Adds a new Handler to the given map with the given name.
5665 Handler.addTo = function (map, name) {
5666 map.addHandler(name, this);
5667 return this;
5668 };
5669
5670 var Mixin = {Events: Events};
5671
5672 /*
5673 * @class Draggable
5674 * @aka L.Draggable
5675 * @inherits Evented
5676 *
5677 * A class for making DOM elements draggable (including touch support).
5678 * Used internally for map and marker dragging. Only works for elements
5679 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5680 *
5681 * @example
5682 * ```js
5683 * var draggable = new L.Draggable(elementToDrag);
5684 * draggable.enable();
5685 * ```
5686 */
5687
5688 var START = touch ? 'touchstart mousedown' : 'mousedown';
5689 var END = {
5690 mousedown: 'mouseup',
5691 touchstart: 'touchend',
5692 pointerdown: 'touchend',
5693 MSPointerDown: 'touchend'
5694 };
5695 var MOVE = {
5696 mousedown: 'mousemove',
5697 touchstart: 'touchmove',
5698 pointerdown: 'touchmove',
5699 MSPointerDown: 'touchmove'
5700 };
5701
5702
5703 var Draggable = Evented.extend({
5704
5705 options: {
5706 // @section
5707 // @aka Draggable options
5708 // @option clickTolerance: Number = 3
5709 // The max number of pixels a user can shift the mouse pointer during a click
5710 // for it to be considered a valid click (as opposed to a mouse drag).
5711 clickTolerance: 3
5712 },
5713
5714 // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5715 // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5716 initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5717 setOptions(this, options);
5718
5719 this._element = element;
5720 this._dragStartTarget = dragStartTarget || element;
5721 this._preventOutline = preventOutline$$1;
5722 },
5723
5724 // @method enable()
5725 // Enables the dragging ability
5726 enable: function () {
5727 if (this._enabled) { return; }
5728
5729 on(this._dragStartTarget, START, this._onDown, this);
5730
5731 this._enabled = true;
5732 },
5733
5734 // @method disable()
5735 // Disables the dragging ability
5736 disable: function () {
5737 if (!this._enabled) { return; }
5738
5739 // If we're currently dragging this draggable,
5740 // disabling it counts as first ending the drag.
5741 if (Draggable._dragging === this) {
5742 this.finishDrag();
5743 }
5744
5745 off(this._dragStartTarget, START, this._onDown, this);
5746
5747 this._enabled = false;
5748 this._moved = false;
5749 },
5750
5751 _onDown: function (e) {
5752 // Ignore simulated events, since we handle both touch and
5753 // mouse explicitly; otherwise we risk getting duplicates of
5754 // touch events, see #4315.
5755 // Also ignore the event if disabled; this happens in IE11
5756 // under some circumstances, see #3666.
5757 if (e._simulated || !this._enabled) { return; }
5758
5759 this._moved = false;
5760
5761 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5762
5763 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5764 Draggable._dragging = this; // Prevent dragging multiple objects at once.
5765
5766 if (this._preventOutline) {
5767 preventOutline(this._element);
5768 }
5769
5770 disableImageDrag();
5771 disableTextSelection();
5772
5773 if (this._moving) { return; }
5774
5775 // @event down: Event
5776 // Fired when a drag is about to start.
5777 this.fire('down');
5778
5779 var first = e.touches ? e.touches[0] : e;
5780
5781 this._startPoint = new Point(first.clientX, first.clientY);
5782
5783 on(document, MOVE[e.type], this._onMove, this);
5784 on(document, END[e.type], this._onUp, this);
5785 },
5786
5787 _onMove: function (e) {
5788 // Ignore simulated events, since we handle both touch and
5789 // mouse explicitly; otherwise we risk getting duplicates of
5790 // touch events, see #4315.
5791 // Also ignore the event if disabled; this happens in IE11
5792 // under some circumstances, see #3666.
5793 if (e._simulated || !this._enabled) { return; }
5794
5795 if (e.touches && e.touches.length > 1) {
5796 this._moved = true;
5797 return;
5798 }
5799
5800 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5801 newPoint = new Point(first.clientX, first.clientY),
5802 offset = newPoint.subtract(this._startPoint);
5803
5804 if (!offset.x && !offset.y) { return; }
5805 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5806
5807 preventDefault(e);
5808
5809 if (!this._moved) {
5810 // @event dragstart: Event
5811 // Fired when a drag starts
5812 this.fire('dragstart');
5813
5814 this._moved = true;
5815 this._startPos = getPosition(this._element).subtract(offset);
5816
5817 addClass(document.body, 'leaflet-dragging');
5818
5819 this._lastTarget = e.target || e.srcElement;
5820 // IE and Edge do not give the <use> element, so fetch it
5821 // if necessary
5822 if ((window.SVGElementInstance) && (this._lastTarget instanceof SVGElementInstance)) {
5823 this._lastTarget = this._lastTarget.correspondingUseElement;
5824 }
5825 addClass(this._lastTarget, 'leaflet-drag-target');
5826 }
5827
5828 this._newPos = this._startPos.add(offset);
5829 this._moving = true;
5830
5831 cancelAnimFrame(this._animRequest);
5832 this._lastEvent = e;
5833 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5834 },
5835
5836 _updatePosition: function () {
5837 var e = {originalEvent: this._lastEvent};
5838
5839 // @event predrag: Event
5840 // Fired continuously during dragging *before* each corresponding
5841 // update of the element's position.
5842 this.fire('predrag', e);
5843 setPosition(this._element, this._newPos);
5844
5845 // @event drag: Event
5846 // Fired continuously during dragging.
5847 this.fire('drag', e);
5848 },
5849
5850 _onUp: function (e) {
5851 // Ignore simulated events, since we handle both touch and
5852 // mouse explicitly; otherwise we risk getting duplicates of
5853 // touch events, see #4315.
5854 // Also ignore the event if disabled; this happens in IE11
5855 // under some circumstances, see #3666.
5856 if (e._simulated || !this._enabled) { return; }
5857 this.finishDrag();
5858 },
5859
5860 finishDrag: function () {
5861 removeClass(document.body, 'leaflet-dragging');
5862
5863 if (this._lastTarget) {
5864 removeClass(this._lastTarget, 'leaflet-drag-target');
5865 this._lastTarget = null;
5866 }
5867
5868 for (var i in MOVE) {
5869 off(document, MOVE[i], this._onMove, this);
5870 off(document, END[i], this._onUp, this);
5871 }
5872
5873 enableImageDrag();
5874 enableTextSelection();
5875
5876 if (this._moved && this._moving) {
5877 // ensure drag is not fired after dragend
5878 cancelAnimFrame(this._animRequest);
5879
5880 // @event dragend: DragEndEvent
5881 // Fired when the drag ends.
5882 this.fire('dragend', {
5883 distance: this._newPos.distanceTo(this._startPos)
5884 });
5885 }
5886
5887 this._moving = false;
5888 Draggable._dragging = false;
5889 }
5890
5891 });
5892
5893 /*
5894 * @namespace LineUtil
5895 *
5896 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
5897 */
5898
5899 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
5900 // Improves rendering performance dramatically by lessening the number of points to draw.
5901
5902 // @function simplify(points: Point[], tolerance: Number): Point[]
5903 // Dramatically reduces the number of points in a polyline while retaining
5904 // its shape and returns a new array of simplified points, using the
5905 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
5906 // Used for a huge performance boost when processing/displaying Leaflet polylines for
5907 // each zoom level and also reducing visual noise. tolerance affects the amount of
5908 // simplification (lesser value means higher quality but slower and with more points).
5909 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
5910 function simplify(points, tolerance) {
5911 if (!tolerance || !points.length) {
5912 return points.slice();
5913 }
5914
5915 var sqTolerance = tolerance * tolerance;
5916
5917 // stage 1: vertex reduction
5918 points = _reducePoints(points, sqTolerance);
5919
5920 // stage 2: Douglas-Peucker simplification
5921 points = _simplifyDP(points, sqTolerance);
5922
5923 return points;
5924 }
5925
5926 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
5927 // Returns the distance between point `p` and segment `p1` to `p2`.
5928 function pointToSegmentDistance(p, p1, p2) {
5929 return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
5930 }
5931
5932 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
5933 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
5934 function closestPointOnSegment(p, p1, p2) {
5935 return _sqClosestPointOnSegment(p, p1, p2);
5936 }
5937
5938 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
5939 function _simplifyDP(points, sqTolerance) {
5940
5941 var len = points.length,
5942 ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
5943 markers = new ArrayConstructor(len);
5944
5945 markers[0] = markers[len - 1] = 1;
5946
5947 _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
5948
5949 var i,
5950 newPoints = [];
5951
5952 for (i = 0; i < len; i++) {
5953 if (markers[i]) {
5954 newPoints.push(points[i]);
5955 }
5956 }
5957
5958 return newPoints;
5959 }
5960
5961 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
5962
5963 var maxSqDist = 0,
5964 index, i, sqDist;
5965
5966 for (i = first + 1; i <= last - 1; i++) {
5967 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
5968
5969 if (sqDist > maxSqDist) {
5970 index = i;
5971 maxSqDist = sqDist;
5972 }
5973 }
5974
5975 if (maxSqDist > sqTolerance) {
5976 markers[index] = 1;
5977
5978 _simplifyDPStep(points, markers, sqTolerance, first, index);
5979 _simplifyDPStep(points, markers, sqTolerance, index, last);
5980 }
5981 }
5982
5983 // reduce points that are too close to each other to a single point
5984 function _reducePoints(points, sqTolerance) {
5985 var reducedPoints = [points[0]];
5986
5987 for (var i = 1, prev = 0, len = points.length; i < len; i++) {
5988 if (_sqDist(points[i], points[prev]) > sqTolerance) {
5989 reducedPoints.push(points[i]);
5990 prev = i;
5991 }
5992 }
5993 if (prev < len - 1) {
5994 reducedPoints.push(points[len - 1]);
5995 }
5996 return reducedPoints;
5997 }
5998
5999 var _lastCode;
6000
6001 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6002 // Clips the segment a to b by rectangular bounds with the
6003 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6004 // (modifying the segment points directly!). Used by Leaflet to only show polyline
6005 // points that are on the screen or near, increasing performance.
6006 function clipSegment(a, b, bounds, useLastCode, round) {
6007 var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6008 codeB = _getBitCode(b, bounds),
6009
6010 codeOut, p, newCode;
6011
6012 // save 2nd code to avoid calculating it on the next segment
6013 _lastCode = codeB;
6014
6015 while (true) {
6016 // if a,b is inside the clip window (trivial accept)
6017 if (!(codeA | codeB)) {
6018 return [a, b];
6019 }
6020
6021 // if a,b is outside the clip window (trivial reject)
6022 if (codeA & codeB) {
6023 return false;
6024 }
6025
6026 // other cases
6027 codeOut = codeA || codeB;
6028 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6029 newCode = _getBitCode(p, bounds);
6030
6031 if (codeOut === codeA) {
6032 a = p;
6033 codeA = newCode;
6034 } else {
6035 b = p;
6036 codeB = newCode;
6037 }
6038 }
6039 }
6040
6041 function _getEdgeIntersection(a, b, code, bounds, round) {
6042 var dx = b.x - a.x,
6043 dy = b.y - a.y,
6044 min = bounds.min,
6045 max = bounds.max,
6046 x, y;
6047
6048 if (code & 8) { // top
6049 x = a.x + dx * (max.y - a.y) / dy;
6050 y = max.y;
6051
6052 } else if (code & 4) { // bottom
6053 x = a.x + dx * (min.y - a.y) / dy;
6054 y = min.y;
6055
6056 } else if (code & 2) { // right
6057 x = max.x;
6058 y = a.y + dy * (max.x - a.x) / dx;
6059
6060 } else if (code & 1) { // left
6061 x = min.x;
6062 y = a.y + dy * (min.x - a.x) / dx;
6063 }
6064
6065 return new Point(x, y, round);
6066 }
6067
6068 function _getBitCode(p, bounds) {
6069 var code = 0;
6070
6071 if (p.x < bounds.min.x) { // left
6072 code |= 1;
6073 } else if (p.x > bounds.max.x) { // right
6074 code |= 2;
6075 }
6076
6077 if (p.y < bounds.min.y) { // bottom
6078 code |= 4;
6079 } else if (p.y > bounds.max.y) { // top
6080 code |= 8;
6081 }
6082
6083 return code;
6084 }
6085
6086 // square distance (to avoid unnecessary Math.sqrt calls)
6087 function _sqDist(p1, p2) {
6088 var dx = p2.x - p1.x,
6089 dy = p2.y - p1.y;
6090 return dx * dx + dy * dy;
6091 }
6092
6093 // return closest point on segment or distance to that point
6094 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6095 var x = p1.x,
6096 y = p1.y,
6097 dx = p2.x - x,
6098 dy = p2.y - y,
6099 dot = dx * dx + dy * dy,
6100 t;
6101
6102 if (dot > 0) {
6103 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6104
6105 if (t > 1) {
6106 x = p2.x;
6107 y = p2.y;
6108 } else if (t > 0) {
6109 x += dx * t;
6110 y += dy * t;
6111 }
6112 }
6113
6114 dx = p.x - x;
6115 dy = p.y - y;
6116
6117 return sqDist ? dx * dx + dy * dy : new Point(x, y);
6118 }
6119
6120
6121 // @function isFlat(latlngs: LatLng[]): Boolean
6122 // Returns true if `latlngs` is a flat array, false is nested.
6123 function isFlat(latlngs) {
6124 return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6125 }
6126
6127 function _flat(latlngs) {
6128 console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6129 return isFlat(latlngs);
6130 }
6131
6132
6133 var LineUtil = (Object.freeze || Object)({
6134 simplify: simplify,
6135 pointToSegmentDistance: pointToSegmentDistance,
6136 closestPointOnSegment: closestPointOnSegment,
6137 clipSegment: clipSegment,
6138 _getEdgeIntersection: _getEdgeIntersection,
6139 _getBitCode: _getBitCode,
6140 _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6141 isFlat: isFlat,
6142 _flat: _flat
6143 });
6144
6145 /*
6146 * @namespace PolyUtil
6147 * Various utility functions for polygon geometries.
6148 */
6149
6150 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6151 * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
6152 * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6153 * performance. Note that polygon points needs different algorithm for clipping
6154 * than polyline, so there's a separate method for it.
6155 */
6156 function clipPolygon(points, bounds, round) {
6157 var clippedPoints,
6158 edges = [1, 4, 2, 8],
6159 i, j, k,
6160 a, b,
6161 len, edge, p;
6162
6163 for (i = 0, len = points.length; i < len; i++) {
6164 points[i]._code = _getBitCode(points[i], bounds);
6165 }
6166
6167 // for each edge (left, bottom, right, top)
6168 for (k = 0; k < 4; k++) {
6169 edge = edges[k];
6170 clippedPoints = [];
6171
6172 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6173 a = points[i];
6174 b = points[j];
6175
6176 // if a is inside the clip window
6177 if (!(a._code & edge)) {
6178 // if b is outside the clip window (a->b goes out of screen)
6179 if (b._code & edge) {
6180 p = _getEdgeIntersection(b, a, edge, bounds, round);
6181 p._code = _getBitCode(p, bounds);
6182 clippedPoints.push(p);
6183 }
6184 clippedPoints.push(a);
6185
6186 // else if b is inside the clip window (a->b enters the screen)
6187 } else if (!(b._code & edge)) {
6188 p = _getEdgeIntersection(b, a, edge, bounds, round);
6189 p._code = _getBitCode(p, bounds);
6190 clippedPoints.push(p);
6191 }
6192 }
6193 points = clippedPoints;
6194 }
6195
6196 return points;
6197 }
6198
6199
6200 var PolyUtil = (Object.freeze || Object)({
6201 clipPolygon: clipPolygon
6202 });
6203
6204 /*
6205 * @namespace Projection
6206 * @section
6207 * Leaflet comes with a set of already defined Projections out of the box:
6208 *
6209 * @projection L.Projection.LonLat
6210 *
6211 * Equirectangular, or Plate Carree projection — the most simple projection,
6212 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6213 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6214 * `EPSG:4326` and `Simple` CRS.
6215 */
6216
6217 var LonLat = {
6218 project: function (latlng) {
6219 return new Point(latlng.lng, latlng.lat);
6220 },
6221
6222 unproject: function (point) {
6223 return new LatLng(point.y, point.x);
6224 },
6225
6226 bounds: new Bounds([-180, -90], [180, 90])
6227 };
6228
6229 /*
6230 * @namespace Projection
6231 * @projection L.Projection.Mercator
6232 *
6233 * Elliptical Mercator projection — more complex than Spherical Mercator. Takes into account that Earth is a geoid, not a perfect sphere. Used by the EPSG:3395 CRS.
6234 */
6235
6236 var Mercator = {
6237 R: 6378137,
6238 R_MINOR: 6356752.314245179,
6239
6240 bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6241
6242 project: function (latlng) {
6243 var d = Math.PI / 180,
6244 r = this.R,
6245 y = latlng.lat * d,
6246 tmp = this.R_MINOR / r,
6247 e = Math.sqrt(1 - tmp * tmp),
6248 con = e * Math.sin(y);
6249
6250 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6251 y = -r * Math.log(Math.max(ts, 1E-10));
6252
6253 return new Point(latlng.lng * d * r, y);
6254 },
6255
6256 unproject: function (point) {
6257 var d = 180 / Math.PI,
6258 r = this.R,
6259 tmp = this.R_MINOR / r,
6260 e = Math.sqrt(1 - tmp * tmp),
6261 ts = Math.exp(-point.y / r),
6262 phi = Math.PI / 2 - 2 * Math.atan(ts);
6263
6264 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6265 con = e * Math.sin(phi);
6266 con = Math.pow((1 - con) / (1 + con), e / 2);
6267 dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6268 phi += dphi;
6269 }
6270
6271 return new LatLng(phi * d, point.x * d / r);
6272 }
6273 };
6274
6275 /*
6276 * @class Projection
6277
6278 * An object with methods for projecting geographical coordinates of the world onto
6279 * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6280
6281 * @property bounds: Bounds
6282 * The bounds (specified in CRS units) where the projection is valid
6283
6284 * @method project(latlng: LatLng): Point
6285 * Projects geographical coordinates into a 2D point.
6286 * Only accepts actual `L.LatLng` instances, not arrays.
6287
6288 * @method unproject(point: Point): LatLng
6289 * The inverse of `project`. Projects a 2D point into a geographical location.
6290 * Only accepts actual `L.Point` instances, not arrays.
6291
6292 * Note that the projection instances do not inherit from Leafet's `Class` object,
6293 * and can't be instantiated. Also, new classes can't inherit from them,
6294 * and methods can't be added to them with the `include` function.
6295
6296 */
6297
6298
6299
6300
6301 var index = (Object.freeze || Object)({
6302 LonLat: LonLat,
6303 Mercator: Mercator,
6304 SphericalMercator: SphericalMercator
6305 });
6306
6307 /*
6308 * @namespace CRS
6309 * @crs L.CRS.EPSG3395
6310 *
6311 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6312 */
6313 var EPSG3395 = extend({}, Earth, {
6314 code: 'EPSG:3395',
6315 projection: Mercator,
6316
6317 transformation: (function () {
6318 var scale = 0.5 / (Math.PI * Mercator.R);
6319 return toTransformation(scale, 0.5, -scale, 0.5);
6320 }())
6321 });
6322
6323 /*
6324 * @namespace CRS
6325 * @crs L.CRS.EPSG4326
6326 *
6327 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6328 *
6329 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6330 * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer`
6331 * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6332 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6333 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6334 */
6335
6336 var EPSG4326 = extend({}, Earth, {
6337 code: 'EPSG:4326',
6338 projection: LonLat,
6339 transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6340 });
6341
6342 /*
6343 * @namespace CRS
6344 * @crs L.CRS.Simple
6345 *
6346 * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6347 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6348 * axis should still be inverted (going from bottom to top). `distance()` returns
6349 * simple euclidean distance.
6350 */
6351
6352 var Simple = extend({}, CRS, {
6353 projection: LonLat,
6354 transformation: toTransformation(1, 0, -1, 0),
6355
6356 scale: function (zoom) {
6357 return Math.pow(2, zoom);
6358 },
6359
6360 zoom: function (scale) {
6361 return Math.log(scale) / Math.LN2;
6362 },
6363
6364 distance: function (latlng1, latlng2) {
6365 var dx = latlng2.lng - latlng1.lng,
6366 dy = latlng2.lat - latlng1.lat;
6367
6368 return Math.sqrt(dx * dx + dy * dy);
6369 },
6370
6371 infinite: true
6372 });
6373
6374 CRS.Earth = Earth;
6375 CRS.EPSG3395 = EPSG3395;
6376 CRS.EPSG3857 = EPSG3857;
6377 CRS.EPSG900913 = EPSG900913;
6378 CRS.EPSG4326 = EPSG4326;
6379 CRS.Simple = Simple;
6380
6381 /*
6382 * @class Layer
6383 * @inherits Evented
6384 * @aka L.Layer
6385 * @aka ILayer
6386 *
6387 * A set of methods from the Layer base class that all Leaflet layers use.
6388 * Inherits all methods, options and events from `L.Evented`.
6389 *
6390 * @example
6391 *
6392 * ```js
6393 * var layer = L.Marker(latlng).addTo(map);
6394 * layer.addTo(map);
6395 * layer.remove();
6396 * ```
6397 *
6398 * @event add: Event
6399 * Fired after the layer is added to a map
6400 *
6401 * @event remove: Event
6402 * Fired after the layer is removed from a map
6403 */
6404
6405
6406 var Layer = Evented.extend({
6407
6408 // Classes extending `L.Layer` will inherit the following options:
6409 options: {
6410 // @option pane: String = 'overlayPane'
6411 // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
6412 pane: 'overlayPane',
6413
6414 // @option attribution: String = null
6415 // String to be shown in the attribution control, describes the layer data, e.g. "© Mapbox".
6416 attribution: null,
6417
6418 bubblingMouseEvents: true
6419 },
6420
6421 /* @section
6422 * Classes extending `L.Layer` will inherit the following methods:
6423 *
6424 * @method addTo(map: Map|LayerGroup): this
6425 * Adds the layer to the given map or layer group.
6426 */
6427 addTo: function (map) {
6428 map.addLayer(this);
6429 return this;
6430 },
6431
6432 // @method remove: this
6433 // Removes the layer from the map it is currently active on.
6434 remove: function () {
6435 return this.removeFrom(this._map || this._mapToAdd);
6436 },
6437
6438 // @method removeFrom(map: Map): this
6439 // Removes the layer from the given map
6440 removeFrom: function (obj) {
6441 if (obj) {
6442 obj.removeLayer(this);
6443 }
6444 return this;
6445 },
6446
6447 // @method getPane(name? : String): HTMLElement
6448 // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6449 getPane: function (name) {
6450 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6451 },
6452
6453 addInteractiveTarget: function (targetEl) {
6454 this._map._targets[stamp(targetEl)] = this;
6455 return this;
6456 },
6457
6458 removeInteractiveTarget: function (targetEl) {
6459 delete this._map._targets[stamp(targetEl)];
6460 return this;
6461 },
6462
6463 // @method getAttribution: String
6464 // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6465 getAttribution: function () {
6466 return this.options.attribution;
6467 },
6468
6469 _layerAdd: function (e) {
6470 var map = e.target;
6471
6472 // check in case layer gets added and then removed before the map is ready
6473 if (!map.hasLayer(this)) { return; }
6474
6475 this._map = map;
6476 this._zoomAnimated = map._zoomAnimated;
6477
6478 if (this.getEvents) {
6479 var events = this.getEvents();
6480 map.on(events, this);
6481 this.once('remove', function () {
6482 map.off(events, this);
6483 }, this);
6484 }
6485
6486 this.onAdd(map);
6487
6488 if (this.getAttribution && map.attributionControl) {
6489 map.attributionControl.addAttribution(this.getAttribution());
6490 }
6491
6492 this.fire('add');
6493 map.fire('layeradd', {layer: this});
6494 }
6495 });
6496
6497 /* @section Extension methods
6498 * @uninheritable
6499 *
6500 * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6501 *
6502 * @method onAdd(map: Map): this
6503 * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
6504 *
6505 * @method onRemove(map: Map): this
6506 * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
6507 *
6508 * @method getEvents(): Object
6509 * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
6510 *
6511 * @method getAttribution(): String
6512 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6513 *
6514 * @method beforeAdd(map: Map): this
6515 * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
6516 */
6517
6518
6519 /* @namespace Map
6520 * @section Layer events
6521 *
6522 * @event layeradd: LayerEvent
6523 * Fired when a new layer is added to the map.
6524 *
6525 * @event layerremove: LayerEvent
6526 * Fired when some layer is removed from the map
6527 *
6528 * @section Methods for Layers and Controls
6529 */
6530 Map.include({
6531 // @method addLayer(layer: Layer): this
6532 // Adds the given layer to the map
6533 addLayer: function (layer) {
6534 if (!layer._layerAdd) {
6535 throw new Error('The provided object is not a Layer.');
6536 }
6537
6538 var id = stamp(layer);
6539 if (this._layers[id]) { return this; }
6540 this._layers[id] = layer;
6541
6542 layer._mapToAdd = this;
6543
6544 if (layer.beforeAdd) {
6545 layer.beforeAdd(this);
6546 }
6547
6548 this.whenReady(layer._layerAdd, layer);
6549
6550 return this;
6551 },
6552
6553 // @method removeLayer(layer: Layer): this
6554 // Removes the given layer from the map.
6555 removeLayer: function (layer) {
6556 var id = stamp(layer);
6557
6558 if (!this._layers[id]) { return this; }
6559
6560 if (this._loaded) {
6561 layer.onRemove(this);
6562 }
6563
6564 if (layer.getAttribution && this.attributionControl) {
6565 this.attributionControl.removeAttribution(layer.getAttribution());
6566 }
6567
6568 delete this._layers[id];
6569
6570 if (this._loaded) {
6571 this.fire('layerremove', {layer: layer});
6572 layer.fire('remove');
6573 }
6574
6575 layer._map = layer._mapToAdd = null;
6576
6577 return this;
6578 },
6579
6580 // @method hasLayer(layer: Layer): Boolean
6581 // Returns `true` if the given layer is currently added to the map
6582 hasLayer: function (layer) {
6583 return !!layer && (stamp(layer) in this._layers);
6584 },
6585
6586 /* @method eachLayer(fn: Function, context?: Object): this
6587 * Iterates over the layers of the map, optionally specifying context of the iterator function.
6588 * ```
6589 * map.eachLayer(function(layer){
6590 * layer.bindPopup('Hello');
6591 * });
6592 * ```
6593 */
6594 eachLayer: function (method, context) {
6595 for (var i in this._layers) {
6596 method.call(context, this._layers[i]);
6597 }
6598 return this;
6599 },
6600
6601 _addLayers: function (layers) {
6602 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6603
6604 for (var i = 0, len = layers.length; i < len; i++) {
6605 this.addLayer(layers[i]);
6606 }
6607 },
6608
6609 _addZoomLimit: function (layer) {
6610 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6611 this._zoomBoundLayers[stamp(layer)] = layer;
6612 this._updateZoomLevels();
6613 }
6614 },
6615
6616 _removeZoomLimit: function (layer) {
6617 var id = stamp(layer);
6618
6619 if (this._zoomBoundLayers[id]) {
6620 delete this._zoomBoundLayers[id];
6621 this._updateZoomLevels();
6622 }
6623 },
6624
6625 _updateZoomLevels: function () {
6626 var minZoom = Infinity,
6627 maxZoom = -Infinity,
6628 oldZoomSpan = this._getZoomSpan();
6629
6630 for (var i in this._zoomBoundLayers) {
6631 var options = this._zoomBoundLayers[i].options;
6632
6633 minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6634 maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6635 }
6636
6637 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6638 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6639
6640 // @section Map state change events
6641 // @event zoomlevelschange: Event
6642 // Fired when the number of zoomlevels on the map is changed due
6643 // to adding or removing a layer.
6644 if (oldZoomSpan !== this._getZoomSpan()) {
6645 this.fire('zoomlevelschange');
6646 }
6647
6648 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6649 this.setZoom(this._layersMaxZoom);
6650 }
6651 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6652 this.setZoom(this._layersMinZoom);
6653 }
6654 }
6655 });
6656
6657 /*
6658 * @class LayerGroup
6659 * @aka L.LayerGroup
6660 * @inherits Layer
6661 *
6662 * Used to group several layers and handle them as one. If you add it to the map,
6663 * any layers added or removed from the group will be added/removed on the map as
6664 * well. Extends `Layer`.
6665 *
6666 * @example
6667 *
6668 * ```js
6669 * L.layerGroup([marker1, marker2])
6670 * .addLayer(polyline)
6671 * .addTo(map);
6672 * ```
6673 */
6674
6675 var LayerGroup = Layer.extend({
6676
6677 initialize: function (layers, options) {
6678 setOptions(this, options);
6679
6680 this._layers = {};
6681
6682 var i, len;
6683
6684 if (layers) {
6685 for (i = 0, len = layers.length; i < len; i++) {
6686 this.addLayer(layers[i]);
6687 }
6688 }
6689 },
6690
6691 // @method addLayer(layer: Layer): this
6692 // Adds the given layer to the group.
6693 addLayer: function (layer) {
6694 var id = this.getLayerId(layer);
6695
6696 this._layers[id] = layer;
6697
6698 if (this._map) {
6699 this._map.addLayer(layer);
6700 }
6701
6702 return this;
6703 },
6704
6705 // @method removeLayer(layer: Layer): this
6706 // Removes the given layer from the group.
6707 // @alternative
6708 // @method removeLayer(id: Number): this
6709 // Removes the layer with the given internal ID from the group.
6710 removeLayer: function (layer) {
6711 var id = layer in this._layers ? layer : this.getLayerId(layer);
6712
6713 if (this._map && this._layers[id]) {
6714 this._map.removeLayer(this._layers[id]);
6715 }
6716
6717 delete this._layers[id];
6718
6719 return this;
6720 },
6721
6722 // @method hasLayer(layer: Layer): Boolean
6723 // Returns `true` if the given layer is currently added to the group.
6724 // @alternative
6725 // @method hasLayer(id: Number): Boolean
6726 // Returns `true` if the given internal ID is currently added to the group.
6727 hasLayer: function (layer) {
6728 return !!layer && (layer in this._layers || this.getLayerId(layer) in this._layers);
6729 },
6730
6731 // @method clearLayers(): this
6732 // Removes all the layers from the group.
6733 clearLayers: function () {
6734 return this.eachLayer(this.removeLayer, this);
6735 },
6736
6737 // @method invoke(methodName: String, …): this
6738 // Calls `methodName` on every layer contained in this group, passing any
6739 // additional parameters. Has no effect if the layers contained do not
6740 // implement `methodName`.
6741 invoke: function (methodName) {
6742 var args = Array.prototype.slice.call(arguments, 1),
6743 i, layer;
6744
6745 for (i in this._layers) {
6746 layer = this._layers[i];
6747
6748 if (layer[methodName]) {
6749 layer[methodName].apply(layer, args);
6750 }
6751 }
6752
6753 return this;
6754 },
6755
6756 onAdd: function (map) {
6757 this.eachLayer(map.addLayer, map);
6758 },
6759
6760 onRemove: function (map) {
6761 this.eachLayer(map.removeLayer, map);
6762 },
6763
6764 // @method eachLayer(fn: Function, context?: Object): this
6765 // Iterates over the layers of the group, optionally specifying context of the iterator function.
6766 // ```js
6767 // group.eachLayer(function (layer) {
6768 // layer.bindPopup('Hello');
6769 // });
6770 // ```
6771 eachLayer: function (method, context) {
6772 for (var i in this._layers) {
6773 method.call(context, this._layers[i]);
6774 }
6775 return this;
6776 },
6777
6778 // @method getLayer(id: Number): Layer
6779 // Returns the layer with the given internal ID.
6780 getLayer: function (id) {
6781 return this._layers[id];
6782 },
6783
6784 // @method getLayers(): Layer[]
6785 // Returns an array of all the layers added to the group.
6786 getLayers: function () {
6787 var layers = [];
6788 this.eachLayer(layers.push, layers);
6789 return layers;
6790 },
6791
6792 // @method setZIndex(zIndex: Number): this
6793 // Calls `setZIndex` on every layer contained in this group, passing the z-index.
6794 setZIndex: function (zIndex) {
6795 return this.invoke('setZIndex', zIndex);
6796 },
6797
6798 // @method getLayerId(layer: Layer): Number
6799 // Returns the internal ID for a layer
6800 getLayerId: function (layer) {
6801 return stamp(layer);
6802 }
6803 });
6804
6805
6806 // @factory L.layerGroup(layers?: Layer[], options?: Object)
6807 // Create a layer group, optionally given an initial set of layers and an `options` object.
6808 var layerGroup = function (layers, options) {
6809 return new LayerGroup(layers, options);
6810 };
6811
6812 /*
6813 * @class FeatureGroup
6814 * @aka L.FeatureGroup
6815 * @inherits LayerGroup
6816 *
6817 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6818 * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6819 * * Events are propagated to the `FeatureGroup`, so if the group has an event
6820 * handler, it will handle events from any of the layers. This includes mouse events
6821 * and custom events.
6822 * * Has `layeradd` and `layerremove` events
6823 *
6824 * @example
6825 *
6826 * ```js
6827 * L.featureGroup([marker1, marker2, polyline])
6828 * .bindPopup('Hello world!')
6829 * .on('click', function() { alert('Clicked on a member of the group!'); })
6830 * .addTo(map);
6831 * ```
6832 */
6833
6834 var FeatureGroup = LayerGroup.extend({
6835
6836 addLayer: function (layer) {
6837 if (this.hasLayer(layer)) {
6838 return this;
6839 }
6840
6841 layer.addEventParent(this);
6842
6843 LayerGroup.prototype.addLayer.call(this, layer);
6844
6845 // @event layeradd: LayerEvent
6846 // Fired when a layer is added to this `FeatureGroup`
6847 return this.fire('layeradd', {layer: layer});
6848 },
6849
6850 removeLayer: function (layer) {
6851 if (!this.hasLayer(layer)) {
6852 return this;
6853 }
6854 if (layer in this._layers) {
6855 layer = this._layers[layer];
6856 }
6857
6858 layer.removeEventParent(this);
6859
6860 LayerGroup.prototype.removeLayer.call(this, layer);
6861
6862 // @event layerremove: LayerEvent
6863 // Fired when a layer is removed from this `FeatureGroup`
6864 return this.fire('layerremove', {layer: layer});
6865 },
6866
6867 // @method setStyle(style: Path options): this
6868 // Sets the given path options to each layer of the group that has a `setStyle` method.
6869 setStyle: function (style) {
6870 return this.invoke('setStyle', style);
6871 },
6872
6873 // @method bringToFront(): this
6874 // Brings the layer group to the top of all other layers
6875 bringToFront: function () {
6876 return this.invoke('bringToFront');
6877 },
6878
6879 // @method bringToBack(): this
6880 // Brings the layer group to the back of all other layers
6881 bringToBack: function () {
6882 return this.invoke('bringToBack');
6883 },
6884
6885 // @method getBounds(): LatLngBounds
6886 // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
6887 getBounds: function () {
6888 var bounds = new LatLngBounds();
6889
6890 for (var id in this._layers) {
6891 var layer = this._layers[id];
6892 bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
6893 }
6894 return bounds;
6895 }
6896 });
6897
6898 // @factory L.featureGroup(layers: Layer[])
6899 // Create a feature group, optionally given an initial set of layers.
6900 var featureGroup = function (layers) {
6901 return new FeatureGroup(layers);
6902 };
6903
6904 /*
6905 * @class Icon
6906 * @aka L.Icon
6907 *
6908 * Represents an icon to provide when creating a marker.
6909 *
6910 * @example
6911 *
6912 * ```js
6913 * var myIcon = L.icon({
6914 * iconUrl: 'my-icon.png',
6915 * iconRetinaUrl: 'my-icon@2x.png',
6916 * iconSize: [38, 95],
6917 * iconAnchor: [22, 94],
6918 * popupAnchor: [-3, -76],
6919 * shadowUrl: 'my-icon-shadow.png',
6920 * shadowRetinaUrl: 'my-icon-shadow@2x.png',
6921 * shadowSize: [68, 95],
6922 * shadowAnchor: [22, 94]
6923 * });
6924 *
6925 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
6926 * ```
6927 *
6928 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
6929 *
6930 */
6931
6932 var Icon = Class.extend({
6933
6934 /* @section
6935 * @aka Icon options
6936 *
6937 * @option iconUrl: String = null
6938 * **(required)** The URL to the icon image (absolute or relative to your script path).
6939 *
6940 * @option iconRetinaUrl: String = null
6941 * The URL to a retina sized version of the icon image (absolute or relative to your
6942 * script path). Used for Retina screen devices.
6943 *
6944 * @option iconSize: Point = null
6945 * Size of the icon image in pixels.
6946 *
6947 * @option iconAnchor: Point = null
6948 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
6949 * will be aligned so that this point is at the marker's geographical location. Centered
6950 * by default if size is specified, also can be set in CSS with negative margins.
6951 *
6952 * @option popupAnchor: Point = [0, 0]
6953 * The coordinates of the point from which popups will "open", relative to the icon anchor.
6954 *
6955 * @option tooltipAnchor: Point = [0, 0]
6956 * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
6957 *
6958 * @option shadowUrl: String = null
6959 * The URL to the icon shadow image. If not specified, no shadow image will be created.
6960 *
6961 * @option shadowRetinaUrl: String = null
6962 *
6963 * @option shadowSize: Point = null
6964 * Size of the shadow image in pixels.
6965 *
6966 * @option shadowAnchor: Point = null
6967 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
6968 * as iconAnchor if not specified).
6969 *
6970 * @option className: String = ''
6971 * A custom class name to assign to both icon and shadow images. Empty by default.
6972 */
6973
6974 options: {
6975 popupAnchor: [0, 0],
6976 tooltipAnchor: [0, 0],
6977 },
6978
6979 initialize: function (options) {
6980 setOptions(this, options);
6981 },
6982
6983 // @method createIcon(oldIcon?: HTMLElement): HTMLElement
6984 // Called internally when the icon has to be shown, returns a `<img>` HTML element
6985 // styled according to the options.
6986 createIcon: function (oldIcon) {
6987 return this._createIcon('icon', oldIcon);
6988 },
6989
6990 // @method createShadow(oldIcon?: HTMLElement): HTMLElement
6991 // As `createIcon`, but for the shadow beneath it.
6992 createShadow: function (oldIcon) {
6993 return this._createIcon('shadow', oldIcon);
6994 },
6995
6996 _createIcon: function (name, oldIcon) {
6997 var src = this._getIconUrl(name);
6998
6999 if (!src) {
7000 if (name === 'icon') {
7001 throw new Error('iconUrl not set in Icon options (see the docs).');
7002 }
7003 return null;
7004 }
7005
7006 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7007 this._setIconStyles(img, name);
7008
7009 return img;
7010 },
7011
7012 _setIconStyles: function (img, name) {
7013 var options = this.options;
7014 var sizeOption = options[name + 'Size'];
7015
7016 if (typeof sizeOption === 'number') {
7017 sizeOption = [sizeOption, sizeOption];
7018 }
7019
7020 var size = toPoint(sizeOption),
7021 anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7022 size && size.divideBy(2, true));
7023
7024 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7025
7026 if (anchor) {
7027 img.style.marginLeft = (-anchor.x) + 'px';
7028 img.style.marginTop = (-anchor.y) + 'px';
7029 }
7030
7031 if (size) {
7032 img.style.width = size.x + 'px';
7033 img.style.height = size.y + 'px';
7034 }
7035 },
7036
7037 _createImg: function (src, el) {
7038 el = el || document.createElement('img');
7039 el.src = src;
7040 return el;
7041 },
7042
7043 _getIconUrl: function (name) {
7044 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7045 }
7046 });
7047
7048
7049 // @factory L.icon(options: Icon options)
7050 // Creates an icon instance with the given options.
7051 function icon(options) {
7052 return new Icon(options);
7053 }
7054
7055 /*
7056 * @miniclass Icon.Default (Icon)
7057 * @aka L.Icon.Default
7058 * @section
7059 *
7060 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7061 * no icon is specified. Points to the blue marker image distributed with Leaflet
7062 * releases.
7063 *
7064 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7065 * (which is a set of `Icon options`).
7066 *
7067 * If you want to _completely_ replace the default icon, override the
7068 * `L.Marker.prototype.options.icon` with your own icon instead.
7069 */
7070
7071 var IconDefault = Icon.extend({
7072
7073 options: {
7074 iconUrl: 'marker-icon.png',
7075 iconRetinaUrl: 'marker-icon-2x.png',
7076 shadowUrl: 'marker-shadow.png',
7077 iconSize: [25, 41],
7078 iconAnchor: [12, 41],
7079 popupAnchor: [1, -34],
7080 tooltipAnchor: [16, -28],
7081 shadowSize: [41, 41]
7082 },
7083
7084 _getIconUrl: function (name) {
7085 if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only
7086 IconDefault.imagePath = this._detectIconPath();
7087 }
7088
7089 // @option imagePath: String
7090 // `Icon.Default` will try to auto-detect the location of the
7091 // blue icon images. If you are placing these images in a non-standard
7092 // way, set this option to point to the right path.
7093 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7094 },
7095
7096 _detectIconPath: function () {
7097 var el = create$1('div', 'leaflet-default-icon-path', document.body);
7098 var path = getStyle(el, 'background-image') ||
7099 getStyle(el, 'backgroundImage'); // IE8
7100
7101 document.body.removeChild(el);
7102
7103 if (path === null || path.indexOf('url') !== 0) {
7104 path = '';
7105 } else {
7106 path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
7107 }
7108
7109 return path;
7110 }
7111 });
7112
7113 /*
7114 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7115 */
7116
7117
7118 /* @namespace Marker
7119 * @section Interaction handlers
7120 *
7121 * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
7122 *
7123 * ```js
7124 * marker.dragging.disable();
7125 * ```
7126 *
7127 * @property dragging: Handler
7128 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7129 */
7130
7131 var MarkerDrag = Handler.extend({
7132 initialize: function (marker) {
7133 this._marker = marker;
7134 },
7135
7136 addHooks: function () {
7137 var icon = this._marker._icon;
7138
7139 if (!this._draggable) {
7140 this._draggable = new Draggable(icon, icon, true);
7141 }
7142
7143 this._draggable.on({
7144 dragstart: this._onDragStart,
7145 predrag: this._onPreDrag,
7146 drag: this._onDrag,
7147 dragend: this._onDragEnd
7148 }, this).enable();
7149
7150 addClass(icon, 'leaflet-marker-draggable');
7151 },
7152
7153 removeHooks: function () {
7154 this._draggable.off({
7155 dragstart: this._onDragStart,
7156 predrag: this._onPreDrag,
7157 drag: this._onDrag,
7158 dragend: this._onDragEnd
7159 }, this).disable();
7160
7161 if (this._marker._icon) {
7162 removeClass(this._marker._icon, 'leaflet-marker-draggable');
7163 }
7164 },
7165
7166 moved: function () {
7167 return this._draggable && this._draggable._moved;
7168 },
7169
7170 _adjustPan: function (e) {
7171 var marker = this._marker,
7172 map = marker._map,
7173 speed = this._marker.options.autoPanSpeed,
7174 padding = this._marker.options.autoPanPadding,
7175 iconPos = L.DomUtil.getPosition(marker._icon),
7176 bounds = map.getPixelBounds(),
7177 origin = map.getPixelOrigin();
7178
7179 var panBounds = toBounds(
7180 bounds.min._subtract(origin).add(padding),
7181 bounds.max._subtract(origin).subtract(padding)
7182 );
7183
7184 if (!panBounds.contains(iconPos)) {
7185 // Compute incremental movement
7186 var movement = toPoint(
7187 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7188 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7189
7190 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7191 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7192 ).multiplyBy(speed);
7193
7194 map.panBy(movement, {animate: false});
7195
7196 this._draggable._newPos._add(movement);
7197 this._draggable._startPos._add(movement);
7198
7199 L.DomUtil.setPosition(marker._icon, this._draggable._newPos);
7200 this._onDrag(e);
7201
7202 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7203 }
7204 },
7205
7206 _onDragStart: function () {
7207 // @section Dragging events
7208 // @event dragstart: Event
7209 // Fired when the user starts dragging the marker.
7210
7211 // @event movestart: Event
7212 // Fired when the marker starts moving (because of dragging).
7213
7214 this._oldLatLng = this._marker.getLatLng();
7215 this._marker
7216 .closePopup()
7217 .fire('movestart')
7218 .fire('dragstart');
7219 },
7220
7221 _onPreDrag: function (e) {
7222 if (this._marker.options.autoPan) {
7223 cancelAnimFrame(this._panRequest);
7224 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7225 }
7226 },
7227
7228 _onDrag: function (e) {
7229 var marker = this._marker,
7230 shadow = marker._shadow,
7231 iconPos = getPosition(marker._icon),
7232 latlng = marker._map.layerPointToLatLng(iconPos);
7233
7234 // update shadow position
7235 if (shadow) {
7236 setPosition(shadow, iconPos);
7237 }
7238
7239 marker._latlng = latlng;
7240 e.latlng = latlng;
7241 e.oldLatLng = this._oldLatLng;
7242
7243 // @event drag: Event
7244 // Fired repeatedly while the user drags the marker.
7245 marker
7246 .fire('move', e)
7247 .fire('drag', e);
7248 },
7249
7250 _onDragEnd: function (e) {
7251 // @event dragend: DragEndEvent
7252 // Fired when the user stops dragging the marker.
7253
7254 cancelAnimFrame(this._panRequest);
7255
7256 // @event moveend: Event
7257 // Fired when the marker stops moving (because of dragging).
7258 delete this._oldLatLng;
7259 this._marker
7260 .fire('moveend')
7261 .fire('dragend', e);
7262 }
7263 });
7264
7265 /*
7266 * @class Marker
7267 * @inherits Interactive layer
7268 * @aka L.Marker
7269 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7270 *
7271 * @example
7272 *
7273 * ```js
7274 * L.marker([50.5, 30.5]).addTo(map);
7275 * ```
7276 */
7277
7278 var Marker = Layer.extend({
7279
7280 // @section
7281 // @aka Marker options
7282 options: {
7283 // @option icon: Icon = *
7284 // Icon instance to use for rendering the marker.
7285 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7286 // If not specified, a common instance of `L.Icon.Default` is used.
7287 icon: new IconDefault(),
7288
7289 // Option inherited from "Interactive layer" abstract class
7290 interactive: true,
7291
7292 // @option draggable: Boolean = false
7293 // Whether the marker is draggable with mouse/touch or not.
7294 draggable: false,
7295
7296 // @option autoPan: Boolean = false
7297 // Set it to `true` if you want the map to do panning animation when marker hits the edges.
7298 autoPan: false,
7299
7300 // @option autoPanPadding: Point = Point(50, 50)
7301 // Equivalent of setting both top left and bottom right autopan padding to the same value.
7302 autoPanPadding: [50, 50],
7303
7304 // @option autoPanSpeed: Number = 10
7305 // Number of pixels the map should move by.
7306 autoPanSpeed: 10,
7307
7308 // @option keyboard: Boolean = true
7309 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7310 keyboard: true,
7311
7312 // @option title: String = ''
7313 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
7314 title: '',
7315
7316 // @option alt: String = ''
7317 // Text for the `alt` attribute of the icon image (useful for accessibility).
7318 alt: '',
7319
7320 // @option zIndexOffset: Number = 0
7321 // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
7322 zIndexOffset: 0,
7323
7324 // @option opacity: Number = 1.0
7325 // The opacity of the marker.
7326 opacity: 1,
7327
7328 // @option riseOnHover: Boolean = false
7329 // If `true`, the marker will get on top of others when you hover the mouse over it.
7330 riseOnHover: false,
7331
7332 // @option riseOffset: Number = 250
7333 // The z-index offset used for the `riseOnHover` feature.
7334 riseOffset: 250,
7335
7336 // @option pane: String = 'markerPane'
7337 // `Map pane` where the markers icon will be added.
7338 pane: 'markerPane',
7339
7340 // @option bubblingMouseEvents: Boolean = false
7341 // When `true`, a mouse event on this marker will trigger the same event on the map
7342 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7343 bubblingMouseEvents: false
7344 },
7345
7346 /* @section
7347 *
7348 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7349 */
7350
7351 initialize: function (latlng, options) {
7352 setOptions(this, options);
7353 this._latlng = toLatLng(latlng);
7354 },
7355
7356 onAdd: function (map) {
7357 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7358
7359 if (this._zoomAnimated) {
7360 map.on('zoomanim', this._animateZoom, this);
7361 }
7362
7363 this._initIcon();
7364 this.update();
7365 },
7366
7367 onRemove: function (map) {
7368 if (this.dragging && this.dragging.enabled()) {
7369 this.options.draggable = true;
7370 this.dragging.removeHooks();
7371 }
7372 delete this.dragging;
7373
7374 if (this._zoomAnimated) {
7375 map.off('zoomanim', this._animateZoom, this);
7376 }
7377
7378 this._removeIcon();
7379 this._removeShadow();
7380 },
7381
7382 getEvents: function () {
7383 return {
7384 zoom: this.update,
7385 viewreset: this.update
7386 };
7387 },
7388
7389 // @method getLatLng: LatLng
7390 // Returns the current geographical position of the marker.
7391 getLatLng: function () {
7392 return this._latlng;
7393 },
7394
7395 // @method setLatLng(latlng: LatLng): this
7396 // Changes the marker position to the given point.
7397 setLatLng: function (latlng) {
7398 var oldLatLng = this._latlng;
7399 this._latlng = toLatLng(latlng);
7400 this.update();
7401
7402 // @event move: Event
7403 // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7404 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7405 },
7406
7407 // @method setZIndexOffset(offset: Number): this
7408 // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7409 setZIndexOffset: function (offset) {
7410 this.options.zIndexOffset = offset;
7411 return this.update();
7412 },
7413
7414 // @method setIcon(icon: Icon): this
7415 // Changes the marker icon.
7416 setIcon: function (icon) {
7417
7418 this.options.icon = icon;
7419
7420 if (this._map) {
7421 this._initIcon();
7422 this.update();
7423 }
7424
7425 if (this._popup) {
7426 this.bindPopup(this._popup, this._popup.options);
7427 }
7428
7429 return this;
7430 },
7431
7432 getElement: function () {
7433 return this._icon;
7434 },
7435
7436 update: function () {
7437
7438 if (this._icon && this._map) {
7439 var pos = this._map.latLngToLayerPoint(this._latlng).round();
7440 this._setPos(pos);
7441 }
7442
7443 return this;
7444 },
7445
7446 _initIcon: function () {
7447 var options = this.options,
7448 classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7449
7450 var icon = options.icon.createIcon(this._icon),
7451 addIcon = false;
7452
7453 // if we're not reusing the icon, remove the old one and init new one
7454 if (icon !== this._icon) {
7455 if (this._icon) {
7456 this._removeIcon();
7457 }
7458 addIcon = true;
7459
7460 if (options.title) {
7461 icon.title = options.title;
7462 }
7463
7464 if (icon.tagName === 'IMG') {
7465 icon.alt = options.alt || '';
7466 }
7467 }
7468
7469 addClass(icon, classToAdd);
7470
7471 if (options.keyboard) {
7472 icon.tabIndex = '0';
7473 }
7474
7475 this._icon = icon;
7476
7477 if (options.riseOnHover) {
7478 this.on({
7479 mouseover: this._bringToFront,
7480 mouseout: this._resetZIndex
7481 });
7482 }
7483
7484 var newShadow = options.icon.createShadow(this._shadow),
7485 addShadow = false;
7486
7487 if (newShadow !== this._shadow) {
7488 this._removeShadow();
7489 addShadow = true;
7490 }
7491
7492 if (newShadow) {
7493 addClass(newShadow, classToAdd);
7494 newShadow.alt = '';
7495 }
7496 this._shadow = newShadow;
7497
7498
7499 if (options.opacity < 1) {
7500 this._updateOpacity();
7501 }
7502
7503
7504 if (addIcon) {
7505 this.getPane().appendChild(this._icon);
7506 }
7507 this._initInteraction();
7508 if (newShadow && addShadow) {
7509 this.getPane('shadowPane').appendChild(this._shadow);
7510 }
7511 },
7512
7513 _removeIcon: function () {
7514 if (this.options.riseOnHover) {
7515 this.off({
7516 mouseover: this._bringToFront,
7517 mouseout: this._resetZIndex
7518 });
7519 }
7520
7521 remove(this._icon);
7522 this.removeInteractiveTarget(this._icon);
7523
7524 this._icon = null;
7525 },
7526
7527 _removeShadow: function () {
7528 if (this._shadow) {
7529 remove(this._shadow);
7530 }
7531 this._shadow = null;
7532 },
7533
7534 _setPos: function (pos) {
7535 setPosition(this._icon, pos);
7536
7537 if (this._shadow) {
7538 setPosition(this._shadow, pos);
7539 }
7540
7541 this._zIndex = pos.y + this.options.zIndexOffset;
7542
7543 this._resetZIndex();
7544 },
7545
7546 _updateZIndex: function (offset) {
7547 this._icon.style.zIndex = this._zIndex + offset;
7548 },
7549
7550 _animateZoom: function (opt) {
7551 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7552
7553 this._setPos(pos);
7554 },
7555
7556 _initInteraction: function () {
7557
7558 if (!this.options.interactive) { return; }
7559
7560 addClass(this._icon, 'leaflet-interactive');
7561
7562 this.addInteractiveTarget(this._icon);
7563
7564 if (MarkerDrag) {
7565 var draggable = this.options.draggable;
7566 if (this.dragging) {
7567 draggable = this.dragging.enabled();
7568 this.dragging.disable();
7569 }
7570
7571 this.dragging = new MarkerDrag(this);
7572
7573 if (draggable) {
7574 this.dragging.enable();
7575 }
7576 }
7577 },
7578
7579 // @method setOpacity(opacity: Number): this
7580 // Changes the opacity of the marker.
7581 setOpacity: function (opacity) {
7582 this.options.opacity = opacity;
7583 if (this._map) {
7584 this._updateOpacity();
7585 }
7586
7587 return this;
7588 },
7589
7590 _updateOpacity: function () {
7591 var opacity = this.options.opacity;
7592
7593 setOpacity(this._icon, opacity);
7594
7595 if (this._shadow) {
7596 setOpacity(this._shadow, opacity);
7597 }
7598 },
7599
7600 _bringToFront: function () {
7601 this._updateZIndex(this.options.riseOffset);
7602 },
7603
7604 _resetZIndex: function () {
7605 this._updateZIndex(0);
7606 },
7607
7608 _getPopupAnchor: function () {
7609 return this.options.icon.options.popupAnchor;
7610 },
7611
7612 _getTooltipAnchor: function () {
7613 return this.options.icon.options.tooltipAnchor;
7614 }
7615 });
7616
7617
7618 // factory L.marker(latlng: LatLng, options? : Marker options)
7619
7620 // @factory L.marker(latlng: LatLng, options? : Marker options)
7621 // Instantiates a Marker object given a geographical point and optionally an options object.
7622 function marker(latlng, options) {
7623 return new Marker(latlng, options);
7624 }
7625
7626 /*
7627 * @class Path
7628 * @aka L.Path
7629 * @inherits Interactive layer
7630 *
7631 * An abstract class that contains options and constants shared between vector
7632 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7633 */
7634
7635 var Path = Layer.extend({
7636
7637 // @section
7638 // @aka Path options
7639 options: {
7640 // @option stroke: Boolean = true
7641 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7642 stroke: true,
7643
7644 // @option color: String = '#3388ff'
7645 // Stroke color
7646 color: '#3388ff',
7647
7648 // @option weight: Number = 3
7649 // Stroke width in pixels
7650 weight: 3,
7651
7652 // @option opacity: Number = 1.0
7653 // Stroke opacity
7654 opacity: 1,
7655
7656 // @option lineCap: String= 'round'
7657 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7658 lineCap: 'round',
7659
7660 // @option lineJoin: String = 'round'
7661 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7662 lineJoin: 'round',
7663
7664 // @option dashArray: String = null
7665 // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7666 dashArray: null,
7667
7668 // @option dashOffset: String = null
7669 // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7670 dashOffset: null,
7671
7672 // @option fill: Boolean = depends
7673 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7674 fill: false,
7675
7676 // @option fillColor: String = *
7677 // Fill color. Defaults to the value of the [`color`](#path-color) option
7678 fillColor: null,
7679
7680 // @option fillOpacity: Number = 0.2
7681 // Fill opacity.
7682 fillOpacity: 0.2,
7683
7684 // @option fillRule: String = 'evenodd'
7685 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7686 fillRule: 'evenodd',
7687
7688 // className: '',
7689
7690 // Option inherited from "Interactive layer" abstract class
7691 interactive: true,
7692
7693 // @option bubblingMouseEvents: Boolean = true
7694 // When `true`, a mouse event on this path will trigger the same event on the map
7695 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7696 bubblingMouseEvents: true
7697 },
7698
7699 beforeAdd: function (map) {
7700 // Renderer is set here because we need to call renderer.getEvents
7701 // before this.getEvents.
7702 this._renderer = map.getRenderer(this);
7703 },
7704
7705 onAdd: function () {
7706 this._renderer._initPath(this);
7707 this._reset();
7708 this._renderer._addPath(this);
7709 },
7710
7711 onRemove: function () {
7712 this._renderer._removePath(this);
7713 },
7714
7715 // @method redraw(): this
7716 // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7717 redraw: function () {
7718 if (this._map) {
7719 this._renderer._updatePath(this);
7720 }
7721 return this;
7722 },
7723
7724 // @method setStyle(style: Path options): this
7725 // Changes the appearance of a Path based on the options in the `Path options` object.
7726 setStyle: function (style) {
7727 setOptions(this, style);
7728 if (this._renderer) {
7729 this._renderer._updateStyle(this);
7730 }
7731 return this;
7732 },
7733
7734 // @method bringToFront(): this
7735 // Brings the layer to the top of all path layers.
7736 bringToFront: function () {
7737 if (this._renderer) {
7738 this._renderer._bringToFront(this);
7739 }
7740 return this;
7741 },
7742
7743 // @method bringToBack(): this
7744 // Brings the layer to the bottom of all path layers.
7745 bringToBack: function () {
7746 if (this._renderer) {
7747 this._renderer._bringToBack(this);
7748 }
7749 return this;
7750 },
7751
7752 getElement: function () {
7753 return this._path;
7754 },
7755
7756 _reset: function () {
7757 // defined in child classes
7758 this._project();
7759 this._update();
7760 },
7761
7762 _clickTolerance: function () {
7763 // used when doing hit detection for Canvas layers
7764 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
7765 }
7766 });
7767
7768 /*
7769 * @class CircleMarker
7770 * @aka L.CircleMarker
7771 * @inherits Path
7772 *
7773 * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7774 */
7775
7776 var CircleMarker = Path.extend({
7777
7778 // @section
7779 // @aka CircleMarker options
7780 options: {
7781 fill: true,
7782
7783 // @option radius: Number = 10
7784 // Radius of the circle marker, in pixels
7785 radius: 10
7786 },
7787
7788 initialize: function (latlng, options) {
7789 setOptions(this, options);
7790 this._latlng = toLatLng(latlng);
7791 this._radius = this.options.radius;
7792 },
7793
7794 // @method setLatLng(latLng: LatLng): this
7795 // Sets the position of a circle marker to a new location.
7796 setLatLng: function (latlng) {
7797 this._latlng = toLatLng(latlng);
7798 this.redraw();
7799 return this.fire('move', {latlng: this._latlng});
7800 },
7801
7802 // @method getLatLng(): LatLng
7803 // Returns the current geographical position of the circle marker
7804 getLatLng: function () {
7805 return this._latlng;
7806 },
7807
7808 // @method setRadius(radius: Number): this
7809 // Sets the radius of a circle marker. Units are in pixels.
7810 setRadius: function (radius) {
7811 this.options.radius = this._radius = radius;
7812 return this.redraw();
7813 },
7814
7815 // @method getRadius(): Number
7816 // Returns the current radius of the circle
7817 getRadius: function () {
7818 return this._radius;
7819 },
7820
7821 setStyle : function (options) {
7822 var radius = options && options.radius || this._radius;
7823 Path.prototype.setStyle.call(this, options);
7824 this.setRadius(radius);
7825 return this;
7826 },
7827
7828 _project: function () {
7829 this._point = this._map.latLngToLayerPoint(this._latlng);
7830 this._updateBounds();
7831 },
7832
7833 _updateBounds: function () {
7834 var r = this._radius,
7835 r2 = this._radiusY || r,
7836 w = this._clickTolerance(),
7837 p = [r + w, r2 + w];
7838 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7839 },
7840
7841 _update: function () {
7842 if (this._map) {
7843 this._updatePath();
7844 }
7845 },
7846
7847 _updatePath: function () {
7848 this._renderer._updateCircle(this);
7849 },
7850
7851 _empty: function () {
7852 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
7853 },
7854
7855 // Needed by the `Canvas` renderer for interactivity
7856 _containsPoint: function (p) {
7857 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
7858 }
7859 });
7860
7861
7862 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
7863 // Instantiates a circle marker object given a geographical point, and an optional options object.
7864 function circleMarker(latlng, options) {
7865 return new CircleMarker(latlng, options);
7866 }
7867
7868 /*
7869 * @class Circle
7870 * @aka L.Circle
7871 * @inherits CircleMarker
7872 *
7873 * A class for drawing circle overlays on a map. Extends `CircleMarker`.
7874 *
7875 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
7876 *
7877 * @example
7878 *
7879 * ```js
7880 * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
7881 * ```
7882 */
7883
7884 var Circle = CircleMarker.extend({
7885
7886 initialize: function (latlng, options, legacyOptions) {
7887 if (typeof options === 'number') {
7888 // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
7889 options = extend({}, legacyOptions, {radius: options});
7890 }
7891 setOptions(this, options);
7892 this._latlng = toLatLng(latlng);
7893
7894 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
7895
7896 // @section
7897 // @aka Circle options
7898 // @option radius: Number; Radius of the circle, in meters.
7899 this._mRadius = this.options.radius;
7900 },
7901
7902 // @method setRadius(radius: Number): this
7903 // Sets the radius of a circle. Units are in meters.
7904 setRadius: function (radius) {
7905 this._mRadius = radius;
7906 return this.redraw();
7907 },
7908
7909 // @method getRadius(): Number
7910 // Returns the current radius of a circle. Units are in meters.
7911 getRadius: function () {
7912 return this._mRadius;
7913 },
7914
7915 // @method getBounds(): LatLngBounds
7916 // Returns the `LatLngBounds` of the path.
7917 getBounds: function () {
7918 var half = [this._radius, this._radiusY || this._radius];
7919
7920 return new LatLngBounds(
7921 this._map.layerPointToLatLng(this._point.subtract(half)),
7922 this._map.layerPointToLatLng(this._point.add(half)));
7923 },
7924
7925 setStyle: Path.prototype.setStyle,
7926
7927 _project: function () {
7928
7929 var lng = this._latlng.lng,
7930 lat = this._latlng.lat,
7931 map = this._map,
7932 crs = map.options.crs;
7933
7934 if (crs.distance === Earth.distance) {
7935 var d = Math.PI / 180,
7936 latR = (this._mRadius / Earth.R) / d,
7937 top = map.project([lat + latR, lng]),
7938 bottom = map.project([lat - latR, lng]),
7939 p = top.add(bottom).divideBy(2),
7940 lat2 = map.unproject(p).lat,
7941 lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
7942 (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
7943
7944 if (isNaN(lngR) || lngR === 0) {
7945 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
7946 }
7947
7948 this._point = p.subtract(map.getPixelOrigin());
7949 this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
7950 this._radiusY = p.y - top.y;
7951
7952 } else {
7953 var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
7954
7955 this._point = map.latLngToLayerPoint(this._latlng);
7956 this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
7957 }
7958
7959 this._updateBounds();
7960 }
7961 });
7962
7963 // @factory L.circle(latlng: LatLng, options?: Circle options)
7964 // Instantiates a circle object given a geographical point, and an options object
7965 // which contains the circle radius.
7966 // @alternative
7967 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
7968 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
7969 // Do not use in new applications or plugins.
7970 function circle(latlng, options, legacyOptions) {
7971 return new Circle(latlng, options, legacyOptions);
7972 }
7973
7974 /*
7975 * @class Polyline
7976 * @aka L.Polyline
7977 * @inherits Path
7978 *
7979 * A class for drawing polyline overlays on a map. Extends `Path`.
7980 *
7981 * @example
7982 *
7983 * ```js
7984 * // create a red polyline from an array of LatLng points
7985 * var latlngs = [
7986 * [45.51, -122.68],
7987 * [37.77, -122.43],
7988 * [34.04, -118.2]
7989 * ];
7990 *
7991 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
7992 *
7993 * // zoom the map to the polyline
7994 * map.fitBounds(polyline.getBounds());
7995 * ```
7996 *
7997 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
7998 *
7999 * ```js
8000 * // create a red polyline from an array of arrays of LatLng points
8001 * var latlngs = [
8002 * [[45.51, -122.68],
8003 * [37.77, -122.43],
8004 * [34.04, -118.2]],
8005 * [[40.78, -73.91],
8006 * [41.83, -87.62],
8007 * [32.76, -96.72]]
8008 * ];
8009 * ```
8010 */
8011
8012
8013 var Polyline = Path.extend({
8014
8015 // @section
8016 // @aka Polyline options
8017 options: {
8018 // @option smoothFactor: Number = 1.0
8019 // How much to simplify the polyline on each zoom level. More means
8020 // better performance and smoother look, and less means more accurate representation.
8021 smoothFactor: 1.0,
8022
8023 // @option noClip: Boolean = false
8024 // Disable polyline clipping.
8025 noClip: false
8026 },
8027
8028 initialize: function (latlngs, options) {
8029 setOptions(this, options);
8030 this._setLatLngs(latlngs);
8031 },
8032
8033 // @method getLatLngs(): LatLng[]
8034 // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8035 getLatLngs: function () {
8036 return this._latlngs;
8037 },
8038
8039 // @method setLatLngs(latlngs: LatLng[]): this
8040 // Replaces all the points in the polyline with the given array of geographical points.
8041 setLatLngs: function (latlngs) {
8042 this._setLatLngs(latlngs);
8043 return this.redraw();
8044 },
8045
8046 // @method isEmpty(): Boolean
8047 // Returns `true` if the Polyline has no LatLngs.
8048 isEmpty: function () {
8049 return !this._latlngs.length;
8050 },
8051
8052 // @method closestLayerPoint: Point
8053 // Returns the point closest to `p` on the Polyline.
8054 closestLayerPoint: function (p) {
8055 var minDistance = Infinity,
8056 minPoint = null,
8057 closest = _sqClosestPointOnSegment,
8058 p1, p2;
8059
8060 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8061 var points = this._parts[j];
8062
8063 for (var i = 1, len = points.length; i < len; i++) {
8064 p1 = points[i - 1];
8065 p2 = points[i];
8066
8067 var sqDist = closest(p, p1, p2, true);
8068
8069 if (sqDist < minDistance) {
8070 minDistance = sqDist;
8071 minPoint = closest(p, p1, p2);
8072 }
8073 }
8074 }
8075 if (minPoint) {
8076 minPoint.distance = Math.sqrt(minDistance);
8077 }
8078 return minPoint;
8079 },
8080
8081 // @method getCenter(): LatLng
8082 // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
8083 getCenter: function () {
8084 // throws error when not yet added to map as this center calculation requires projected coordinates
8085 if (!this._map) {
8086 throw new Error('Must add layer to map before using getCenter()');
8087 }
8088
8089 var i, halfDist, segDist, dist, p1, p2, ratio,
8090 points = this._rings[0],
8091 len = points.length;
8092
8093 if (!len) { return null; }
8094
8095 // polyline centroid algorithm; only uses the first ring if there are multiple
8096
8097 for (i = 0, halfDist = 0; i < len - 1; i++) {
8098 halfDist += points[i].distanceTo(points[i + 1]) / 2;
8099 }
8100
8101 // The line is so small in the current view that all points are on the same pixel.
8102 if (halfDist === 0) {
8103 return this._map.layerPointToLatLng(points[0]);
8104 }
8105
8106 for (i = 0, dist = 0; i < len - 1; i++) {
8107 p1 = points[i];
8108 p2 = points[i + 1];
8109 segDist = p1.distanceTo(p2);
8110 dist += segDist;
8111
8112 if (dist > halfDist) {
8113 ratio = (dist - halfDist) / segDist;
8114 return this._map.layerPointToLatLng([
8115 p2.x - ratio * (p2.x - p1.x),
8116 p2.y - ratio * (p2.y - p1.y)
8117 ]);
8118 }
8119 }
8120 },
8121
8122 // @method getBounds(): LatLngBounds
8123 // Returns the `LatLngBounds` of the path.
8124 getBounds: function () {
8125 return this._bounds;
8126 },
8127
8128 // @method addLatLng(latlng: LatLng, latlngs? LatLng[]): this
8129 // Adds a given point to the polyline. By default, adds to the first ring of
8130 // the polyline in case of a multi-polyline, but can be overridden by passing
8131 // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8132 addLatLng: function (latlng, latlngs) {
8133 latlngs = latlngs || this._defaultShape();
8134 latlng = toLatLng(latlng);
8135 latlngs.push(latlng);
8136 this._bounds.extend(latlng);
8137 return this.redraw();
8138 },
8139
8140 _setLatLngs: function (latlngs) {
8141 this._bounds = new LatLngBounds();
8142 this._latlngs = this._convertLatLngs(latlngs);
8143 },
8144
8145 _defaultShape: function () {
8146 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8147 },
8148
8149 // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8150 _convertLatLngs: function (latlngs) {
8151 var result = [],
8152 flat = isFlat(latlngs);
8153
8154 for (var i = 0, len = latlngs.length; i < len; i++) {
8155 if (flat) {
8156 result[i] = toLatLng(latlngs[i]);
8157 this._bounds.extend(result[i]);
8158 } else {
8159 result[i] = this._convertLatLngs(latlngs[i]);
8160 }
8161 }
8162
8163 return result;
8164 },
8165
8166 _project: function () {
8167 var pxBounds = new Bounds();
8168 this._rings = [];
8169 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8170
8171 var w = this._clickTolerance(),
8172 p = new Point(w, w);
8173
8174 if (this._bounds.isValid() && pxBounds.isValid()) {
8175 pxBounds.min._subtract(p);
8176 pxBounds.max._add(p);
8177 this._pxBounds = pxBounds;
8178 }
8179 },
8180
8181 // recursively turns latlngs into a set of rings with projected coordinates
8182 _projectLatlngs: function (latlngs, result, projectedBounds) {
8183 var flat = latlngs[0] instanceof LatLng,
8184 len = latlngs.length,
8185 i, ring;
8186
8187 if (flat) {
8188 ring = [];
8189 for (i = 0; i < len; i++) {
8190 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8191 projectedBounds.extend(ring[i]);
8192 }
8193 result.push(ring);
8194 } else {
8195 for (i = 0; i < len; i++) {
8196 this._projectLatlngs(latlngs[i], result, projectedBounds);
8197 }
8198 }
8199 },
8200
8201 // clip polyline by renderer bounds so that we have less to render for performance
8202 _clipPoints: function () {
8203 var bounds = this._renderer._bounds;
8204
8205 this._parts = [];
8206 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8207 return;
8208 }
8209
8210 if (this.options.noClip) {
8211 this._parts = this._rings;
8212 return;
8213 }
8214
8215 var parts = this._parts,
8216 i, j, k, len, len2, segment, points;
8217
8218 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8219 points = this._rings[i];
8220
8221 for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8222 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8223
8224 if (!segment) { continue; }
8225
8226 parts[k] = parts[k] || [];
8227 parts[k].push(segment[0]);
8228
8229 // if segment goes out of screen, or it's the last one, it's the end of the line part
8230 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8231 parts[k].push(segment[1]);
8232 k++;
8233 }
8234 }
8235 }
8236 },
8237
8238 // simplify each clipped part of the polyline for performance
8239 _simplifyPoints: function () {
8240 var parts = this._parts,
8241 tolerance = this.options.smoothFactor;
8242
8243 for (var i = 0, len = parts.length; i < len; i++) {
8244 parts[i] = simplify(parts[i], tolerance);
8245 }
8246 },
8247
8248 _update: function () {
8249 if (!this._map) { return; }
8250
8251 this._clipPoints();
8252 this._simplifyPoints();
8253 this._updatePath();
8254 },
8255
8256 _updatePath: function () {
8257 this._renderer._updatePoly(this);
8258 },
8259
8260 // Needed by the `Canvas` renderer for interactivity
8261 _containsPoint: function (p, closed) {
8262 var i, j, k, len, len2, part,
8263 w = this._clickTolerance();
8264
8265 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8266
8267 // hit detection for polylines
8268 for (i = 0, len = this._parts.length; i < len; i++) {
8269 part = this._parts[i];
8270
8271 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8272 if (!closed && (j === 0)) { continue; }
8273
8274 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8275 return true;
8276 }
8277 }
8278 }
8279 return false;
8280 }
8281 });
8282
8283 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8284 // Instantiates a polyline object given an array of geographical points and
8285 // optionally an options object. You can create a `Polyline` object with
8286 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
8287 // of geographic points.
8288 function polyline(latlngs, options) {
8289 return new Polyline(latlngs, options);
8290 }
8291
8292 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8293 Polyline._flat = _flat;
8294
8295 /*
8296 * @class Polygon
8297 * @aka L.Polygon
8298 * @inherits Polyline
8299 *
8300 * A class for drawing polygon overlays on a map. Extends `Polyline`.
8301 *
8302 * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
8303 *
8304 *
8305 * @example
8306 *
8307 * ```js
8308 * // create a red polygon from an array of LatLng points
8309 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8310 *
8311 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8312 *
8313 * // zoom the map to the polygon
8314 * map.fitBounds(polygon.getBounds());
8315 * ```
8316 *
8317 * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
8318 *
8319 * ```js
8320 * var latlngs = [
8321 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8322 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8323 * ];
8324 * ```
8325 *
8326 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8327 *
8328 * ```js
8329 * var latlngs = [
8330 * [ // first polygon
8331 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8332 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8333 * ],
8334 * [ // second polygon
8335 * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8336 * ]
8337 * ];
8338 * ```
8339 */
8340
8341 var Polygon = Polyline.extend({
8342
8343 options: {
8344 fill: true
8345 },
8346
8347 isEmpty: function () {
8348 return !this._latlngs.length || !this._latlngs[0].length;
8349 },
8350
8351 getCenter: function () {
8352 // throws error when not yet added to map as this center calculation requires projected coordinates
8353 if (!this._map) {
8354 throw new Error('Must add layer to map before using getCenter()');
8355 }
8356
8357 var i, j, p1, p2, f, area, x, y, center,
8358 points = this._rings[0],
8359 len = points.length;
8360
8361 if (!len) { return null; }
8362
8363 // polygon centroid algorithm; only uses the first ring if there are multiple
8364
8365 area = x = y = 0;
8366
8367 for (i = 0, j = len - 1; i < len; j = i++) {
8368 p1 = points[i];
8369 p2 = points[j];
8370
8371 f = p1.y * p2.x - p2.y * p1.x;
8372 x += (p1.x + p2.x) * f;
8373 y += (p1.y + p2.y) * f;
8374 area += f * 3;
8375 }
8376
8377 if (area === 0) {
8378 // Polygon is so small that all points are on same pixel.
8379 center = points[0];
8380 } else {
8381 center = [x / area, y / area];
8382 }
8383 return this._map.layerPointToLatLng(center);
8384 },
8385
8386 _convertLatLngs: function (latlngs) {
8387 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8388 len = result.length;
8389
8390 // remove last point if it equals first one
8391 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8392 result.pop();
8393 }
8394 return result;
8395 },
8396
8397 _setLatLngs: function (latlngs) {
8398 Polyline.prototype._setLatLngs.call(this, latlngs);
8399 if (isFlat(this._latlngs)) {
8400 this._latlngs = [this._latlngs];
8401 }
8402 },
8403
8404 _defaultShape: function () {
8405 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8406 },
8407
8408 _clipPoints: function () {
8409 // polygons need a different clipping algorithm so we redefine that
8410
8411 var bounds = this._renderer._bounds,
8412 w = this.options.weight,
8413 p = new Point(w, w);
8414
8415 // increase clip padding by stroke width to avoid stroke on clip edges
8416 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8417
8418 this._parts = [];
8419 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8420 return;
8421 }
8422
8423 if (this.options.noClip) {
8424 this._parts = this._rings;
8425 return;
8426 }
8427
8428 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8429 clipped = clipPolygon(this._rings[i], bounds, true);
8430 if (clipped.length) {
8431 this._parts.push(clipped);
8432 }
8433 }
8434 },
8435
8436 _updatePath: function () {
8437 this._renderer._updatePoly(this, true);
8438 },
8439
8440 // Needed by the `Canvas` renderer for interactivity
8441 _containsPoint: function (p) {
8442 var inside = false,
8443 part, p1, p2, i, j, k, len, len2;
8444
8445 if (!this._pxBounds.contains(p)) { return false; }
8446
8447 // ray casting algorithm for detecting if point is in polygon
8448 for (i = 0, len = this._parts.length; i < len; i++) {
8449 part = this._parts[i];
8450
8451 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8452 p1 = part[j];
8453 p2 = part[k];
8454
8455 if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
8456 inside = !inside;
8457 }
8458 }
8459 }
8460
8461 // also check if it's on polygon stroke
8462 return inside || Polyline.prototype._containsPoint.call(this, p, true);
8463 }
8464
8465 });
8466
8467
8468 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8469 function polygon(latlngs, options) {
8470 return new Polygon(latlngs, options);
8471 }
8472
8473 /*
8474 * @class GeoJSON
8475 * @aka L.GeoJSON
8476 * @inherits FeatureGroup
8477 *
8478 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8479 * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8480 *
8481 * @example
8482 *
8483 * ```js
8484 * L.geoJSON(data, {
8485 * style: function (feature) {
8486 * return {color: feature.properties.color};
8487 * }
8488 * }).bindPopup(function (layer) {
8489 * return layer.feature.properties.description;
8490 * }).addTo(map);
8491 * ```
8492 */
8493
8494 var GeoJSON = FeatureGroup.extend({
8495
8496 /* @section
8497 * @aka GeoJSON options
8498 *
8499 * @option pointToLayer: Function = *
8500 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8501 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8502 * The default is to spawn a default `Marker`:
8503 * ```js
8504 * function(geoJsonPoint, latlng) {
8505 * return L.marker(latlng);
8506 * }
8507 * ```
8508 *
8509 * @option style: Function = *
8510 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8511 * called internally when data is added.
8512 * The default value is to not override any defaults:
8513 * ```js
8514 * function (geoJsonFeature) {
8515 * return {}
8516 * }
8517 * ```
8518 *
8519 * @option onEachFeature: Function = *
8520 * A `Function` that will be called once for each created `Feature`, after it has
8521 * been created and styled. Useful for attaching events and popups to features.
8522 * The default is to do nothing with the newly created layers:
8523 * ```js
8524 * function (feature, layer) {}
8525 * ```
8526 *
8527 * @option filter: Function = *
8528 * A `Function` that will be used to decide whether to include a feature or not.
8529 * The default is to include all features:
8530 * ```js
8531 * function (geoJsonFeature) {
8532 * return true;
8533 * }
8534 * ```
8535 * Note: dynamically changing the `filter` option will have effect only on newly
8536 * added data. It will _not_ re-evaluate already included features.
8537 *
8538 * @option coordsToLatLng: Function = *
8539 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8540 * The default is the `coordsToLatLng` static method.
8541 */
8542
8543 initialize: function (geojson, options) {
8544 setOptions(this, options);
8545
8546 this._layers = {};
8547
8548 if (geojson) {
8549 this.addData(geojson);
8550 }
8551 },
8552
8553 // @method addData( <GeoJSON> data ): this
8554 // Adds a GeoJSON object to the layer.
8555 addData: function (geojson) {
8556 var features = isArray(geojson) ? geojson : geojson.features,
8557 i, len, feature;
8558
8559 if (features) {
8560 for (i = 0, len = features.length; i < len; i++) {
8561 // only add this if geometry or geometries are set and not null
8562 feature = features[i];
8563 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8564 this.addData(feature);
8565 }
8566 }
8567 return this;
8568 }
8569
8570 var options = this.options;
8571
8572 if (options.filter && !options.filter(geojson)) { return this; }
8573
8574 var layer = geometryToLayer(geojson, options);
8575 if (!layer) {
8576 return this;
8577 }
8578 layer.feature = asFeature(geojson);
8579
8580 layer.defaultOptions = layer.options;
8581 this.resetStyle(layer);
8582
8583 if (options.onEachFeature) {
8584 options.onEachFeature(geojson, layer);
8585 }
8586
8587 return this.addLayer(layer);
8588 },
8589
8590 // @method resetStyle( <Path> layer ): this
8591 // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8592 resetStyle: function (layer) {
8593 // reset any custom styles
8594 layer.options = extend({}, layer.defaultOptions);
8595 this._setLayerStyle(layer, this.options.style);
8596 return this;
8597 },
8598
8599 // @method setStyle( <Function> style ): this
8600 // Changes styles of GeoJSON vector layers with the given style function.
8601 setStyle: function (style) {
8602 return this.eachLayer(function (layer) {
8603 this._setLayerStyle(layer, style);
8604 }, this);
8605 },
8606
8607 _setLayerStyle: function (layer, style) {
8608 if (typeof style === 'function') {
8609 style = style(layer.feature);
8610 }
8611 if (layer.setStyle) {
8612 layer.setStyle(style);
8613 }
8614 }
8615 });
8616
8617 // @section
8618 // There are several static functions which can be called without instantiating L.GeoJSON:
8619
8620 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8621 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
8622 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8623 // functions if provided as options.
8624 function geometryToLayer(geojson, options) {
8625
8626 var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8627 coords = geometry ? geometry.coordinates : null,
8628 layers = [],
8629 pointToLayer = options && options.pointToLayer,
8630 _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8631 latlng, latlngs, i, len;
8632
8633 if (!coords && !geometry) {
8634 return null;
8635 }
8636
8637 switch (geometry.type) {
8638 case 'Point':
8639 latlng = _coordsToLatLng(coords);
8640 return pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng);
8641
8642 case 'MultiPoint':
8643 for (i = 0, len = coords.length; i < len; i++) {
8644 latlng = _coordsToLatLng(coords[i]);
8645 layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new Marker(latlng));
8646 }
8647 return new FeatureGroup(layers);
8648
8649 case 'LineString':
8650 case 'MultiLineString':
8651 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8652 return new Polyline(latlngs, options);
8653
8654 case 'Polygon':
8655 case 'MultiPolygon':
8656 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8657 return new Polygon(latlngs, options);
8658
8659 case 'GeometryCollection':
8660 for (i = 0, len = geometry.geometries.length; i < len; i++) {
8661 var layer = geometryToLayer({
8662 geometry: geometry.geometries[i],
8663 type: 'Feature',
8664 properties: geojson.properties
8665 }, options);
8666
8667 if (layer) {
8668 layers.push(layer);
8669 }
8670 }
8671 return new FeatureGroup(layers);
8672
8673 default:
8674 throw new Error('Invalid GeoJSON object.');
8675 }
8676 }
8677
8678 // @function coordsToLatLng(coords: Array): LatLng
8679 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8680 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8681 function coordsToLatLng(coords) {
8682 return new LatLng(coords[1], coords[0], coords[2]);
8683 }
8684
8685 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8686 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8687 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8688 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8689 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8690 var latlngs = [];
8691
8692 for (var i = 0, len = coords.length, latlng; i < len; i++) {
8693 latlng = levelsDeep ?
8694 coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8695 (_coordsToLatLng || coordsToLatLng)(coords[i]);
8696
8697 latlngs.push(latlng);
8698 }
8699
8700 return latlngs;
8701 }
8702
8703 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8704 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8705 function latLngToCoords(latlng, precision) {
8706 precision = typeof precision === 'number' ? precision : 6;
8707 return latlng.alt !== undefined ?
8708 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8709 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8710 }
8711
8712 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8713 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8714 // `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
8715 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8716 var coords = [];
8717
8718 for (var i = 0, len = latlngs.length; i < len; i++) {
8719 coords.push(levelsDeep ?
8720 latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8721 latLngToCoords(latlngs[i], precision));
8722 }
8723
8724 if (!levelsDeep && closed) {
8725 coords.push(coords[0]);
8726 }
8727
8728 return coords;
8729 }
8730
8731 function getFeature(layer, newGeometry) {
8732 return layer.feature ?
8733 extend({}, layer.feature, {geometry: newGeometry}) :
8734 asFeature(newGeometry);
8735 }
8736
8737 // @function asFeature(geojson: Object): Object
8738 // Normalize GeoJSON geometries/features into GeoJSON features.
8739 function asFeature(geojson) {
8740 if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8741 return geojson;
8742 }
8743
8744 return {
8745 type: 'Feature',
8746 properties: {},
8747 geometry: geojson
8748 };
8749 }
8750
8751 var PointToGeoJSON = {
8752 toGeoJSON: function (precision) {
8753 return getFeature(this, {
8754 type: 'Point',
8755 coordinates: latLngToCoords(this.getLatLng(), precision)
8756 });
8757 }
8758 };
8759
8760 // @namespace Marker
8761 // @method toGeoJSON(): Object
8762 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8763 Marker.include(PointToGeoJSON);
8764
8765 // @namespace CircleMarker
8766 // @method toGeoJSON(): Object
8767 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8768 Circle.include(PointToGeoJSON);
8769 CircleMarker.include(PointToGeoJSON);
8770
8771
8772 // @namespace Polyline
8773 // @method toGeoJSON(): Object
8774 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8775 Polyline.include({
8776 toGeoJSON: function (precision) {
8777 var multi = !isFlat(this._latlngs);
8778
8779 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8780
8781 return getFeature(this, {
8782 type: (multi ? 'Multi' : '') + 'LineString',
8783 coordinates: coords
8784 });
8785 }
8786 });
8787
8788 // @namespace Polygon
8789 // @method toGeoJSON(): Object
8790 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8791 Polygon.include({
8792 toGeoJSON: function (precision) {
8793 var holes = !isFlat(this._latlngs),
8794 multi = holes && !isFlat(this._latlngs[0]);
8795
8796 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8797
8798 if (!holes) {
8799 coords = [coords];
8800 }
8801
8802 return getFeature(this, {
8803 type: (multi ? 'Multi' : '') + 'Polygon',
8804 coordinates: coords
8805 });
8806 }
8807 });
8808
8809
8810 // @namespace LayerGroup
8811 LayerGroup.include({
8812 toMultiPoint: function (precision) {
8813 var coords = [];
8814
8815 this.eachLayer(function (layer) {
8816 coords.push(layer.toGeoJSON(precision).geometry.coordinates);
8817 });
8818
8819 return getFeature(this, {
8820 type: 'MultiPoint',
8821 coordinates: coords
8822 });
8823 },
8824
8825 // @method toGeoJSON(): Object
8826 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
8827 toGeoJSON: function (precision) {
8828
8829 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
8830
8831 if (type === 'MultiPoint') {
8832 return this.toMultiPoint(precision);
8833 }
8834
8835 var isGeometryCollection = type === 'GeometryCollection',
8836 jsons = [];
8837
8838 this.eachLayer(function (layer) {
8839 if (layer.toGeoJSON) {
8840 var json = layer.toGeoJSON(precision);
8841 if (isGeometryCollection) {
8842 jsons.push(json.geometry);
8843 } else {
8844 var feature = asFeature(json);
8845 // Squash nested feature collections
8846 if (feature.type === 'FeatureCollection') {
8847 jsons.push.apply(jsons, feature.features);
8848 } else {
8849 jsons.push(feature);
8850 }
8851 }
8852 }
8853 });
8854
8855 if (isGeometryCollection) {
8856 return getFeature(this, {
8857 geometries: jsons,
8858 type: 'GeometryCollection'
8859 });
8860 }
8861
8862 return {
8863 type: 'FeatureCollection',
8864 features: jsons
8865 };
8866 }
8867 });
8868
8869 // @namespace GeoJSON
8870 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
8871 // Creates a GeoJSON layer. Optionally accepts an object in
8872 // [GeoJSON format](http://geojson.org/geojson-spec.html) to display on the map
8873 // (you can alternatively add it later with `addData` method) and an `options` object.
8874 function geoJSON(geojson, options) {
8875 return new GeoJSON(geojson, options);
8876 }
8877
8878 // Backward compatibility.
8879 var geoJson = geoJSON;
8880
8881 /*
8882 * @class ImageOverlay
8883 * @aka L.ImageOverlay
8884 * @inherits Interactive layer
8885 *
8886 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
8887 *
8888 * @example
8889 *
8890 * ```js
8891 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
8892 * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
8893 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
8894 * ```
8895 */
8896
8897 var ImageOverlay = Layer.extend({
8898
8899 // @section
8900 // @aka ImageOverlay options
8901 options: {
8902 // @option opacity: Number = 1.0
8903 // The opacity of the image overlay.
8904 opacity: 1,
8905
8906 // @option alt: String = ''
8907 // Text for the `alt` attribute of the image (useful for accessibility).
8908 alt: '',
8909
8910 // @option interactive: Boolean = false
8911 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
8912 interactive: false,
8913
8914 // @option crossOrigin: Boolean = false
8915 // If true, the image will have its crossOrigin attribute set to ''. This is needed if you want to access image pixel data.
8916 crossOrigin: false,
8917
8918 // @option errorOverlayUrl: String = ''
8919 // URL to the overlay image to show in place of the overlay that failed to load.
8920 errorOverlayUrl: '',
8921
8922 // @option zIndex: Number = 1
8923 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the tile layer.
8924 zIndex: 1,
8925
8926 // @option className: String = ''
8927 // A custom class name to assign to the image. Empty by default.
8928 className: '',
8929 },
8930
8931 initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
8932 this._url = url;
8933 this._bounds = toLatLngBounds(bounds);
8934
8935 setOptions(this, options);
8936 },
8937
8938 onAdd: function () {
8939 if (!this._image) {
8940 this._initImage();
8941
8942 if (this.options.opacity < 1) {
8943 this._updateOpacity();
8944 }
8945 }
8946
8947 if (this.options.interactive) {
8948 addClass(this._image, 'leaflet-interactive');
8949 this.addInteractiveTarget(this._image);
8950 }
8951
8952 this.getPane().appendChild(this._image);
8953 this._reset();
8954 },
8955
8956 onRemove: function () {
8957 remove(this._image);
8958 if (this.options.interactive) {
8959 this.removeInteractiveTarget(this._image);
8960 }
8961 },
8962
8963 // @method setOpacity(opacity: Number): this
8964 // Sets the opacity of the overlay.
8965 setOpacity: function (opacity) {
8966 this.options.opacity = opacity;
8967
8968 if (this._image) {
8969 this._updateOpacity();
8970 }
8971 return this;
8972 },
8973
8974 setStyle: function (styleOpts) {
8975 if (styleOpts.opacity) {
8976 this.setOpacity(styleOpts.opacity);
8977 }
8978 return this;
8979 },
8980
8981 // @method bringToFront(): this
8982 // Brings the layer to the top of all overlays.
8983 bringToFront: function () {
8984 if (this._map) {
8985 toFront(this._image);
8986 }
8987 return this;
8988 },
8989
8990 // @method bringToBack(): this
8991 // Brings the layer to the bottom of all overlays.
8992 bringToBack: function () {
8993 if (this._map) {
8994 toBack(this._image);
8995 }
8996 return this;
8997 },
8998
8999 // @method setUrl(url: String): this
9000 // Changes the URL of the image.
9001 setUrl: function (url) {
9002 this._url = url;
9003
9004 if (this._image) {
9005 this._image.src = url;
9006 }
9007 return this;
9008 },
9009
9010 // @method setBounds(bounds: LatLngBounds): this
9011 // Update the bounds that this ImageOverlay covers
9012 setBounds: function (bounds) {
9013 this._bounds = toLatLngBounds(bounds);
9014
9015 if (this._map) {
9016 this._reset();
9017 }
9018 return this;
9019 },
9020
9021 getEvents: function () {
9022 var events = {
9023 zoom: this._reset,
9024 viewreset: this._reset
9025 };
9026
9027 if (this._zoomAnimated) {
9028 events.zoomanim = this._animateZoom;
9029 }
9030
9031 return events;
9032 },
9033
9034 // @method: setZIndex(value: Number) : this
9035 // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9036 setZIndex: function (value) {
9037 this.options.zIndex = value;
9038 this._updateZIndex();
9039 return this;
9040 },
9041
9042 // @method getBounds(): LatLngBounds
9043 // Get the bounds that this ImageOverlay covers
9044 getBounds: function () {
9045 return this._bounds;
9046 },
9047
9048 // @method getElement(): HTMLElement
9049 // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9050 // used by this overlay.
9051 getElement: function () {
9052 return this._image;
9053 },
9054
9055 _initImage: function () {
9056 var wasElementSupplied = this._url.tagName === 'IMG';
9057 var img = this._image = wasElementSupplied ? this._url : create$1('img');
9058
9059 addClass(img, 'leaflet-image-layer');
9060 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9061 if (this.options.className) { addClass(img, this.options.className); }
9062
9063 img.onselectstart = falseFn;
9064 img.onmousemove = falseFn;
9065
9066 // @event load: Event
9067 // Fired when the ImageOverlay layer has loaded its image
9068 img.onload = bind(this.fire, this, 'load');
9069 img.onerror = bind(this._overlayOnError, this, 'error');
9070
9071 if (this.options.crossOrigin) {
9072 img.crossOrigin = '';
9073 }
9074
9075 if (this.options.zIndex) {
9076 this._updateZIndex();
9077 }
9078
9079 if (wasElementSupplied) {
9080 this._url = img.src;
9081 return;
9082 }
9083
9084 img.src = this._url;
9085 img.alt = this.options.alt;
9086 },
9087
9088 _animateZoom: function (e) {
9089 var scale = this._map.getZoomScale(e.zoom),
9090 offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9091
9092 setTransform(this._image, offset, scale);
9093 },
9094
9095 _reset: function () {
9096 var image = this._image,
9097 bounds = new Bounds(
9098 this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9099 this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9100 size = bounds.getSize();
9101
9102 setPosition(image, bounds.min);
9103
9104 image.style.width = size.x + 'px';
9105 image.style.height = size.y + 'px';
9106 },
9107
9108 _updateOpacity: function () {
9109 setOpacity(this._image, this.options.opacity);
9110 },
9111
9112 _updateZIndex: function () {
9113 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9114 this._image.style.zIndex = this.options.zIndex;
9115 }
9116 },
9117
9118 _overlayOnError: function () {
9119 // @event error: Event
9120 // Fired when the ImageOverlay layer has loaded its image
9121 this.fire('error');
9122
9123 var errorUrl = this.options.errorOverlayUrl;
9124 if (errorUrl && this._url !== errorUrl) {
9125 this._url = errorUrl;
9126 this._image.src = errorUrl;
9127 }
9128 }
9129 });
9130
9131 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9132 // Instantiates an image overlay object given the URL of the image and the
9133 // geographical bounds it is tied to.
9134 var imageOverlay = function (url, bounds, options) {
9135 return new ImageOverlay(url, bounds, options);
9136 };
9137
9138 /*
9139 * @class VideoOverlay
9140 * @aka L.VideoOverlay
9141 * @inherits ImageOverlay
9142 *
9143 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9144 *
9145 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9146 * HTML5 element.
9147 *
9148 * @example
9149 *
9150 * ```js
9151 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9152 * videoBounds = [[ 32, -130], [ 13, -100]];
9153 * L.VideoOverlay(videoUrl, videoBounds ).addTo(map);
9154 * ```
9155 */
9156
9157 var VideoOverlay = ImageOverlay.extend({
9158
9159 // @section
9160 // @aka VideoOverlay options
9161 options: {
9162 // @option autoplay: Boolean = true
9163 // Whether the video starts playing automatically when loaded.
9164 autoplay: true,
9165
9166 // @option loop: Boolean = true
9167 // Whether the video will loop back to the beginning when played.
9168 loop: true
9169 },
9170
9171 _initImage: function () {
9172 var wasElementSupplied = this._url.tagName === 'VIDEO';
9173 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9174
9175 addClass(vid, 'leaflet-image-layer');
9176 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9177
9178 vid.onselectstart = falseFn;
9179 vid.onmousemove = falseFn;
9180
9181 // @event load: Event
9182 // Fired when the video has finished loading the first frame
9183 vid.onloadeddata = bind(this.fire, this, 'load');
9184
9185 if (wasElementSupplied) {
9186 var sourceElements = vid.getElementsByTagName('source');
9187 var sources = [];
9188 for (var j = 0; j < sourceElements.length; j++) {
9189 sources.push(sourceElements[j].src);
9190 }
9191
9192 this._url = (sourceElements.length > 0) ? sources : [vid.src];
9193 return;
9194 }
9195
9196 if (!isArray(this._url)) { this._url = [this._url]; }
9197
9198 vid.autoplay = !!this.options.autoplay;
9199 vid.loop = !!this.options.loop;
9200 for (var i = 0; i < this._url.length; i++) {
9201 var source = create$1('source');
9202 source.src = this._url[i];
9203 vid.appendChild(source);
9204 }
9205 }
9206
9207 // @method getElement(): HTMLVideoElement
9208 // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9209 // used by this overlay.
9210 });
9211
9212
9213 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9214 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9215 // geographical bounds it is tied to.
9216
9217 function videoOverlay(video, bounds, options) {
9218 return new VideoOverlay(video, bounds, options);
9219 }
9220
9221 /*
9222 * @class DivOverlay
9223 * @inherits Layer
9224 * @aka L.DivOverlay
9225 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
9226 */
9227
9228 // @namespace DivOverlay
9229 var DivOverlay = Layer.extend({
9230
9231 // @section
9232 // @aka DivOverlay options
9233 options: {
9234 // @option offset: Point = Point(0, 7)
9235 // The offset of the popup position. Useful to control the anchor
9236 // of the popup when opening it on some overlays.
9237 offset: [0, 7],
9238
9239 // @option className: String = ''
9240 // A custom CSS class name to assign to the popup.
9241 className: '',
9242
9243 // @option pane: String = 'popupPane'
9244 // `Map pane` where the popup will be added.
9245 pane: 'popupPane'
9246 },
9247
9248 initialize: function (options, source) {
9249 setOptions(this, options);
9250
9251 this._source = source;
9252 },
9253
9254 onAdd: function (map) {
9255 this._zoomAnimated = map._zoomAnimated;
9256
9257 if (!this._container) {
9258 this._initLayout();
9259 }
9260
9261 if (map._fadeAnimated) {
9262 setOpacity(this._container, 0);
9263 }
9264
9265 clearTimeout(this._removeTimeout);
9266 this.getPane().appendChild(this._container);
9267 this.update();
9268
9269 if (map._fadeAnimated) {
9270 setOpacity(this._container, 1);
9271 }
9272
9273 this.bringToFront();
9274 },
9275
9276 onRemove: function (map) {
9277 if (map._fadeAnimated) {
9278 setOpacity(this._container, 0);
9279 this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9280 } else {
9281 remove(this._container);
9282 }
9283 },
9284
9285 // @namespace Popup
9286 // @method getLatLng: LatLng
9287 // Returns the geographical point of popup.
9288 getLatLng: function () {
9289 return this._latlng;
9290 },
9291
9292 // @method setLatLng(latlng: LatLng): this
9293 // Sets the geographical point where the popup will open.
9294 setLatLng: function (latlng) {
9295 this._latlng = toLatLng(latlng);
9296 if (this._map) {
9297 this._updatePosition();
9298 this._adjustPan();
9299 }
9300 return this;
9301 },
9302
9303 // @method getContent: String|HTMLElement
9304 // Returns the content of the popup.
9305 getContent: function () {
9306 return this._content;
9307 },
9308
9309 // @method setContent(htmlContent: String|HTMLElement|Function): this
9310 // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
9311 setContent: function (content) {
9312 this._content = content;
9313 this.update();
9314 return this;
9315 },
9316
9317 // @method getElement: String|HTMLElement
9318 // Alias for [getContent()](#popup-getcontent)
9319 getElement: function () {
9320 return this._container;
9321 },
9322
9323 // @method update: null
9324 // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
9325 update: function () {
9326 if (!this._map) { return; }
9327
9328 this._container.style.visibility = 'hidden';
9329
9330 this._updateContent();
9331 this._updateLayout();
9332 this._updatePosition();
9333
9334 this._container.style.visibility = '';
9335
9336 this._adjustPan();
9337 },
9338
9339 getEvents: function () {
9340 var events = {
9341 zoom: this._updatePosition,
9342 viewreset: this._updatePosition
9343 };
9344
9345 if (this._zoomAnimated) {
9346 events.zoomanim = this._animateZoom;
9347 }
9348 return events;
9349 },
9350
9351 // @method isOpen: Boolean
9352 // Returns `true` when the popup is visible on the map.
9353 isOpen: function () {
9354 return !!this._map && this._map.hasLayer(this);
9355 },
9356
9357 // @method bringToFront: this
9358 // Brings this popup in front of other popups (in the same map pane).
9359 bringToFront: function () {
9360 if (this._map) {
9361 toFront(this._container);
9362 }
9363 return this;
9364 },
9365
9366 // @method bringToBack: this
9367 // Brings this popup to the back of other popups (in the same map pane).
9368 bringToBack: function () {
9369 if (this._map) {
9370 toBack(this._container);
9371 }
9372 return this;
9373 },
9374
9375 _updateContent: function () {
9376 if (!this._content) { return; }
9377
9378 var node = this._contentNode;
9379 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9380
9381 if (typeof content === 'string') {
9382 node.innerHTML = content;
9383 } else {
9384 while (node.hasChildNodes()) {
9385 node.removeChild(node.firstChild);
9386 }
9387 node.appendChild(content);
9388 }
9389 this.fire('contentupdate');
9390 },
9391
9392 _updatePosition: function () {
9393 if (!this._map) { return; }
9394
9395 var pos = this._map.latLngToLayerPoint(this._latlng),
9396 offset = toPoint(this.options.offset),
9397 anchor = this._getAnchor();
9398
9399 if (this._zoomAnimated) {
9400 setPosition(this._container, pos.add(anchor));
9401 } else {
9402 offset = offset.add(pos).add(anchor);
9403 }
9404
9405 var bottom = this._containerBottom = -offset.y,
9406 left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9407
9408 // bottom position the popup in case the height of the popup changes (images loading etc)
9409 this._container.style.bottom = bottom + 'px';
9410 this._container.style.left = left + 'px';
9411 },
9412
9413 _getAnchor: function () {
9414 return [0, 0];
9415 }
9416
9417 });
9418
9419 /*
9420 * @class Popup
9421 * @inherits DivOverlay
9422 * @aka L.Popup
9423 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9424 * open popups while making sure that only one popup is open at one time
9425 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9426 *
9427 * @example
9428 *
9429 * If you want to just bind a popup to marker click and then open it, it's really easy:
9430 *
9431 * ```js
9432 * marker.bindPopup(popupContent).openPopup();
9433 * ```
9434 * Path overlays like polylines also have a `bindPopup` method.
9435 * Here's a more complicated way to open a popup on a map:
9436 *
9437 * ```js
9438 * var popup = L.popup()
9439 * .setLatLng(latlng)
9440 * .setContent('<p>Hello world!<br />This is a nice popup.</p>')
9441 * .openOn(map);
9442 * ```
9443 */
9444
9445
9446 // @namespace Popup
9447 var Popup = DivOverlay.extend({
9448
9449 // @section
9450 // @aka Popup options
9451 options: {
9452 // @option maxWidth: Number = 300
9453 // Max width of the popup, in pixels.
9454 maxWidth: 300,
9455
9456 // @option minWidth: Number = 50
9457 // Min width of the popup, in pixels.
9458 minWidth: 50,
9459
9460 // @option maxHeight: Number = null
9461 // If set, creates a scrollable container of the given height
9462 // inside a popup if its content exceeds it.
9463 maxHeight: null,
9464
9465 // @option autoPan: Boolean = true
9466 // Set it to `false` if you don't want the map to do panning animation
9467 // to fit the opened popup.
9468 autoPan: true,
9469
9470 // @option autoPanPaddingTopLeft: Point = null
9471 // The margin between the popup and the top left corner of the map
9472 // view after autopanning was performed.
9473 autoPanPaddingTopLeft: null,
9474
9475 // @option autoPanPaddingBottomRight: Point = null
9476 // The margin between the popup and the bottom right corner of the map
9477 // view after autopanning was performed.
9478 autoPanPaddingBottomRight: null,
9479
9480 // @option autoPanPadding: Point = Point(5, 5)
9481 // Equivalent of setting both top left and bottom right autopan padding to the same value.
9482 autoPanPadding: [5, 5],
9483
9484 // @option keepInView: Boolean = false
9485 // Set it to `true` if you want to prevent users from panning the popup
9486 // off of the screen while it is open.
9487 keepInView: false,
9488
9489 // @option closeButton: Boolean = true
9490 // Controls the presence of a close button in the popup.
9491 closeButton: true,
9492
9493 // @option autoClose: Boolean = true
9494 // Set it to `false` if you want to override the default behavior of
9495 // the popup closing when another popup is opened.
9496 autoClose: true,
9497
9498 // @option closeOnEscapeKey: Boolean = true
9499 // Set it to `false` if you want to override the default behavior of
9500 // the ESC key for closing of the popup.
9501 closeOnEscapeKey: true,
9502
9503 // @option closeOnClick: Boolean = *
9504 // Set it if you want to override the default behavior of the popup closing when user clicks
9505 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9506
9507 // @option className: String = ''
9508 // A custom CSS class name to assign to the popup.
9509 className: ''
9510 },
9511
9512 // @namespace Popup
9513 // @method openOn(map: Map): this
9514 // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
9515 openOn: function (map) {
9516 map.openPopup(this);
9517 return this;
9518 },
9519
9520 onAdd: function (map) {
9521 DivOverlay.prototype.onAdd.call(this, map);
9522
9523 // @namespace Map
9524 // @section Popup events
9525 // @event popupopen: PopupEvent
9526 // Fired when a popup is opened in the map
9527 map.fire('popupopen', {popup: this});
9528
9529 if (this._source) {
9530 // @namespace Layer
9531 // @section Popup events
9532 // @event popupopen: PopupEvent
9533 // Fired when a popup bound to this layer is opened
9534 this._source.fire('popupopen', {popup: this}, true);
9535 // For non-path layers, we toggle the popup when clicking
9536 // again the layer, so prevent the map to reopen it.
9537 if (!(this._source instanceof Path)) {
9538 this._source.on('preclick', stopPropagation);
9539 }
9540 }
9541 },
9542
9543 onRemove: function (map) {
9544 DivOverlay.prototype.onRemove.call(this, map);
9545
9546 // @namespace Map
9547 // @section Popup events
9548 // @event popupclose: PopupEvent
9549 // Fired when a popup in the map is closed
9550 map.fire('popupclose', {popup: this});
9551
9552 if (this._source) {
9553 // @namespace Layer
9554 // @section Popup events
9555 // @event popupclose: PopupEvent
9556 // Fired when a popup bound to this layer is closed
9557 this._source.fire('popupclose', {popup: this}, true);
9558 if (!(this._source instanceof Path)) {
9559 this._source.off('preclick', stopPropagation);
9560 }
9561 }
9562 },
9563
9564 getEvents: function () {
9565 var events = DivOverlay.prototype.getEvents.call(this);
9566
9567 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9568 events.preclick = this._close;
9569 }
9570
9571 if (this.options.keepInView) {
9572 events.moveend = this._adjustPan;
9573 }
9574
9575 return events;
9576 },
9577
9578 _close: function () {
9579 if (this._map) {
9580 this._map.closePopup(this);
9581 }
9582 },
9583
9584 _initLayout: function () {
9585 var prefix = 'leaflet-popup',
9586 container = this._container = create$1('div',
9587 prefix + ' ' + (this.options.className || '') +
9588 ' leaflet-zoom-animated');
9589
9590 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9591 this._contentNode = create$1('div', prefix + '-content', wrapper);
9592
9593 disableClickPropagation(wrapper);
9594 disableScrollPropagation(this._contentNode);
9595 on(wrapper, 'contextmenu', stopPropagation);
9596
9597 this._tipContainer = create$1('div', prefix + '-tip-container', container);
9598 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
9599
9600 if (this.options.closeButton) {
9601 var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
9602 closeButton.href = '#close';
9603 closeButton.innerHTML = '&#215;';
9604
9605 on(closeButton, 'click', this._onCloseButtonClick, this);
9606 }
9607 },
9608
9609 _updateLayout: function () {
9610 var container = this._contentNode,
9611 style = container.style;
9612
9613 style.width = '';
9614 style.whiteSpace = 'nowrap';
9615
9616 var width = container.offsetWidth;
9617 width = Math.min(width, this.options.maxWidth);
9618 width = Math.max(width, this.options.minWidth);
9619
9620 style.width = (width + 1) + 'px';
9621 style.whiteSpace = '';
9622
9623 style.height = '';
9624
9625 var height = container.offsetHeight,
9626 maxHeight = this.options.maxHeight,
9627 scrolledClass = 'leaflet-popup-scrolled';
9628
9629 if (maxHeight && height > maxHeight) {
9630 style.height = maxHeight + 'px';
9631 addClass(container, scrolledClass);
9632 } else {
9633 removeClass(container, scrolledClass);
9634 }
9635
9636 this._containerWidth = this._container.offsetWidth;
9637 },
9638
9639 _animateZoom: function (e) {
9640 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
9641 anchor = this._getAnchor();
9642 setPosition(this._container, pos.add(anchor));
9643 },
9644
9645 _adjustPan: function () {
9646 if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
9647
9648 var map = this._map,
9649 marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
9650 containerHeight = this._container.offsetHeight + marginBottom,
9651 containerWidth = this._containerWidth,
9652 layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
9653
9654 layerPos._add(getPosition(this._container));
9655
9656 var containerPos = map.layerPointToContainerPoint(layerPos),
9657 padding = toPoint(this.options.autoPanPadding),
9658 paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
9659 paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
9660 size = map.getSize(),
9661 dx = 0,
9662 dy = 0;
9663
9664 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
9665 dx = containerPos.x + containerWidth - size.x + paddingBR.x;
9666 }
9667 if (containerPos.x - dx - paddingTL.x < 0) { // left
9668 dx = containerPos.x - paddingTL.x;
9669 }
9670 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
9671 dy = containerPos.y + containerHeight - size.y + paddingBR.y;
9672 }
9673 if (containerPos.y - dy - paddingTL.y < 0) { // top
9674 dy = containerPos.y - paddingTL.y;
9675 }
9676
9677 // @namespace Map
9678 // @section Popup events
9679 // @event autopanstart: Event
9680 // Fired when the map starts autopanning when opening a popup.
9681 if (dx || dy) {
9682 map
9683 .fire('autopanstart')
9684 .panBy([dx, dy]);
9685 }
9686 },
9687
9688 _onCloseButtonClick: function (e) {
9689 this._close();
9690 stop(e);
9691 },
9692
9693 _getAnchor: function () {
9694 // Where should we anchor the popup on the source layer?
9695 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
9696 }
9697
9698 });
9699
9700 // @namespace Popup
9701 // @factory L.popup(options?: Popup options, source?: Layer)
9702 // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
9703 var popup = function (options, source) {
9704 return new Popup(options, source);
9705 };
9706
9707
9708 /* @namespace Map
9709 * @section Interaction Options
9710 * @option closePopupOnClick: Boolean = true
9711 * Set it to `false` if you don't want popups to close when user clicks the map.
9712 */
9713 Map.mergeOptions({
9714 closePopupOnClick: true
9715 });
9716
9717
9718 // @namespace Map
9719 // @section Methods for Layers and Controls
9720 Map.include({
9721 // @method openPopup(popup: Popup): this
9722 // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
9723 // @alternative
9724 // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
9725 // Creates a popup with the specified content and options and opens it in the given point on a map.
9726 openPopup: function (popup, latlng, options) {
9727 if (!(popup instanceof Popup)) {
9728 popup = new Popup(options).setContent(popup);
9729 }
9730
9731 if (latlng) {
9732 popup.setLatLng(latlng);
9733 }
9734
9735 if (this.hasLayer(popup)) {
9736 return this;
9737 }
9738
9739 if (this._popup && this._popup.options.autoClose) {
9740 this.closePopup();
9741 }
9742
9743 this._popup = popup;
9744 return this.addLayer(popup);
9745 },
9746
9747 // @method closePopup(popup?: Popup): this
9748 // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
9749 closePopup: function (popup) {
9750 if (!popup || popup === this._popup) {
9751 popup = this._popup;
9752 this._popup = null;
9753 }
9754 if (popup) {
9755 this.removeLayer(popup);
9756 }
9757 return this;
9758 }
9759 });
9760
9761 /*
9762 * @namespace Layer
9763 * @section Popup methods example
9764 *
9765 * All layers share a set of methods convenient for binding popups to it.
9766 *
9767 * ```js
9768 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
9769 * layer.openPopup();
9770 * layer.closePopup();
9771 * ```
9772 *
9773 * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
9774 */
9775
9776 // @section Popup methods
9777 Layer.include({
9778
9779 // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
9780 // Binds a popup to the layer with the passed `content` and sets up the
9781 // necessary event listeners. If a `Function` is passed it will receive
9782 // the layer as the first argument and should return a `String` or `HTMLElement`.
9783 bindPopup: function (content, options) {
9784
9785 if (content instanceof Popup) {
9786 setOptions(content, options);
9787 this._popup = content;
9788 content._source = this;
9789 } else {
9790 if (!this._popup || options) {
9791 this._popup = new Popup(options, this);
9792 }
9793 this._popup.setContent(content);
9794 }
9795
9796 if (!this._popupHandlersAdded) {
9797 this.on({
9798 click: this._openPopup,
9799 keypress: this._onKeyPress,
9800 remove: this.closePopup,
9801 move: this._movePopup
9802 });
9803 this._popupHandlersAdded = true;
9804 }
9805
9806 return this;
9807 },
9808
9809 // @method unbindPopup(): this
9810 // Removes the popup previously bound with `bindPopup`.
9811 unbindPopup: function () {
9812 if (this._popup) {
9813 this.off({
9814 click: this._openPopup,
9815 keypress: this._onKeyPress,
9816 remove: this.closePopup,
9817 move: this._movePopup
9818 });
9819 this._popupHandlersAdded = false;
9820 this._popup = null;
9821 }
9822 return this;
9823 },
9824
9825 // @method openPopup(latlng?: LatLng): this
9826 // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
9827 openPopup: function (layer, latlng) {
9828 if (!(layer instanceof Layer)) {
9829 latlng = layer;
9830 layer = this;
9831 }
9832
9833 if (layer instanceof FeatureGroup) {
9834 for (var id in this._layers) {
9835 layer = this._layers[id];
9836 break;
9837 }
9838 }
9839
9840 if (!latlng) {
9841 latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
9842 }
9843
9844 if (this._popup && this._map) {
9845 // set popup source to this layer
9846 this._popup._source = layer;
9847
9848 // update the popup (content, layout, ect...)
9849 this._popup.update();
9850
9851 // open the popup on the map
9852 this._map.openPopup(this._popup, latlng);
9853 }
9854
9855 return this;
9856 },
9857
9858 // @method closePopup(): this
9859 // Closes the popup bound to this layer if it is open.
9860 closePopup: function () {
9861 if (this._popup) {
9862 this._popup._close();
9863 }
9864 return this;
9865 },
9866
9867 // @method togglePopup(): this
9868 // Opens or closes the popup bound to this layer depending on its current state.
9869 togglePopup: function (target) {
9870 if (this._popup) {
9871 if (this._popup._map) {
9872 this.closePopup();
9873 } else {
9874 this.openPopup(target);
9875 }
9876 }
9877 return this;
9878 },
9879
9880 // @method isPopupOpen(): boolean
9881 // Returns `true` if the popup bound to this layer is currently open.
9882 isPopupOpen: function () {
9883 return (this._popup ? this._popup.isOpen() : false);
9884 },
9885
9886 // @method setPopupContent(content: String|HTMLElement|Popup): this
9887 // Sets the content of the popup bound to this layer.
9888 setPopupContent: function (content) {
9889 if (this._popup) {
9890 this._popup.setContent(content);
9891 }
9892 return this;
9893 },
9894
9895 // @method getPopup(): Popup
9896 // Returns the popup bound to this layer.
9897 getPopup: function () {
9898 return this._popup;
9899 },
9900
9901 _openPopup: function (e) {
9902 var layer = e.layer || e.target;
9903
9904 if (!this._popup) {
9905 return;
9906 }
9907
9908 if (!this._map) {
9909 return;
9910 }
9911
9912 // prevent map click
9913 stop(e);
9914
9915 // if this inherits from Path its a vector and we can just
9916 // open the popup at the new location
9917 if (layer instanceof Path) {
9918 this.openPopup(e.layer || e.target, e.latlng);
9919 return;
9920 }
9921
9922 // otherwise treat it like a marker and figure out
9923 // if we should toggle it open/closed
9924 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
9925 this.closePopup();
9926 } else {
9927 this.openPopup(layer, e.latlng);
9928 }
9929 },
9930
9931 _movePopup: function (e) {
9932 this._popup.setLatLng(e.latlng);
9933 },
9934
9935 _onKeyPress: function (e) {
9936 if (e.originalEvent.keyCode === 13) {
9937 this._openPopup(e);
9938 }
9939 }
9940 });
9941
9942 /*
9943 * @class Tooltip
9944 * @inherits DivOverlay
9945 * @aka L.Tooltip
9946 * Used to display small texts on top of map layers.
9947 *
9948 * @example
9949 *
9950 * ```js
9951 * marker.bindTooltip("my tooltip text").openTooltip();
9952 * ```
9953 * Note about tooltip offset. Leaflet takes two options in consideration
9954 * for computing tooltip offsetting:
9955 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
9956 * Add a positive x offset to move the tooltip to the right, and a positive y offset to
9957 * move it to the bottom. Negatives will move to the left and top.
9958 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
9959 * should adapt this value if you use a custom icon.
9960 */
9961
9962
9963 // @namespace Tooltip
9964 var Tooltip = DivOverlay.extend({
9965
9966 // @section
9967 // @aka Tooltip options
9968 options: {
9969 // @option pane: String = 'tooltipPane'
9970 // `Map pane` where the tooltip will be added.
9971 pane: 'tooltipPane',
9972
9973 // @option offset: Point = Point(0, 0)
9974 // Optional offset of the tooltip position.
9975 offset: [0, 0],
9976
9977 // @option direction: String = 'auto'
9978 // Direction where to open the tooltip. Possible values are: `right`, `left`,
9979 // `top`, `bottom`, `center`, `auto`.
9980 // `auto` will dynamically switch between `right` and `left` according to the tooltip
9981 // position on the map.
9982 direction: 'auto',
9983
9984 // @option permanent: Boolean = false
9985 // Whether to open the tooltip permanently or only on mouseover.
9986 permanent: false,
9987
9988 // @option sticky: Boolean = false
9989 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
9990 sticky: false,
9991
9992 // @option interactive: Boolean = false
9993 // If true, the tooltip will listen to the feature events.
9994 interactive: false,
9995
9996 // @option opacity: Number = 0.9
9997 // Tooltip container opacity.
9998 opacity: 0.9
9999 },
10000
10001 onAdd: function (map) {
10002 DivOverlay.prototype.onAdd.call(this, map);
10003 this.setOpacity(this.options.opacity);
10004
10005 // @namespace Map
10006 // @section Tooltip events
10007 // @event tooltipopen: TooltipEvent
10008 // Fired when a tooltip is opened in the map.
10009 map.fire('tooltipopen', {tooltip: this});
10010
10011 if (this._source) {
10012 // @namespace Layer
10013 // @section Tooltip events
10014 // @event tooltipopen: TooltipEvent
10015 // Fired when a tooltip bound to this layer is opened.
10016 this._source.fire('tooltipopen', {tooltip: this}, true);
10017 }
10018 },
10019
10020 onRemove: function (map) {
10021 DivOverlay.prototype.onRemove.call(this, map);
10022
10023 // @namespace Map
10024 // @section Tooltip events
10025 // @event tooltipclose: TooltipEvent
10026 // Fired when a tooltip in the map is closed.
10027 map.fire('tooltipclose', {tooltip: this});
10028
10029 if (this._source) {
10030 // @namespace Layer
10031 // @section Tooltip events
10032 // @event tooltipclose: TooltipEvent
10033 // Fired when a tooltip bound to this layer is closed.
10034 this._source.fire('tooltipclose', {tooltip: this}, true);
10035 }
10036 },
10037
10038 getEvents: function () {
10039 var events = DivOverlay.prototype.getEvents.call(this);
10040
10041 if (touch && !this.options.permanent) {
10042 events.preclick = this._close;
10043 }
10044
10045 return events;
10046 },
10047
10048 _close: function () {
10049 if (this._map) {
10050 this._map.closeTooltip(this);
10051 }
10052 },
10053
10054 _initLayout: function () {
10055 var prefix = 'leaflet-tooltip',
10056 className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10057
10058 this._contentNode = this._container = create$1('div', className);
10059 },
10060
10061 _updateLayout: function () {},
10062
10063 _adjustPan: function () {},
10064
10065 _setPosition: function (pos) {
10066 var map = this._map,
10067 container = this._container,
10068 centerPoint = map.latLngToContainerPoint(map.getCenter()),
10069 tooltipPoint = map.layerPointToContainerPoint(pos),
10070 direction = this.options.direction,
10071 tooltipWidth = container.offsetWidth,
10072 tooltipHeight = container.offsetHeight,
10073 offset = toPoint(this.options.offset),
10074 anchor = this._getAnchor();
10075
10076 if (direction === 'top') {
10077 pos = pos.add(toPoint(-tooltipWidth / 2 + offset.x, -tooltipHeight + offset.y + anchor.y, true));
10078 } else if (direction === 'bottom') {
10079 pos = pos.subtract(toPoint(tooltipWidth / 2 - offset.x, -offset.y, true));
10080 } else if (direction === 'center') {
10081 pos = pos.subtract(toPoint(tooltipWidth / 2 + offset.x, tooltipHeight / 2 - anchor.y + offset.y, true));
10082 } else if (direction === 'right' || direction === 'auto' && tooltipPoint.x < centerPoint.x) {
10083 direction = 'right';
10084 pos = pos.add(toPoint(offset.x + anchor.x, anchor.y - tooltipHeight / 2 + offset.y, true));
10085 } else {
10086 direction = 'left';
10087 pos = pos.subtract(toPoint(tooltipWidth + anchor.x - offset.x, tooltipHeight / 2 - anchor.y - offset.y, true));
10088 }
10089
10090 removeClass(container, 'leaflet-tooltip-right');
10091 removeClass(container, 'leaflet-tooltip-left');
10092 removeClass(container, 'leaflet-tooltip-top');
10093 removeClass(container, 'leaflet-tooltip-bottom');
10094 addClass(container, 'leaflet-tooltip-' + direction);
10095 setPosition(container, pos);
10096 },
10097
10098 _updatePosition: function () {
10099 var pos = this._map.latLngToLayerPoint(this._latlng);
10100 this._setPosition(pos);
10101 },
10102
10103 setOpacity: function (opacity) {
10104 this.options.opacity = opacity;
10105
10106 if (this._container) {
10107 setOpacity(this._container, opacity);
10108 }
10109 },
10110
10111 _animateZoom: function (e) {
10112 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10113 this._setPosition(pos);
10114 },
10115
10116 _getAnchor: function () {
10117 // Where should we anchor the tooltip on the source layer?
10118 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10119 }
10120
10121 });
10122
10123 // @namespace Tooltip
10124 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
10125 // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
10126 var tooltip = function (options, source) {
10127 return new Tooltip(options, source);
10128 };
10129
10130 // @namespace Map
10131 // @section Methods for Layers and Controls
10132 Map.include({
10133
10134 // @method openTooltip(tooltip: Tooltip): this
10135 // Opens the specified tooltip.
10136 // @alternative
10137 // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10138 // Creates a tooltip with the specified content and options and open it.
10139 openTooltip: function (tooltip, latlng, options) {
10140 if (!(tooltip instanceof Tooltip)) {
10141 tooltip = new Tooltip(options).setContent(tooltip);
10142 }
10143
10144 if (latlng) {
10145 tooltip.setLatLng(latlng);
10146 }
10147
10148 if (this.hasLayer(tooltip)) {
10149 return this;
10150 }
10151
10152 return this.addLayer(tooltip);
10153 },
10154
10155 // @method closeTooltip(tooltip?: Tooltip): this
10156 // Closes the tooltip given as parameter.
10157 closeTooltip: function (tooltip) {
10158 if (tooltip) {
10159 this.removeLayer(tooltip);
10160 }
10161 return this;
10162 }
10163
10164 });
10165
10166 /*
10167 * @namespace Layer
10168 * @section Tooltip methods example
10169 *
10170 * All layers share a set of methods convenient for binding tooltips to it.
10171 *
10172 * ```js
10173 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10174 * layer.openTooltip();
10175 * layer.closeTooltip();
10176 * ```
10177 */
10178
10179 // @section Tooltip methods
10180 Layer.include({
10181
10182 // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10183 // Binds a tooltip to the layer with the passed `content` and sets up the
10184 // necessary event listeners. If a `Function` is passed it will receive
10185 // the layer as the first argument and should return a `String` or `HTMLElement`.
10186 bindTooltip: function (content, options) {
10187
10188 if (content instanceof Tooltip) {
10189 setOptions(content, options);
10190 this._tooltip = content;
10191 content._source = this;
10192 } else {
10193 if (!this._tooltip || options) {
10194 this._tooltip = new Tooltip(options, this);
10195 }
10196 this._tooltip.setContent(content);
10197
10198 }
10199
10200 this._initTooltipInteractions();
10201
10202 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10203 this.openTooltip();
10204 }
10205
10206 return this;
10207 },
10208
10209 // @method unbindTooltip(): this
10210 // Removes the tooltip previously bound with `bindTooltip`.
10211 unbindTooltip: function () {
10212 if (this._tooltip) {
10213 this._initTooltipInteractions(true);
10214 this.closeTooltip();
10215 this._tooltip = null;
10216 }
10217 return this;
10218 },
10219
10220 _initTooltipInteractions: function (remove$$1) {
10221 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
10222 var onOff = remove$$1 ? 'off' : 'on',
10223 events = {
10224 remove: this.closeTooltip,
10225 move: this._moveTooltip
10226 };
10227 if (!this._tooltip.options.permanent) {
10228 events.mouseover = this._openTooltip;
10229 events.mouseout = this.closeTooltip;
10230 if (this._tooltip.options.sticky) {
10231 events.mousemove = this._moveTooltip;
10232 }
10233 if (touch) {
10234 events.click = this._openTooltip;
10235 }
10236 } else {
10237 events.add = this._openTooltip;
10238 }
10239 this[onOff](events);
10240 this._tooltipHandlersAdded = !remove$$1;
10241 },
10242
10243 // @method openTooltip(latlng?: LatLng): this
10244 // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10245 openTooltip: function (layer, latlng) {
10246 if (!(layer instanceof Layer)) {
10247 latlng = layer;
10248 layer = this;
10249 }
10250
10251 if (layer instanceof FeatureGroup) {
10252 for (var id in this._layers) {
10253 layer = this._layers[id];
10254 break;
10255 }
10256 }
10257
10258 if (!latlng) {
10259 latlng = layer.getCenter ? layer.getCenter() : layer.getLatLng();
10260 }
10261
10262 if (this._tooltip && this._map) {
10263
10264 // set tooltip source to this layer
10265 this._tooltip._source = layer;
10266
10267 // update the tooltip (content, layout, ect...)
10268 this._tooltip.update();
10269
10270 // open the tooltip on the map
10271 this._map.openTooltip(this._tooltip, latlng);
10272
10273 // Tooltip container may not be defined if not permanent and never
10274 // opened.
10275 if (this._tooltip.options.interactive && this._tooltip._container) {
10276 addClass(this._tooltip._container, 'leaflet-clickable');
10277 this.addInteractiveTarget(this._tooltip._container);
10278 }
10279 }
10280
10281 return this;
10282 },
10283
10284 // @method closeTooltip(): this
10285 // Closes the tooltip bound to this layer if it is open.
10286 closeTooltip: function () {
10287 if (this._tooltip) {
10288 this._tooltip._close();
10289 if (this._tooltip.options.interactive && this._tooltip._container) {
10290 removeClass(this._tooltip._container, 'leaflet-clickable');
10291 this.removeInteractiveTarget(this._tooltip._container);
10292 }
10293 }
10294 return this;
10295 },
10296
10297 // @method toggleTooltip(): this
10298 // Opens or closes the tooltip bound to this layer depending on its current state.
10299 toggleTooltip: function (target) {
10300 if (this._tooltip) {
10301 if (this._tooltip._map) {
10302 this.closeTooltip();
10303 } else {
10304 this.openTooltip(target);
10305 }
10306 }
10307 return this;
10308 },
10309
10310 // @method isTooltipOpen(): boolean
10311 // Returns `true` if the tooltip bound to this layer is currently open.
10312 isTooltipOpen: function () {
10313 return this._tooltip.isOpen();
10314 },
10315
10316 // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10317 // Sets the content of the tooltip bound to this layer.
10318 setTooltipContent: function (content) {
10319 if (this._tooltip) {
10320 this._tooltip.setContent(content);
10321 }
10322 return this;
10323 },
10324
10325 // @method getTooltip(): Tooltip
10326 // Returns the tooltip bound to this layer.
10327 getTooltip: function () {
10328 return this._tooltip;
10329 },
10330
10331 _openTooltip: function (e) {
10332 var layer = e.layer || e.target;
10333
10334 if (!this._tooltip || !this._map) {
10335 return;
10336 }
10337 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
10338 },
10339
10340 _moveTooltip: function (e) {
10341 var latlng = e.latlng, containerPoint, layerPoint;
10342 if (this._tooltip.options.sticky && e.originalEvent) {
10343 containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10344 layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10345 latlng = this._map.layerPointToLatLng(layerPoint);
10346 }
10347 this._tooltip.setLatLng(latlng);
10348 }
10349 });
10350
10351 /*
10352 * @class DivIcon
10353 * @aka L.DivIcon
10354 * @inherits Icon
10355 *
10356 * Represents a lightweight icon for markers that uses a simple `<div>`
10357 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10358 *
10359 * @example
10360 * ```js
10361 * var myIcon = L.divIcon({className: 'my-div-icon'});
10362 * // you can set .my-div-icon styles in CSS
10363 *
10364 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10365 * ```
10366 *
10367 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10368 */
10369
10370 var DivIcon = Icon.extend({
10371 options: {
10372 // @section
10373 // @aka DivIcon options
10374 iconSize: [12, 12], // also can be set through CSS
10375
10376 // iconAnchor: (Point),
10377 // popupAnchor: (Point),
10378
10379 // @option html: String = ''
10380 // Custom HTML code to put inside the div element, empty by default.
10381 html: false,
10382
10383 // @option bgPos: Point = [0, 0]
10384 // Optional relative position of the background, in pixels
10385 bgPos: null,
10386
10387 className: 'leaflet-div-icon'
10388 },
10389
10390 createIcon: function (oldIcon) {
10391 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10392 options = this.options;
10393
10394 div.innerHTML = options.html !== false ? options.html : '';
10395
10396 if (options.bgPos) {
10397 var bgPos = toPoint(options.bgPos);
10398 div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10399 }
10400 this._setIconStyles(div, 'icon');
10401
10402 return div;
10403 },
10404
10405 createShadow: function () {
10406 return null;
10407 }
10408 });
10409
10410 // @factory L.divIcon(options: DivIcon options)
10411 // Creates a `DivIcon` instance with the given options.
10412 function divIcon(options) {
10413 return new DivIcon(options);
10414 }
10415
10416 Icon.Default = IconDefault;
10417
10418 /*
10419 * @class GridLayer
10420 * @inherits Layer
10421 * @aka L.GridLayer
10422 *
10423 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10424 * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
10425 *
10426 *
10427 * @section Synchronous usage
10428 * @example
10429 *
10430 * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
10431 *
10432 * ```js
10433 * var CanvasLayer = L.GridLayer.extend({
10434 * createTile: function(coords){
10435 * // create a <canvas> element for drawing
10436 * var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10437 *
10438 * // setup tile width and height according to the options
10439 * var size = this.getTileSize();
10440 * tile.width = size.x;
10441 * tile.height = size.y;
10442 *
10443 * // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10444 * var ctx = tile.getContext('2d');
10445 *
10446 * // return the tile so it can be rendered on screen
10447 * return tile;
10448 * }
10449 * });
10450 * ```
10451 *
10452 * @section Asynchronous usage
10453 * @example
10454 *
10455 * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
10456 *
10457 * ```js
10458 * var CanvasLayer = L.GridLayer.extend({
10459 * createTile: function(coords, done){
10460 * var error;
10461 *
10462 * // create a <canvas> element for drawing
10463 * var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10464 *
10465 * // setup tile width and height according to the options
10466 * var size = this.getTileSize();
10467 * tile.width = size.x;
10468 * tile.height = size.y;
10469 *
10470 * // draw something asynchronously and pass the tile to the done() callback
10471 * setTimeout(function() {
10472 * done(error, tile);
10473 * }, 1000);
10474 *
10475 * return tile;
10476 * }
10477 * });
10478 * ```
10479 *
10480 * @section
10481 */
10482
10483
10484 var GridLayer = Layer.extend({
10485
10486 // @section
10487 // @aka GridLayer options
10488 options: {
10489 // @option tileSize: Number|Point = 256
10490 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10491 tileSize: 256,
10492
10493 // @option opacity: Number = 1.0
10494 // Opacity of the tiles. Can be used in the `createTile()` function.
10495 opacity: 1,
10496
10497 // @option updateWhenIdle: Boolean = (depends)
10498 // Load new tiles only when panning ends.
10499 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
10500 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
10501 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
10502 updateWhenIdle: mobile,
10503
10504 // @option updateWhenZooming: Boolean = true
10505 // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
10506 updateWhenZooming: true,
10507
10508 // @option updateInterval: Number = 200
10509 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
10510 updateInterval: 200,
10511
10512 // @option zIndex: Number = 1
10513 // The explicit zIndex of the tile layer.
10514 zIndex: 1,
10515
10516 // @option bounds: LatLngBounds = undefined
10517 // If set, tiles will only be loaded inside the set `LatLngBounds`.
10518 bounds: null,
10519
10520 // @option minZoom: Number = 0
10521 // The minimum zoom level down to which this layer will be displayed (inclusive).
10522 minZoom: 0,
10523
10524 // @option maxZoom: Number = undefined
10525 // The maximum zoom level up to which this layer will be displayed (inclusive).
10526 maxZoom: undefined,
10527
10528 // @option maxNativeZoom: Number = undefined
10529 // Maximum zoom number the tile source has available. If it is specified,
10530 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10531 // from `maxNativeZoom` level and auto-scaled.
10532 maxNativeZoom: undefined,
10533
10534 // @option minNativeZoom: Number = undefined
10535 // Minimum zoom number the tile source has available. If it is specified,
10536 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10537 // from `minNativeZoom` level and auto-scaled.
10538 minNativeZoom: undefined,
10539
10540 // @option noWrap: Boolean = false
10541 // Whether the layer is wrapped around the antimeridian. If `true`, the
10542 // GridLayer will only be displayed once at low zoom levels. Has no
10543 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10544 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10545 // tiles outside the CRS limits.
10546 noWrap: false,
10547
10548 // @option pane: String = 'tilePane'
10549 // `Map pane` where the grid layer will be added.
10550 pane: 'tilePane',
10551
10552 // @option className: String = ''
10553 // A custom class name to assign to the tile layer. Empty by default.
10554 className: '',
10555
10556 // @option keepBuffer: Number = 2
10557 // When panning the map, keep this many rows and columns of tiles before unloading them.
10558 keepBuffer: 2
10559 },
10560
10561 initialize: function (options) {
10562 setOptions(this, options);
10563 },
10564
10565 onAdd: function () {
10566 this._initContainer();
10567
10568 this._levels = {};
10569 this._tiles = {};
10570
10571 this._resetView();
10572 this._update();
10573 },
10574
10575 beforeAdd: function (map) {
10576 map._addZoomLimit(this);
10577 },
10578
10579 onRemove: function (map) {
10580 this._removeAllTiles();
10581 remove(this._container);
10582 map._removeZoomLimit(this);
10583 this._container = null;
10584 this._tileZoom = undefined;
10585 },
10586
10587 // @method bringToFront: this
10588 // Brings the tile layer to the top of all tile layers.
10589 bringToFront: function () {
10590 if (this._map) {
10591 toFront(this._container);
10592 this._setAutoZIndex(Math.max);
10593 }
10594 return this;
10595 },
10596
10597 // @method bringToBack: this
10598 // Brings the tile layer to the bottom of all tile layers.
10599 bringToBack: function () {
10600 if (this._map) {
10601 toBack(this._container);
10602 this._setAutoZIndex(Math.min);
10603 }
10604 return this;
10605 },
10606
10607 // @method getContainer: HTMLElement
10608 // Returns the HTML element that contains the tiles for this layer.
10609 getContainer: function () {
10610 return this._container;
10611 },
10612
10613 // @method setOpacity(opacity: Number): this
10614 // Changes the [opacity](#gridlayer-opacity) of the grid layer.
10615 setOpacity: function (opacity) {
10616 this.options.opacity = opacity;
10617 this._updateOpacity();
10618 return this;
10619 },
10620
10621 // @method setZIndex(zIndex: Number): this
10622 // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10623 setZIndex: function (zIndex) {
10624 this.options.zIndex = zIndex;
10625 this._updateZIndex();
10626
10627 return this;
10628 },
10629
10630 // @method isLoading: Boolean
10631 // Returns `true` if any tile in the grid layer has not finished loading.
10632 isLoading: function () {
10633 return this._loading;
10634 },
10635
10636 // @method redraw: this
10637 // Causes the layer to clear all the tiles and request them again.
10638 redraw: function () {
10639 if (this._map) {
10640 this._removeAllTiles();
10641 this._update();
10642 }
10643 return this;
10644 },
10645
10646 getEvents: function () {
10647 var events = {
10648 viewprereset: this._invalidateAll,
10649 viewreset: this._resetView,
10650 zoom: this._resetView,
10651 moveend: this._onMoveEnd
10652 };
10653
10654 if (!this.options.updateWhenIdle) {
10655 // update tiles on move, but not more often than once per given interval
10656 if (!this._onMove) {
10657 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10658 }
10659
10660 events.move = this._onMove;
10661 }
10662
10663 if (this._zoomAnimated) {
10664 events.zoomanim = this._animateZoom;
10665 }
10666
10667 return events;
10668 },
10669
10670 // @section Extension methods
10671 // Layers extending `GridLayer` shall reimplement the following method.
10672 // @method createTile(coords: Object, done?: Function): HTMLElement
10673 // Called only internally, must be overridden by classes extending `GridLayer`.
10674 // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10675 // is specified, it must be called when the tile has finished loading and drawing.
10676 createTile: function () {
10677 return document.createElement('div');
10678 },
10679
10680 // @section
10681 // @method getTileSize: Point
10682 // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10683 getTileSize: function () {
10684 var s = this.options.tileSize;
10685 return s instanceof Point ? s : new Point(s, s);
10686 },
10687
10688 _updateZIndex: function () {
10689 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10690 this._container.style.zIndex = this.options.zIndex;
10691 }
10692 },
10693
10694 _setAutoZIndex: function (compare) {
10695 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10696
10697 var layers = this.getPane().children,
10698 edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10699
10700 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10701
10702 zIndex = layers[i].style.zIndex;
10703
10704 if (layers[i] !== this._container && zIndex) {
10705 edgeZIndex = compare(edgeZIndex, +zIndex);
10706 }
10707 }
10708
10709 if (isFinite(edgeZIndex)) {
10710 this.options.zIndex = edgeZIndex + compare(-1, 1);
10711 this._updateZIndex();
10712 }
10713 },
10714
10715 _updateOpacity: function () {
10716 if (!this._map) { return; }
10717
10718 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
10719 if (ielt9) { return; }
10720
10721 setOpacity(this._container, this.options.opacity);
10722
10723 var now = +new Date(),
10724 nextFrame = false,
10725 willPrune = false;
10726
10727 for (var key in this._tiles) {
10728 var tile = this._tiles[key];
10729 if (!tile.current || !tile.loaded) { continue; }
10730
10731 var fade = Math.min(1, (now - tile.loaded) / 200);
10732
10733 setOpacity(tile.el, fade);
10734 if (fade < 1) {
10735 nextFrame = true;
10736 } else {
10737 if (tile.active) {
10738 willPrune = true;
10739 } else {
10740 this._onOpaqueTile(tile);
10741 }
10742 tile.active = true;
10743 }
10744 }
10745
10746 if (willPrune && !this._noPrune) { this._pruneTiles(); }
10747
10748 if (nextFrame) {
10749 cancelAnimFrame(this._fadeFrame);
10750 this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
10751 }
10752 },
10753
10754 _onOpaqueTile: falseFn,
10755
10756 _initContainer: function () {
10757 if (this._container) { return; }
10758
10759 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
10760 this._updateZIndex();
10761
10762 if (this.options.opacity < 1) {
10763 this._updateOpacity();
10764 }
10765
10766 this.getPane().appendChild(this._container);
10767 },
10768
10769 _updateLevels: function () {
10770
10771 var zoom = this._tileZoom,
10772 maxZoom = this.options.maxZoom;
10773
10774 if (zoom === undefined) { return undefined; }
10775
10776 for (var z in this._levels) {
10777 if (this._levels[z].el.children.length || z === zoom) {
10778 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
10779 this._onUpdateLevel(z);
10780 } else {
10781 remove(this._levels[z].el);
10782 this._removeTilesAtZoom(z);
10783 this._onRemoveLevel(z);
10784 delete this._levels[z];
10785 }
10786 }
10787
10788 var level = this._levels[zoom],
10789 map = this._map;
10790
10791 if (!level) {
10792 level = this._levels[zoom] = {};
10793
10794 level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
10795 level.el.style.zIndex = maxZoom;
10796
10797 level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
10798 level.zoom = zoom;
10799
10800 this._setZoomTransform(level, map.getCenter(), map.getZoom());
10801
10802 // force the browser to consider the newly added element for transition
10803 falseFn(level.el.offsetWidth);
10804
10805 this._onCreateLevel(level);
10806 }
10807
10808 this._level = level;
10809
10810 return level;
10811 },
10812
10813 _onUpdateLevel: falseFn,
10814
10815 _onRemoveLevel: falseFn,
10816
10817 _onCreateLevel: falseFn,
10818
10819 _pruneTiles: function () {
10820 if (!this._map) {
10821 return;
10822 }
10823
10824 var key, tile;
10825
10826 var zoom = this._map.getZoom();
10827 if (zoom > this.options.maxZoom ||
10828 zoom < this.options.minZoom) {
10829 this._removeAllTiles();
10830 return;
10831 }
10832
10833 for (key in this._tiles) {
10834 tile = this._tiles[key];
10835 tile.retain = tile.current;
10836 }
10837
10838 for (key in this._tiles) {
10839 tile = this._tiles[key];
10840 if (tile.current && !tile.active) {
10841 var coords = tile.coords;
10842 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
10843 this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
10844 }
10845 }
10846 }
10847
10848 for (key in this._tiles) {
10849 if (!this._tiles[key].retain) {
10850 this._removeTile(key);
10851 }
10852 }
10853 },
10854
10855 _removeTilesAtZoom: function (zoom) {
10856 for (var key in this._tiles) {
10857 if (this._tiles[key].coords.z !== zoom) {
10858 continue;
10859 }
10860 this._removeTile(key);
10861 }
10862 },
10863
10864 _removeAllTiles: function () {
10865 for (var key in this._tiles) {
10866 this._removeTile(key);
10867 }
10868 },
10869
10870 _invalidateAll: function () {
10871 for (var z in this._levels) {
10872 remove(this._levels[z].el);
10873 this._onRemoveLevel(z);
10874 delete this._levels[z];
10875 }
10876 this._removeAllTiles();
10877
10878 this._tileZoom = undefined;
10879 },
10880
10881 _retainParent: function (x, y, z, minZoom) {
10882 var x2 = Math.floor(x / 2),
10883 y2 = Math.floor(y / 2),
10884 z2 = z - 1,
10885 coords2 = new Point(+x2, +y2);
10886 coords2.z = +z2;
10887
10888 var key = this._tileCoordsToKey(coords2),
10889 tile = this._tiles[key];
10890
10891 if (tile && tile.active) {
10892 tile.retain = true;
10893 return true;
10894
10895 } else if (tile && tile.loaded) {
10896 tile.retain = true;
10897 }
10898
10899 if (z2 > minZoom) {
10900 return this._retainParent(x2, y2, z2, minZoom);
10901 }
10902
10903 return false;
10904 },
10905
10906 _retainChildren: function (x, y, z, maxZoom) {
10907
10908 for (var i = 2 * x; i < 2 * x + 2; i++) {
10909 for (var j = 2 * y; j < 2 * y + 2; j++) {
10910
10911 var coords = new Point(i, j);
10912 coords.z = z + 1;
10913
10914 var key = this._tileCoordsToKey(coords),
10915 tile = this._tiles[key];
10916
10917 if (tile && tile.active) {
10918 tile.retain = true;
10919 continue;
10920
10921 } else if (tile && tile.loaded) {
10922 tile.retain = true;
10923 }
10924
10925 if (z + 1 < maxZoom) {
10926 this._retainChildren(i, j, z + 1, maxZoom);
10927 }
10928 }
10929 }
10930 },
10931
10932 _resetView: function (e) {
10933 var animating = e && (e.pinch || e.flyTo);
10934 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
10935 },
10936
10937 _animateZoom: function (e) {
10938 this._setView(e.center, e.zoom, true, e.noUpdate);
10939 },
10940
10941 _clampZoom: function (zoom) {
10942 var options = this.options;
10943
10944 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
10945 return options.minNativeZoom;
10946 }
10947
10948 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
10949 return options.maxNativeZoom;
10950 }
10951
10952 return zoom;
10953 },
10954
10955 _setView: function (center, zoom, noPrune, noUpdate) {
10956 var tileZoom = this._clampZoom(Math.round(zoom));
10957 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
10958 (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
10959 tileZoom = undefined;
10960 }
10961
10962 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
10963
10964 if (!noUpdate || tileZoomChanged) {
10965
10966 this._tileZoom = tileZoom;
10967
10968 if (this._abortLoading) {
10969 this._abortLoading();
10970 }
10971
10972 this._updateLevels();
10973 this._resetGrid();
10974
10975 if (tileZoom !== undefined) {
10976 this._update(center);
10977 }
10978
10979 if (!noPrune) {
10980 this._pruneTiles();
10981 }
10982
10983 // Flag to prevent _updateOpacity from pruning tiles during
10984 // a zoom anim or a pinch gesture
10985 this._noPrune = !!noPrune;
10986 }
10987
10988 this._setZoomTransforms(center, zoom);
10989 },
10990
10991 _setZoomTransforms: function (center, zoom) {
10992 for (var i in this._levels) {
10993 this._setZoomTransform(this._levels[i], center, zoom);
10994 }
10995 },
10996
10997 _setZoomTransform: function (level, center, zoom) {
10998 var scale = this._map.getZoomScale(zoom, level.zoom),
10999 translate = level.origin.multiplyBy(scale)
11000 .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11001
11002 if (any3d) {
11003 setTransform(level.el, translate, scale);
11004 } else {
11005 setPosition(level.el, translate);
11006 }
11007 },
11008
11009 _resetGrid: function () {
11010 var map = this._map,
11011 crs = map.options.crs,
11012 tileSize = this._tileSize = this.getTileSize(),
11013 tileZoom = this._tileZoom;
11014
11015 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11016 if (bounds) {
11017 this._globalTileRange = this._pxBoundsToTileRange(bounds);
11018 }
11019
11020 this._wrapX = crs.wrapLng && !this.options.noWrap && [
11021 Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11022 Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11023 ];
11024 this._wrapY = crs.wrapLat && !this.options.noWrap && [
11025 Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11026 Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11027 ];
11028 },
11029
11030 _onMoveEnd: function () {
11031 if (!this._map || this._map._animatingZoom) { return; }
11032
11033 this._update();
11034 },
11035
11036 _getTiledPixelBounds: function (center) {
11037 var map = this._map,
11038 mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11039 scale = map.getZoomScale(mapZoom, this._tileZoom),
11040 pixelCenter = map.project(center, this._tileZoom).floor(),
11041 halfSize = map.getSize().divideBy(scale * 2);
11042
11043 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11044 },
11045
11046 // Private method to load tiles in the grid's active zoom level according to map bounds
11047 _update: function (center) {
11048 var map = this._map;
11049 if (!map) { return; }
11050 var zoom = this._clampZoom(map.getZoom());
11051
11052 if (center === undefined) { center = map.getCenter(); }
11053 if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom
11054
11055 var pixelBounds = this._getTiledPixelBounds(center),
11056 tileRange = this._pxBoundsToTileRange(pixelBounds),
11057 tileCenter = tileRange.getCenter(),
11058 queue = [],
11059 margin = this.options.keepBuffer,
11060 noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11061 tileRange.getTopRight().add([margin, -margin]));
11062
11063 // Sanity check: panic if the tile range contains Infinity somewhere.
11064 if (!(isFinite(tileRange.min.x) &&
11065 isFinite(tileRange.min.y) &&
11066 isFinite(tileRange.max.x) &&
11067 isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11068
11069 for (var key in this._tiles) {
11070 var c = this._tiles[key].coords;
11071 if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11072 this._tiles[key].current = false;
11073 }
11074 }
11075
11076 // _update just loads more tiles. If the tile zoom level differs too much
11077 // from the map's, let _setView reset levels and prune old tiles.
11078 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11079
11080 // create a queue of coordinates to load tiles from
11081 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11082 for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11083 var coords = new Point(i, j);
11084 coords.z = this._tileZoom;
11085
11086 if (!this._isValidTile(coords)) { continue; }
11087
11088 var tile = this._tiles[this._tileCoordsToKey(coords)];
11089 if (tile) {
11090 tile.current = true;
11091 } else {
11092 queue.push(coords);
11093 }
11094 }
11095 }
11096
11097 // sort tile queue to load tiles in order of their distance to center
11098 queue.sort(function (a, b) {
11099 return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11100 });
11101
11102 if (queue.length !== 0) {
11103 // if it's the first batch of tiles to load
11104 if (!this._loading) {
11105 this._loading = true;
11106 // @event loading: Event
11107 // Fired when the grid layer starts loading tiles.
11108 this.fire('loading');
11109 }
11110
11111 // create DOM fragment to append tiles in one batch
11112 var fragment = document.createDocumentFragment();
11113
11114 for (i = 0; i < queue.length; i++) {
11115 this._addTile(queue[i], fragment);
11116 }
11117
11118 this._level.el.appendChild(fragment);
11119 }
11120 },
11121
11122 _isValidTile: function (coords) {
11123 var crs = this._map.options.crs;
11124
11125 if (!crs.infinite) {
11126 // don't load tile if it's out of bounds and not wrapped
11127 var bounds = this._globalTileRange;
11128 if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11129 (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11130 }
11131
11132 if (!this.options.bounds) { return true; }
11133
11134 // don't load tile if it doesn't intersect the bounds in options
11135 var tileBounds = this._tileCoordsToBounds(coords);
11136 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11137 },
11138
11139 _keyToBounds: function (key) {
11140 return this._tileCoordsToBounds(this._keyToTileCoords(key));
11141 },
11142
11143 _tileCoordsToNwSe: function (coords) {
11144 var map = this._map,
11145 tileSize = this.getTileSize(),
11146 nwPoint = coords.scaleBy(tileSize),
11147 sePoint = nwPoint.add(tileSize),
11148 nw = map.unproject(nwPoint, coords.z),
11149 se = map.unproject(sePoint, coords.z);
11150 return [nw, se];
11151 },
11152
11153 // converts tile coordinates to its geographical bounds
11154 _tileCoordsToBounds: function (coords) {
11155 var bp = this._tileCoordsToNwSe(coords),
11156 bounds = new LatLngBounds(bp[0], bp[1]);
11157
11158 if (!this.options.noWrap) {
11159 bounds = this._map.wrapLatLngBounds(bounds);
11160 }
11161 return bounds;
11162 },
11163 // converts tile coordinates to key for the tile cache
11164 _tileCoordsToKey: function (coords) {
11165 return coords.x + ':' + coords.y + ':' + coords.z;
11166 },
11167
11168 // converts tile cache key to coordinates
11169 _keyToTileCoords: function (key) {
11170 var k = key.split(':'),
11171 coords = new Point(+k[0], +k[1]);
11172 coords.z = +k[2];
11173 return coords;
11174 },
11175
11176 _removeTile: function (key) {
11177 var tile = this._tiles[key];
11178 if (!tile) { return; }
11179
11180 // Cancels any pending http requests associated with the tile
11181 // unless we're on Android's stock browser,
11182 // see https://github.com/Leaflet/Leaflet/issues/137
11183 if (!androidStock) {
11184 tile.el.setAttribute('src', emptyImageUrl);
11185 }
11186 remove(tile.el);
11187
11188 delete this._tiles[key];
11189
11190 // @event tileunload: TileEvent
11191 // Fired when a tile is removed (e.g. when a tile goes off the screen).
11192 this.fire('tileunload', {
11193 tile: tile.el,
11194 coords: this._keyToTileCoords(key)
11195 });
11196 },
11197
11198 _initTile: function (tile) {
11199 addClass(tile, 'leaflet-tile');
11200
11201 var tileSize = this.getTileSize();
11202 tile.style.width = tileSize.x + 'px';
11203 tile.style.height = tileSize.y + 'px';
11204
11205 tile.onselectstart = falseFn;
11206 tile.onmousemove = falseFn;
11207
11208 // update opacity on tiles in IE7-8 because of filter inheritance problems
11209 if (ielt9 && this.options.opacity < 1) {
11210 setOpacity(tile, this.options.opacity);
11211 }
11212
11213 // without this hack, tiles disappear after zoom on Chrome for Android
11214 // https://github.com/Leaflet/Leaflet/issues/2078
11215 if (android && !android23) {
11216 tile.style.WebkitBackfaceVisibility = 'hidden';
11217 }
11218 },
11219
11220 _addTile: function (coords, container) {
11221 var tilePos = this._getTilePos(coords),
11222 key = this._tileCoordsToKey(coords);
11223
11224 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11225
11226 this._initTile(tile);
11227
11228 // if createTile is defined with a second argument ("done" callback),
11229 // we know that tile is async and will be ready later; otherwise
11230 if (this.createTile.length < 2) {
11231 // mark tile as ready, but delay one frame for opacity animation to happen
11232 requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11233 }
11234
11235 setPosition(tile, tilePos);
11236
11237 // save tile in cache
11238 this._tiles[key] = {
11239 el: tile,
11240 coords: coords,
11241 current: true
11242 };
11243
11244 container.appendChild(tile);
11245 // @event tileloadstart: TileEvent
11246 // Fired when a tile is requested and starts loading.
11247 this.fire('tileloadstart', {
11248 tile: tile,
11249 coords: coords
11250 });
11251 },
11252
11253 _tileReady: function (coords, err, tile) {
11254 if (!this._map) { return; }
11255
11256 if (err) {
11257 // @event tileerror: TileErrorEvent
11258 // Fired when there is an error loading a tile.
11259 this.fire('tileerror', {
11260 error: err,
11261 tile: tile,
11262 coords: coords
11263 });
11264 }
11265
11266 var key = this._tileCoordsToKey(coords);
11267
11268 tile = this._tiles[key];
11269 if (!tile) { return; }
11270
11271 tile.loaded = +new Date();
11272 if (this._map._fadeAnimated) {
11273 setOpacity(tile.el, 0);
11274 cancelAnimFrame(this._fadeFrame);
11275 this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11276 } else {
11277 tile.active = true;
11278 this._pruneTiles();
11279 }
11280
11281 if (!err) {
11282 addClass(tile.el, 'leaflet-tile-loaded');
11283
11284 // @event tileload: TileEvent
11285 // Fired when a tile loads.
11286 this.fire('tileload', {
11287 tile: tile.el,
11288 coords: coords
11289 });
11290 }
11291
11292 if (this._noTilesToLoad()) {
11293 this._loading = false;
11294 // @event load: Event
11295 // Fired when the grid layer loaded all visible tiles.
11296 this.fire('load');
11297
11298 if (ielt9 || !this._map._fadeAnimated) {
11299 requestAnimFrame(this._pruneTiles, this);
11300 } else {
11301 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11302 // to trigger a pruning.
11303 setTimeout(bind(this._pruneTiles, this), 250);
11304 }
11305 }
11306 },
11307
11308 _getTilePos: function (coords) {
11309 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11310 },
11311
11312 _wrapCoords: function (coords) {
11313 var newCoords = new Point(
11314 this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11315 this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11316 newCoords.z = coords.z;
11317 return newCoords;
11318 },
11319
11320 _pxBoundsToTileRange: function (bounds) {
11321 var tileSize = this.getTileSize();
11322 return new Bounds(
11323 bounds.min.unscaleBy(tileSize).floor(),
11324 bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11325 },
11326
11327 _noTilesToLoad: function () {
11328 for (var key in this._tiles) {
11329 if (!this._tiles[key].loaded) { return false; }
11330 }
11331 return true;
11332 }
11333 });
11334
11335 // @factory L.gridLayer(options?: GridLayer options)
11336 // Creates a new instance of GridLayer with the supplied options.
11337 function gridLayer(options) {
11338 return new GridLayer(options);
11339 }
11340
11341 /*
11342 * @class TileLayer
11343 * @inherits GridLayer
11344 * @aka L.TileLayer
11345 * Used to load and display tile layers on the map. Extends `GridLayer`.
11346 *
11347 * @example
11348 *
11349 * ```js
11350 * L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar'}).addTo(map);
11351 * ```
11352 *
11353 * @section URL template
11354 * @example
11355 *
11356 * A string of the following form:
11357 *
11358 * ```
11359 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11360 * ```
11361 *
11362 * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.
11363 *
11364 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11365 *
11366 * ```
11367 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11368 * ```
11369 */
11370
11371
11372 var TileLayer = GridLayer.extend({
11373
11374 // @section
11375 // @aka TileLayer options
11376 options: {
11377 // @option minZoom: Number = 0
11378 // The minimum zoom level down to which this layer will be displayed (inclusive).
11379 minZoom: 0,
11380
11381 // @option maxZoom: Number = 18
11382 // The maximum zoom level up to which this layer will be displayed (inclusive).
11383 maxZoom: 18,
11384
11385 // @option subdomains: String|String[] = 'abc'
11386 // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
11387 subdomains: 'abc',
11388
11389 // @option errorTileUrl: String = ''
11390 // URL to the tile image to show in place of the tile that failed to load.
11391 errorTileUrl: '',
11392
11393 // @option zoomOffset: Number = 0
11394 // The zoom number used in tile URLs will be offset with this value.
11395 zoomOffset: 0,
11396
11397 // @option tms: Boolean = false
11398 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11399 tms: false,
11400
11401 // @option zoomReverse: Boolean = false
11402 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11403 zoomReverse: false,
11404
11405 // @option detectRetina: Boolean = false
11406 // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
11407 detectRetina: false,
11408
11409 // @option crossOrigin: Boolean = false
11410 // If true, all tiles will have their crossOrigin attribute set to ''. This is needed if you want to access tile pixel data.
11411 crossOrigin: false
11412 },
11413
11414 initialize: function (url, options) {
11415
11416 this._url = url;
11417
11418 options = setOptions(this, options);
11419
11420 // detecting retina displays, adjusting tileSize and zoom levels
11421 if (options.detectRetina && retina && options.maxZoom > 0) {
11422
11423 options.tileSize = Math.floor(options.tileSize / 2);
11424
11425 if (!options.zoomReverse) {
11426 options.zoomOffset++;
11427 options.maxZoom--;
11428 } else {
11429 options.zoomOffset--;
11430 options.minZoom++;
11431 }
11432
11433 options.minZoom = Math.max(0, options.minZoom);
11434 }
11435
11436 if (typeof options.subdomains === 'string') {
11437 options.subdomains = options.subdomains.split('');
11438 }
11439
11440 // for https://github.com/Leaflet/Leaflet/issues/137
11441 if (!android) {
11442 this.on('tileunload', this._onTileRemove);
11443 }
11444 },
11445
11446 // @method setUrl(url: String, noRedraw?: Boolean): this
11447 // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11448 setUrl: function (url, noRedraw) {
11449 this._url = url;
11450
11451 if (!noRedraw) {
11452 this.redraw();
11453 }
11454 return this;
11455 },
11456
11457 // @method createTile(coords: Object, done?: Function): HTMLElement
11458 // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11459 // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
11460 // callback is called when the tile has been loaded.
11461 createTile: function (coords, done) {
11462 var tile = document.createElement('img');
11463
11464 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11465 on(tile, 'error', bind(this._tileOnError, this, done, tile));
11466
11467 if (this.options.crossOrigin) {
11468 tile.crossOrigin = '';
11469 }
11470
11471 /*
11472 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11473 http://www.w3.org/TR/WCAG20-TECHS/H67
11474 */
11475 tile.alt = '';
11476
11477 /*
11478 Set role="presentation" to force screen readers to ignore this
11479 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11480 */
11481 tile.setAttribute('role', 'presentation');
11482
11483 tile.src = this.getTileUrl(coords);
11484
11485 return tile;
11486 },
11487
11488 // @section Extension methods
11489 // @uninheritable
11490 // Layers extending `TileLayer` might reimplement the following method.
11491 // @method getTileUrl(coords: Object): String
11492 // Called only internally, returns the URL for a tile given its coordinates.
11493 // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11494 getTileUrl: function (coords) {
11495 var data = {
11496 r: retina ? '@2x' : '',
11497 s: this._getSubdomain(coords),
11498 x: coords.x,
11499 y: coords.y,
11500 z: this._getZoomForUrl()
11501 };
11502 if (this._map && !this._map.options.crs.infinite) {
11503 var invertedY = this._globalTileRange.max.y - coords.y;
11504 if (this.options.tms) {
11505 data['y'] = invertedY;
11506 }
11507 data['-y'] = invertedY;
11508 }
11509
11510 return template(this._url, extend(data, this.options));
11511 },
11512
11513 _tileOnLoad: function (done, tile) {
11514 // For https://github.com/Leaflet/Leaflet/issues/3332
11515 if (ielt9) {
11516 setTimeout(bind(done, this, null, tile), 0);
11517 } else {
11518 done(null, tile);
11519 }
11520 },
11521
11522 _tileOnError: function (done, tile, e) {
11523 var errorUrl = this.options.errorTileUrl;
11524 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
11525 tile.src = errorUrl;
11526 }
11527 done(e, tile);
11528 },
11529
11530 _onTileRemove: function (e) {
11531 e.tile.onload = null;
11532 },
11533
11534 _getZoomForUrl: function () {
11535 var zoom = this._tileZoom,
11536 maxZoom = this.options.maxZoom,
11537 zoomReverse = this.options.zoomReverse,
11538 zoomOffset = this.options.zoomOffset;
11539
11540 if (zoomReverse) {
11541 zoom = maxZoom - zoom;
11542 }
11543
11544 return zoom + zoomOffset;
11545 },
11546
11547 _getSubdomain: function (tilePoint) {
11548 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11549 return this.options.subdomains[index];
11550 },
11551
11552 // stops loading all tiles in the background layer
11553 _abortLoading: function () {
11554 var i, tile;
11555 for (i in this._tiles) {
11556 if (this._tiles[i].coords.z !== this._tileZoom) {
11557 tile = this._tiles[i].el;
11558
11559 tile.onload = falseFn;
11560 tile.onerror = falseFn;
11561
11562 if (!tile.complete) {
11563 tile.src = emptyImageUrl;
11564 remove(tile);
11565 delete this._tiles[i];
11566 }
11567 }
11568 }
11569 }
11570 });
11571
11572
11573 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11574 // Instantiates a tile layer object given a `URL template` and optionally an options object.
11575
11576 function tileLayer(url, options) {
11577 return new TileLayer(url, options);
11578 }
11579
11580 /*
11581 * @class TileLayer.WMS
11582 * @inherits TileLayer
11583 * @aka L.TileLayer.WMS
11584 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11585 *
11586 * @example
11587 *
11588 * ```js
11589 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11590 * layers: 'nexrad-n0r-900913',
11591 * format: 'image/png',
11592 * transparent: true,
11593 * attribution: "Weather data © 2012 IEM Nexrad"
11594 * });
11595 * ```
11596 */
11597
11598 var TileLayerWMS = TileLayer.extend({
11599
11600 // @section
11601 // @aka TileLayer.WMS options
11602 // If any custom options not documented here are used, they will be sent to the
11603 // WMS server as extra parameters in each request URL. This can be useful for
11604 // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11605 defaultWmsParams: {
11606 service: 'WMS',
11607 request: 'GetMap',
11608
11609 // @option layers: String = ''
11610 // **(required)** Comma-separated list of WMS layers to show.
11611 layers: '',
11612
11613 // @option styles: String = ''
11614 // Comma-separated list of WMS styles.
11615 styles: '',
11616
11617 // @option format: String = 'image/jpeg'
11618 // WMS image format (use `'image/png'` for layers with transparency).
11619 format: 'image/jpeg',
11620
11621 // @option transparent: Boolean = false
11622 // If `true`, the WMS service will return images with transparency.
11623 transparent: false,
11624
11625 // @option version: String = '1.1.1'
11626 // Version of the WMS service to use
11627 version: '1.1.1'
11628 },
11629
11630 options: {
11631 // @option crs: CRS = null
11632 // Coordinate Reference System to use for the WMS requests, defaults to
11633 // map CRS. Don't change this if you're not sure what it means.
11634 crs: null,
11635
11636 // @option uppercase: Boolean = false
11637 // If `true`, WMS request parameter keys will be uppercase.
11638 uppercase: false
11639 },
11640
11641 initialize: function (url, options) {
11642
11643 this._url = url;
11644
11645 var wmsParams = extend({}, this.defaultWmsParams);
11646
11647 // all keys that are not TileLayer options go to WMS params
11648 for (var i in options) {
11649 if (!(i in this.options)) {
11650 wmsParams[i] = options[i];
11651 }
11652 }
11653
11654 options = setOptions(this, options);
11655
11656 var realRetina = options.detectRetina && retina ? 2 : 1;
11657 var tileSize = this.getTileSize();
11658 wmsParams.width = tileSize.x * realRetina;
11659 wmsParams.height = tileSize.y * realRetina;
11660
11661 this.wmsParams = wmsParams;
11662 },
11663
11664 onAdd: function (map) {
11665
11666 this._crs = this.options.crs || map.options.crs;
11667 this._wmsVersion = parseFloat(this.wmsParams.version);
11668
11669 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
11670 this.wmsParams[projectionKey] = this._crs.code;
11671
11672 TileLayer.prototype.onAdd.call(this, map);
11673 },
11674
11675 getTileUrl: function (coords) {
11676
11677 var tileBounds = this._tileCoordsToNwSe(coords),
11678 crs = this._crs,
11679 bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
11680 min = bounds.min,
11681 max = bounds.max,
11682 bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
11683 [min.y, min.x, max.y, max.x] :
11684 [min.x, min.y, max.x, max.y]).join(','),
11685 url = L.TileLayer.prototype.getTileUrl.call(this, coords);
11686 return url +
11687 getParamString(this.wmsParams, url, this.options.uppercase) +
11688 (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
11689 },
11690
11691 // @method setParams(params: Object, noRedraw?: Boolean): this
11692 // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
11693 setParams: function (params, noRedraw) {
11694
11695 extend(this.wmsParams, params);
11696
11697 if (!noRedraw) {
11698 this.redraw();
11699 }
11700
11701 return this;
11702 }
11703 });
11704
11705
11706 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
11707 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
11708 function tileLayerWMS(url, options) {
11709 return new TileLayerWMS(url, options);
11710 }
11711
11712 TileLayer.WMS = TileLayerWMS;
11713 tileLayer.wms = tileLayerWMS;
11714
11715 /*
11716 * @class Renderer
11717 * @inherits Layer
11718 * @aka L.Renderer
11719 *
11720 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
11721 * DOM container of the renderer, its bounds, and its zoom animation.
11722 *
11723 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
11724 * itself can be added or removed to the map. All paths use a renderer, which can
11725 * be implicit (the map will decide the type of renderer and use it automatically)
11726 * or explicit (using the [`renderer`](#path-renderer) option of the path).
11727 *
11728 * Do not use this class directly, use `SVG` and `Canvas` instead.
11729 *
11730 * @event update: Event
11731 * Fired when the renderer updates its bounds, center and zoom, for example when
11732 * its map has moved
11733 */
11734
11735 var Renderer = Layer.extend({
11736
11737 // @section
11738 // @aka Renderer options
11739 options: {
11740 // @option padding: Number = 0.1
11741 // How much to extend the clip area around the map view (relative to its size)
11742 // e.g. 0.1 would be 10% of map view in each direction
11743 padding: 0.1,
11744
11745 // @option tolerance: Number = 0
11746 // How much to extend click tolerance round a path/object on the map
11747 tolerance : 0
11748 },
11749
11750 initialize: function (options) {
11751 setOptions(this, options);
11752 stamp(this);
11753 this._layers = this._layers || {};
11754 },
11755
11756 onAdd: function () {
11757 if (!this._container) {
11758 this._initContainer(); // defined by renderer implementations
11759
11760 if (this._zoomAnimated) {
11761 addClass(this._container, 'leaflet-zoom-animated');
11762 }
11763 }
11764
11765 this.getPane().appendChild(this._container);
11766 this._update();
11767 this.on('update', this._updatePaths, this);
11768 },
11769
11770 onRemove: function () {
11771 this.off('update', this._updatePaths, this);
11772 this._destroyContainer();
11773 },
11774
11775 getEvents: function () {
11776 var events = {
11777 viewreset: this._reset,
11778 zoom: this._onZoom,
11779 moveend: this._update,
11780 zoomend: this._onZoomEnd
11781 };
11782 if (this._zoomAnimated) {
11783 events.zoomanim = this._onAnimZoom;
11784 }
11785 return events;
11786 },
11787
11788 _onAnimZoom: function (ev) {
11789 this._updateTransform(ev.center, ev.zoom);
11790 },
11791
11792 _onZoom: function () {
11793 this._updateTransform(this._map.getCenter(), this._map.getZoom());
11794 },
11795
11796 _updateTransform: function (center, zoom) {
11797 var scale = this._map.getZoomScale(zoom, this._zoom),
11798 position = getPosition(this._container),
11799 viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
11800 currentCenterPoint = this._map.project(this._center, zoom),
11801 destCenterPoint = this._map.project(center, zoom),
11802 centerOffset = destCenterPoint.subtract(currentCenterPoint),
11803
11804 topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
11805
11806 if (any3d) {
11807 setTransform(this._container, topLeftOffset, scale);
11808 } else {
11809 setPosition(this._container, topLeftOffset);
11810 }
11811 },
11812
11813 _reset: function () {
11814 this._update();
11815 this._updateTransform(this._center, this._zoom);
11816
11817 for (var id in this._layers) {
11818 this._layers[id]._reset();
11819 }
11820 },
11821
11822 _onZoomEnd: function () {
11823 for (var id in this._layers) {
11824 this._layers[id]._project();
11825 }
11826 },
11827
11828 _updatePaths: function () {
11829 for (var id in this._layers) {
11830 this._layers[id]._update();
11831 }
11832 },
11833
11834 _update: function () {
11835 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
11836 // Subclasses are responsible of firing the 'update' event.
11837 var p = this.options.padding,
11838 size = this._map.getSize(),
11839 min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
11840
11841 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
11842
11843 this._center = this._map.getCenter();
11844 this._zoom = this._map.getZoom();
11845 }
11846 });
11847
11848 /*
11849 * @class Canvas
11850 * @inherits Renderer
11851 * @aka L.Canvas
11852 *
11853 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
11854 * Inherits `Renderer`.
11855 *
11856 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
11857 * available in all web browsers, notably IE8, and overlapping geometries might
11858 * not display properly in some edge cases.
11859 *
11860 * @example
11861 *
11862 * Use Canvas by default for all paths in the map:
11863 *
11864 * ```js
11865 * var map = L.map('map', {
11866 * renderer: L.canvas()
11867 * });
11868 * ```
11869 *
11870 * Use a Canvas renderer with extra padding for specific vector geometries:
11871 *
11872 * ```js
11873 * var map = L.map('map');
11874 * var myRenderer = L.canvas({ padding: 0.5 });
11875 * var line = L.polyline( coordinates, { renderer: myRenderer } );
11876 * var circle = L.circle( center, { renderer: myRenderer } );
11877 * ```
11878 */
11879
11880 var Canvas = Renderer.extend({
11881 getEvents: function () {
11882 var events = Renderer.prototype.getEvents.call(this);
11883 events.viewprereset = this._onViewPreReset;
11884 return events;
11885 },
11886
11887 _onViewPreReset: function () {
11888 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
11889 this._postponeUpdatePaths = true;
11890 },
11891
11892 onAdd: function () {
11893 Renderer.prototype.onAdd.call(this);
11894
11895 // Redraw vectors since canvas is cleared upon removal,
11896 // in case of removing the renderer itself from the map.
11897 this._draw();
11898 },
11899
11900 _initContainer: function () {
11901 var container = this._container = document.createElement('canvas');
11902
11903 on(container, 'mousemove', throttle(this._onMouseMove, 32, this), this);
11904 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
11905 on(container, 'mouseout', this._handleMouseOut, this);
11906
11907 this._ctx = container.getContext('2d');
11908 },
11909
11910 _destroyContainer: function () {
11911 delete this._ctx;
11912 remove(this._container);
11913 off(this._container);
11914 delete this._container;
11915 },
11916
11917 _updatePaths: function () {
11918 if (this._postponeUpdatePaths) { return; }
11919
11920 var layer;
11921 this._redrawBounds = null;
11922 for (var id in this._layers) {
11923 layer = this._layers[id];
11924 layer._update();
11925 }
11926 this._redraw();
11927 },
11928
11929 _update: function () {
11930 if (this._map._animatingZoom && this._bounds) { return; }
11931
11932 this._drawnLayers = {};
11933
11934 Renderer.prototype._update.call(this);
11935
11936 var b = this._bounds,
11937 container = this._container,
11938 size = b.getSize(),
11939 m = retina ? 2 : 1;
11940
11941 setPosition(container, b.min);
11942
11943 // set canvas size (also clearing it); use double size on retina
11944 container.width = m * size.x;
11945 container.height = m * size.y;
11946 container.style.width = size.x + 'px';
11947 container.style.height = size.y + 'px';
11948
11949 if (retina) {
11950 this._ctx.scale(2, 2);
11951 }
11952
11953 // translate so we use the same path coordinates after canvas element moves
11954 this._ctx.translate(-b.min.x, -b.min.y);
11955
11956 // Tell paths to redraw themselves
11957 this.fire('update');
11958 },
11959
11960 _reset: function () {
11961 Renderer.prototype._reset.call(this);
11962
11963 if (this._postponeUpdatePaths) {
11964 this._postponeUpdatePaths = false;
11965 this._updatePaths();
11966 }
11967 },
11968
11969 _initPath: function (layer) {
11970 this._updateDashArray(layer);
11971 this._layers[stamp(layer)] = layer;
11972
11973 var order = layer._order = {
11974 layer: layer,
11975 prev: this._drawLast,
11976 next: null
11977 };
11978 if (this._drawLast) { this._drawLast.next = order; }
11979 this._drawLast = order;
11980 this._drawFirst = this._drawFirst || this._drawLast;
11981 },
11982
11983 _addPath: function (layer) {
11984 this._requestRedraw(layer);
11985 },
11986
11987 _removePath: function (layer) {
11988 var order = layer._order;
11989 var next = order.next;
11990 var prev = order.prev;
11991
11992 if (next) {
11993 next.prev = prev;
11994 } else {
11995 this._drawLast = prev;
11996 }
11997 if (prev) {
11998 prev.next = next;
11999 } else {
12000 this._drawFirst = next;
12001 }
12002
12003 delete layer._order;
12004
12005 delete this._layers[L.stamp(layer)];
12006
12007 this._requestRedraw(layer);
12008 },
12009
12010 _updatePath: function (layer) {
12011 // Redraw the union of the layer's old pixel
12012 // bounds and the new pixel bounds.
12013 this._extendRedrawBounds(layer);
12014 layer._project();
12015 layer._update();
12016 // The redraw will extend the redraw bounds
12017 // with the new pixel bounds.
12018 this._requestRedraw(layer);
12019 },
12020
12021 _updateStyle: function (layer) {
12022 this._updateDashArray(layer);
12023 this._requestRedraw(layer);
12024 },
12025
12026 _updateDashArray: function (layer) {
12027 if (layer.options.dashArray) {
12028 var parts = layer.options.dashArray.split(','),
12029 dashArray = [],
12030 i;
12031 for (i = 0; i < parts.length; i++) {
12032 dashArray.push(Number(parts[i]));
12033 }
12034 layer.options._dashArray = dashArray;
12035 }
12036 },
12037
12038 _requestRedraw: function (layer) {
12039 if (!this._map) { return; }
12040
12041 this._extendRedrawBounds(layer);
12042 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12043 },
12044
12045 _extendRedrawBounds: function (layer) {
12046 if (layer._pxBounds) {
12047 var padding = (layer.options.weight || 0) + 1;
12048 this._redrawBounds = this._redrawBounds || new Bounds();
12049 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12050 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12051 }
12052 },
12053
12054 _redraw: function () {
12055 this._redrawRequest = null;
12056
12057 if (this._redrawBounds) {
12058 this._redrawBounds.min._floor();
12059 this._redrawBounds.max._ceil();
12060 }
12061
12062 this._clear(); // clear layers in redraw bounds
12063 this._draw(); // draw layers
12064
12065 this._redrawBounds = null;
12066 },
12067
12068 _clear: function () {
12069 var bounds = this._redrawBounds;
12070 if (bounds) {
12071 var size = bounds.getSize();
12072 this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12073 } else {
12074 this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12075 }
12076 },
12077
12078 _draw: function () {
12079 var layer, bounds = this._redrawBounds;
12080 this._ctx.save();
12081 if (bounds) {
12082 var size = bounds.getSize();
12083 this._ctx.beginPath();
12084 this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12085 this._ctx.clip();
12086 }
12087
12088 this._drawing = true;
12089
12090 for (var order = this._drawFirst; order; order = order.next) {
12091 layer = order.layer;
12092 if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12093 layer._updatePath();
12094 }
12095 }
12096
12097 this._drawing = false;
12098
12099 this._ctx.restore(); // Restore state before clipping.
12100 },
12101
12102 _updatePoly: function (layer, closed) {
12103 if (!this._drawing) { return; }
12104
12105 var i, j, len2, p,
12106 parts = layer._parts,
12107 len = parts.length,
12108 ctx = this._ctx;
12109
12110 if (!len) { return; }
12111
12112 this._drawnLayers[layer._leaflet_id] = layer;
12113
12114 ctx.beginPath();
12115
12116 for (i = 0; i < len; i++) {
12117 for (j = 0, len2 = parts[i].length; j < len2; j++) {
12118 p = parts[i][j];
12119 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12120 }
12121 if (closed) {
12122 ctx.closePath();
12123 }
12124 }
12125
12126 this._fillStroke(ctx, layer);
12127
12128 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12129 },
12130
12131 _updateCircle: function (layer) {
12132
12133 if (!this._drawing || layer._empty()) { return; }
12134
12135 var p = layer._point,
12136 ctx = this._ctx,
12137 r = Math.max(Math.round(layer._radius), 1),
12138 s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12139
12140 this._drawnLayers[layer._leaflet_id] = layer;
12141
12142 if (s !== 1) {
12143 ctx.save();
12144 ctx.scale(1, s);
12145 }
12146
12147 ctx.beginPath();
12148 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12149
12150 if (s !== 1) {
12151 ctx.restore();
12152 }
12153
12154 this._fillStroke(ctx, layer);
12155 },
12156
12157 _fillStroke: function (ctx, layer) {
12158 var options = layer.options;
12159
12160 if (options.fill) {
12161 ctx.globalAlpha = options.fillOpacity;
12162 ctx.fillStyle = options.fillColor || options.color;
12163 ctx.fill(options.fillRule || 'evenodd');
12164 }
12165
12166 if (options.stroke && options.weight !== 0) {
12167 if (ctx.setLineDash) {
12168 ctx.setLineDash(layer.options && layer.options._dashArray || []);
12169 }
12170 ctx.globalAlpha = options.opacity;
12171 ctx.lineWidth = options.weight;
12172 ctx.strokeStyle = options.color;
12173 ctx.lineCap = options.lineCap;
12174 ctx.lineJoin = options.lineJoin;
12175 ctx.stroke();
12176 }
12177 },
12178
12179 // Canvas obviously doesn't have mouse events for individual drawn objects,
12180 // so we emulate that by calculating what's under the mouse on mousemove/click manually
12181
12182 _onClick: function (e) {
12183 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12184
12185 for (var order = this._drawFirst; order; order = order.next) {
12186 layer = order.layer;
12187 if (layer.options.interactive && layer._containsPoint(point) && !this._map._draggableMoved(layer)) {
12188 clickedLayer = layer;
12189 }
12190 }
12191 if (clickedLayer) {
12192 fakeStop(e);
12193 this._fireEvent([clickedLayer], e);
12194 }
12195 },
12196
12197 _onMouseMove: function (e) {
12198 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12199
12200 var point = this._map.mouseEventToLayerPoint(e);
12201 this._handleMouseHover(e, point);
12202 },
12203
12204
12205 _handleMouseOut: function (e) {
12206 var layer = this._hoveredLayer;
12207 if (layer) {
12208 // if we're leaving the layer, fire mouseout
12209 removeClass(this._container, 'leaflet-interactive');
12210 this._fireEvent([layer], e, 'mouseout');
12211 this._hoveredLayer = null;
12212 }
12213 },
12214
12215 _handleMouseHover: function (e, point) {
12216 var layer, candidateHoveredLayer;
12217
12218 for (var order = this._drawFirst; order; order = order.next) {
12219 layer = order.layer;
12220 if (layer.options.interactive && layer._containsPoint(point)) {
12221 candidateHoveredLayer = layer;
12222 }
12223 }
12224
12225 if (candidateHoveredLayer !== this._hoveredLayer) {
12226 this._handleMouseOut(e);
12227
12228 if (candidateHoveredLayer) {
12229 addClass(this._container, 'leaflet-interactive'); // change cursor
12230 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12231 this._hoveredLayer = candidateHoveredLayer;
12232 }
12233 }
12234
12235 if (this._hoveredLayer) {
12236 this._fireEvent([this._hoveredLayer], e);
12237 }
12238 },
12239
12240 _fireEvent: function (layers, e, type) {
12241 this._map._fireDOMEvent(e, type || e.type, layers);
12242 },
12243
12244 _bringToFront: function (layer) {
12245 var order = layer._order;
12246 var next = order.next;
12247 var prev = order.prev;
12248
12249 if (next) {
12250 next.prev = prev;
12251 } else {
12252 // Already last
12253 return;
12254 }
12255 if (prev) {
12256 prev.next = next;
12257 } else if (next) {
12258 // Update first entry unless this is the
12259 // single entry
12260 this._drawFirst = next;
12261 }
12262
12263 order.prev = this._drawLast;
12264 this._drawLast.next = order;
12265
12266 order.next = null;
12267 this._drawLast = order;
12268
12269 this._requestRedraw(layer);
12270 },
12271
12272 _bringToBack: function (layer) {
12273 var order = layer._order;
12274 var next = order.next;
12275 var prev = order.prev;
12276
12277 if (prev) {
12278 prev.next = next;
12279 } else {
12280 // Already first
12281 return;
12282 }
12283 if (next) {
12284 next.prev = prev;
12285 } else if (prev) {
12286 // Update last entry unless this is the
12287 // single entry
12288 this._drawLast = prev;
12289 }
12290
12291 order.prev = null;
12292
12293 order.next = this._drawFirst;
12294 this._drawFirst.prev = order;
12295 this._drawFirst = order;
12296
12297 this._requestRedraw(layer);
12298 }
12299 });
12300
12301 // @factory L.canvas(options?: Renderer options)
12302 // Creates a Canvas renderer with the given options.
12303 function canvas$1(options) {
12304 return canvas ? new Canvas(options) : null;
12305 }
12306
12307 /*
12308 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12309 */
12310
12311
12312 var vmlCreate = (function () {
12313 try {
12314 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12315 return function (name) {
12316 return document.createElement('<lvml:' + name + ' class="lvml">');
12317 };
12318 } catch (e) {
12319 return function (name) {
12320 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12321 };
12322 }
12323 })();
12324
12325
12326 /*
12327 * @class SVG
12328 *
12329 * Although SVG is not available on IE7 and IE8, these browsers support [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language), and the SVG renderer will fall back to VML in this case.
12330 *
12331 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12332 * with old versions of Internet Explorer.
12333 */
12334
12335 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12336 var vmlMixin = {
12337
12338 _initContainer: function () {
12339 this._container = create$1('div', 'leaflet-vml-container');
12340 },
12341
12342 _update: function () {
12343 if (this._map._animatingZoom) { return; }
12344 Renderer.prototype._update.call(this);
12345 this.fire('update');
12346 },
12347
12348 _initPath: function (layer) {
12349 var container = layer._container = vmlCreate('shape');
12350
12351 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12352
12353 container.coordsize = '1 1';
12354
12355 layer._path = vmlCreate('path');
12356 container.appendChild(layer._path);
12357
12358 this._updateStyle(layer);
12359 this._layers[stamp(layer)] = layer;
12360 },
12361
12362 _addPath: function (layer) {
12363 var container = layer._container;
12364 this._container.appendChild(container);
12365
12366 if (layer.options.interactive) {
12367 layer.addInteractiveTarget(container);
12368 }
12369 },
12370
12371 _removePath: function (layer) {
12372 var container = layer._container;
12373 remove(container);
12374 layer.removeInteractiveTarget(container);
12375 delete this._layers[stamp(layer)];
12376 },
12377
12378 _updateStyle: function (layer) {
12379 var stroke = layer._stroke,
12380 fill = layer._fill,
12381 options = layer.options,
12382 container = layer._container;
12383
12384 container.stroked = !!options.stroke;
12385 container.filled = !!options.fill;
12386
12387 if (options.stroke) {
12388 if (!stroke) {
12389 stroke = layer._stroke = vmlCreate('stroke');
12390 }
12391 container.appendChild(stroke);
12392 stroke.weight = options.weight + 'px';
12393 stroke.color = options.color;
12394 stroke.opacity = options.opacity;
12395
12396 if (options.dashArray) {
12397 stroke.dashStyle = isArray(options.dashArray) ?
12398 options.dashArray.join(' ') :
12399 options.dashArray.replace(/( *, *)/g, ' ');
12400 } else {
12401 stroke.dashStyle = '';
12402 }
12403 stroke.endcap = options.lineCap.replace('butt', 'flat');
12404 stroke.joinstyle = options.lineJoin;
12405
12406 } else if (stroke) {
12407 container.removeChild(stroke);
12408 layer._stroke = null;
12409 }
12410
12411 if (options.fill) {
12412 if (!fill) {
12413 fill = layer._fill = vmlCreate('fill');
12414 }
12415 container.appendChild(fill);
12416 fill.color = options.fillColor || options.color;
12417 fill.opacity = options.fillOpacity;
12418
12419 } else if (fill) {
12420 container.removeChild(fill);
12421 layer._fill = null;
12422 }
12423 },
12424
12425 _updateCircle: function (layer) {
12426 var p = layer._point.round(),
12427 r = Math.round(layer._radius),
12428 r2 = Math.round(layer._radiusY || r);
12429
12430 this._setPath(layer, layer._empty() ? 'M0 0' :
12431 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12432 },
12433
12434 _setPath: function (layer, path) {
12435 layer._path.v = path;
12436 },
12437
12438 _bringToFront: function (layer) {
12439 toFront(layer._container);
12440 },
12441
12442 _bringToBack: function (layer) {
12443 toBack(layer._container);
12444 }
12445 };
12446
12447 var create$2 = vml ? vmlCreate : svgCreate;
12448
12449 /*
12450 * @class SVG
12451 * @inherits Renderer
12452 * @aka L.SVG
12453 *
12454 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12455 * Inherits `Renderer`.
12456 *
12457 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
12458 * available in all web browsers, notably Android 2.x and 3.x.
12459 *
12460 * Although SVG is not available on IE7 and IE8, these browsers support
12461 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12462 * (a now deprecated technology), and the SVG renderer will fall back to VML in
12463 * this case.
12464 *
12465 * @example
12466 *
12467 * Use SVG by default for all paths in the map:
12468 *
12469 * ```js
12470 * var map = L.map('map', {
12471 * renderer: L.svg()
12472 * });
12473 * ```
12474 *
12475 * Use a SVG renderer with extra padding for specific vector geometries:
12476 *
12477 * ```js
12478 * var map = L.map('map');
12479 * var myRenderer = L.svg({ padding: 0.5 });
12480 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12481 * var circle = L.circle( center, { renderer: myRenderer } );
12482 * ```
12483 */
12484
12485 var SVG = Renderer.extend({
12486
12487 getEvents: function () {
12488 var events = Renderer.prototype.getEvents.call(this);
12489 events.zoomstart = this._onZoomStart;
12490 return events;
12491 },
12492
12493 _initContainer: function () {
12494 this._container = create$2('svg');
12495
12496 // makes it possible to click through svg root; we'll reset it back in individual paths
12497 this._container.setAttribute('pointer-events', 'none');
12498
12499 this._rootGroup = create$2('g');
12500 this._container.appendChild(this._rootGroup);
12501 },
12502
12503 _destroyContainer: function () {
12504 remove(this._container);
12505 off(this._container);
12506 delete this._container;
12507 delete this._rootGroup;
12508 delete this._svgSize;
12509 },
12510
12511 _onZoomStart: function () {
12512 // Drag-then-pinch interactions might mess up the center and zoom.
12513 // In this case, the easiest way to prevent this is re-do the renderer
12514 // bounds and padding when the zooming starts.
12515 this._update();
12516 },
12517
12518 _update: function () {
12519 if (this._map._animatingZoom && this._bounds) { return; }
12520
12521 Renderer.prototype._update.call(this);
12522
12523 var b = this._bounds,
12524 size = b.getSize(),
12525 container = this._container;
12526
12527 // set size of svg-container if changed
12528 if (!this._svgSize || !this._svgSize.equals(size)) {
12529 this._svgSize = size;
12530 container.setAttribute('width', size.x);
12531 container.setAttribute('height', size.y);
12532 }
12533
12534 // movement: update container viewBox so that we don't have to change coordinates of individual layers
12535 setPosition(container, b.min);
12536 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12537
12538 this.fire('update');
12539 },
12540
12541 // methods below are called by vector layers implementations
12542
12543 _initPath: function (layer) {
12544 var path = layer._path = create$2('path');
12545
12546 // @namespace Path
12547 // @option className: String = null
12548 // Custom class name set on an element. Only for SVG renderer.
12549 if (layer.options.className) {
12550 addClass(path, layer.options.className);
12551 }
12552
12553 if (layer.options.interactive) {
12554 addClass(path, 'leaflet-interactive');
12555 }
12556
12557 this._updateStyle(layer);
12558 this._layers[stamp(layer)] = layer;
12559 },
12560
12561 _addPath: function (layer) {
12562 if (!this._rootGroup) { this._initContainer(); }
12563 this._rootGroup.appendChild(layer._path);
12564 layer.addInteractiveTarget(layer._path);
12565 },
12566
12567 _removePath: function (layer) {
12568 remove(layer._path);
12569 layer.removeInteractiveTarget(layer._path);
12570 delete this._layers[stamp(layer)];
12571 },
12572
12573 _updatePath: function (layer) {
12574 layer._project();
12575 layer._update();
12576 },
12577
12578 _updateStyle: function (layer) {
12579 var path = layer._path,
12580 options = layer.options;
12581
12582 if (!path) { return; }
12583
12584 if (options.stroke) {
12585 path.setAttribute('stroke', options.color);
12586 path.setAttribute('stroke-opacity', options.opacity);
12587 path.setAttribute('stroke-width', options.weight);
12588 path.setAttribute('stroke-linecap', options.lineCap);
12589 path.setAttribute('stroke-linejoin', options.lineJoin);
12590
12591 if (options.dashArray) {
12592 path.setAttribute('stroke-dasharray', options.dashArray);
12593 } else {
12594 path.removeAttribute('stroke-dasharray');
12595 }
12596
12597 if (options.dashOffset) {
12598 path.setAttribute('stroke-dashoffset', options.dashOffset);
12599 } else {
12600 path.removeAttribute('stroke-dashoffset');
12601 }
12602 } else {
12603 path.setAttribute('stroke', 'none');
12604 }
12605
12606 if (options.fill) {
12607 path.setAttribute('fill', options.fillColor || options.color);
12608 path.setAttribute('fill-opacity', options.fillOpacity);
12609 path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12610 } else {
12611 path.setAttribute('fill', 'none');
12612 }
12613 },
12614
12615 _updatePoly: function (layer, closed) {
12616 this._setPath(layer, pointsToPath(layer._parts, closed));
12617 },
12618
12619 _updateCircle: function (layer) {
12620 var p = layer._point,
12621 r = Math.max(Math.round(layer._radius), 1),
12622 r2 = Math.max(Math.round(layer._radiusY), 1) || r,
12623 arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12624
12625 // drawing a circle with two half-arcs
12626 var d = layer._empty() ? 'M0 0' :
12627 'M' + (p.x - r) + ',' + p.y +
12628 arc + (r * 2) + ',0 ' +
12629 arc + (-r * 2) + ',0 ';
12630
12631 this._setPath(layer, d);
12632 },
12633
12634 _setPath: function (layer, path) {
12635 layer._path.setAttribute('d', path);
12636 },
12637
12638 // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12639 _bringToFront: function (layer) {
12640 toFront(layer._path);
12641 },
12642
12643 _bringToBack: function (layer) {
12644 toBack(layer._path);
12645 }
12646 });
12647
12648 if (vml) {
12649 SVG.include(vmlMixin);
12650 }
12651
12652 // @namespace SVG
12653 // @factory L.svg(options?: Renderer options)
12654 // Creates a SVG renderer with the given options.
12655 function svg$1(options) {
12656 return svg || vml ? new SVG(options) : null;
12657 }
12658
12659 Map.include({
12660 // @namespace Map; @method getRenderer(layer: Path): Renderer
12661 // Returns the instance of `Renderer` that should be used to render the given
12662 // `Path`. It will ensure that the `renderer` options of the map and paths
12663 // are respected, and that the renderers do exist on the map.
12664 getRenderer: function (layer) {
12665 // @namespace Path; @option renderer: Renderer
12666 // Use this specific instance of `Renderer` for this path. Takes
12667 // precedence over the map's [default renderer](#map-renderer).
12668 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
12669
12670 if (!renderer) {
12671 // @namespace Map; @option preferCanvas: Boolean = false
12672 // Whether `Path`s should be rendered on a `Canvas` renderer.
12673 // By default, all `Path`s are rendered in a `SVG` renderer.
12674 renderer = this._renderer = (this.options.preferCanvas && canvas$1()) || svg$1();
12675 }
12676
12677 if (!this.hasLayer(renderer)) {
12678 this.addLayer(renderer);
12679 }
12680 return renderer;
12681 },
12682
12683 _getPaneRenderer: function (name) {
12684 if (name === 'overlayPane' || name === undefined) {
12685 return false;
12686 }
12687
12688 var renderer = this._paneRenderers[name];
12689 if (renderer === undefined) {
12690 renderer = (SVG && svg$1({pane: name})) || (Canvas && canvas$1({pane: name}));
12691 this._paneRenderers[name] = renderer;
12692 }
12693 return renderer;
12694 }
12695 });
12696
12697 /*
12698 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
12699 */
12700
12701 /*
12702 * @class Rectangle
12703 * @aka L.Rectangle
12704 * @inherits Polygon
12705 *
12706 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
12707 *
12708 * @example
12709 *
12710 * ```js
12711 * // define rectangle geographical bounds
12712 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
12713 *
12714 * // create an orange rectangle
12715 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
12716 *
12717 * // zoom the map to the rectangle bounds
12718 * map.fitBounds(bounds);
12719 * ```
12720 *
12721 */
12722
12723
12724 var Rectangle = Polygon.extend({
12725 initialize: function (latLngBounds, options) {
12726 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
12727 },
12728
12729 // @method setBounds(latLngBounds: LatLngBounds): this
12730 // Redraws the rectangle with the passed bounds.
12731 setBounds: function (latLngBounds) {
12732 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
12733 },
12734
12735 _boundsToLatLngs: function (latLngBounds) {
12736 latLngBounds = toLatLngBounds(latLngBounds);
12737 return [
12738 latLngBounds.getSouthWest(),
12739 latLngBounds.getNorthWest(),
12740 latLngBounds.getNorthEast(),
12741 latLngBounds.getSouthEast()
12742 ];
12743 }
12744 });
12745
12746
12747 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
12748 function rectangle(latLngBounds, options) {
12749 return new Rectangle(latLngBounds, options);
12750 }
12751
12752 SVG.create = create$2;
12753 SVG.pointsToPath = pointsToPath;
12754
12755 GeoJSON.geometryToLayer = geometryToLayer;
12756 GeoJSON.coordsToLatLng = coordsToLatLng;
12757 GeoJSON.coordsToLatLngs = coordsToLatLngs;
12758 GeoJSON.latLngToCoords = latLngToCoords;
12759 GeoJSON.latLngsToCoords = latLngsToCoords;
12760 GeoJSON.getFeature = getFeature;
12761 GeoJSON.asFeature = asFeature;
12762
12763 /*
12764 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
12765 * (zoom to a selected bounding box), enabled by default.
12766 */
12767
12768 // @namespace Map
12769 // @section Interaction Options
12770 Map.mergeOptions({
12771 // @option boxZoom: Boolean = true
12772 // Whether the map can be zoomed to a rectangular area specified by
12773 // dragging the mouse while pressing the shift key.
12774 boxZoom: true
12775 });
12776
12777 var BoxZoom = Handler.extend({
12778 initialize: function (map) {
12779 this._map = map;
12780 this._container = map._container;
12781 this._pane = map._panes.overlayPane;
12782 this._resetStateTimeout = 0;
12783 map.on('unload', this._destroy, this);
12784 },
12785
12786 addHooks: function () {
12787 on(this._container, 'mousedown', this._onMouseDown, this);
12788 },
12789
12790 removeHooks: function () {
12791 off(this._container, 'mousedown', this._onMouseDown, this);
12792 },
12793
12794 moved: function () {
12795 return this._moved;
12796 },
12797
12798 _destroy: function () {
12799 remove(this._pane);
12800 delete this._pane;
12801 },
12802
12803 _resetState: function () {
12804 this._resetStateTimeout = 0;
12805 this._moved = false;
12806 },
12807
12808 _clearDeferredResetState: function () {
12809 if (this._resetStateTimeout !== 0) {
12810 clearTimeout(this._resetStateTimeout);
12811 this._resetStateTimeout = 0;
12812 }
12813 },
12814
12815 _onMouseDown: function (e) {
12816 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
12817
12818 // Clear the deferred resetState if it hasn't executed yet, otherwise it
12819 // will interrupt the interaction and orphan a box element in the container.
12820 this._clearDeferredResetState();
12821 this._resetState();
12822
12823 disableTextSelection();
12824 disableImageDrag();
12825
12826 this._startPoint = this._map.mouseEventToContainerPoint(e);
12827
12828 on(document, {
12829 contextmenu: stop,
12830 mousemove: this._onMouseMove,
12831 mouseup: this._onMouseUp,
12832 keydown: this._onKeyDown
12833 }, this);
12834 },
12835
12836 _onMouseMove: function (e) {
12837 if (!this._moved) {
12838 this._moved = true;
12839
12840 this._box = create$1('div', 'leaflet-zoom-box', this._container);
12841 addClass(this._container, 'leaflet-crosshair');
12842
12843 this._map.fire('boxzoomstart');
12844 }
12845
12846 this._point = this._map.mouseEventToContainerPoint(e);
12847
12848 var bounds = new Bounds(this._point, this._startPoint),
12849 size = bounds.getSize();
12850
12851 setPosition(this._box, bounds.min);
12852
12853 this._box.style.width = size.x + 'px';
12854 this._box.style.height = size.y + 'px';
12855 },
12856
12857 _finish: function () {
12858 if (this._moved) {
12859 remove(this._box);
12860 removeClass(this._container, 'leaflet-crosshair');
12861 }
12862
12863 enableTextSelection();
12864 enableImageDrag();
12865
12866 off(document, {
12867 contextmenu: stop,
12868 mousemove: this._onMouseMove,
12869 mouseup: this._onMouseUp,
12870 keydown: this._onKeyDown
12871 }, this);
12872 },
12873
12874 _onMouseUp: function (e) {
12875 if ((e.which !== 1) && (e.button !== 1)) { return; }
12876
12877 this._finish();
12878
12879 if (!this._moved) { return; }
12880 // Postpone to next JS tick so internal click event handling
12881 // still see it as "moved".
12882 this._clearDeferredResetState();
12883 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
12884
12885 var bounds = new LatLngBounds(
12886 this._map.containerPointToLatLng(this._startPoint),
12887 this._map.containerPointToLatLng(this._point));
12888
12889 this._map
12890 .fitBounds(bounds)
12891 .fire('boxzoomend', {boxZoomBounds: bounds});
12892 },
12893
12894 _onKeyDown: function (e) {
12895 if (e.keyCode === 27) {
12896 this._finish();
12897 }
12898 }
12899 });
12900
12901 // @section Handlers
12902 // @property boxZoom: Handler
12903 // Box (shift-drag with mouse) zoom handler.
12904 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
12905
12906 /*
12907 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
12908 */
12909
12910 // @namespace Map
12911 // @section Interaction Options
12912
12913 Map.mergeOptions({
12914 // @option doubleClickZoom: Boolean|String = true
12915 // Whether the map can be zoomed in by double clicking on it and
12916 // zoomed out by double clicking while holding shift. If passed
12917 // `'center'`, double-click zoom will zoom to the center of the
12918 // view regardless of where the mouse was.
12919 doubleClickZoom: true
12920 });
12921
12922 var DoubleClickZoom = Handler.extend({
12923 addHooks: function () {
12924 this._map.on('dblclick', this._onDoubleClick, this);
12925 },
12926
12927 removeHooks: function () {
12928 this._map.off('dblclick', this._onDoubleClick, this);
12929 },
12930
12931 _onDoubleClick: function (e) {
12932 var map = this._map,
12933 oldZoom = map.getZoom(),
12934 delta = map.options.zoomDelta,
12935 zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
12936
12937 if (map.options.doubleClickZoom === 'center') {
12938 map.setZoom(zoom);
12939 } else {
12940 map.setZoomAround(e.containerPoint, zoom);
12941 }
12942 }
12943 });
12944
12945 // @section Handlers
12946 //
12947 // Map properties include interaction handlers that allow you to control
12948 // interaction behavior in runtime, enabling or disabling certain features such
12949 // as dragging or touch zoom (see `Handler` methods). For example:
12950 //
12951 // ```js
12952 // map.doubleClickZoom.disable();
12953 // ```
12954 //
12955 // @property doubleClickZoom: Handler
12956 // Double click zoom handler.
12957 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
12958
12959 /*
12960 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
12961 */
12962
12963 // @namespace Map
12964 // @section Interaction Options
12965 Map.mergeOptions({
12966 // @option dragging: Boolean = true
12967 // Whether the map be draggable with mouse/touch or not.
12968 dragging: true,
12969
12970 // @section Panning Inertia Options
12971 // @option inertia: Boolean = *
12972 // If enabled, panning of the map will have an inertia effect where
12973 // the map builds momentum while dragging and continues moving in
12974 // the same direction for some time. Feels especially nice on touch
12975 // devices. Enabled by default unless running on old Android devices.
12976 inertia: !android23,
12977
12978 // @option inertiaDeceleration: Number = 3000
12979 // The rate with which the inertial movement slows down, in pixels/second².
12980 inertiaDeceleration: 3400, // px/s^2
12981
12982 // @option inertiaMaxSpeed: Number = Infinity
12983 // Max speed of the inertial movement, in pixels/second.
12984 inertiaMaxSpeed: Infinity, // px/s
12985
12986 // @option easeLinearity: Number = 0.2
12987 easeLinearity: 0.2,
12988
12989 // TODO refactor, move to CRS
12990 // @option worldCopyJump: Boolean = false
12991 // With this option enabled, the map tracks when you pan to another "copy"
12992 // of the world and seamlessly jumps to the original one so that all overlays
12993 // like markers and vector layers are still visible.
12994 worldCopyJump: false,
12995
12996 // @option maxBoundsViscosity: Number = 0.0
12997 // If `maxBounds` is set, this option will control how solid the bounds
12998 // are when dragging the map around. The default value of `0.0` allows the
12999 // user to drag outside the bounds at normal speed, higher values will
13000 // slow down map dragging outside bounds, and `1.0` makes the bounds fully
13001 // solid, preventing the user from dragging outside the bounds.
13002 maxBoundsViscosity: 0.0
13003 });
13004
13005 var Drag = Handler.extend({
13006 addHooks: function () {
13007 if (!this._draggable) {
13008 var map = this._map;
13009
13010 this._draggable = new Draggable(map._mapPane, map._container);
13011
13012 this._draggable.on({
13013 dragstart: this._onDragStart,
13014 drag: this._onDrag,
13015 dragend: this._onDragEnd
13016 }, this);
13017
13018 this._draggable.on('predrag', this._onPreDragLimit, this);
13019 if (map.options.worldCopyJump) {
13020 this._draggable.on('predrag', this._onPreDragWrap, this);
13021 map.on('zoomend', this._onZoomEnd, this);
13022
13023 map.whenReady(this._onZoomEnd, this);
13024 }
13025 }
13026 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13027 this._draggable.enable();
13028 this._positions = [];
13029 this._times = [];
13030 },
13031
13032 removeHooks: function () {
13033 removeClass(this._map._container, 'leaflet-grab');
13034 removeClass(this._map._container, 'leaflet-touch-drag');
13035 this._draggable.disable();
13036 },
13037
13038 moved: function () {
13039 return this._draggable && this._draggable._moved;
13040 },
13041
13042 moving: function () {
13043 return this._draggable && this._draggable._moving;
13044 },
13045
13046 _onDragStart: function () {
13047 var map = this._map;
13048
13049 map._stop();
13050 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13051 var bounds = toLatLngBounds(this._map.options.maxBounds);
13052
13053 this._offsetLimit = toBounds(
13054 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13055 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13056 .add(this._map.getSize()));
13057
13058 this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13059 } else {
13060 this._offsetLimit = null;
13061 }
13062
13063 map
13064 .fire('movestart')
13065 .fire('dragstart');
13066
13067 if (map.options.inertia) {
13068 this._positions = [];
13069 this._times = [];
13070 }
13071 },
13072
13073 _onDrag: function (e) {
13074 if (this._map.options.inertia) {
13075 var time = this._lastTime = +new Date(),
13076 pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13077
13078 this._positions.push(pos);
13079 this._times.push(time);
13080
13081 this._prunePositions(time);
13082 }
13083
13084 this._map
13085 .fire('move', e)
13086 .fire('drag', e);
13087 },
13088
13089 _prunePositions: function (time) {
13090 while (this._positions.length > 1 && time - this._times[0] > 50) {
13091 this._positions.shift();
13092 this._times.shift();
13093 }
13094 },
13095
13096 _onZoomEnd: function () {
13097 var pxCenter = this._map.getSize().divideBy(2),
13098 pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13099
13100 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13101 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13102 },
13103
13104 _viscousLimit: function (value, threshold) {
13105 return value - (value - threshold) * this._viscosity;
13106 },
13107
13108 _onPreDragLimit: function () {
13109 if (!this._viscosity || !this._offsetLimit) { return; }
13110
13111 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13112
13113 var limit = this._offsetLimit;
13114 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13115 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13116 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13117 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13118
13119 this._draggable._newPos = this._draggable._startPos.add(offset);
13120 },
13121
13122 _onPreDragWrap: function () {
13123 // TODO refactor to be able to adjust map pane position after zoom
13124 var worldWidth = this._worldWidth,
13125 halfWidth = Math.round(worldWidth / 2),
13126 dx = this._initialWorldOffset,
13127 x = this._draggable._newPos.x,
13128 newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13129 newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13130 newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13131
13132 this._draggable._absPos = this._draggable._newPos.clone();
13133 this._draggable._newPos.x = newX;
13134 },
13135
13136 _onDragEnd: function (e) {
13137 var map = this._map,
13138 options = map.options,
13139
13140 noInertia = !options.inertia || this._times.length < 2;
13141
13142 map.fire('dragend', e);
13143
13144 if (noInertia) {
13145 map.fire('moveend');
13146
13147 } else {
13148 this._prunePositions(+new Date());
13149
13150 var direction = this._lastPos.subtract(this._positions[0]),
13151 duration = (this._lastTime - this._times[0]) / 1000,
13152 ease = options.easeLinearity,
13153
13154 speedVector = direction.multiplyBy(ease / duration),
13155 speed = speedVector.distanceTo([0, 0]),
13156
13157 limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13158 limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13159
13160 decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13161 offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13162
13163 if (!offset.x && !offset.y) {
13164 map.fire('moveend');
13165
13166 } else {
13167 offset = map._limitOffset(offset, map.options.maxBounds);
13168
13169 requestAnimFrame(function () {
13170 map.panBy(offset, {
13171 duration: decelerationDuration,
13172 easeLinearity: ease,
13173 noMoveStart: true,
13174 animate: true
13175 });
13176 });
13177 }
13178 }
13179 }
13180 });
13181
13182 // @section Handlers
13183 // @property dragging: Handler
13184 // Map dragging handler (by both mouse and touch).
13185 Map.addInitHook('addHandler', 'dragging', Drag);
13186
13187 /*
13188 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13189 */
13190
13191 // @namespace Map
13192 // @section Keyboard Navigation Options
13193 Map.mergeOptions({
13194 // @option keyboard: Boolean = true
13195 // Makes the map focusable and allows users to navigate the map with keyboard
13196 // arrows and `+`/`-` keys.
13197 keyboard: true,
13198
13199 // @option keyboardPanDelta: Number = 80
13200 // Amount of pixels to pan when pressing an arrow key.
13201 keyboardPanDelta: 80
13202 });
13203
13204 var Keyboard = Handler.extend({
13205
13206 keyCodes: {
13207 left: [37],
13208 right: [39],
13209 down: [40],
13210 up: [38],
13211 zoomIn: [187, 107, 61, 171],
13212 zoomOut: [189, 109, 54, 173]
13213 },
13214
13215 initialize: function (map) {
13216 this._map = map;
13217
13218 this._setPanDelta(map.options.keyboardPanDelta);
13219 this._setZoomDelta(map.options.zoomDelta);
13220 },
13221
13222 addHooks: function () {
13223 var container = this._map._container;
13224
13225 // make the container focusable by tabbing
13226 if (container.tabIndex <= 0) {
13227 container.tabIndex = '0';
13228 }
13229
13230 on(container, {
13231 focus: this._onFocus,
13232 blur: this._onBlur,
13233 mousedown: this._onMouseDown
13234 }, this);
13235
13236 this._map.on({
13237 focus: this._addHooks,
13238 blur: this._removeHooks
13239 }, this);
13240 },
13241
13242 removeHooks: function () {
13243 this._removeHooks();
13244
13245 off(this._map._container, {
13246 focus: this._onFocus,
13247 blur: this._onBlur,
13248 mousedown: this._onMouseDown
13249 }, this);
13250
13251 this._map.off({
13252 focus: this._addHooks,
13253 blur: this._removeHooks
13254 }, this);
13255 },
13256
13257 _onMouseDown: function () {
13258 if (this._focused) { return; }
13259
13260 var body = document.body,
13261 docEl = document.documentElement,
13262 top = body.scrollTop || docEl.scrollTop,
13263 left = body.scrollLeft || docEl.scrollLeft;
13264
13265 this._map._container.focus();
13266
13267 window.scrollTo(left, top);
13268 },
13269
13270 _onFocus: function () {
13271 this._focused = true;
13272 this._map.fire('focus');
13273 },
13274
13275 _onBlur: function () {
13276 this._focused = false;
13277 this._map.fire('blur');
13278 },
13279
13280 _setPanDelta: function (panDelta) {
13281 var keys = this._panKeys = {},
13282 codes = this.keyCodes,
13283 i, len;
13284
13285 for (i = 0, len = codes.left.length; i < len; i++) {
13286 keys[codes.left[i]] = [-1 * panDelta, 0];
13287 }
13288 for (i = 0, len = codes.right.length; i < len; i++) {
13289 keys[codes.right[i]] = [panDelta, 0];
13290 }
13291 for (i = 0, len = codes.down.length; i < len; i++) {
13292 keys[codes.down[i]] = [0, panDelta];
13293 }
13294 for (i = 0, len = codes.up.length; i < len; i++) {
13295 keys[codes.up[i]] = [0, -1 * panDelta];
13296 }
13297 },
13298
13299 _setZoomDelta: function (zoomDelta) {
13300 var keys = this._zoomKeys = {},
13301 codes = this.keyCodes,
13302 i, len;
13303
13304 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13305 keys[codes.zoomIn[i]] = zoomDelta;
13306 }
13307 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13308 keys[codes.zoomOut[i]] = -zoomDelta;
13309 }
13310 },
13311
13312 _addHooks: function () {
13313 on(document, 'keydown', this._onKeyDown, this);
13314 },
13315
13316 _removeHooks: function () {
13317 off(document, 'keydown', this._onKeyDown, this);
13318 },
13319
13320 _onKeyDown: function (e) {
13321 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13322
13323 var key = e.keyCode,
13324 map = this._map,
13325 offset;
13326
13327 if (key in this._panKeys) {
13328
13329 if (map._panAnim && map._panAnim._inProgress) { return; }
13330
13331 offset = this._panKeys[key];
13332 if (e.shiftKey) {
13333 offset = toPoint(offset).multiplyBy(3);
13334 }
13335
13336 map.panBy(offset);
13337
13338 if (map.options.maxBounds) {
13339 map.panInsideBounds(map.options.maxBounds);
13340 }
13341
13342 } else if (key in this._zoomKeys) {
13343 map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13344
13345 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
13346 map.closePopup();
13347
13348 } else {
13349 return;
13350 }
13351
13352 stop(e);
13353 }
13354 });
13355
13356 // @section Handlers
13357 // @section Handlers
13358 // @property keyboard: Handler
13359 // Keyboard navigation handler.
13360 Map.addInitHook('addHandler', 'keyboard', Keyboard);
13361
13362 /*
13363 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13364 */
13365
13366 // @namespace Map
13367 // @section Interaction Options
13368 Map.mergeOptions({
13369 // @section Mousewheel options
13370 // @option scrollWheelZoom: Boolean|String = true
13371 // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13372 // it will zoom to the center of the view regardless of where the mouse was.
13373 scrollWheelZoom: true,
13374
13375 // @option wheelDebounceTime: Number = 40
13376 // Limits the rate at which a wheel can fire (in milliseconds). By default
13377 // user can't zoom via wheel more often than once per 40 ms.
13378 wheelDebounceTime: 40,
13379
13380 // @option wheelPxPerZoomLevel: Number = 60
13381 // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13382 // mean a change of one full zoom level. Smaller values will make wheel-zooming
13383 // faster (and vice versa).
13384 wheelPxPerZoomLevel: 60
13385 });
13386
13387 var ScrollWheelZoom = Handler.extend({
13388 addHooks: function () {
13389 on(this._map._container, 'mousewheel', this._onWheelScroll, this);
13390
13391 this._delta = 0;
13392 },
13393
13394 removeHooks: function () {
13395 off(this._map._container, 'mousewheel', this._onWheelScroll, this);
13396 },
13397
13398 _onWheelScroll: function (e) {
13399 var delta = getWheelDelta(e);
13400
13401 var debounce = this._map.options.wheelDebounceTime;
13402
13403 this._delta += delta;
13404 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13405
13406 if (!this._startTime) {
13407 this._startTime = +new Date();
13408 }
13409
13410 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13411
13412 clearTimeout(this._timer);
13413 this._timer = setTimeout(bind(this._performZoom, this), left);
13414
13415 stop(e);
13416 },
13417
13418 _performZoom: function () {
13419 var map = this._map,
13420 zoom = map.getZoom(),
13421 snap = this._map.options.zoomSnap || 0;
13422
13423 map._stop(); // stop panning and fly animations if any
13424
13425 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
13426 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13427 d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13428 d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13429 delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13430
13431 this._delta = 0;
13432 this._startTime = null;
13433
13434 if (!delta) { return; }
13435
13436 if (map.options.scrollWheelZoom === 'center') {
13437 map.setZoom(zoom + delta);
13438 } else {
13439 map.setZoomAround(this._lastMousePos, zoom + delta);
13440 }
13441 }
13442 });
13443
13444 // @section Handlers
13445 // @property scrollWheelZoom: Handler
13446 // Scroll wheel zoom handler.
13447 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13448
13449 /*
13450 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
13451 */
13452
13453 // @namespace Map
13454 // @section Interaction Options
13455 Map.mergeOptions({
13456 // @section Touch interaction options
13457 // @option tap: Boolean = true
13458 // Enables mobile hacks for supporting instant taps (fixing 200ms click
13459 // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
13460 tap: true,
13461
13462 // @option tapTolerance: Number = 15
13463 // The max number of pixels a user can shift his finger during touch
13464 // for it to be considered a valid tap.
13465 tapTolerance: 15
13466 });
13467
13468 var Tap = Handler.extend({
13469 addHooks: function () {
13470 on(this._map._container, 'touchstart', this._onDown, this);
13471 },
13472
13473 removeHooks: function () {
13474 off(this._map._container, 'touchstart', this._onDown, this);
13475 },
13476
13477 _onDown: function (e) {
13478 if (!e.touches) { return; }
13479
13480 preventDefault(e);
13481
13482 this._fireClick = true;
13483
13484 // don't simulate click or track longpress if more than 1 touch
13485 if (e.touches.length > 1) {
13486 this._fireClick = false;
13487 clearTimeout(this._holdTimeout);
13488 return;
13489 }
13490
13491 var first = e.touches[0],
13492 el = first.target;
13493
13494 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13495
13496 // if touching a link, highlight it
13497 if (el.tagName && el.tagName.toLowerCase() === 'a') {
13498 addClass(el, 'leaflet-active');
13499 }
13500
13501 // simulate long hold but setting a timeout
13502 this._holdTimeout = setTimeout(bind(function () {
13503 if (this._isTapValid()) {
13504 this._fireClick = false;
13505 this._onUp();
13506 this._simulateEvent('contextmenu', first);
13507 }
13508 }, this), 1000);
13509
13510 this._simulateEvent('mousedown', first);
13511
13512 on(document, {
13513 touchmove: this._onMove,
13514 touchend: this._onUp
13515 }, this);
13516 },
13517
13518 _onUp: function (e) {
13519 clearTimeout(this._holdTimeout);
13520
13521 off(document, {
13522 touchmove: this._onMove,
13523 touchend: this._onUp
13524 }, this);
13525
13526 if (this._fireClick && e && e.changedTouches) {
13527
13528 var first = e.changedTouches[0],
13529 el = first.target;
13530
13531 if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
13532 removeClass(el, 'leaflet-active');
13533 }
13534
13535 this._simulateEvent('mouseup', first);
13536
13537 // simulate click if the touch didn't move too much
13538 if (this._isTapValid()) {
13539 this._simulateEvent('click', first);
13540 }
13541 }
13542 },
13543
13544 _isTapValid: function () {
13545 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13546 },
13547
13548 _onMove: function (e) {
13549 var first = e.touches[0];
13550 this._newPos = new Point(first.clientX, first.clientY);
13551 this._simulateEvent('mousemove', first);
13552 },
13553
13554 _simulateEvent: function (type, e) {
13555 var simulatedEvent = document.createEvent('MouseEvents');
13556
13557 simulatedEvent._simulated = true;
13558 e.target._simulatedClick = true;
13559
13560 simulatedEvent.initMouseEvent(
13561 type, true, true, window, 1,
13562 e.screenX, e.screenY,
13563 e.clientX, e.clientY,
13564 false, false, false, false, 0, null);
13565
13566 e.target.dispatchEvent(simulatedEvent);
13567 }
13568 });
13569
13570 // @section Handlers
13571 // @property tap: Handler
13572 // Mobile touch hacks (quick tap and touch hold) handler.
13573 if (touch && !pointer) {
13574 Map.addInitHook('addHandler', 'tap', Tap);
13575 }
13576
13577 /*
13578 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
13579 */
13580
13581 // @namespace Map
13582 // @section Interaction Options
13583 Map.mergeOptions({
13584 // @section Touch interaction options
13585 // @option touchZoom: Boolean|String = *
13586 // Whether the map can be zoomed by touch-dragging with two fingers. If
13587 // passed `'center'`, it will zoom to the center of the view regardless of
13588 // where the touch events (fingers) were. Enabled for touch-capable web
13589 // browsers except for old Androids.
13590 touchZoom: touch && !android23,
13591
13592 // @option bounceAtZoomLimits: Boolean = true
13593 // Set it to false if you don't want the map to zoom beyond min/max zoom
13594 // and then bounce back when pinch-zooming.
13595 bounceAtZoomLimits: true
13596 });
13597
13598 var TouchZoom = Handler.extend({
13599 addHooks: function () {
13600 addClass(this._map._container, 'leaflet-touch-zoom');
13601 on(this._map._container, 'touchstart', this._onTouchStart, this);
13602 },
13603
13604 removeHooks: function () {
13605 removeClass(this._map._container, 'leaflet-touch-zoom');
13606 off(this._map._container, 'touchstart', this._onTouchStart, this);
13607 },
13608
13609 _onTouchStart: function (e) {
13610 var map = this._map;
13611 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13612
13613 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13614 p2 = map.mouseEventToContainerPoint(e.touches[1]);
13615
13616 this._centerPoint = map.getSize()._divideBy(2);
13617 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13618 if (map.options.touchZoom !== 'center') {
13619 this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13620 }
13621
13622 this._startDist = p1.distanceTo(p2);
13623 this._startZoom = map.getZoom();
13624
13625 this._moved = false;
13626 this._zooming = true;
13627
13628 map._stop();
13629
13630 on(document, 'touchmove', this._onTouchMove, this);
13631 on(document, 'touchend', this._onTouchEnd, this);
13632
13633 preventDefault(e);
13634 },
13635
13636 _onTouchMove: function (e) {
13637 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13638
13639 var map = this._map,
13640 p1 = map.mouseEventToContainerPoint(e.touches[0]),
13641 p2 = map.mouseEventToContainerPoint(e.touches[1]),
13642 scale = p1.distanceTo(p2) / this._startDist;
13643
13644 this._zoom = map.getScaleZoom(scale, this._startZoom);
13645
13646 if (!map.options.bounceAtZoomLimits && (
13647 (this._zoom < map.getMinZoom() && scale < 1) ||
13648 (this._zoom > map.getMaxZoom() && scale > 1))) {
13649 this._zoom = map._limitZoom(this._zoom);
13650 }
13651
13652 if (map.options.touchZoom === 'center') {
13653 this._center = this._startLatLng;
13654 if (scale === 1) { return; }
13655 } else {
13656 // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13657 var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13658 if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13659 this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13660 }
13661
13662 if (!this._moved) {
13663 map._moveStart(true, false);
13664 this._moved = true;
13665 }
13666
13667 cancelAnimFrame(this._animRequest);
13668
13669 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13670 this._animRequest = requestAnimFrame(moveFn, this, true);
13671
13672 preventDefault(e);
13673 },
13674
13675 _onTouchEnd: function () {
13676 if (!this._moved || !this._zooming) {
13677 this._zooming = false;
13678 return;
13679 }
13680
13681 this._zooming = false;
13682 cancelAnimFrame(this._animRequest);
13683
13684 off(document, 'touchmove', this._onTouchMove);
13685 off(document, 'touchend', this._onTouchEnd);
13686
13687 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
13688 if (this._map.options.zoomAnimation) {
13689 this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
13690 } else {
13691 this._map._resetView(this._center, this._map._limitZoom(this._zoom));
13692 }
13693 }
13694 });
13695
13696 // @section Handlers
13697 // @property touchZoom: Handler
13698 // Touch zoom handler.
13699 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
13700
13701 Map.BoxZoom = BoxZoom;
13702 Map.DoubleClickZoom = DoubleClickZoom;
13703 Map.Drag = Drag;
13704 Map.Keyboard = Keyboard;
13705 Map.ScrollWheelZoom = ScrollWheelZoom;
13706 Map.Tap = Tap;
13707 Map.TouchZoom = TouchZoom;
13708
13709 // misc
13710
13711 var oldL = window.L;
13712 function noConflict() {
13713 window.L = oldL;
13714 return this;
13715 }
13716
13717 // Always export us to window global (see #2364)
13718 window.L = exports;
13719
13720 Object.freeze = freeze;
13721
13722 exports.version = version;
13723 exports.noConflict = noConflict;
13724 exports.Control = Control;
13725 exports.control = control;
13726 exports.Browser = Browser;
13727 exports.Evented = Evented;
13728 exports.Mixin = Mixin;
13729 exports.Util = Util;
13730 exports.Class = Class;
13731 exports.Handler = Handler;
13732 exports.extend = extend;
13733 exports.bind = bind;
13734 exports.stamp = stamp;
13735 exports.setOptions = setOptions;
13736 exports.DomEvent = DomEvent;
13737 exports.DomUtil = DomUtil;
13738 exports.PosAnimation = PosAnimation;
13739 exports.Draggable = Draggable;
13740 exports.LineUtil = LineUtil;
13741 exports.PolyUtil = PolyUtil;
13742 exports.Point = Point;
13743 exports.point = toPoint;
13744 exports.Bounds = Bounds;
13745 exports.bounds = toBounds;
13746 exports.Transformation = Transformation;
13747 exports.transformation = toTransformation;
13748 exports.Projection = index;
13749 exports.LatLng = LatLng;
13750 exports.latLng = toLatLng;
13751 exports.LatLngBounds = LatLngBounds;
13752 exports.latLngBounds = toLatLngBounds;
13753 exports.CRS = CRS;
13754 exports.GeoJSON = GeoJSON;
13755 exports.geoJSON = geoJSON;
13756 exports.geoJson = geoJson;
13757 exports.Layer = Layer;
13758 exports.LayerGroup = LayerGroup;
13759 exports.layerGroup = layerGroup;
13760 exports.FeatureGroup = FeatureGroup;
13761 exports.featureGroup = featureGroup;
13762 exports.ImageOverlay = ImageOverlay;
13763 exports.imageOverlay = imageOverlay;
13764 exports.VideoOverlay = VideoOverlay;
13765 exports.videoOverlay = videoOverlay;
13766 exports.DivOverlay = DivOverlay;
13767 exports.Popup = Popup;
13768 exports.popup = popup;
13769 exports.Tooltip = Tooltip;
13770 exports.tooltip = tooltip;
13771 exports.Icon = Icon;
13772 exports.icon = icon;
13773 exports.DivIcon = DivIcon;
13774 exports.divIcon = divIcon;
13775 exports.Marker = Marker;
13776 exports.marker = marker;
13777 exports.TileLayer = TileLayer;
13778 exports.tileLayer = tileLayer;
13779 exports.GridLayer = GridLayer;
13780 exports.gridLayer = gridLayer;
13781 exports.SVG = SVG;
13782 exports.svg = svg$1;
13783 exports.Renderer = Renderer;
13784 exports.Canvas = Canvas;
13785 exports.canvas = canvas$1;
13786 exports.Path = Path;
13787 exports.CircleMarker = CircleMarker;
13788 exports.circleMarker = circleMarker;
13789 exports.Circle = Circle;
13790 exports.circle = circle;
13791 exports.Polyline = Polyline;
13792 exports.polyline = polyline;
13793 exports.Polygon = Polygon;
13794 exports.polygon = polygon;
13795 exports.Rectangle = Rectangle;
13796 exports.rectangle = rectangle;
13797 exports.Map = Map;
13798 exports.map = createMap;
11 var version = "1.7.1";
12
13 /*
14 * @namespace Util
15 *
16 * Various utility functions, used by Leaflet internally.
17 */
18
19 // @function extend(dest: Object, src?: Object): Object
20 // Merges the properties of the `src` object (or multiple objects) into `dest` object and returns the latter. Has an `L.extend` shortcut.
21 function extend(dest) {
22 var i, j, len, src;
23
24 for (j = 1, len = arguments.length; j < len; j++) {
25 src = arguments[j];
26 for (i in src) {
27 dest[i] = src[i];
28 }
29 }
30 return dest;
31 }
32
33 // @function create(proto: Object, properties?: Object): Object
34 // Compatibility polyfill for [Object.create](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/create)
35 var create = Object.create || (function () {
36 function F() {}
37 return function (proto) {
38 F.prototype = proto;
39 return new F();
40 };
41 })();
42
43 // @function bind(fn: Function, …): Function
44 // Returns a new function bound to the arguments passed, like [Function.prototype.bind](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
45 // Has a `L.bind()` shortcut.
46 function bind(fn, obj) {
47 var slice = Array.prototype.slice;
48
49 if (fn.bind) {
50 return fn.bind.apply(fn, slice.call(arguments, 1));
51 }
52
53 var args = slice.call(arguments, 2);
54
55 return function () {
56 return fn.apply(obj, args.length ? args.concat(slice.call(arguments)) : arguments);
57 };
58 }
59
60 // @property lastId: Number
61 // Last unique ID used by [`stamp()`](#util-stamp)
62 var lastId = 0;
63
64 // @function stamp(obj: Object): Number
65 // Returns the unique ID of an object, assigning it one if it doesn't have it.
66 function stamp(obj) {
67 /*eslint-disable */
68 obj._leaflet_id = obj._leaflet_id || ++lastId;
69 return obj._leaflet_id;
70 /* eslint-enable */
71 }
72
73 // @function throttle(fn: Function, time: Number, context: Object): Function
74 // Returns a function which executes function `fn` with the given scope `context`
75 // (so that the `this` keyword refers to `context` inside `fn`'s code). The function
76 // `fn` will be called no more than one time per given amount of `time`. The arguments
77 // received by the bound function will be any arguments passed when binding the
78 // function, followed by any arguments passed when invoking the bound function.
79 // Has an `L.throttle` shortcut.
80 function throttle(fn, time, context) {
81 var lock, args, wrapperFn, later;
82
83 later = function () {
84 // reset lock and call if queued
85 lock = false;
86 if (args) {
87 wrapperFn.apply(context, args);
88 args = false;
89 }
90 };
91
92 wrapperFn = function () {
93 if (lock) {
94 // called too soon, queue to call later
95 args = arguments;
96
97 } else {
98 // call and lock until later
99 fn.apply(context, arguments);
100 setTimeout(later, time);
101 lock = true;
102 }
103 };
104
105 return wrapperFn;
106 }
107
108 // @function wrapNum(num: Number, range: Number[], includeMax?: Boolean): Number
109 // Returns the number `num` modulo `range` in such a way so it lies within
110 // `range[0]` and `range[1]`. The returned value will be always smaller than
111 // `range[1]` unless `includeMax` is set to `true`.
112 function wrapNum(x, range, includeMax) {
113 var max = range[1],
114 min = range[0],
115 d = max - min;
116 return x === max && includeMax ? x : ((x - min) % d + d) % d + min;
117 }
118
119 // @function falseFn(): Function
120 // Returns a function which always returns `false`.
121 function falseFn() { return false; }
122
123 // @function formatNum(num: Number, digits?: Number): Number
124 // Returns the number `num` rounded to `digits` decimals, or to 6 decimals by default.
125 function formatNum(num, digits) {
126 var pow = Math.pow(10, (digits === undefined ? 6 : digits));
127 return Math.round(num * pow) / pow;
128 }
129
130 // @function trim(str: String): String
131 // Compatibility polyfill for [String.prototype.trim](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)
132 function trim(str) {
133 return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
134 }
135
136 // @function splitWords(str: String): String[]
137 // Trims and splits the string on whitespace and returns the array of parts.
138 function splitWords(str) {
139 return trim(str).split(/\s+/);
140 }
141
142 // @function setOptions(obj: Object, options: Object): Object
143 // Merges the given properties to the `options` of the `obj` object, returning the resulting options. See `Class options`. Has an `L.setOptions` shortcut.
144 function setOptions(obj, options) {
145 if (!Object.prototype.hasOwnProperty.call(obj, 'options')) {
146 obj.options = obj.options ? create(obj.options) : {};
147 }
148 for (var i in options) {
149 obj.options[i] = options[i];
150 }
151 return obj.options;
152 }
153
154 // @function getParamString(obj: Object, existingUrl?: String, uppercase?: Boolean): String
155 // Converts an object into a parameter URL string, e.g. `{a: "foo", b: "bar"}`
156 // translates to `'?a=foo&b=bar'`. If `existingUrl` is set, the parameters will
157 // be appended at the end. If `uppercase` is `true`, the parameter names will
158 // be uppercased (e.g. `'?A=foo&B=bar'`)
159 function getParamString(obj, existingUrl, uppercase) {
160 var params = [];
161 for (var i in obj) {
162 params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
163 }
164 return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
165 }
166
167 var templateRe = /\{ *([\w_-]+) *\}/g;
168
169 // @function template(str: String, data: Object): String
170 // Simple templating facility, accepts a template string of the form `'Hello {a}, {b}'`
171 // and a data object like `{a: 'foo', b: 'bar'}`, returns evaluated string
172 // `('Hello foo, bar')`. You can also specify functions instead of strings for
173 // data values — they will be evaluated passing `data` as an argument.
174 function template(str, data) {
175 return str.replace(templateRe, function (str, key) {
176 var value = data[key];
177
178 if (value === undefined) {
179 throw new Error('No value provided for variable ' + str);
180
181 } else if (typeof value === 'function') {
182 value = value(data);
183 }
184 return value;
185 });
186 }
187
188 // @function isArray(obj): Boolean
189 // Compatibility polyfill for [Array.isArray](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)
190 var isArray = Array.isArray || function (obj) {
191 return (Object.prototype.toString.call(obj) === '[object Array]');
192 };
193
194 // @function indexOf(array: Array, el: Object): Number
195 // Compatibility polyfill for [Array.prototype.indexOf](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf)
196 function indexOf(array, el) {
197 for (var i = 0; i < array.length; i++) {
198 if (array[i] === el) { return i; }
199 }
200 return -1;
201 }
202
203 // @property emptyImageUrl: String
204 // Data URI string containing a base64-encoded empty GIF image.
205 // Used as a hack to free memory from unused images on WebKit-powered
206 // mobile devices (by setting image `src` to this string).
207 var emptyImageUrl = 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=';
208
209 // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/
210
211 function getPrefixed(name) {
212 return window['webkit' + name] || window['moz' + name] || window['ms' + name];
213 }
214
215 var lastTime = 0;
216
217 // fallback for IE 7-8
218 function timeoutDefer(fn) {
219 var time = +new Date(),
220 timeToCall = Math.max(0, 16 - (time - lastTime));
221
222 lastTime = time + timeToCall;
223 return window.setTimeout(fn, timeToCall);
224 }
225
226 var requestFn = window.requestAnimationFrame || getPrefixed('RequestAnimationFrame') || timeoutDefer;
227 var cancelFn = window.cancelAnimationFrame || getPrefixed('CancelAnimationFrame') ||
228 getPrefixed('CancelRequestAnimationFrame') || function (id) { window.clearTimeout(id); };
229
230 // @function requestAnimFrame(fn: Function, context?: Object, immediate?: Boolean): Number
231 // Schedules `fn` to be executed when the browser repaints. `fn` is bound to
232 // `context` if given. When `immediate` is set, `fn` is called immediately if
233 // the browser doesn't have native support for
234 // [`window.requestAnimationFrame`](https://developer.mozilla.org/docs/Web/API/window/requestAnimationFrame),
235 // otherwise it's delayed. Returns a request ID that can be used to cancel the request.
236 function requestAnimFrame(fn, context, immediate) {
237 if (immediate && requestFn === timeoutDefer) {
238 fn.call(context);
239 } else {
240 return requestFn.call(window, bind(fn, context));
241 }
242 }
243
244 // @function cancelAnimFrame(id: Number): undefined
245 // Cancels a previous `requestAnimFrame`. See also [window.cancelAnimationFrame](https://developer.mozilla.org/docs/Web/API/window/cancelAnimationFrame).
246 function cancelAnimFrame(id) {
247 if (id) {
248 cancelFn.call(window, id);
249 }
250 }
251
252 var Util = ({
253 extend: extend,
254 create: create,
255 bind: bind,
256 lastId: lastId,
257 stamp: stamp,
258 throttle: throttle,
259 wrapNum: wrapNum,
260 falseFn: falseFn,
261 formatNum: formatNum,
262 trim: trim,
263 splitWords: splitWords,
264 setOptions: setOptions,
265 getParamString: getParamString,
266 template: template,
267 isArray: isArray,
268 indexOf: indexOf,
269 emptyImageUrl: emptyImageUrl,
270 requestFn: requestFn,
271 cancelFn: cancelFn,
272 requestAnimFrame: requestAnimFrame,
273 cancelAnimFrame: cancelAnimFrame
274 });
275
276 // @class Class
277 // @aka L.Class
278
279 // @section
280 // @uninheritable
281
282 // Thanks to John Resig and Dean Edwards for inspiration!
283
284 function Class() {}
285
286 Class.extend = function (props) {
287
288 // @function extend(props: Object): Function
289 // [Extends the current class](#class-inheritance) given the properties to be included.
290 // Returns a Javascript function that is a class constructor (to be called with `new`).
291 var NewClass = function () {
292
293 // call the constructor
294 if (this.initialize) {
295 this.initialize.apply(this, arguments);
296 }
297
298 // call all constructor hooks
299 this.callInitHooks();
300 };
301
302 var parentProto = NewClass.__super__ = this.prototype;
303
304 var proto = create(parentProto);
305 proto.constructor = NewClass;
306
307 NewClass.prototype = proto;
308
309 // inherit parent's statics
310 for (var i in this) {
311 if (Object.prototype.hasOwnProperty.call(this, i) && i !== 'prototype' && i !== '__super__') {
312 NewClass[i] = this[i];
313 }
314 }
315
316 // mix static properties into the class
317 if (props.statics) {
318 extend(NewClass, props.statics);
319 delete props.statics;
320 }
321
322 // mix includes into the prototype
323 if (props.includes) {
324 checkDeprecatedMixinEvents(props.includes);
325 extend.apply(null, [proto].concat(props.includes));
326 delete props.includes;
327 }
328
329 // merge options
330 if (proto.options) {
331 props.options = extend(create(proto.options), props.options);
332 }
333
334 // mix given properties into the prototype
335 extend(proto, props);
336
337 proto._initHooks = [];
338
339 // add method for calling all hooks
340 proto.callInitHooks = function () {
341
342 if (this._initHooksCalled) { return; }
343
344 if (parentProto.callInitHooks) {
345 parentProto.callInitHooks.call(this);
346 }
347
348 this._initHooksCalled = true;
349
350 for (var i = 0, len = proto._initHooks.length; i < len; i++) {
351 proto._initHooks[i].call(this);
352 }
353 };
354
355 return NewClass;
356 };
357
358
359 // @function include(properties: Object): this
360 // [Includes a mixin](#class-includes) into the current class.
361 Class.include = function (props) {
362 extend(this.prototype, props);
363 return this;
364 };
365
366 // @function mergeOptions(options: Object): this
367 // [Merges `options`](#class-options) into the defaults of the class.
368 Class.mergeOptions = function (options) {
369 extend(this.prototype.options, options);
370 return this;
371 };
372
373 // @function addInitHook(fn: Function): this
374 // Adds a [constructor hook](#class-constructor-hooks) to the class.
375 Class.addInitHook = function (fn) { // (Function) || (String, args...)
376 var args = Array.prototype.slice.call(arguments, 1);
377
378 var init = typeof fn === 'function' ? fn : function () {
379 this[fn].apply(this, args);
380 };
381
382 this.prototype._initHooks = this.prototype._initHooks || [];
383 this.prototype._initHooks.push(init);
384 return this;
385 };
386
387 function checkDeprecatedMixinEvents(includes) {
388 if (typeof L === 'undefined' || !L || !L.Mixin) { return; }
389
390 includes = isArray(includes) ? includes : [includes];
391
392 for (var i = 0; i < includes.length; i++) {
393 if (includes[i] === L.Mixin.Events) {
394 console.warn('Deprecated include of L.Mixin.Events: ' +
395 'this property will be removed in future releases, ' +
396 'please inherit from L.Evented instead.', new Error().stack);
397 }
398 }
399 }
400
401 /*
402 * @class Evented
403 * @aka L.Evented
404 * @inherits Class
405 *
406 * A set of methods shared between event-powered classes (like `Map` and `Marker`). Generally, events allow you to execute some function when something happens with an object (e.g. the user clicks on the map, causing the map to fire `'click'` event).
407 *
408 * @example
409 *
410 * ```js
411 * map.on('click', function(e) {
412 * alert(e.latlng);
413 * } );
414 * ```
415 *
416 * Leaflet deals with event listeners by reference, so if you want to add a listener and then remove it, define it as a function:
417 *
418 * ```js
419 * function onClick(e) { ... }
420 *
421 * map.on('click', onClick);
422 * map.off('click', onClick);
423 * ```
424 */
425
426 var Events = {
427 /* @method on(type: String, fn: Function, context?: Object): this
428 * Adds a listener function (`fn`) to a particular event type of the object. You can optionally specify the context of the listener (object the this keyword will point to). You can also pass several space-separated types (e.g. `'click dblclick'`).
429 *
430 * @alternative
431 * @method on(eventMap: Object): this
432 * Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
433 */
434 on: function (types, fn, context) {
435
436 // types can be a map of types/handlers
437 if (typeof types === 'object') {
438 for (var type in types) {
439 // we don't process space-separated events here for performance;
440 // it's a hot path since Layer uses the on(obj) syntax
441 this._on(type, types[type], fn);
442 }
443
444 } else {
445 // types can be a string of space-separated words
446 types = splitWords(types);
447
448 for (var i = 0, len = types.length; i < len; i++) {
449 this._on(types[i], fn, context);
450 }
451 }
452
453 return this;
454 },
455
456 /* @method off(type: String, fn?: Function, context?: Object): this
457 * Removes a previously added listener function. If no function is specified, it will remove all the listeners of that particular event from the object. Note that if you passed a custom context to `on`, you must pass the same context to `off` in order to remove the listener.
458 *
459 * @alternative
460 * @method off(eventMap: Object): this
461 * Removes a set of type/listener pairs.
462 *
463 * @alternative
464 * @method off: this
465 * Removes all listeners to all events on the object. This includes implicitly attached events.
466 */
467 off: function (types, fn, context) {
468
469 if (!types) {
470 // clear all listeners if called without arguments
471 delete this._events;
472
473 } else if (typeof types === 'object') {
474 for (var type in types) {
475 this._off(type, types[type], fn);
476 }
477
478 } else {
479 types = splitWords(types);
480
481 for (var i = 0, len = types.length; i < len; i++) {
482 this._off(types[i], fn, context);
483 }
484 }
485
486 return this;
487 },
488
489 // attach listener (without syntactic sugar now)
490 _on: function (type, fn, context) {
491 this._events = this._events || {};
492
493 /* get/init listeners for type */
494 var typeListeners = this._events[type];
495 if (!typeListeners) {
496 typeListeners = [];
497 this._events[type] = typeListeners;
498 }
499
500 if (context === this) {
501 // Less memory footprint.
502 context = undefined;
503 }
504 var newListener = {fn: fn, ctx: context},
505 listeners = typeListeners;
506
507 // check if fn already there
508 for (var i = 0, len = listeners.length; i < len; i++) {
509 if (listeners[i].fn === fn && listeners[i].ctx === context) {
510 return;
511 }
512 }
513
514 listeners.push(newListener);
515 },
516
517 _off: function (type, fn, context) {
518 var listeners,
519 i,
520 len;
521
522 if (!this._events) { return; }
523
524 listeners = this._events[type];
525
526 if (!listeners) {
527 return;
528 }
529
530 if (!fn) {
531 // Set all removed listeners to noop so they are not called if remove happens in fire
532 for (i = 0, len = listeners.length; i < len; i++) {
533 listeners[i].fn = falseFn;
534 }
535 // clear all listeners for a type if function isn't specified
536 delete this._events[type];
537 return;
538 }
539
540 if (context === this) {
541 context = undefined;
542 }
543
544 if (listeners) {
545
546 // find fn and remove it
547 for (i = 0, len = listeners.length; i < len; i++) {
548 var l = listeners[i];
549 if (l.ctx !== context) { continue; }
550 if (l.fn === fn) {
551
552 // set the removed listener to noop so that's not called if remove happens in fire
553 l.fn = falseFn;
554
555 if (this._firingCount) {
556 /* copy array in case events are being fired */
557 this._events[type] = listeners = listeners.slice();
558 }
559 listeners.splice(i, 1);
560
561 return;
562 }
563 }
564 }
565 },
566
567 // @method fire(type: String, data?: Object, propagate?: Boolean): this
568 // Fires an event of the specified type. You can optionally provide an data
569 // object — the first argument of the listener function will contain its
570 // properties. The event can optionally be propagated to event parents.
571 fire: function (type, data, propagate) {
572 if (!this.listens(type, propagate)) { return this; }
573
574 var event = extend({}, data, {
575 type: type,
576 target: this,
577 sourceTarget: data && data.sourceTarget || this
578 });
579
580 if (this._events) {
581 var listeners = this._events[type];
582
583 if (listeners) {
584 this._firingCount = (this._firingCount + 1) || 1;
585 for (var i = 0, len = listeners.length; i < len; i++) {
586 var l = listeners[i];
587 l.fn.call(l.ctx || this, event);
588 }
589
590 this._firingCount--;
591 }
592 }
593
594 if (propagate) {
595 // propagate the event to parents (set with addEventParent)
596 this._propagateEvent(event);
597 }
598
599 return this;
600 },
601
602 // @method listens(type: String): Boolean
603 // Returns `true` if a particular event type has any listeners attached to it.
604 listens: function (type, propagate) {
605 var listeners = this._events && this._events[type];
606 if (listeners && listeners.length) { return true; }
607
608 if (propagate) {
609 // also check parents for listeners if event propagates
610 for (var id in this._eventParents) {
611 if (this._eventParents[id].listens(type, propagate)) { return true; }
612 }
613 }
614 return false;
615 },
616
617 // @method once(…): this
618 // Behaves as [`on(…)`](#evented-on), except the listener will only get fired once and then removed.
619 once: function (types, fn, context) {
620
621 if (typeof types === 'object') {
622 for (var type in types) {
623 this.once(type, types[type], fn);
624 }
625 return this;
626 }
627
628 var handler = bind(function () {
629 this
630 .off(types, fn, context)
631 .off(types, handler, context);
632 }, this);
633
634 // add a listener that's executed once and removed after that
635 return this
636 .on(types, fn, context)
637 .on(types, handler, context);
638 },
639
640 // @method addEventParent(obj: Evented): this
641 // Adds an event parent - an `Evented` that will receive propagated events
642 addEventParent: function (obj) {
643 this._eventParents = this._eventParents || {};
644 this._eventParents[stamp(obj)] = obj;
645 return this;
646 },
647
648 // @method removeEventParent(obj: Evented): this
649 // Removes an event parent, so it will stop receiving propagated events
650 removeEventParent: function (obj) {
651 if (this._eventParents) {
652 delete this._eventParents[stamp(obj)];
653 }
654 return this;
655 },
656
657 _propagateEvent: function (e) {
658 for (var id in this._eventParents) {
659 this._eventParents[id].fire(e.type, extend({
660 layer: e.target,
661 propagatedFrom: e.target
662 }, e), true);
663 }
664 }
665 };
666
667 // aliases; we should ditch those eventually
668
669 // @method addEventListener(…): this
670 // Alias to [`on(…)`](#evented-on)
671 Events.addEventListener = Events.on;
672
673 // @method removeEventListener(…): this
674 // Alias to [`off(…)`](#evented-off)
675
676 // @method clearAllEventListeners(…): this
677 // Alias to [`off()`](#evented-off)
678 Events.removeEventListener = Events.clearAllEventListeners = Events.off;
679
680 // @method addOneTimeEventListener(…): this
681 // Alias to [`once(…)`](#evented-once)
682 Events.addOneTimeEventListener = Events.once;
683
684 // @method fireEvent(…): this
685 // Alias to [`fire(…)`](#evented-fire)
686 Events.fireEvent = Events.fire;
687
688 // @method hasEventListeners(…): Boolean
689 // Alias to [`listens(…)`](#evented-listens)
690 Events.hasEventListeners = Events.listens;
691
692 var Evented = Class.extend(Events);
693
694 /*
695 * @class Point
696 * @aka L.Point
697 *
698 * Represents a point with `x` and `y` coordinates in pixels.
699 *
700 * @example
701 *
702 * ```js
703 * var point = L.point(200, 300);
704 * ```
705 *
706 * All Leaflet methods and options that accept `Point` objects also accept them in a simple Array form (unless noted otherwise), so these lines are equivalent:
707 *
708 * ```js
709 * map.panBy([200, 300]);
710 * map.panBy(L.point(200, 300));
711 * ```
712 *
713 * Note that `Point` does not inherit from Leaflet's `Class` object,
714 * which means new classes can't inherit from it, and new methods
715 * can't be added to it with the `include` function.
716 */
717
718 function Point(x, y, round) {
719 // @property x: Number; The `x` coordinate of the point
720 this.x = (round ? Math.round(x) : x);
721 // @property y: Number; The `y` coordinate of the point
722 this.y = (round ? Math.round(y) : y);
723 }
724
725 var trunc = Math.trunc || function (v) {
726 return v > 0 ? Math.floor(v) : Math.ceil(v);
727 };
728
729 Point.prototype = {
730
731 // @method clone(): Point
732 // Returns a copy of the current point.
733 clone: function () {
734 return new Point(this.x, this.y);
735 },
736
737 // @method add(otherPoint: Point): Point
738 // Returns the result of addition of the current and the given points.
739 add: function (point) {
740 // non-destructive, returns a new point
741 return this.clone()._add(toPoint(point));
742 },
743
744 _add: function (point) {
745 // destructive, used directly for performance in situations where it's safe to modify existing point
746 this.x += point.x;
747 this.y += point.y;
748 return this;
749 },
750
751 // @method subtract(otherPoint: Point): Point
752 // Returns the result of subtraction of the given point from the current.
753 subtract: function (point) {
754 return this.clone()._subtract(toPoint(point));
755 },
756
757 _subtract: function (point) {
758 this.x -= point.x;
759 this.y -= point.y;
760 return this;
761 },
762
763 // @method divideBy(num: Number): Point
764 // Returns the result of division of the current point by the given number.
765 divideBy: function (num) {
766 return this.clone()._divideBy(num);
767 },
768
769 _divideBy: function (num) {
770 this.x /= num;
771 this.y /= num;
772 return this;
773 },
774
775 // @method multiplyBy(num: Number): Point
776 // Returns the result of multiplication of the current point by the given number.
777 multiplyBy: function (num) {
778 return this.clone()._multiplyBy(num);
779 },
780
781 _multiplyBy: function (num) {
782 this.x *= num;
783 this.y *= num;
784 return this;
785 },
786
787 // @method scaleBy(scale: Point): Point
788 // Multiply each coordinate of the current point by each coordinate of
789 // `scale`. In linear algebra terms, multiply the point by the
790 // [scaling matrix](https://en.wikipedia.org/wiki/Scaling_%28geometry%29#Matrix_representation)
791 // defined by `scale`.
792 scaleBy: function (point) {
793 return new Point(this.x * point.x, this.y * point.y);
794 },
795
796 // @method unscaleBy(scale: Point): Point
797 // Inverse of `scaleBy`. Divide each coordinate of the current point by
798 // each coordinate of `scale`.
799 unscaleBy: function (point) {
800 return new Point(this.x / point.x, this.y / point.y);
801 },
802
803 // @method round(): Point
804 // Returns a copy of the current point with rounded coordinates.
805 round: function () {
806 return this.clone()._round();
807 },
808
809 _round: function () {
810 this.x = Math.round(this.x);
811 this.y = Math.round(this.y);
812 return this;
813 },
814
815 // @method floor(): Point
816 // Returns a copy of the current point with floored coordinates (rounded down).
817 floor: function () {
818 return this.clone()._floor();
819 },
820
821 _floor: function () {
822 this.x = Math.floor(this.x);
823 this.y = Math.floor(this.y);
824 return this;
825 },
826
827 // @method ceil(): Point
828 // Returns a copy of the current point with ceiled coordinates (rounded up).
829 ceil: function () {
830 return this.clone()._ceil();
831 },
832
833 _ceil: function () {
834 this.x = Math.ceil(this.x);
835 this.y = Math.ceil(this.y);
836 return this;
837 },
838
839 // @method trunc(): Point
840 // Returns a copy of the current point with truncated coordinates (rounded towards zero).
841 trunc: function () {
842 return this.clone()._trunc();
843 },
844
845 _trunc: function () {
846 this.x = trunc(this.x);
847 this.y = trunc(this.y);
848 return this;
849 },
850
851 // @method distanceTo(otherPoint: Point): Number
852 // Returns the cartesian distance between the current and the given points.
853 distanceTo: function (point) {
854 point = toPoint(point);
855
856 var x = point.x - this.x,
857 y = point.y - this.y;
858
859 return Math.sqrt(x * x + y * y);
860 },
861
862 // @method equals(otherPoint: Point): Boolean
863 // Returns `true` if the given point has the same coordinates.
864 equals: function (point) {
865 point = toPoint(point);
866
867 return point.x === this.x &&
868 point.y === this.y;
869 },
870
871 // @method contains(otherPoint: Point): Boolean
872 // Returns `true` if both coordinates of the given point are less than the corresponding current point coordinates (in absolute values).
873 contains: function (point) {
874 point = toPoint(point);
875
876 return Math.abs(point.x) <= Math.abs(this.x) &&
877 Math.abs(point.y) <= Math.abs(this.y);
878 },
879
880 // @method toString(): String
881 // Returns a string representation of the point for debugging purposes.
882 toString: function () {
883 return 'Point(' +
884 formatNum(this.x) + ', ' +
885 formatNum(this.y) + ')';
886 }
887 };
888
889 // @factory L.point(x: Number, y: Number, round?: Boolean)
890 // Creates a Point object with the given `x` and `y` coordinates. If optional `round` is set to true, rounds the `x` and `y` values.
891
892 // @alternative
893 // @factory L.point(coords: Number[])
894 // Expects an array of the form `[x, y]` instead.
895
896 // @alternative
897 // @factory L.point(coords: Object)
898 // Expects a plain object of the form `{x: Number, y: Number}` instead.
899 function toPoint(x, y, round) {
900 if (x instanceof Point) {
901 return x;
902 }
903 if (isArray(x)) {
904 return new Point(x[0], x[1]);
905 }
906 if (x === undefined || x === null) {
907 return x;
908 }
909 if (typeof x === 'object' && 'x' in x && 'y' in x) {
910 return new Point(x.x, x.y);
911 }
912 return new Point(x, y, round);
913 }
914
915 /*
916 * @class Bounds
917 * @aka L.Bounds
918 *
919 * Represents a rectangular area in pixel coordinates.
920 *
921 * @example
922 *
923 * ```js
924 * var p1 = L.point(10, 10),
925 * p2 = L.point(40, 60),
926 * bounds = L.bounds(p1, p2);
927 * ```
928 *
929 * All Leaflet methods that accept `Bounds` objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
930 *
931 * ```js
932 * otherBounds.intersects([[10, 10], [40, 60]]);
933 * ```
934 *
935 * Note that `Bounds` does not inherit from Leaflet's `Class` object,
936 * which means new classes can't inherit from it, and new methods
937 * can't be added to it with the `include` function.
938 */
939
940 function Bounds(a, b) {
941 if (!a) { return; }
942
943 var points = b ? [a, b] : a;
944
945 for (var i = 0, len = points.length; i < len; i++) {
946 this.extend(points[i]);
947 }
948 }
949
950 Bounds.prototype = {
951 // @method extend(point: Point): this
952 // Extends the bounds to contain the given point.
953 extend: function (point) { // (Point)
954 point = toPoint(point);
955
956 // @property min: Point
957 // The top left corner of the rectangle.
958 // @property max: Point
959 // The bottom right corner of the rectangle.
960 if (!this.min && !this.max) {
961 this.min = point.clone();
962 this.max = point.clone();
963 } else {
964 this.min.x = Math.min(point.x, this.min.x);
965 this.max.x = Math.max(point.x, this.max.x);
966 this.min.y = Math.min(point.y, this.min.y);
967 this.max.y = Math.max(point.y, this.max.y);
968 }
969 return this;
970 },
971
972 // @method getCenter(round?: Boolean): Point
973 // Returns the center point of the bounds.
974 getCenter: function (round) {
975 return new Point(
976 (this.min.x + this.max.x) / 2,
977 (this.min.y + this.max.y) / 2, round);
978 },
979
980 // @method getBottomLeft(): Point
981 // Returns the bottom-left point of the bounds.
982 getBottomLeft: function () {
983 return new Point(this.min.x, this.max.y);
984 },
985
986 // @method getTopRight(): Point
987 // Returns the top-right point of the bounds.
988 getTopRight: function () { // -> Point
989 return new Point(this.max.x, this.min.y);
990 },
991
992 // @method getTopLeft(): Point
993 // Returns the top-left point of the bounds (i.e. [`this.min`](#bounds-min)).
994 getTopLeft: function () {
995 return this.min; // left, top
996 },
997
998 // @method getBottomRight(): Point
999 // Returns the bottom-right point of the bounds (i.e. [`this.max`](#bounds-max)).
1000 getBottomRight: function () {
1001 return this.max; // right, bottom
1002 },
1003
1004 // @method getSize(): Point
1005 // Returns the size of the given bounds
1006 getSize: function () {
1007 return this.max.subtract(this.min);
1008 },
1009
1010 // @method contains(otherBounds: Bounds): Boolean
1011 // Returns `true` if the rectangle contains the given one.
1012 // @alternative
1013 // @method contains(point: Point): Boolean
1014 // Returns `true` if the rectangle contains the given point.
1015 contains: function (obj) {
1016 var min, max;
1017
1018 if (typeof obj[0] === 'number' || obj instanceof Point) {
1019 obj = toPoint(obj);
1020 } else {
1021 obj = toBounds(obj);
1022 }
1023
1024 if (obj instanceof Bounds) {
1025 min = obj.min;
1026 max = obj.max;
1027 } else {
1028 min = max = obj;
1029 }
1030
1031 return (min.x >= this.min.x) &&
1032 (max.x <= this.max.x) &&
1033 (min.y >= this.min.y) &&
1034 (max.y <= this.max.y);
1035 },
1036
1037 // @method intersects(otherBounds: Bounds): Boolean
1038 // Returns `true` if the rectangle intersects the given bounds. Two bounds
1039 // intersect if they have at least one point in common.
1040 intersects: function (bounds) { // (Bounds) -> Boolean
1041 bounds = toBounds(bounds);
1042
1043 var min = this.min,
1044 max = this.max,
1045 min2 = bounds.min,
1046 max2 = bounds.max,
1047 xIntersects = (max2.x >= min.x) && (min2.x <= max.x),
1048 yIntersects = (max2.y >= min.y) && (min2.y <= max.y);
1049
1050 return xIntersects && yIntersects;
1051 },
1052
1053 // @method overlaps(otherBounds: Bounds): Boolean
1054 // Returns `true` if the rectangle overlaps the given bounds. Two bounds
1055 // overlap if their intersection is an area.
1056 overlaps: function (bounds) { // (Bounds) -> Boolean
1057 bounds = toBounds(bounds);
1058
1059 var min = this.min,
1060 max = this.max,
1061 min2 = bounds.min,
1062 max2 = bounds.max,
1063 xOverlaps = (max2.x > min.x) && (min2.x < max.x),
1064 yOverlaps = (max2.y > min.y) && (min2.y < max.y);
1065
1066 return xOverlaps && yOverlaps;
1067 },
1068
1069 isValid: function () {
1070 return !!(this.min && this.max);
1071 }
1072 };
1073
1074
1075 // @factory L.bounds(corner1: Point, corner2: Point)
1076 // Creates a Bounds object from two corners coordinate pairs.
1077 // @alternative
1078 // @factory L.bounds(points: Point[])
1079 // Creates a Bounds object from the given array of points.
1080 function toBounds(a, b) {
1081 if (!a || a instanceof Bounds) {
1082 return a;
1083 }
1084 return new Bounds(a, b);
1085 }
1086
1087 /*
1088 * @class LatLngBounds
1089 * @aka L.LatLngBounds
1090 *
1091 * Represents a rectangular geographical area on a map.
1092 *
1093 * @example
1094 *
1095 * ```js
1096 * var corner1 = L.latLng(40.712, -74.227),
1097 * corner2 = L.latLng(40.774, -74.125),
1098 * bounds = L.latLngBounds(corner1, corner2);
1099 * ```
1100 *
1101 * All Leaflet methods that accept LatLngBounds objects also accept them in a simple Array form (unless noted otherwise), so the bounds example above can be passed like this:
1102 *
1103 * ```js
1104 * map.fitBounds([
1105 * [40.712, -74.227],
1106 * [40.774, -74.125]
1107 * ]);
1108 * ```
1109 *
1110 * Caution: if the area crosses the antimeridian (often confused with the International Date Line), you must specify corners _outside_ the [-180, 180] degrees longitude range.
1111 *
1112 * Note that `LatLngBounds` does not inherit from Leaflet's `Class` object,
1113 * which means new classes can't inherit from it, and new methods
1114 * can't be added to it with the `include` function.
1115 */
1116
1117 function LatLngBounds(corner1, corner2) { // (LatLng, LatLng) or (LatLng[])
1118 if (!corner1) { return; }
1119
1120 var latlngs = corner2 ? [corner1, corner2] : corner1;
1121
1122 for (var i = 0, len = latlngs.length; i < len; i++) {
1123 this.extend(latlngs[i]);
1124 }
1125 }
1126
1127 LatLngBounds.prototype = {
1128
1129 // @method extend(latlng: LatLng): this
1130 // Extend the bounds to contain the given point
1131
1132 // @alternative
1133 // @method extend(otherBounds: LatLngBounds): this
1134 // Extend the bounds to contain the given bounds
1135 extend: function (obj) {
1136 var sw = this._southWest,
1137 ne = this._northEast,
1138 sw2, ne2;
1139
1140 if (obj instanceof LatLng) {
1141 sw2 = obj;
1142 ne2 = obj;
1143
1144 } else if (obj instanceof LatLngBounds) {
1145 sw2 = obj._southWest;
1146 ne2 = obj._northEast;
1147
1148 if (!sw2 || !ne2) { return this; }
1149
1150 } else {
1151 return obj ? this.extend(toLatLng(obj) || toLatLngBounds(obj)) : this;
1152 }
1153
1154 if (!sw && !ne) {
1155 this._southWest = new LatLng(sw2.lat, sw2.lng);
1156 this._northEast = new LatLng(ne2.lat, ne2.lng);
1157 } else {
1158 sw.lat = Math.min(sw2.lat, sw.lat);
1159 sw.lng = Math.min(sw2.lng, sw.lng);
1160 ne.lat = Math.max(ne2.lat, ne.lat);
1161 ne.lng = Math.max(ne2.lng, ne.lng);
1162 }
1163
1164 return this;
1165 },
1166
1167 // @method pad(bufferRatio: Number): LatLngBounds
1168 // Returns bounds created by extending or retracting the current bounds by a given ratio in each direction.
1169 // For example, a ratio of 0.5 extends the bounds by 50% in each direction.
1170 // Negative values will retract the bounds.
1171 pad: function (bufferRatio) {
1172 var sw = this._southWest,
1173 ne = this._northEast,
1174 heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio,
1175 widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio;
1176
1177 return new LatLngBounds(
1178 new LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer),
1179 new LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer));
1180 },
1181
1182 // @method getCenter(): LatLng
1183 // Returns the center point of the bounds.
1184 getCenter: function () {
1185 return new LatLng(
1186 (this._southWest.lat + this._northEast.lat) / 2,
1187 (this._southWest.lng + this._northEast.lng) / 2);
1188 },
1189
1190 // @method getSouthWest(): LatLng
1191 // Returns the south-west point of the bounds.
1192 getSouthWest: function () {
1193 return this._southWest;
1194 },
1195
1196 // @method getNorthEast(): LatLng
1197 // Returns the north-east point of the bounds.
1198 getNorthEast: function () {
1199 return this._northEast;
1200 },
1201
1202 // @method getNorthWest(): LatLng
1203 // Returns the north-west point of the bounds.
1204 getNorthWest: function () {
1205 return new LatLng(this.getNorth(), this.getWest());
1206 },
1207
1208 // @method getSouthEast(): LatLng
1209 // Returns the south-east point of the bounds.
1210 getSouthEast: function () {
1211 return new LatLng(this.getSouth(), this.getEast());
1212 },
1213
1214 // @method getWest(): Number
1215 // Returns the west longitude of the bounds
1216 getWest: function () {
1217 return this._southWest.lng;
1218 },
1219
1220 // @method getSouth(): Number
1221 // Returns the south latitude of the bounds
1222 getSouth: function () {
1223 return this._southWest.lat;
1224 },
1225
1226 // @method getEast(): Number
1227 // Returns the east longitude of the bounds
1228 getEast: function () {
1229 return this._northEast.lng;
1230 },
1231
1232 // @method getNorth(): Number
1233 // Returns the north latitude of the bounds
1234 getNorth: function () {
1235 return this._northEast.lat;
1236 },
1237
1238 // @method contains(otherBounds: LatLngBounds): Boolean
1239 // Returns `true` if the rectangle contains the given one.
1240
1241 // @alternative
1242 // @method contains (latlng: LatLng): Boolean
1243 // Returns `true` if the rectangle contains the given point.
1244 contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean
1245 if (typeof obj[0] === 'number' || obj instanceof LatLng || 'lat' in obj) {
1246 obj = toLatLng(obj);
1247 } else {
1248 obj = toLatLngBounds(obj);
1249 }
1250
1251 var sw = this._southWest,
1252 ne = this._northEast,
1253 sw2, ne2;
1254
1255 if (obj instanceof LatLngBounds) {
1256 sw2 = obj.getSouthWest();
1257 ne2 = obj.getNorthEast();
1258 } else {
1259 sw2 = ne2 = obj;
1260 }
1261
1262 return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) &&
1263 (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng);
1264 },
1265
1266 // @method intersects(otherBounds: LatLngBounds): Boolean
1267 // Returns `true` if the rectangle intersects the given bounds. Two bounds intersect if they have at least one point in common.
1268 intersects: function (bounds) {
1269 bounds = toLatLngBounds(bounds);
1270
1271 var sw = this._southWest,
1272 ne = this._northEast,
1273 sw2 = bounds.getSouthWest(),
1274 ne2 = bounds.getNorthEast(),
1275
1276 latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat),
1277 lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng);
1278
1279 return latIntersects && lngIntersects;
1280 },
1281
1282 // @method overlaps(otherBounds: LatLngBounds): Boolean
1283 // Returns `true` if the rectangle overlaps the given bounds. Two bounds overlap if their intersection is an area.
1284 overlaps: function (bounds) {
1285 bounds = toLatLngBounds(bounds);
1286
1287 var sw = this._southWest,
1288 ne = this._northEast,
1289 sw2 = bounds.getSouthWest(),
1290 ne2 = bounds.getNorthEast(),
1291
1292 latOverlaps = (ne2.lat > sw.lat) && (sw2.lat < ne.lat),
1293 lngOverlaps = (ne2.lng > sw.lng) && (sw2.lng < ne.lng);
1294
1295 return latOverlaps && lngOverlaps;
1296 },
1297
1298 // @method toBBoxString(): String
1299 // Returns a string with bounding box coordinates in a 'southwest_lng,southwest_lat,northeast_lng,northeast_lat' format. Useful for sending requests to web services that return geo data.
1300 toBBoxString: function () {
1301 return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(',');
1302 },
1303
1304 // @method equals(otherBounds: LatLngBounds, maxMargin?: Number): Boolean
1305 // Returns `true` if the rectangle is equivalent (within a small margin of error) to the given bounds. The margin of error can be overridden by setting `maxMargin` to a small number.
1306 equals: function (bounds, maxMargin) {
1307 if (!bounds) { return false; }
1308
1309 bounds = toLatLngBounds(bounds);
1310
1311 return this._southWest.equals(bounds.getSouthWest(), maxMargin) &&
1312 this._northEast.equals(bounds.getNorthEast(), maxMargin);
1313 },
1314
1315 // @method isValid(): Boolean
1316 // Returns `true` if the bounds are properly initialized.
1317 isValid: function () {
1318 return !!(this._southWest && this._northEast);
1319 }
1320 };
1321
1322 // TODO International date line?
1323
1324 // @factory L.latLngBounds(corner1: LatLng, corner2: LatLng)
1325 // Creates a `LatLngBounds` object by defining two diagonally opposite corners of the rectangle.
1326
1327 // @alternative
1328 // @factory L.latLngBounds(latlngs: LatLng[])
1329 // Creates a `LatLngBounds` object defined by the geographical points it contains. Very useful for zooming the map to fit a particular set of locations with [`fitBounds`](#map-fitbounds).
1330 function toLatLngBounds(a, b) {
1331 if (a instanceof LatLngBounds) {
1332 return a;
1333 }
1334 return new LatLngBounds(a, b);
1335 }
1336
1337 /* @class LatLng
1338 * @aka L.LatLng
1339 *
1340 * Represents a geographical point with a certain latitude and longitude.
1341 *
1342 * @example
1343 *
1344 * ```
1345 * var latlng = L.latLng(50.5, 30.5);
1346 * ```
1347 *
1348 * All Leaflet methods that accept LatLng objects also accept them in a simple Array form and simple object form (unless noted otherwise), so these lines are equivalent:
1349 *
1350 * ```
1351 * map.panTo([50, 30]);
1352 * map.panTo({lon: 30, lat: 50});
1353 * map.panTo({lat: 50, lng: 30});
1354 * map.panTo(L.latLng(50, 30));
1355 * ```
1356 *
1357 * Note that `LatLng` does not inherit from Leaflet's `Class` object,
1358 * which means new classes can't inherit from it, and new methods
1359 * can't be added to it with the `include` function.
1360 */
1361
1362 function LatLng(lat, lng, alt) {
1363 if (isNaN(lat) || isNaN(lng)) {
1364 throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')');
1365 }
1366
1367 // @property lat: Number
1368 // Latitude in degrees
1369 this.lat = +lat;
1370
1371 // @property lng: Number
1372 // Longitude in degrees
1373 this.lng = +lng;
1374
1375 // @property alt: Number
1376 // Altitude in meters (optional)
1377 if (alt !== undefined) {
1378 this.alt = +alt;
1379 }
1380 }
1381
1382 LatLng.prototype = {
1383 // @method equals(otherLatLng: LatLng, maxMargin?: Number): Boolean
1384 // Returns `true` if the given `LatLng` point is at the same position (within a small margin of error). The margin of error can be overridden by setting `maxMargin` to a small number.
1385 equals: function (obj, maxMargin) {
1386 if (!obj) { return false; }
1387
1388 obj = toLatLng(obj);
1389
1390 var margin = Math.max(
1391 Math.abs(this.lat - obj.lat),
1392 Math.abs(this.lng - obj.lng));
1393
1394 return margin <= (maxMargin === undefined ? 1.0E-9 : maxMargin);
1395 },
1396
1397 // @method toString(): String
1398 // Returns a string representation of the point (for debugging purposes).
1399 toString: function (precision) {
1400 return 'LatLng(' +
1401 formatNum(this.lat, precision) + ', ' +
1402 formatNum(this.lng, precision) + ')';
1403 },
1404
1405 // @method distanceTo(otherLatLng: LatLng): Number
1406 // Returns the distance (in meters) to the given `LatLng` calculated using the [Spherical Law of Cosines](https://en.wikipedia.org/wiki/Spherical_law_of_cosines).
1407 distanceTo: function (other) {
1408 return Earth.distance(this, toLatLng(other));
1409 },
1410
1411 // @method wrap(): LatLng
1412 // Returns a new `LatLng` object with the longitude wrapped so it's always between -180 and +180 degrees.
1413 wrap: function () {
1414 return Earth.wrapLatLng(this);
1415 },
1416
1417 // @method toBounds(sizeInMeters: Number): LatLngBounds
1418 // Returns a new `LatLngBounds` object in which each boundary is `sizeInMeters/2` meters apart from the `LatLng`.
1419 toBounds: function (sizeInMeters) {
1420 var latAccuracy = 180 * sizeInMeters / 40075017,
1421 lngAccuracy = latAccuracy / Math.cos((Math.PI / 180) * this.lat);
1422
1423 return toLatLngBounds(
1424 [this.lat - latAccuracy, this.lng - lngAccuracy],
1425 [this.lat + latAccuracy, this.lng + lngAccuracy]);
1426 },
1427
1428 clone: function () {
1429 return new LatLng(this.lat, this.lng, this.alt);
1430 }
1431 };
1432
1433
1434
1435 // @factory L.latLng(latitude: Number, longitude: Number, altitude?: Number): LatLng
1436 // Creates an object representing a geographical point with the given latitude and longitude (and optionally altitude).
1437
1438 // @alternative
1439 // @factory L.latLng(coords: Array): LatLng
1440 // Expects an array of the form `[Number, Number]` or `[Number, Number, Number]` instead.
1441
1442 // @alternative
1443 // @factory L.latLng(coords: Object): LatLng
1444 // Expects an plain object of the form `{lat: Number, lng: Number}` or `{lat: Number, lng: Number, alt: Number}` instead.
1445
1446 function toLatLng(a, b, c) {
1447 if (a instanceof LatLng) {
1448 return a;
1449 }
1450 if (isArray(a) && typeof a[0] !== 'object') {
1451 if (a.length === 3) {
1452 return new LatLng(a[0], a[1], a[2]);
1453 }
1454 if (a.length === 2) {
1455 return new LatLng(a[0], a[1]);
1456 }
1457 return null;
1458 }
1459 if (a === undefined || a === null) {
1460 return a;
1461 }
1462 if (typeof a === 'object' && 'lat' in a) {
1463 return new LatLng(a.lat, 'lng' in a ? a.lng : a.lon, a.alt);
1464 }
1465 if (b === undefined) {
1466 return null;
1467 }
1468 return new LatLng(a, b, c);
1469 }
1470
1471 /*
1472 * @namespace CRS
1473 * @crs L.CRS.Base
1474 * Object that defines coordinate reference systems for projecting
1475 * geographical points into pixel (screen) coordinates and back (and to
1476 * coordinates in other units for [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services). See
1477 * [spatial reference system](http://en.wikipedia.org/wiki/Coordinate_reference_system).
1478 *
1479 * Leaflet defines the most usual CRSs by default. If you want to use a
1480 * CRS not defined by default, take a look at the
1481 * [Proj4Leaflet](https://github.com/kartena/Proj4Leaflet) plugin.
1482 *
1483 * Note that the CRS instances do not inherit from Leaflet's `Class` object,
1484 * and can't be instantiated. Also, new classes can't inherit from them,
1485 * and methods can't be added to them with the `include` function.
1486 */
1487
1488 var CRS = {
1489 // @method latLngToPoint(latlng: LatLng, zoom: Number): Point
1490 // Projects geographical coordinates into pixel coordinates for a given zoom.
1491 latLngToPoint: function (latlng, zoom) {
1492 var projectedPoint = this.projection.project(latlng),
1493 scale = this.scale(zoom);
1494
1495 return this.transformation._transform(projectedPoint, scale);
1496 },
1497
1498 // @method pointToLatLng(point: Point, zoom: Number): LatLng
1499 // The inverse of `latLngToPoint`. Projects pixel coordinates on a given
1500 // zoom into geographical coordinates.
1501 pointToLatLng: function (point, zoom) {
1502 var scale = this.scale(zoom),
1503 untransformedPoint = this.transformation.untransform(point, scale);
1504
1505 return this.projection.unproject(untransformedPoint);
1506 },
1507
1508 // @method project(latlng: LatLng): Point
1509 // Projects geographical coordinates into coordinates in units accepted for
1510 // this CRS (e.g. meters for EPSG:3857, for passing it to WMS services).
1511 project: function (latlng) {
1512 return this.projection.project(latlng);
1513 },
1514
1515 // @method unproject(point: Point): LatLng
1516 // Given a projected coordinate returns the corresponding LatLng.
1517 // The inverse of `project`.
1518 unproject: function (point) {
1519 return this.projection.unproject(point);
1520 },
1521
1522 // @method scale(zoom: Number): Number
1523 // Returns the scale used when transforming projected coordinates into
1524 // pixel coordinates for a particular zoom. For example, it returns
1525 // `256 * 2^zoom` for Mercator-based CRS.
1526 scale: function (zoom) {
1527 return 256 * Math.pow(2, zoom);
1528 },
1529
1530 // @method zoom(scale: Number): Number
1531 // Inverse of `scale()`, returns the zoom level corresponding to a scale
1532 // factor of `scale`.
1533 zoom: function (scale) {
1534 return Math.log(scale / 256) / Math.LN2;
1535 },
1536
1537 // @method getProjectedBounds(zoom: Number): Bounds
1538 // Returns the projection's bounds scaled and transformed for the provided `zoom`.
1539 getProjectedBounds: function (zoom) {
1540 if (this.infinite) { return null; }
1541
1542 var b = this.projection.bounds,
1543 s = this.scale(zoom),
1544 min = this.transformation.transform(b.min, s),
1545 max = this.transformation.transform(b.max, s);
1546
1547 return new Bounds(min, max);
1548 },
1549
1550 // @method distance(latlng1: LatLng, latlng2: LatLng): Number
1551 // Returns the distance between two geographical coordinates.
1552
1553 // @property code: String
1554 // Standard code name of the CRS passed into WMS services (e.g. `'EPSG:3857'`)
1555 //
1556 // @property wrapLng: Number[]
1557 // An array of two numbers defining whether the longitude (horizontal) coordinate
1558 // axis wraps around a given range and how. Defaults to `[-180, 180]` in most
1559 // geographical CRSs. If `undefined`, the longitude axis does not wrap around.
1560 //
1561 // @property wrapLat: Number[]
1562 // Like `wrapLng`, but for the latitude (vertical) axis.
1563
1564 // wrapLng: [min, max],
1565 // wrapLat: [min, max],
1566
1567 // @property infinite: Boolean
1568 // If true, the coordinate space will be unbounded (infinite in both axes)
1569 infinite: false,
1570
1571 // @method wrapLatLng(latlng: LatLng): LatLng
1572 // Returns a `LatLng` where lat and lng has been wrapped according to the
1573 // CRS's `wrapLat` and `wrapLng` properties, if they are outside the CRS's bounds.
1574 wrapLatLng: function (latlng) {
1575 var lng = this.wrapLng ? wrapNum(latlng.lng, this.wrapLng, true) : latlng.lng,
1576 lat = this.wrapLat ? wrapNum(latlng.lat, this.wrapLat, true) : latlng.lat,
1577 alt = latlng.alt;
1578
1579 return new LatLng(lat, lng, alt);
1580 },
1581
1582 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
1583 // Returns a `LatLngBounds` with the same size as the given one, ensuring
1584 // that its center is within the CRS's bounds.
1585 // Only accepts actual `L.LatLngBounds` instances, not arrays.
1586 wrapLatLngBounds: function (bounds) {
1587 var center = bounds.getCenter(),
1588 newCenter = this.wrapLatLng(center),
1589 latShift = center.lat - newCenter.lat,
1590 lngShift = center.lng - newCenter.lng;
1591
1592 if (latShift === 0 && lngShift === 0) {
1593 return bounds;
1594 }
1595
1596 var sw = bounds.getSouthWest(),
1597 ne = bounds.getNorthEast(),
1598 newSw = new LatLng(sw.lat - latShift, sw.lng - lngShift),
1599 newNe = new LatLng(ne.lat - latShift, ne.lng - lngShift);
1600
1601 return new LatLngBounds(newSw, newNe);
1602 }
1603 };
1604
1605 /*
1606 * @namespace CRS
1607 * @crs L.CRS.Earth
1608 *
1609 * Serves as the base for CRS that are global such that they cover the earth.
1610 * Can only be used as the base for other CRS and cannot be used directly,
1611 * since it does not have a `code`, `projection` or `transformation`. `distance()` returns
1612 * meters.
1613 */
1614
1615 var Earth = extend({}, CRS, {
1616 wrapLng: [-180, 180],
1617
1618 // Mean Earth Radius, as recommended for use by
1619 // the International Union of Geodesy and Geophysics,
1620 // see http://rosettacode.org/wiki/Haversine_formula
1621 R: 6371000,
1622
1623 // distance between two geographical points using spherical law of cosines approximation
1624 distance: function (latlng1, latlng2) {
1625 var rad = Math.PI / 180,
1626 lat1 = latlng1.lat * rad,
1627 lat2 = latlng2.lat * rad,
1628 sinDLat = Math.sin((latlng2.lat - latlng1.lat) * rad / 2),
1629 sinDLon = Math.sin((latlng2.lng - latlng1.lng) * rad / 2),
1630 a = sinDLat * sinDLat + Math.cos(lat1) * Math.cos(lat2) * sinDLon * sinDLon,
1631 c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
1632 return this.R * c;
1633 }
1634 });
1635
1636 /*
1637 * @namespace Projection
1638 * @projection L.Projection.SphericalMercator
1639 *
1640 * Spherical Mercator projection — the most common projection for online maps,
1641 * used by almost all free and commercial tile providers. Assumes that Earth is
1642 * a sphere. Used by the `EPSG:3857` CRS.
1643 */
1644
1645 var earthRadius = 6378137;
1646
1647 var SphericalMercator = {
1648
1649 R: earthRadius,
1650 MAX_LATITUDE: 85.0511287798,
1651
1652 project: function (latlng) {
1653 var d = Math.PI / 180,
1654 max = this.MAX_LATITUDE,
1655 lat = Math.max(Math.min(max, latlng.lat), -max),
1656 sin = Math.sin(lat * d);
1657
1658 return new Point(
1659 this.R * latlng.lng * d,
1660 this.R * Math.log((1 + sin) / (1 - sin)) / 2);
1661 },
1662
1663 unproject: function (point) {
1664 var d = 180 / Math.PI;
1665
1666 return new LatLng(
1667 (2 * Math.atan(Math.exp(point.y / this.R)) - (Math.PI / 2)) * d,
1668 point.x * d / this.R);
1669 },
1670
1671 bounds: (function () {
1672 var d = earthRadius * Math.PI;
1673 return new Bounds([-d, -d], [d, d]);
1674 })()
1675 };
1676
1677 /*
1678 * @class Transformation
1679 * @aka L.Transformation
1680 *
1681 * Represents an affine transformation: a set of coefficients `a`, `b`, `c`, `d`
1682 * for transforming a point of a form `(x, y)` into `(a*x + b, c*y + d)` and doing
1683 * the reverse. Used by Leaflet in its projections code.
1684 *
1685 * @example
1686 *
1687 * ```js
1688 * var transformation = L.transformation(2, 5, -1, 10),
1689 * p = L.point(1, 2),
1690 * p2 = transformation.transform(p), // L.point(7, 8)
1691 * p3 = transformation.untransform(p2); // L.point(1, 2)
1692 * ```
1693 */
1694
1695
1696 // factory new L.Transformation(a: Number, b: Number, c: Number, d: Number)
1697 // Creates a `Transformation` object with the given coefficients.
1698 function Transformation(a, b, c, d) {
1699 if (isArray(a)) {
1700 // use array properties
1701 this._a = a[0];
1702 this._b = a[1];
1703 this._c = a[2];
1704 this._d = a[3];
1705 return;
1706 }
1707 this._a = a;
1708 this._b = b;
1709 this._c = c;
1710 this._d = d;
1711 }
1712
1713 Transformation.prototype = {
1714 // @method transform(point: Point, scale?: Number): Point
1715 // Returns a transformed point, optionally multiplied by the given scale.
1716 // Only accepts actual `L.Point` instances, not arrays.
1717 transform: function (point, scale) { // (Point, Number) -> Point
1718 return this._transform(point.clone(), scale);
1719 },
1720
1721 // destructive transform (faster)
1722 _transform: function (point, scale) {
1723 scale = scale || 1;
1724 point.x = scale * (this._a * point.x + this._b);
1725 point.y = scale * (this._c * point.y + this._d);
1726 return point;
1727 },
1728
1729 // @method untransform(point: Point, scale?: Number): Point
1730 // Returns the reverse transformation of the given point, optionally divided
1731 // by the given scale. Only accepts actual `L.Point` instances, not arrays.
1732 untransform: function (point, scale) {
1733 scale = scale || 1;
1734 return new Point(
1735 (point.x / scale - this._b) / this._a,
1736 (point.y / scale - this._d) / this._c);
1737 }
1738 };
1739
1740 // factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1741
1742 // @factory L.transformation(a: Number, b: Number, c: Number, d: Number)
1743 // Instantiates a Transformation object with the given coefficients.
1744
1745 // @alternative
1746 // @factory L.transformation(coefficients: Array): Transformation
1747 // Expects an coefficients array of the form
1748 // `[a: Number, b: Number, c: Number, d: Number]`.
1749
1750 function toTransformation(a, b, c, d) {
1751 return new Transformation(a, b, c, d);
1752 }
1753
1754 /*
1755 * @namespace CRS
1756 * @crs L.CRS.EPSG3857
1757 *
1758 * The most common CRS for online maps, used by almost all free and commercial
1759 * tile providers. Uses Spherical Mercator projection. Set in by default in
1760 * Map's `crs` option.
1761 */
1762
1763 var EPSG3857 = extend({}, Earth, {
1764 code: 'EPSG:3857',
1765 projection: SphericalMercator,
1766
1767 transformation: (function () {
1768 var scale = 0.5 / (Math.PI * SphericalMercator.R);
1769 return toTransformation(scale, 0.5, -scale, 0.5);
1770 }())
1771 });
1772
1773 var EPSG900913 = extend({}, EPSG3857, {
1774 code: 'EPSG:900913'
1775 });
1776
1777 // @namespace SVG; @section
1778 // There are several static functions which can be called without instantiating L.SVG:
1779
1780 // @function create(name: String): SVGElement
1781 // Returns a instance of [SVGElement](https://developer.mozilla.org/docs/Web/API/SVGElement),
1782 // corresponding to the class name passed. For example, using 'line' will return
1783 // an instance of [SVGLineElement](https://developer.mozilla.org/docs/Web/API/SVGLineElement).
1784 function svgCreate(name) {
1785 return document.createElementNS('http://www.w3.org/2000/svg', name);
1786 }
1787
1788 // @function pointsToPath(rings: Point[], closed: Boolean): String
1789 // Generates a SVG path string for multiple rings, with each ring turning
1790 // into "M..L..L.." instructions
1791 function pointsToPath(rings, closed) {
1792 var str = '',
1793 i, j, len, len2, points, p;
1794
1795 for (i = 0, len = rings.length; i < len; i++) {
1796 points = rings[i];
1797
1798 for (j = 0, len2 = points.length; j < len2; j++) {
1799 p = points[j];
1800 str += (j ? 'L' : 'M') + p.x + ' ' + p.y;
1801 }
1802
1803 // closes the ring for polygons; "x" is VML syntax
1804 str += closed ? (svg ? 'z' : 'x') : '';
1805 }
1806
1807 // SVG complains about empty path strings
1808 return str || 'M0 0';
1809 }
1810
1811 /*
1812 * @namespace Browser
1813 * @aka L.Browser
1814 *
1815 * A namespace with static properties for browser/feature detection used by Leaflet internally.
1816 *
1817 * @example
1818 *
1819 * ```js
1820 * if (L.Browser.ielt9) {
1821 * alert('Upgrade your browser, dude!');
1822 * }
1823 * ```
1824 */
1825
1826 var style$1 = document.documentElement.style;
1827
1828 // @property ie: Boolean; `true` for all Internet Explorer versions (not Edge).
1829 var ie = 'ActiveXObject' in window;
1830
1831 // @property ielt9: Boolean; `true` for Internet Explorer versions less than 9.
1832 var ielt9 = ie && !document.addEventListener;
1833
1834 // @property edge: Boolean; `true` for the Edge web browser.
1835 var edge = 'msLaunchUri' in navigator && !('documentMode' in document);
1836
1837 // @property webkit: Boolean;
1838 // `true` for webkit-based browsers like Chrome and Safari (including mobile versions).
1839 var webkit = userAgentContains('webkit');
1840
1841 // @property android: Boolean
1842 // `true` for any browser running on an Android platform.
1843 var android = userAgentContains('android');
1844
1845 // @property android23: Boolean; `true` for browsers running on Android 2 or Android 3.
1846 var android23 = userAgentContains('android 2') || userAgentContains('android 3');
1847
1848 /* See https://stackoverflow.com/a/17961266 for details on detecting stock Android */
1849 var webkitVer = parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1], 10); // also matches AppleWebKit
1850 // @property androidStock: Boolean; `true` for the Android stock browser (i.e. not Chrome)
1851 var androidStock = android && userAgentContains('Google') && webkitVer < 537 && !('AudioNode' in window);
1852
1853 // @property opera: Boolean; `true` for the Opera browser
1854 var opera = !!window.opera;
1855
1856 // @property chrome: Boolean; `true` for the Chrome browser.
1857 var chrome = !edge && userAgentContains('chrome');
1858
1859 // @property gecko: Boolean; `true` for gecko-based browsers like Firefox.
1860 var gecko = userAgentContains('gecko') && !webkit && !opera && !ie;
1861
1862 // @property safari: Boolean; `true` for the Safari browser.
1863 var safari = !chrome && userAgentContains('safari');
1864
1865 var phantom = userAgentContains('phantom');
1866
1867 // @property opera12: Boolean
1868 // `true` for the Opera browser supporting CSS transforms (version 12 or later).
1869 var opera12 = 'OTransition' in style$1;
1870
1871 // @property win: Boolean; `true` when the browser is running in a Windows platform
1872 var win = navigator.platform.indexOf('Win') === 0;
1873
1874 // @property ie3d: Boolean; `true` for all Internet Explorer versions supporting CSS transforms.
1875 var ie3d = ie && ('transition' in style$1);
1876
1877 // @property webkit3d: Boolean; `true` for webkit-based browsers supporting CSS transforms.
1878 var webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23;
1879
1880 // @property gecko3d: Boolean; `true` for gecko-based browsers supporting CSS transforms.
1881 var gecko3d = 'MozPerspective' in style$1;
1882
1883 // @property any3d: Boolean
1884 // `true` for all browsers supporting CSS transforms.
1885 var any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d) && !opera12 && !phantom;
1886
1887 // @property mobile: Boolean; `true` for all browsers running in a mobile device.
1888 var mobile = typeof orientation !== 'undefined' || userAgentContains('mobile');
1889
1890 // @property mobileWebkit: Boolean; `true` for all webkit-based browsers in a mobile device.
1891 var mobileWebkit = mobile && webkit;
1892
1893 // @property mobileWebkit3d: Boolean
1894 // `true` for all webkit-based browsers in a mobile device supporting CSS transforms.
1895 var mobileWebkit3d = mobile && webkit3d;
1896
1897 // @property msPointer: Boolean
1898 // `true` for browsers implementing the Microsoft touch events model (notably IE10).
1899 var msPointer = !window.PointerEvent && window.MSPointerEvent;
1900
1901 // @property pointer: Boolean
1902 // `true` for all browsers supporting [pointer events](https://msdn.microsoft.com/en-us/library/dn433244%28v=vs.85%29.aspx).
1903 var pointer = !!(window.PointerEvent || msPointer);
1904
1905 // @property touch: Boolean
1906 // `true` for all browsers supporting [touch events](https://developer.mozilla.org/docs/Web/API/Touch_events).
1907 // This does not necessarily mean that the browser is running in a computer with
1908 // a touchscreen, it only means that the browser is capable of understanding
1909 // touch events.
1910 var touch = !window.L_NO_TOUCH && (pointer || 'ontouchstart' in window ||
1911 (window.DocumentTouch && document instanceof window.DocumentTouch));
1912
1913 // @property mobileOpera: Boolean; `true` for the Opera browser in a mobile device.
1914 var mobileOpera = mobile && opera;
1915
1916 // @property mobileGecko: Boolean
1917 // `true` for gecko-based browsers running in a mobile device.
1918 var mobileGecko = mobile && gecko;
1919
1920 // @property retina: Boolean
1921 // `true` for browsers on a high-resolution "retina" screen or on any screen when browser's display zoom is more than 100%.
1922 var retina = (window.devicePixelRatio || (window.screen.deviceXDPI / window.screen.logicalXDPI)) > 1;
1923
1924 // @property passiveEvents: Boolean
1925 // `true` for browsers that support passive events.
1926 var passiveEvents = (function () {
1927 var supportsPassiveOption = false;
1928 try {
1929 var opts = Object.defineProperty({}, 'passive', {
1930 get: function () { // eslint-disable-line getter-return
1931 supportsPassiveOption = true;
1932 }
1933 });
1934 window.addEventListener('testPassiveEventSupport', falseFn, opts);
1935 window.removeEventListener('testPassiveEventSupport', falseFn, opts);
1936 } catch (e) {
1937 // Errors can safely be ignored since this is only a browser support test.
1938 }
1939 return supportsPassiveOption;
1940 }());
1941
1942 // @property canvas: Boolean
1943 // `true` when the browser supports [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
1944 var canvas = (function () {
1945 return !!document.createElement('canvas').getContext;
1946 }());
1947
1948 // @property svg: Boolean
1949 // `true` when the browser supports [SVG](https://developer.mozilla.org/docs/Web/SVG).
1950 var svg = !!(document.createElementNS && svgCreate('svg').createSVGRect);
1951
1952 // @property vml: Boolean
1953 // `true` if the browser supports [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language).
1954 var vml = !svg && (function () {
1955 try {
1956 var div = document.createElement('div');
1957 div.innerHTML = '<v:shape adj="1"/>';
1958
1959 var shape = div.firstChild;
1960 shape.style.behavior = 'url(#default#VML)';
1961
1962 return shape && (typeof shape.adj === 'object');
1963
1964 } catch (e) {
1965 return false;
1966 }
1967 }());
1968
1969
1970 function userAgentContains(str) {
1971 return navigator.userAgent.toLowerCase().indexOf(str) >= 0;
1972 }
1973
1974 var Browser = ({
1975 ie: ie,
1976 ielt9: ielt9,
1977 edge: edge,
1978 webkit: webkit,
1979 android: android,
1980 android23: android23,
1981 androidStock: androidStock,
1982 opera: opera,
1983 chrome: chrome,
1984 gecko: gecko,
1985 safari: safari,
1986 phantom: phantom,
1987 opera12: opera12,
1988 win: win,
1989 ie3d: ie3d,
1990 webkit3d: webkit3d,
1991 gecko3d: gecko3d,
1992 any3d: any3d,
1993 mobile: mobile,
1994 mobileWebkit: mobileWebkit,
1995 mobileWebkit3d: mobileWebkit3d,
1996 msPointer: msPointer,
1997 pointer: pointer,
1998 touch: touch,
1999 mobileOpera: mobileOpera,
2000 mobileGecko: mobileGecko,
2001 retina: retina,
2002 passiveEvents: passiveEvents,
2003 canvas: canvas,
2004 svg: svg,
2005 vml: vml
2006 });
2007
2008 /*
2009 * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices.
2010 */
2011
2012
2013 var POINTER_DOWN = msPointer ? 'MSPointerDown' : 'pointerdown';
2014 var POINTER_MOVE = msPointer ? 'MSPointerMove' : 'pointermove';
2015 var POINTER_UP = msPointer ? 'MSPointerUp' : 'pointerup';
2016 var POINTER_CANCEL = msPointer ? 'MSPointerCancel' : 'pointercancel';
2017
2018 var _pointers = {};
2019 var _pointerDocListener = false;
2020
2021 // Provides a touch events wrapper for (ms)pointer events.
2022 // ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890
2023
2024 function addPointerListener(obj, type, handler, id) {
2025 if (type === 'touchstart') {
2026 _addPointerStart(obj, handler, id);
2027
2028 } else if (type === 'touchmove') {
2029 _addPointerMove(obj, handler, id);
2030
2031 } else if (type === 'touchend') {
2032 _addPointerEnd(obj, handler, id);
2033 }
2034
2035 return this;
2036 }
2037
2038 function removePointerListener(obj, type, id) {
2039 var handler = obj['_leaflet_' + type + id];
2040
2041 if (type === 'touchstart') {
2042 obj.removeEventListener(POINTER_DOWN, handler, false);
2043
2044 } else if (type === 'touchmove') {
2045 obj.removeEventListener(POINTER_MOVE, handler, false);
2046
2047 } else if (type === 'touchend') {
2048 obj.removeEventListener(POINTER_UP, handler, false);
2049 obj.removeEventListener(POINTER_CANCEL, handler, false);
2050 }
2051
2052 return this;
2053 }
2054
2055 function _addPointerStart(obj, handler, id) {
2056 var onDown = bind(function (e) {
2057 // IE10 specific: MsTouch needs preventDefault. See #2000
2058 if (e.MSPOINTER_TYPE_TOUCH && e.pointerType === e.MSPOINTER_TYPE_TOUCH) {
2059 preventDefault(e);
2060 }
2061
2062 _handlePointer(e, handler);
2063 });
2064
2065 obj['_leaflet_touchstart' + id] = onDown;
2066 obj.addEventListener(POINTER_DOWN, onDown, false);
2067
2068 // need to keep track of what pointers and how many are active to provide e.touches emulation
2069 if (!_pointerDocListener) {
2070 // we listen document as any drags that end by moving the touch off the screen get fired there
2071 document.addEventListener(POINTER_DOWN, _globalPointerDown, true);
2072 document.addEventListener(POINTER_MOVE, _globalPointerMove, true);
2073 document.addEventListener(POINTER_UP, _globalPointerUp, true);
2074 document.addEventListener(POINTER_CANCEL, _globalPointerUp, true);
2075
2076 _pointerDocListener = true;
2077 }
2078 }
2079
2080 function _globalPointerDown(e) {
2081 _pointers[e.pointerId] = e;
2082 }
2083
2084 function _globalPointerMove(e) {
2085 if (_pointers[e.pointerId]) {
2086 _pointers[e.pointerId] = e;
2087 }
2088 }
2089
2090 function _globalPointerUp(e) {
2091 delete _pointers[e.pointerId];
2092 }
2093
2094 function _handlePointer(e, handler) {
2095 e.touches = [];
2096 for (var i in _pointers) {
2097 e.touches.push(_pointers[i]);
2098 }
2099 e.changedTouches = [e];
2100
2101 handler(e);
2102 }
2103
2104 function _addPointerMove(obj, handler, id) {
2105 var onMove = function (e) {
2106 // don't fire touch moves when mouse isn't down
2107 if ((e.pointerType === (e.MSPOINTER_TYPE_MOUSE || 'mouse')) && e.buttons === 0) {
2108 return;
2109 }
2110
2111 _handlePointer(e, handler);
2112 };
2113
2114 obj['_leaflet_touchmove' + id] = onMove;
2115 obj.addEventListener(POINTER_MOVE, onMove, false);
2116 }
2117
2118 function _addPointerEnd(obj, handler, id) {
2119 var onUp = function (e) {
2120 _handlePointer(e, handler);
2121 };
2122
2123 obj['_leaflet_touchend' + id] = onUp;
2124 obj.addEventListener(POINTER_UP, onUp, false);
2125 obj.addEventListener(POINTER_CANCEL, onUp, false);
2126 }
2127
2128 /*
2129 * Extends the event handling code with double tap support for mobile browsers.
2130 */
2131
2132 var _touchstart = msPointer ? 'MSPointerDown' : pointer ? 'pointerdown' : 'touchstart';
2133 var _touchend = msPointer ? 'MSPointerUp' : pointer ? 'pointerup' : 'touchend';
2134 var _pre = '_leaflet_';
2135
2136 // inspired by Zepto touch code by Thomas Fuchs
2137 function addDoubleTapListener(obj, handler, id) {
2138 var last, touch$$1,
2139 doubleTap = false,
2140 delay = 250;
2141
2142 function onTouchStart(e) {
2143
2144 if (pointer) {
2145 if (!e.isPrimary) { return; }
2146 if (e.pointerType === 'mouse') { return; } // mouse fires native dblclick
2147 } else if (e.touches.length > 1) {
2148 return;
2149 }
2150
2151 var now = Date.now(),
2152 delta = now - (last || now);
2153
2154 touch$$1 = e.touches ? e.touches[0] : e;
2155 doubleTap = (delta > 0 && delta <= delay);
2156 last = now;
2157 }
2158
2159 function onTouchEnd(e) {
2160 if (doubleTap && !touch$$1.cancelBubble) {
2161 if (pointer) {
2162 if (e.pointerType === 'mouse') { return; }
2163 // work around .type being readonly with MSPointer* events
2164 var newTouch = {},
2165 prop, i;
2166
2167 for (i in touch$$1) {
2168 prop = touch$$1[i];
2169 newTouch[i] = prop && prop.bind ? prop.bind(touch$$1) : prop;
2170 }
2171 touch$$1 = newTouch;
2172 }
2173 touch$$1.type = 'dblclick';
2174 touch$$1.button = 0;
2175 handler(touch$$1);
2176 last = null;
2177 }
2178 }
2179
2180 obj[_pre + _touchstart + id] = onTouchStart;
2181 obj[_pre + _touchend + id] = onTouchEnd;
2182 obj[_pre + 'dblclick' + id] = handler;
2183
2184 obj.addEventListener(_touchstart, onTouchStart, passiveEvents ? {passive: false} : false);
2185 obj.addEventListener(_touchend, onTouchEnd, passiveEvents ? {passive: false} : false);
2186
2187 // On some platforms (notably, chrome<55 on win10 + touchscreen + mouse),
2188 // the browser doesn't fire touchend/pointerup events but does fire
2189 // native dblclicks. See #4127.
2190 // Edge 14 also fires native dblclicks, but only for pointerType mouse, see #5180.
2191 obj.addEventListener('dblclick', handler, false);
2192
2193 return this;
2194 }
2195
2196 function removeDoubleTapListener(obj, id) {
2197 var touchstart = obj[_pre + _touchstart + id],
2198 touchend = obj[_pre + _touchend + id],
2199 dblclick = obj[_pre + 'dblclick' + id];
2200
2201 obj.removeEventListener(_touchstart, touchstart, passiveEvents ? {passive: false} : false);
2202 obj.removeEventListener(_touchend, touchend, passiveEvents ? {passive: false} : false);
2203 obj.removeEventListener('dblclick', dblclick, false);
2204
2205 return this;
2206 }
2207
2208 /*
2209 * @namespace DomUtil
2210 *
2211 * Utility functions to work with the [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)
2212 * tree, used by Leaflet internally.
2213 *
2214 * Most functions expecting or returning a `HTMLElement` also work for
2215 * SVG elements. The only difference is that classes refer to CSS classes
2216 * in HTML and SVG classes in SVG.
2217 */
2218
2219
2220 // @property TRANSFORM: String
2221 // Vendor-prefixed transform style name (e.g. `'webkitTransform'` for WebKit).
2222 var TRANSFORM = testProp(
2223 ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']);
2224
2225 // webkitTransition comes first because some browser versions that drop vendor prefix don't do
2226 // the same for the transitionend event, in particular the Android 4.1 stock browser
2227
2228 // @property TRANSITION: String
2229 // Vendor-prefixed transition style name.
2230 var TRANSITION = testProp(
2231 ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']);
2232
2233 // @property TRANSITION_END: String
2234 // Vendor-prefixed transitionend event name.
2235 var TRANSITION_END =
2236 TRANSITION === 'webkitTransition' || TRANSITION === 'OTransition' ? TRANSITION + 'End' : 'transitionend';
2237
2238
2239 // @function get(id: String|HTMLElement): HTMLElement
2240 // Returns an element given its DOM id, or returns the element itself
2241 // if it was passed directly.
2242 function get(id) {
2243 return typeof id === 'string' ? document.getElementById(id) : id;
2244 }
2245
2246 // @function getStyle(el: HTMLElement, styleAttrib: String): String
2247 // Returns the value for a certain style attribute on an element,
2248 // including computed values or values set through CSS.
2249 function getStyle(el, style) {
2250 var value = el.style[style] || (el.currentStyle && el.currentStyle[style]);
2251
2252 if ((!value || value === 'auto') && document.defaultView) {
2253 var css = document.defaultView.getComputedStyle(el, null);
2254 value = css ? css[style] : null;
2255 }
2256 return value === 'auto' ? null : value;
2257 }
2258
2259 // @function create(tagName: String, className?: String, container?: HTMLElement): HTMLElement
2260 // Creates an HTML element with `tagName`, sets its class to `className`, and optionally appends it to `container` element.
2261 function create$1(tagName, className, container) {
2262 var el = document.createElement(tagName);
2263 el.className = className || '';
2264
2265 if (container) {
2266 container.appendChild(el);
2267 }
2268 return el;
2269 }
2270
2271 // @function remove(el: HTMLElement)
2272 // Removes `el` from its parent element
2273 function remove(el) {
2274 var parent = el.parentNode;
2275 if (parent) {
2276 parent.removeChild(el);
2277 }
2278 }
2279
2280 // @function empty(el: HTMLElement)
2281 // Removes all of `el`'s children elements from `el`
2282 function empty(el) {
2283 while (el.firstChild) {
2284 el.removeChild(el.firstChild);
2285 }
2286 }
2287
2288 // @function toFront(el: HTMLElement)
2289 // Makes `el` the last child of its parent, so it renders in front of the other children.
2290 function toFront(el) {
2291 var parent = el.parentNode;
2292 if (parent && parent.lastChild !== el) {
2293 parent.appendChild(el);
2294 }
2295 }
2296
2297 // @function toBack(el: HTMLElement)
2298 // Makes `el` the first child of its parent, so it renders behind the other children.
2299 function toBack(el) {
2300 var parent = el.parentNode;
2301 if (parent && parent.firstChild !== el) {
2302 parent.insertBefore(el, parent.firstChild);
2303 }
2304 }
2305
2306 // @function hasClass(el: HTMLElement, name: String): Boolean
2307 // Returns `true` if the element's class attribute contains `name`.
2308 function hasClass(el, name) {
2309 if (el.classList !== undefined) {
2310 return el.classList.contains(name);
2311 }
2312 var className = getClass(el);
2313 return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className);
2314 }
2315
2316 // @function addClass(el: HTMLElement, name: String)
2317 // Adds `name` to the element's class attribute.
2318 function addClass(el, name) {
2319 if (el.classList !== undefined) {
2320 var classes = splitWords(name);
2321 for (var i = 0, len = classes.length; i < len; i++) {
2322 el.classList.add(classes[i]);
2323 }
2324 } else if (!hasClass(el, name)) {
2325 var className = getClass(el);
2326 setClass(el, (className ? className + ' ' : '') + name);
2327 }
2328 }
2329
2330 // @function removeClass(el: HTMLElement, name: String)
2331 // Removes `name` from the element's class attribute.
2332 function removeClass(el, name) {
2333 if (el.classList !== undefined) {
2334 el.classList.remove(name);
2335 } else {
2336 setClass(el, trim((' ' + getClass(el) + ' ').replace(' ' + name + ' ', ' ')));
2337 }
2338 }
2339
2340 // @function setClass(el: HTMLElement, name: String)
2341 // Sets the element's class.
2342 function setClass(el, name) {
2343 if (el.className.baseVal === undefined) {
2344 el.className = name;
2345 } else {
2346 // in case of SVG element
2347 el.className.baseVal = name;
2348 }
2349 }
2350
2351 // @function getClass(el: HTMLElement): String
2352 // Returns the element's class.
2353 function getClass(el) {
2354 // Check if the element is an SVGElementInstance and use the correspondingElement instead
2355 // (Required for linked SVG elements in IE11.)
2356 if (el.correspondingElement) {
2357 el = el.correspondingElement;
2358 }
2359 return el.className.baseVal === undefined ? el.className : el.className.baseVal;
2360 }
2361
2362 // @function setOpacity(el: HTMLElement, opacity: Number)
2363 // Set the opacity of an element (including old IE support).
2364 // `opacity` must be a number from `0` to `1`.
2365 function setOpacity(el, value) {
2366 if ('opacity' in el.style) {
2367 el.style.opacity = value;
2368 } else if ('filter' in el.style) {
2369 _setOpacityIE(el, value);
2370 }
2371 }
2372
2373 function _setOpacityIE(el, value) {
2374 var filter = false,
2375 filterName = 'DXImageTransform.Microsoft.Alpha';
2376
2377 // filters collection throws an error if we try to retrieve a filter that doesn't exist
2378 try {
2379 filter = el.filters.item(filterName);
2380 } catch (e) {
2381 // don't set opacity to 1 if we haven't already set an opacity,
2382 // it isn't needed and breaks transparent pngs.
2383 if (value === 1) { return; }
2384 }
2385
2386 value = Math.round(value * 100);
2387
2388 if (filter) {
2389 filter.Enabled = (value !== 100);
2390 filter.Opacity = value;
2391 } else {
2392 el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')';
2393 }
2394 }
2395
2396 // @function testProp(props: String[]): String|false
2397 // Goes through the array of style names and returns the first name
2398 // that is a valid style name for an element. If no such name is found,
2399 // it returns false. Useful for vendor-prefixed styles like `transform`.
2400 function testProp(props) {
2401 var style = document.documentElement.style;
2402
2403 for (var i = 0; i < props.length; i++) {
2404 if (props[i] in style) {
2405 return props[i];
2406 }
2407 }
2408 return false;
2409 }
2410
2411 // @function setTransform(el: HTMLElement, offset: Point, scale?: Number)
2412 // Resets the 3D CSS transform of `el` so it is translated by `offset` pixels
2413 // and optionally scaled by `scale`. Does not have an effect if the
2414 // browser doesn't support 3D CSS transforms.
2415 function setTransform(el, offset, scale) {
2416 var pos = offset || new Point(0, 0);
2417
2418 el.style[TRANSFORM] =
2419 (ie3d ?
2420 'translate(' + pos.x + 'px,' + pos.y + 'px)' :
2421 'translate3d(' + pos.x + 'px,' + pos.y + 'px,0)') +
2422 (scale ? ' scale(' + scale + ')' : '');
2423 }
2424
2425 // @function setPosition(el: HTMLElement, position: Point)
2426 // Sets the position of `el` to coordinates specified by `position`,
2427 // using CSS translate or top/left positioning depending on the browser
2428 // (used by Leaflet internally to position its layers).
2429 function setPosition(el, point) {
2430
2431 /*eslint-disable */
2432 el._leaflet_pos = point;
2433 /* eslint-enable */
2434
2435 if (any3d) {
2436 setTransform(el, point);
2437 } else {
2438 el.style.left = point.x + 'px';
2439 el.style.top = point.y + 'px';
2440 }
2441 }
2442
2443 // @function getPosition(el: HTMLElement): Point
2444 // Returns the coordinates of an element previously positioned with setPosition.
2445 function getPosition(el) {
2446 // this method is only used for elements previously positioned using setPosition,
2447 // so it's safe to cache the position for performance
2448
2449 return el._leaflet_pos || new Point(0, 0);
2450 }
2451
2452 // @function disableTextSelection()
2453 // Prevents the user from generating `selectstart` DOM events, usually generated
2454 // when the user drags the mouse through a page with text. Used internally
2455 // by Leaflet to override the behaviour of any click-and-drag interaction on
2456 // the map. Affects drag interactions on the whole document.
2457
2458 // @function enableTextSelection()
2459 // Cancels the effects of a previous [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection).
2460 var disableTextSelection;
2461 var enableTextSelection;
2462 var _userSelect;
2463 if ('onselectstart' in document) {
2464 disableTextSelection = function () {
2465 on(window, 'selectstart', preventDefault);
2466 };
2467 enableTextSelection = function () {
2468 off(window, 'selectstart', preventDefault);
2469 };
2470 } else {
2471 var userSelectProperty = testProp(
2472 ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']);
2473
2474 disableTextSelection = function () {
2475 if (userSelectProperty) {
2476 var style = document.documentElement.style;
2477 _userSelect = style[userSelectProperty];
2478 style[userSelectProperty] = 'none';
2479 }
2480 };
2481 enableTextSelection = function () {
2482 if (userSelectProperty) {
2483 document.documentElement.style[userSelectProperty] = _userSelect;
2484 _userSelect = undefined;
2485 }
2486 };
2487 }
2488
2489 // @function disableImageDrag()
2490 // As [`L.DomUtil.disableTextSelection`](#domutil-disabletextselection), but
2491 // for `dragstart` DOM events, usually generated when the user drags an image.
2492 function disableImageDrag() {
2493 on(window, 'dragstart', preventDefault);
2494 }
2495
2496 // @function enableImageDrag()
2497 // Cancels the effects of a previous [`L.DomUtil.disableImageDrag`](#domutil-disabletextselection).
2498 function enableImageDrag() {
2499 off(window, 'dragstart', preventDefault);
2500 }
2501
2502 var _outlineElement, _outlineStyle;
2503 // @function preventOutline(el: HTMLElement)
2504 // Makes the [outline](https://developer.mozilla.org/docs/Web/CSS/outline)
2505 // of the element `el` invisible. Used internally by Leaflet to prevent
2506 // focusable elements from displaying an outline when the user performs a
2507 // drag interaction on them.
2508 function preventOutline(element) {
2509 while (element.tabIndex === -1) {
2510 element = element.parentNode;
2511 }
2512 if (!element.style) { return; }
2513 restoreOutline();
2514 _outlineElement = element;
2515 _outlineStyle = element.style.outline;
2516 element.style.outline = 'none';
2517 on(window, 'keydown', restoreOutline);
2518 }
2519
2520 // @function restoreOutline()
2521 // Cancels the effects of a previous [`L.DomUtil.preventOutline`]().
2522 function restoreOutline() {
2523 if (!_outlineElement) { return; }
2524 _outlineElement.style.outline = _outlineStyle;
2525 _outlineElement = undefined;
2526 _outlineStyle = undefined;
2527 off(window, 'keydown', restoreOutline);
2528 }
2529
2530 // @function getSizedParentNode(el: HTMLElement): HTMLElement
2531 // Finds the closest parent node which size (width and height) is not null.
2532 function getSizedParentNode(element) {
2533 do {
2534 element = element.parentNode;
2535 } while ((!element.offsetWidth || !element.offsetHeight) && element !== document.body);
2536 return element;
2537 }
2538
2539 // @function getScale(el: HTMLElement): Object
2540 // Computes the CSS scale currently applied on the element.
2541 // Returns an object with `x` and `y` members as horizontal and vertical scales respectively,
2542 // and `boundingClientRect` as the result of [`getBoundingClientRect()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
2543 function getScale(element) {
2544 var rect = element.getBoundingClientRect(); // Read-only in old browsers.
2545
2546 return {
2547 x: rect.width / element.offsetWidth || 1,
2548 y: rect.height / element.offsetHeight || 1,
2549 boundingClientRect: rect
2550 };
2551 }
2552
2553 var DomUtil = ({
2554 TRANSFORM: TRANSFORM,
2555 TRANSITION: TRANSITION,
2556 TRANSITION_END: TRANSITION_END,
2557 get: get,
2558 getStyle: getStyle,
2559 create: create$1,
2560 remove: remove,
2561 empty: empty,
2562 toFront: toFront,
2563 toBack: toBack,
2564 hasClass: hasClass,
2565 addClass: addClass,
2566 removeClass: removeClass,
2567 setClass: setClass,
2568 getClass: getClass,
2569 setOpacity: setOpacity,
2570 testProp: testProp,
2571 setTransform: setTransform,
2572 setPosition: setPosition,
2573 getPosition: getPosition,
2574 disableTextSelection: disableTextSelection,
2575 enableTextSelection: enableTextSelection,
2576 disableImageDrag: disableImageDrag,
2577 enableImageDrag: enableImageDrag,
2578 preventOutline: preventOutline,
2579 restoreOutline: restoreOutline,
2580 getSizedParentNode: getSizedParentNode,
2581 getScale: getScale
2582 });
2583
2584 /*
2585 * @namespace DomEvent
2586 * Utility functions to work with the [DOM events](https://developer.mozilla.org/docs/Web/API/Event), used by Leaflet internally.
2587 */
2588
2589 // Inspired by John Resig, Dean Edwards and YUI addEvent implementations.
2590
2591 // @function on(el: HTMLElement, types: String, fn: Function, context?: Object): this
2592 // Adds a listener function (`fn`) to a particular DOM event type of the
2593 // element `el`. You can optionally specify the context of the listener
2594 // (object the `this` keyword will point to). You can also pass several
2595 // space-separated types (e.g. `'click dblclick'`).
2596
2597 // @alternative
2598 // @function on(el: HTMLElement, eventMap: Object, context?: Object): this
2599 // Adds a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2600 function on(obj, types, fn, context) {
2601
2602 if (typeof types === 'object') {
2603 for (var type in types) {
2604 addOne(obj, type, types[type], fn);
2605 }
2606 } else {
2607 types = splitWords(types);
2608
2609 for (var i = 0, len = types.length; i < len; i++) {
2610 addOne(obj, types[i], fn, context);
2611 }
2612 }
2613
2614 return this;
2615 }
2616
2617 var eventsKey = '_leaflet_events';
2618
2619 // @function off(el: HTMLElement, types: String, fn: Function, context?: Object): this
2620 // Removes a previously added listener function.
2621 // Note that if you passed a custom context to on, you must pass the same
2622 // context to `off` in order to remove the listener.
2623
2624 // @alternative
2625 // @function off(el: HTMLElement, eventMap: Object, context?: Object): this
2626 // Removes a set of type/listener pairs, e.g. `{click: onClick, mousemove: onMouseMove}`
2627 function off(obj, types, fn, context) {
2628
2629 if (typeof types === 'object') {
2630 for (var type in types) {
2631 removeOne(obj, type, types[type], fn);
2632 }
2633 } else if (types) {
2634 types = splitWords(types);
2635
2636 for (var i = 0, len = types.length; i < len; i++) {
2637 removeOne(obj, types[i], fn, context);
2638 }
2639 } else {
2640 for (var j in obj[eventsKey]) {
2641 removeOne(obj, j, obj[eventsKey][j]);
2642 }
2643 delete obj[eventsKey];
2644 }
2645
2646 return this;
2647 }
2648
2649 function browserFiresNativeDblClick() {
2650 // See https://github.com/w3c/pointerevents/issues/171
2651 if (pointer) {
2652 return !(edge || safari);
2653 }
2654 }
2655
2656 var mouseSubst = {
2657 mouseenter: 'mouseover',
2658 mouseleave: 'mouseout',
2659 wheel: !('onwheel' in window) && 'mousewheel'
2660 };
2661
2662 function addOne(obj, type, fn, context) {
2663 var id = type + stamp(fn) + (context ? '_' + stamp(context) : '');
2664
2665 if (obj[eventsKey] && obj[eventsKey][id]) { return this; }
2666
2667 var handler = function (e) {
2668 return fn.call(context || obj, e || window.event);
2669 };
2670
2671 var originalHandler = handler;
2672
2673 if (pointer && type.indexOf('touch') === 0) {
2674 // Needs DomEvent.Pointer.js
2675 addPointerListener(obj, type, handler, id);
2676
2677 } else if (touch && (type === 'dblclick') && !browserFiresNativeDblClick()) {
2678 addDoubleTapListener(obj, handler, id);
2679
2680 } else if ('addEventListener' in obj) {
2681
2682 if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' || type === 'mousewheel') {
2683 obj.addEventListener(mouseSubst[type] || type, handler, passiveEvents ? {passive: false} : false);
2684
2685 } else if (type === 'mouseenter' || type === 'mouseleave') {
2686 handler = function (e) {
2687 e = e || window.event;
2688 if (isExternalTarget(obj, e)) {
2689 originalHandler(e);
2690 }
2691 };
2692 obj.addEventListener(mouseSubst[type], handler, false);
2693
2694 } else {
2695 obj.addEventListener(type, originalHandler, false);
2696 }
2697
2698 } else if ('attachEvent' in obj) {
2699 obj.attachEvent('on' + type, handler);
2700 }
2701
2702 obj[eventsKey] = obj[eventsKey] || {};
2703 obj[eventsKey][id] = handler;
2704 }
2705
2706 function removeOne(obj, type, fn, context) {
2707
2708 var id = type + stamp(fn) + (context ? '_' + stamp(context) : ''),
2709 handler = obj[eventsKey] && obj[eventsKey][id];
2710
2711 if (!handler) { return this; }
2712
2713 if (pointer && type.indexOf('touch') === 0) {
2714 removePointerListener(obj, type, id);
2715
2716 } else if (touch && (type === 'dblclick') && !browserFiresNativeDblClick()) {
2717 removeDoubleTapListener(obj, id);
2718
2719 } else if ('removeEventListener' in obj) {
2720
2721 obj.removeEventListener(mouseSubst[type] || type, handler, false);
2722
2723 } else if ('detachEvent' in obj) {
2724 obj.detachEvent('on' + type, handler);
2725 }
2726
2727 obj[eventsKey][id] = null;
2728 }
2729
2730 // @function stopPropagation(ev: DOMEvent): this
2731 // Stop the given event from propagation to parent elements. Used inside the listener functions:
2732 // ```js
2733 // L.DomEvent.on(div, 'click', function (ev) {
2734 // L.DomEvent.stopPropagation(ev);
2735 // });
2736 // ```
2737 function stopPropagation(e) {
2738
2739 if (e.stopPropagation) {
2740 e.stopPropagation();
2741 } else if (e.originalEvent) { // In case of Leaflet event.
2742 e.originalEvent._stopped = true;
2743 } else {
2744 e.cancelBubble = true;
2745 }
2746 skipped(e);
2747
2748 return this;
2749 }
2750
2751 // @function disableScrollPropagation(el: HTMLElement): this
2752 // Adds `stopPropagation` to the element's `'wheel'` events (plus browser variants).
2753 function disableScrollPropagation(el) {
2754 addOne(el, 'wheel', stopPropagation);
2755 return this;
2756 }
2757
2758 // @function disableClickPropagation(el: HTMLElement): this
2759 // Adds `stopPropagation` to the element's `'click'`, `'doubleclick'`,
2760 // `'mousedown'` and `'touchstart'` events (plus browser variants).
2761 function disableClickPropagation(el) {
2762 on(el, 'mousedown touchstart dblclick', stopPropagation);
2763 addOne(el, 'click', fakeStop);
2764 return this;
2765 }
2766
2767 // @function preventDefault(ev: DOMEvent): this
2768 // Prevents the default action of the DOM Event `ev` from happening (such as
2769 // following a link in the href of the a element, or doing a POST request
2770 // with page reload when a `<form>` is submitted).
2771 // Use it inside listener functions.
2772 function preventDefault(e) {
2773 if (e.preventDefault) {
2774 e.preventDefault();
2775 } else {
2776 e.returnValue = false;
2777 }
2778 return this;
2779 }
2780
2781 // @function stop(ev: DOMEvent): this
2782 // Does `stopPropagation` and `preventDefault` at the same time.
2783 function stop(e) {
2784 preventDefault(e);
2785 stopPropagation(e);
2786 return this;
2787 }
2788
2789 // @function getMousePosition(ev: DOMEvent, container?: HTMLElement): Point
2790 // Gets normalized mouse position from a DOM event relative to the
2791 // `container` (border excluded) or to the whole page if not specified.
2792 function getMousePosition(e, container) {
2793 if (!container) {
2794 return new Point(e.clientX, e.clientY);
2795 }
2796
2797 var scale = getScale(container),
2798 offset = scale.boundingClientRect; // left and top values are in page scale (like the event clientX/Y)
2799
2800 return new Point(
2801 // offset.left/top values are in page scale (like clientX/Y),
2802 // whereas clientLeft/Top (border width) values are the original values (before CSS scale applies).
2803 (e.clientX - offset.left) / scale.x - container.clientLeft,
2804 (e.clientY - offset.top) / scale.y - container.clientTop
2805 );
2806 }
2807
2808 // Chrome on Win scrolls double the pixels as in other platforms (see #4538),
2809 // and Firefox scrolls device pixels, not CSS pixels
2810 var wheelPxFactor =
2811 (win && chrome) ? 2 * window.devicePixelRatio :
2812 gecko ? window.devicePixelRatio : 1;
2813
2814 // @function getWheelDelta(ev: DOMEvent): Number
2815 // Gets normalized wheel delta from a wheel DOM event, in vertical
2816 // pixels scrolled (negative if scrolling down).
2817 // Events from pointing devices without precise scrolling are mapped to
2818 // a best guess of 60 pixels.
2819 function getWheelDelta(e) {
2820 return (edge) ? e.wheelDeltaY / 2 : // Don't trust window-geometry-based delta
2821 (e.deltaY && e.deltaMode === 0) ? -e.deltaY / wheelPxFactor : // Pixels
2822 (e.deltaY && e.deltaMode === 1) ? -e.deltaY * 20 : // Lines
2823 (e.deltaY && e.deltaMode === 2) ? -e.deltaY * 60 : // Pages
2824 (e.deltaX || e.deltaZ) ? 0 : // Skip horizontal/depth wheel events
2825 e.wheelDelta ? (e.wheelDeltaY || e.wheelDelta) / 2 : // Legacy IE pixels
2826 (e.detail && Math.abs(e.detail) < 32765) ? -e.detail * 20 : // Legacy Moz lines
2827 e.detail ? e.detail / -32765 * 60 : // Legacy Moz pages
2828 0;
2829 }
2830
2831 var skipEvents = {};
2832
2833 function fakeStop(e) {
2834 // fakes stopPropagation by setting a special event flag, checked/reset with skipped(e)
2835 skipEvents[e.type] = true;
2836 }
2837
2838 function skipped(e) {
2839 var events = skipEvents[e.type];
2840 // reset when checking, as it's only used in map container and propagates outside of the map
2841 skipEvents[e.type] = false;
2842 return events;
2843 }
2844
2845 // check if element really left/entered the event target (for mouseenter/mouseleave)
2846 function isExternalTarget(el, e) {
2847
2848 var related = e.relatedTarget;
2849
2850 if (!related) { return true; }
2851
2852 try {
2853 while (related && (related !== el)) {
2854 related = related.parentNode;
2855 }
2856 } catch (err) {
2857 return false;
2858 }
2859 return (related !== el);
2860 }
2861
2862 var DomEvent = ({
2863 on: on,
2864 off: off,
2865 stopPropagation: stopPropagation,
2866 disableScrollPropagation: disableScrollPropagation,
2867 disableClickPropagation: disableClickPropagation,
2868 preventDefault: preventDefault,
2869 stop: stop,
2870 getMousePosition: getMousePosition,
2871 getWheelDelta: getWheelDelta,
2872 fakeStop: fakeStop,
2873 skipped: skipped,
2874 isExternalTarget: isExternalTarget,
2875 addListener: on,
2876 removeListener: off
2877 });
2878
2879 /*
2880 * @class PosAnimation
2881 * @aka L.PosAnimation
2882 * @inherits Evented
2883 * Used internally for panning animations, utilizing CSS3 Transitions for modern browsers and a timer fallback for IE6-9.
2884 *
2885 * @example
2886 * ```js
2887 * var fx = new L.PosAnimation();
2888 * fx.run(el, [300, 500], 0.5);
2889 * ```
2890 *
2891 * @constructor L.PosAnimation()
2892 * Creates a `PosAnimation` object.
2893 *
2894 */
2895
2896 var PosAnimation = Evented.extend({
2897
2898 // @method run(el: HTMLElement, newPos: Point, duration?: Number, easeLinearity?: Number)
2899 // Run an animation of a given element to a new position, optionally setting
2900 // duration in seconds (`0.25` by default) and easing linearity factor (3rd
2901 // argument of the [cubic bezier curve](http://cubic-bezier.com/#0,0,.5,1),
2902 // `0.5` by default).
2903 run: function (el, newPos, duration, easeLinearity) {
2904 this.stop();
2905
2906 this._el = el;
2907 this._inProgress = true;
2908 this._duration = duration || 0.25;
2909 this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2);
2910
2911 this._startPos = getPosition(el);
2912 this._offset = newPos.subtract(this._startPos);
2913 this._startTime = +new Date();
2914
2915 // @event start: Event
2916 // Fired when the animation starts
2917 this.fire('start');
2918
2919 this._animate();
2920 },
2921
2922 // @method stop()
2923 // Stops the animation (if currently running).
2924 stop: function () {
2925 if (!this._inProgress) { return; }
2926
2927 this._step(true);
2928 this._complete();
2929 },
2930
2931 _animate: function () {
2932 // animation loop
2933 this._animId = requestAnimFrame(this._animate, this);
2934 this._step();
2935 },
2936
2937 _step: function (round) {
2938 var elapsed = (+new Date()) - this._startTime,
2939 duration = this._duration * 1000;
2940
2941 if (elapsed < duration) {
2942 this._runFrame(this._easeOut(elapsed / duration), round);
2943 } else {
2944 this._runFrame(1);
2945 this._complete();
2946 }
2947 },
2948
2949 _runFrame: function (progress, round) {
2950 var pos = this._startPos.add(this._offset.multiplyBy(progress));
2951 if (round) {
2952 pos._round();
2953 }
2954 setPosition(this._el, pos);
2955
2956 // @event step: Event
2957 // Fired continuously during the animation.
2958 this.fire('step');
2959 },
2960
2961 _complete: function () {
2962 cancelAnimFrame(this._animId);
2963
2964 this._inProgress = false;
2965 // @event end: Event
2966 // Fired when the animation ends.
2967 this.fire('end');
2968 },
2969
2970 _easeOut: function (t) {
2971 return 1 - Math.pow(1 - t, this._easeOutPower);
2972 }
2973 });
2974
2975 /*
2976 * @class Map
2977 * @aka L.Map
2978 * @inherits Evented
2979 *
2980 * The central class of the API — it is used to create a map on a page and manipulate it.
2981 *
2982 * @example
2983 *
2984 * ```js
2985 * // initialize the map on the "map" div with a given center and zoom
2986 * var map = L.map('map', {
2987 * center: [51.505, -0.09],
2988 * zoom: 13
2989 * });
2990 * ```
2991 *
2992 */
2993
2994 var Map = Evented.extend({
2995
2996 options: {
2997 // @section Map State Options
2998 // @option crs: CRS = L.CRS.EPSG3857
2999 // The [Coordinate Reference System](#crs) to use. Don't change this if you're not
3000 // sure what it means.
3001 crs: EPSG3857,
3002
3003 // @option center: LatLng = undefined
3004 // Initial geographic center of the map
3005 center: undefined,
3006
3007 // @option zoom: Number = undefined
3008 // Initial map zoom level
3009 zoom: undefined,
3010
3011 // @option minZoom: Number = *
3012 // Minimum zoom level of the map.
3013 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3014 // the lowest of their `minZoom` options will be used instead.
3015 minZoom: undefined,
3016
3017 // @option maxZoom: Number = *
3018 // Maximum zoom level of the map.
3019 // If not specified and at least one `GridLayer` or `TileLayer` is in the map,
3020 // the highest of their `maxZoom` options will be used instead.
3021 maxZoom: undefined,
3022
3023 // @option layers: Layer[] = []
3024 // Array of layers that will be added to the map initially
3025 layers: [],
3026
3027 // @option maxBounds: LatLngBounds = null
3028 // When this option is set, the map restricts the view to the given
3029 // geographical bounds, bouncing the user back if the user tries to pan
3030 // outside the view. To set the restriction dynamically, use
3031 // [`setMaxBounds`](#map-setmaxbounds) method.
3032 maxBounds: undefined,
3033
3034 // @option renderer: Renderer = *
3035 // The default method for drawing vector layers on the map. `L.SVG`
3036 // or `L.Canvas` by default depending on browser support.
3037 renderer: undefined,
3038
3039
3040 // @section Animation Options
3041 // @option zoomAnimation: Boolean = true
3042 // Whether the map zoom animation is enabled. By default it's enabled
3043 // in all browsers that support CSS3 Transitions except Android.
3044 zoomAnimation: true,
3045
3046 // @option zoomAnimationThreshold: Number = 4
3047 // Won't animate zoom if the zoom difference exceeds this value.
3048 zoomAnimationThreshold: 4,
3049
3050 // @option fadeAnimation: Boolean = true
3051 // Whether the tile fade animation is enabled. By default it's enabled
3052 // in all browsers that support CSS3 Transitions except Android.
3053 fadeAnimation: true,
3054
3055 // @option markerZoomAnimation: Boolean = true
3056 // Whether markers animate their zoom with the zoom animation, if disabled
3057 // they will disappear for the length of the animation. By default it's
3058 // enabled in all browsers that support CSS3 Transitions except Android.
3059 markerZoomAnimation: true,
3060
3061 // @option transform3DLimit: Number = 2^23
3062 // Defines the maximum size of a CSS translation transform. The default
3063 // value should not be changed unless a web browser positions layers in
3064 // the wrong place after doing a large `panBy`.
3065 transform3DLimit: 8388608, // Precision limit of a 32-bit float
3066
3067 // @section Interaction Options
3068 // @option zoomSnap: Number = 1
3069 // Forces the map's zoom level to always be a multiple of this, particularly
3070 // right after a [`fitBounds()`](#map-fitbounds) or a pinch-zoom.
3071 // By default, the zoom level snaps to the nearest integer; lower values
3072 // (e.g. `0.5` or `0.1`) allow for greater granularity. A value of `0`
3073 // means the zoom level will not be snapped after `fitBounds` or a pinch-zoom.
3074 zoomSnap: 1,
3075
3076 // @option zoomDelta: Number = 1
3077 // Controls how much the map's zoom level will change after a
3078 // [`zoomIn()`](#map-zoomin), [`zoomOut()`](#map-zoomout), pressing `+`
3079 // or `-` on the keyboard, or using the [zoom controls](#control-zoom).
3080 // Values smaller than `1` (e.g. `0.5`) allow for greater granularity.
3081 zoomDelta: 1,
3082
3083 // @option trackResize: Boolean = true
3084 // Whether the map automatically handles browser window resize to update itself.
3085 trackResize: true
3086 },
3087
3088 initialize: function (id, options) { // (HTMLElement or String, Object)
3089 options = setOptions(this, options);
3090
3091 // Make sure to assign internal flags at the beginning,
3092 // to avoid inconsistent state in some edge cases.
3093 this._handlers = [];
3094 this._layers = {};
3095 this._zoomBoundLayers = {};
3096 this._sizeChanged = true;
3097
3098 this._initContainer(id);
3099 this._initLayout();
3100
3101 // hack for https://github.com/Leaflet/Leaflet/issues/1980
3102 this._onResize = bind(this._onResize, this);
3103
3104 this._initEvents();
3105
3106 if (options.maxBounds) {
3107 this.setMaxBounds(options.maxBounds);
3108 }
3109
3110 if (options.zoom !== undefined) {
3111 this._zoom = this._limitZoom(options.zoom);
3112 }
3113
3114 if (options.center && options.zoom !== undefined) {
3115 this.setView(toLatLng(options.center), options.zoom, {reset: true});
3116 }
3117
3118 this.callInitHooks();
3119
3120 // don't animate on browsers without hardware-accelerated transitions or old Android/Opera
3121 this._zoomAnimated = TRANSITION && any3d && !mobileOpera &&
3122 this.options.zoomAnimation;
3123
3124 // zoom transitions run with the same duration for all layers, so if one of transitionend events
3125 // happens after starting zoom animation (propagating to the map pane), we know that it ended globally
3126 if (this._zoomAnimated) {
3127 this._createAnimProxy();
3128 on(this._proxy, TRANSITION_END, this._catchTransitionEnd, this);
3129 }
3130
3131 this._addLayers(this.options.layers);
3132 },
3133
3134
3135 // @section Methods for modifying map state
3136
3137 // @method setView(center: LatLng, zoom: Number, options?: Zoom/pan options): this
3138 // Sets the view of the map (geographical center and zoom) with the given
3139 // animation options.
3140 setView: function (center, zoom, options) {
3141
3142 zoom = zoom === undefined ? this._zoom : this._limitZoom(zoom);
3143 center = this._limitCenter(toLatLng(center), zoom, this.options.maxBounds);
3144 options = options || {};
3145
3146 this._stop();
3147
3148 if (this._loaded && !options.reset && options !== true) {
3149
3150 if (options.animate !== undefined) {
3151 options.zoom = extend({animate: options.animate}, options.zoom);
3152 options.pan = extend({animate: options.animate, duration: options.duration}, options.pan);
3153 }
3154
3155 // try animating pan or zoom
3156 var moved = (this._zoom !== zoom) ?
3157 this._tryAnimatedZoom && this._tryAnimatedZoom(center, zoom, options.zoom) :
3158 this._tryAnimatedPan(center, options.pan);
3159
3160 if (moved) {
3161 // prevent resize handler call, the view will refresh after animation anyway
3162 clearTimeout(this._sizeTimer);
3163 return this;
3164 }
3165 }
3166
3167 // animation didn't start, just reset the map view
3168 this._resetView(center, zoom);
3169
3170 return this;
3171 },
3172
3173 // @method setZoom(zoom: Number, options?: Zoom/pan options): this
3174 // Sets the zoom of the map.
3175 setZoom: function (zoom, options) {
3176 if (!this._loaded) {
3177 this._zoom = zoom;
3178 return this;
3179 }
3180 return this.setView(this.getCenter(), zoom, {zoom: options});
3181 },
3182
3183 // @method zoomIn(delta?: Number, options?: Zoom options): this
3184 // Increases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3185 zoomIn: function (delta, options) {
3186 delta = delta || (any3d ? this.options.zoomDelta : 1);
3187 return this.setZoom(this._zoom + delta, options);
3188 },
3189
3190 // @method zoomOut(delta?: Number, options?: Zoom options): this
3191 // Decreases the zoom of the map by `delta` ([`zoomDelta`](#map-zoomdelta) by default).
3192 zoomOut: function (delta, options) {
3193 delta = delta || (any3d ? this.options.zoomDelta : 1);
3194 return this.setZoom(this._zoom - delta, options);
3195 },
3196
3197 // @method setZoomAround(latlng: LatLng, zoom: Number, options: Zoom options): this
3198 // Zooms the map while keeping a specified geographical point on the map
3199 // stationary (e.g. used internally for scroll zoom and double-click zoom).
3200 // @alternative
3201 // @method setZoomAround(offset: Point, zoom: Number, options: Zoom options): this
3202 // Zooms the map while keeping a specified pixel on the map (relative to the top-left corner) stationary.
3203 setZoomAround: function (latlng, zoom, options) {
3204 var scale = this.getZoomScale(zoom),
3205 viewHalf = this.getSize().divideBy(2),
3206 containerPoint = latlng instanceof Point ? latlng : this.latLngToContainerPoint(latlng),
3207
3208 centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale),
3209 newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset));
3210
3211 return this.setView(newCenter, zoom, {zoom: options});
3212 },
3213
3214 _getBoundsCenterZoom: function (bounds, options) {
3215
3216 options = options || {};
3217 bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
3218
3219 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3220 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3221
3222 zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
3223
3224 zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
3225
3226 if (zoom === Infinity) {
3227 return {
3228 center: bounds.getCenter(),
3229 zoom: zoom
3230 };
3231 }
3232
3233 var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2),
3234
3235 swPoint = this.project(bounds.getSouthWest(), zoom),
3236 nePoint = this.project(bounds.getNorthEast(), zoom),
3237 center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom);
3238
3239 return {
3240 center: center,
3241 zoom: zoom
3242 };
3243 },
3244
3245 // @method fitBounds(bounds: LatLngBounds, options?: fitBounds options): this
3246 // Sets a map view that contains the given geographical bounds with the
3247 // maximum zoom level possible.
3248 fitBounds: function (bounds, options) {
3249
3250 bounds = toLatLngBounds(bounds);
3251
3252 if (!bounds.isValid()) {
3253 throw new Error('Bounds are not valid.');
3254 }
3255
3256 var target = this._getBoundsCenterZoom(bounds, options);
3257 return this.setView(target.center, target.zoom, options);
3258 },
3259
3260 // @method fitWorld(options?: fitBounds options): this
3261 // Sets a map view that mostly contains the whole world with the maximum
3262 // zoom level possible.
3263 fitWorld: function (options) {
3264 return this.fitBounds([[-90, -180], [90, 180]], options);
3265 },
3266
3267 // @method panTo(latlng: LatLng, options?: Pan options): this
3268 // Pans the map to a given center.
3269 panTo: function (center, options) { // (LatLng)
3270 return this.setView(center, this._zoom, {pan: options});
3271 },
3272
3273 // @method panBy(offset: Point, options?: Pan options): this
3274 // Pans the map by a given number of pixels (animated).
3275 panBy: function (offset, options) {
3276 offset = toPoint(offset).round();
3277 options = options || {};
3278
3279 if (!offset.x && !offset.y) {
3280 return this.fire('moveend');
3281 }
3282 // If we pan too far, Chrome gets issues with tiles
3283 // and makes them disappear or appear in the wrong place (slightly offset) #2602
3284 if (options.animate !== true && !this.getSize().contains(offset)) {
3285 this._resetView(this.unproject(this.project(this.getCenter()).add(offset)), this.getZoom());
3286 return this;
3287 }
3288
3289 if (!this._panAnim) {
3290 this._panAnim = new PosAnimation();
3291
3292 this._panAnim.on({
3293 'step': this._onPanTransitionStep,
3294 'end': this._onPanTransitionEnd
3295 }, this);
3296 }
3297
3298 // don't fire movestart if animating inertia
3299 if (!options.noMoveStart) {
3300 this.fire('movestart');
3301 }
3302
3303 // animate pan unless animate: false specified
3304 if (options.animate !== false) {
3305 addClass(this._mapPane, 'leaflet-pan-anim');
3306
3307 var newPos = this._getMapPanePos().subtract(offset).round();
3308 this._panAnim.run(this._mapPane, newPos, options.duration || 0.25, options.easeLinearity);
3309 } else {
3310 this._rawPanBy(offset);
3311 this.fire('move').fire('moveend');
3312 }
3313
3314 return this;
3315 },
3316
3317 // @method flyTo(latlng: LatLng, zoom?: Number, options?: Zoom/pan options): this
3318 // Sets the view of the map (geographical center and zoom) performing a smooth
3319 // pan-zoom animation.
3320 flyTo: function (targetCenter, targetZoom, options) {
3321
3322 options = options || {};
3323 if (options.animate === false || !any3d) {
3324 return this.setView(targetCenter, targetZoom, options);
3325 }
3326
3327 this._stop();
3328
3329 var from = this.project(this.getCenter()),
3330 to = this.project(targetCenter),
3331 size = this.getSize(),
3332 startZoom = this._zoom;
3333
3334 targetCenter = toLatLng(targetCenter);
3335 targetZoom = targetZoom === undefined ? startZoom : targetZoom;
3336
3337 var w0 = Math.max(size.x, size.y),
3338 w1 = w0 * this.getZoomScale(startZoom, targetZoom),
3339 u1 = (to.distanceTo(from)) || 1,
3340 rho = 1.42,
3341 rho2 = rho * rho;
3342
3343 function r(i) {
3344 var s1 = i ? -1 : 1,
3345 s2 = i ? w1 : w0,
3346 t1 = w1 * w1 - w0 * w0 + s1 * rho2 * rho2 * u1 * u1,
3347 b1 = 2 * s2 * rho2 * u1,
3348 b = t1 / b1,
3349 sq = Math.sqrt(b * b + 1) - b;
3350
3351 // workaround for floating point precision bug when sq = 0, log = -Infinite,
3352 // thus triggering an infinite loop in flyTo
3353 var log = sq < 0.000000001 ? -18 : Math.log(sq);
3354
3355 return log;
3356 }
3357
3358 function sinh(n) { return (Math.exp(n) - Math.exp(-n)) / 2; }
3359 function cosh(n) { return (Math.exp(n) + Math.exp(-n)) / 2; }
3360 function tanh(n) { return sinh(n) / cosh(n); }
3361
3362 var r0 = r(0);
3363
3364 function w(s) { return w0 * (cosh(r0) / cosh(r0 + rho * s)); }
3365 function u(s) { return w0 * (cosh(r0) * tanh(r0 + rho * s) - sinh(r0)) / rho2; }
3366
3367 function easeOut(t) { return 1 - Math.pow(1 - t, 1.5); }
3368
3369 var start = Date.now(),
3370 S = (r(1) - r0) / rho,
3371 duration = options.duration ? 1000 * options.duration : 1000 * S * 0.8;
3372
3373 function frame() {
3374 var t = (Date.now() - start) / duration,
3375 s = easeOut(t) * S;
3376
3377 if (t <= 1) {
3378 this._flyToFrame = requestAnimFrame(frame, this);
3379
3380 this._move(
3381 this.unproject(from.add(to.subtract(from).multiplyBy(u(s) / u1)), startZoom),
3382 this.getScaleZoom(w0 / w(s), startZoom),
3383 {flyTo: true});
3384
3385 } else {
3386 this
3387 ._move(targetCenter, targetZoom)
3388 ._moveEnd(true);
3389 }
3390 }
3391
3392 this._moveStart(true, options.noMoveStart);
3393
3394 frame.call(this);
3395 return this;
3396 },
3397
3398 // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
3399 // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
3400 // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
3401 flyToBounds: function (bounds, options) {
3402 var target = this._getBoundsCenterZoom(bounds, options);
3403 return this.flyTo(target.center, target.zoom, options);
3404 },
3405
3406 // @method setMaxBounds(bounds: LatLngBounds): this
3407 // Restricts the map view to the given bounds (see the [maxBounds](#map-maxbounds) option).
3408 setMaxBounds: function (bounds) {
3409 bounds = toLatLngBounds(bounds);
3410
3411 if (!bounds.isValid()) {
3412 this.options.maxBounds = null;
3413 return this.off('moveend', this._panInsideMaxBounds);
3414 } else if (this.options.maxBounds) {
3415 this.off('moveend', this._panInsideMaxBounds);
3416 }
3417
3418 this.options.maxBounds = bounds;
3419
3420 if (this._loaded) {
3421 this._panInsideMaxBounds();
3422 }
3423
3424 return this.on('moveend', this._panInsideMaxBounds);
3425 },
3426
3427 // @method setMinZoom(zoom: Number): this
3428 // Sets the lower limit for the available zoom levels (see the [minZoom](#map-minzoom) option).
3429 setMinZoom: function (zoom) {
3430 var oldZoom = this.options.minZoom;
3431 this.options.minZoom = zoom;
3432
3433 if (this._loaded && oldZoom !== zoom) {
3434 this.fire('zoomlevelschange');
3435
3436 if (this.getZoom() < this.options.minZoom) {
3437 return this.setZoom(zoom);
3438 }
3439 }
3440
3441 return this;
3442 },
3443
3444 // @method setMaxZoom(zoom: Number): this
3445 // Sets the upper limit for the available zoom levels (see the [maxZoom](#map-maxzoom) option).
3446 setMaxZoom: function (zoom) {
3447 var oldZoom = this.options.maxZoom;
3448 this.options.maxZoom = zoom;
3449
3450 if (this._loaded && oldZoom !== zoom) {
3451 this.fire('zoomlevelschange');
3452
3453 if (this.getZoom() > this.options.maxZoom) {
3454 return this.setZoom(zoom);
3455 }
3456 }
3457
3458 return this;
3459 },
3460
3461 // @method panInsideBounds(bounds: LatLngBounds, options?: Pan options): this
3462 // Pans the map to the closest view that would lie inside the given bounds (if it's not already), controlling the animation using the options specific, if any.
3463 panInsideBounds: function (bounds, options) {
3464 this._enforcingBounds = true;
3465 var center = this.getCenter(),
3466 newCenter = this._limitCenter(center, this._zoom, toLatLngBounds(bounds));
3467
3468 if (!center.equals(newCenter)) {
3469 this.panTo(newCenter, options);
3470 }
3471
3472 this._enforcingBounds = false;
3473 return this;
3474 },
3475
3476 // @method panInside(latlng: LatLng, options?: options): this
3477 // Pans the map the minimum amount to make the `latlng` visible. Use
3478 // `padding`, `paddingTopLeft` and `paddingTopRight` options to fit
3479 // the display to more restricted bounds, like [`fitBounds`](#map-fitbounds).
3480 // If `latlng` is already within the (optionally padded) display bounds,
3481 // the map will not be panned.
3482 panInside: function (latlng, options) {
3483 options = options || {};
3484
3485 var paddingTL = toPoint(options.paddingTopLeft || options.padding || [0, 0]),
3486 paddingBR = toPoint(options.paddingBottomRight || options.padding || [0, 0]),
3487 center = this.getCenter(),
3488 pixelCenter = this.project(center),
3489 pixelPoint = this.project(latlng),
3490 pixelBounds = this.getPixelBounds(),
3491 halfPixelBounds = pixelBounds.getSize().divideBy(2),
3492 paddedBounds = toBounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]);
3493
3494 if (!paddedBounds.contains(pixelPoint)) {
3495 this._enforcingBounds = true;
3496 var diff = pixelCenter.subtract(pixelPoint),
3497 newCenter = toPoint(pixelPoint.x + diff.x, pixelPoint.y + diff.y);
3498
3499 if (pixelPoint.x < paddedBounds.min.x || pixelPoint.x > paddedBounds.max.x) {
3500 newCenter.x = pixelCenter.x - diff.x;
3501 if (diff.x > 0) {
3502 newCenter.x += halfPixelBounds.x - paddingTL.x;
3503 } else {
3504 newCenter.x -= halfPixelBounds.x - paddingBR.x;
3505 }
3506 }
3507 if (pixelPoint.y < paddedBounds.min.y || pixelPoint.y > paddedBounds.max.y) {
3508 newCenter.y = pixelCenter.y - diff.y;
3509 if (diff.y > 0) {
3510 newCenter.y += halfPixelBounds.y - paddingTL.y;
3511 } else {
3512 newCenter.y -= halfPixelBounds.y - paddingBR.y;
3513 }
3514 }
3515 this.panTo(this.unproject(newCenter), options);
3516 this._enforcingBounds = false;
3517 }
3518 return this;
3519 },
3520
3521 // @method invalidateSize(options: Zoom/pan options): this
3522 // Checks if the map container size changed and updates the map if so —
3523 // call it after you've changed the map size dynamically, also animating
3524 // pan by default. If `options.pan` is `false`, panning will not occur.
3525 // If `options.debounceMoveend` is `true`, it will delay `moveend` event so
3526 // that it doesn't happen often even if the method is called many
3527 // times in a row.
3528
3529 // @alternative
3530 // @method invalidateSize(animate: Boolean): this
3531 // Checks if the map container size changed and updates the map if so —
3532 // call it after you've changed the map size dynamically, also animating
3533 // pan by default.
3534 invalidateSize: function (options) {
3535 if (!this._loaded) { return this; }
3536
3537 options = extend({
3538 animate: false,
3539 pan: true
3540 }, options === true ? {animate: true} : options);
3541
3542 var oldSize = this.getSize();
3543 this._sizeChanged = true;
3544 this._lastCenter = null;
3545
3546 var newSize = this.getSize(),
3547 oldCenter = oldSize.divideBy(2).round(),
3548 newCenter = newSize.divideBy(2).round(),
3549 offset = oldCenter.subtract(newCenter);
3550
3551 if (!offset.x && !offset.y) { return this; }
3552
3553 if (options.animate && options.pan) {
3554 this.panBy(offset);
3555
3556 } else {
3557 if (options.pan) {
3558 this._rawPanBy(offset);
3559 }
3560
3561 this.fire('move');
3562
3563 if (options.debounceMoveend) {
3564 clearTimeout(this._sizeTimer);
3565 this._sizeTimer = setTimeout(bind(this.fire, this, 'moveend'), 200);
3566 } else {
3567 this.fire('moveend');
3568 }
3569 }
3570
3571 // @section Map state change events
3572 // @event resize: ResizeEvent
3573 // Fired when the map is resized.
3574 return this.fire('resize', {
3575 oldSize: oldSize,
3576 newSize: newSize
3577 });
3578 },
3579
3580 // @section Methods for modifying map state
3581 // @method stop(): this
3582 // Stops the currently running `panTo` or `flyTo` animation, if any.
3583 stop: function () {
3584 this.setZoom(this._limitZoom(this._zoom));
3585 if (!this.options.zoomSnap) {
3586 this.fire('viewreset');
3587 }
3588 return this._stop();
3589 },
3590
3591 // @section Geolocation methods
3592 // @method locate(options?: Locate options): this
3593 // Tries to locate the user using the Geolocation API, firing a [`locationfound`](#map-locationfound)
3594 // event with location data on success or a [`locationerror`](#map-locationerror) event on failure,
3595 // and optionally sets the map view to the user's location with respect to
3596 // detection accuracy (or to the world view if geolocation failed).
3597 // Note that, if your page doesn't use HTTPS, this method will fail in
3598 // modern browsers ([Chrome 50 and newer](https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-powerful-features-on-insecure-origins))
3599 // See `Locate options` for more details.
3600 locate: function (options) {
3601
3602 options = this._locateOptions = extend({
3603 timeout: 10000,
3604 watch: false
3605 // setView: false
3606 // maxZoom: <Number>
3607 // maximumAge: 0
3608 // enableHighAccuracy: false
3609 }, options);
3610
3611 if (!('geolocation' in navigator)) {
3612 this._handleGeolocationError({
3613 code: 0,
3614 message: 'Geolocation not supported.'
3615 });
3616 return this;
3617 }
3618
3619 var onResponse = bind(this._handleGeolocationResponse, this),
3620 onError = bind(this._handleGeolocationError, this);
3621
3622 if (options.watch) {
3623 this._locationWatchId =
3624 navigator.geolocation.watchPosition(onResponse, onError, options);
3625 } else {
3626 navigator.geolocation.getCurrentPosition(onResponse, onError, options);
3627 }
3628 return this;
3629 },
3630
3631 // @method stopLocate(): this
3632 // Stops watching location previously initiated by `map.locate({watch: true})`
3633 // and aborts resetting the map view if map.locate was called with
3634 // `{setView: true}`.
3635 stopLocate: function () {
3636 if (navigator.geolocation && navigator.geolocation.clearWatch) {
3637 navigator.geolocation.clearWatch(this._locationWatchId);
3638 }
3639 if (this._locateOptions) {
3640 this._locateOptions.setView = false;
3641 }
3642 return this;
3643 },
3644
3645 _handleGeolocationError: function (error) {
3646 var c = error.code,
3647 message = error.message ||
3648 (c === 1 ? 'permission denied' :
3649 (c === 2 ? 'position unavailable' : 'timeout'));
3650
3651 if (this._locateOptions.setView && !this._loaded) {
3652 this.fitWorld();
3653 }
3654
3655 // @section Location events
3656 // @event locationerror: ErrorEvent
3657 // Fired when geolocation (using the [`locate`](#map-locate) method) failed.
3658 this.fire('locationerror', {
3659 code: c,
3660 message: 'Geolocation error: ' + message + '.'
3661 });
3662 },
3663
3664 _handleGeolocationResponse: function (pos) {
3665 var lat = pos.coords.latitude,
3666 lng = pos.coords.longitude,
3667 latlng = new LatLng(lat, lng),
3668 bounds = latlng.toBounds(pos.coords.accuracy * 2),
3669 options = this._locateOptions;
3670
3671 if (options.setView) {
3672 var zoom = this.getBoundsZoom(bounds);
3673 this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
3674 }
3675
3676 var data = {
3677 latlng: latlng,
3678 bounds: bounds,
3679 timestamp: pos.timestamp
3680 };
3681
3682 for (var i in pos.coords) {
3683 if (typeof pos.coords[i] === 'number') {
3684 data[i] = pos.coords[i];
3685 }
3686 }
3687
3688 // @event locationfound: LocationEvent
3689 // Fired when geolocation (using the [`locate`](#map-locate) method)
3690 // went successfully.
3691 this.fire('locationfound', data);
3692 },
3693
3694 // TODO Appropriate docs section?
3695 // @section Other Methods
3696 // @method addHandler(name: String, HandlerClass: Function): this
3697 // Adds a new `Handler` to the map, given its name and constructor function.
3698 addHandler: function (name, HandlerClass) {
3699 if (!HandlerClass) { return this; }
3700
3701 var handler = this[name] = new HandlerClass(this);
3702
3703 this._handlers.push(handler);
3704
3705 if (this.options[name]) {
3706 handler.enable();
3707 }
3708
3709 return this;
3710 },
3711
3712 // @method remove(): this
3713 // Destroys the map and clears all related event listeners.
3714 remove: function () {
3715
3716 this._initEvents(true);
3717 this.off('moveend', this._panInsideMaxBounds);
3718
3719 if (this._containerId !== this._container._leaflet_id) {
3720 throw new Error('Map container is being reused by another instance');
3721 }
3722
3723 try {
3724 // throws error in IE6-8
3725 delete this._container._leaflet_id;
3726 delete this._containerId;
3727 } catch (e) {
3728 /*eslint-disable */
3729 this._container._leaflet_id = undefined;
3730 /* eslint-enable */
3731 this._containerId = undefined;
3732 }
3733
3734 if (this._locationWatchId !== undefined) {
3735 this.stopLocate();
3736 }
3737
3738 this._stop();
3739
3740 remove(this._mapPane);
3741
3742 if (this._clearControlPos) {
3743 this._clearControlPos();
3744 }
3745 if (this._resizeRequest) {
3746 cancelAnimFrame(this._resizeRequest);
3747 this._resizeRequest = null;
3748 }
3749
3750 this._clearHandlers();
3751
3752 if (this._loaded) {
3753 // @section Map state change events
3754 // @event unload: Event
3755 // Fired when the map is destroyed with [remove](#map-remove) method.
3756 this.fire('unload');
3757 }
3758
3759 var i;
3760 for (i in this._layers) {
3761 this._layers[i].remove();
3762 }
3763 for (i in this._panes) {
3764 remove(this._panes[i]);
3765 }
3766
3767 this._layers = [];
3768 this._panes = [];
3769 delete this._mapPane;
3770 delete this._renderer;
3771
3772 return this;
3773 },
3774
3775 // @section Other Methods
3776 // @method createPane(name: String, container?: HTMLElement): HTMLElement
3777 // Creates a new [map pane](#map-pane) with the given name if it doesn't exist already,
3778 // then returns it. The pane is created as a child of `container`, or
3779 // as a child of the main map pane if not set.
3780 createPane: function (name, container) {
3781 var className = 'leaflet-pane' + (name ? ' leaflet-' + name.replace('Pane', '') + '-pane' : ''),
3782 pane = create$1('div', className, container || this._mapPane);
3783
3784 if (name) {
3785 this._panes[name] = pane;
3786 }
3787 return pane;
3788 },
3789
3790 // @section Methods for Getting Map State
3791
3792 // @method getCenter(): LatLng
3793 // Returns the geographical center of the map view
3794 getCenter: function () {
3795 this._checkIfLoaded();
3796
3797 if (this._lastCenter && !this._moved()) {
3798 return this._lastCenter;
3799 }
3800 return this.layerPointToLatLng(this._getCenterLayerPoint());
3801 },
3802
3803 // @method getZoom(): Number
3804 // Returns the current zoom level of the map view
3805 getZoom: function () {
3806 return this._zoom;
3807 },
3808
3809 // @method getBounds(): LatLngBounds
3810 // Returns the geographical bounds visible in the current map view
3811 getBounds: function () {
3812 var bounds = this.getPixelBounds(),
3813 sw = this.unproject(bounds.getBottomLeft()),
3814 ne = this.unproject(bounds.getTopRight());
3815
3816 return new LatLngBounds(sw, ne);
3817 },
3818
3819 // @method getMinZoom(): Number
3820 // Returns the minimum zoom level of the map (if set in the `minZoom` option of the map or of any layers), or `0` by default.
3821 getMinZoom: function () {
3822 return this.options.minZoom === undefined ? this._layersMinZoom || 0 : this.options.minZoom;
3823 },
3824
3825 // @method getMaxZoom(): Number
3826 // Returns the maximum zoom level of the map (if set in the `maxZoom` option of the map or of any layers).
3827 getMaxZoom: function () {
3828 return this.options.maxZoom === undefined ?
3829 (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) :
3830 this.options.maxZoom;
3831 },
3832
3833 // @method getBoundsZoom(bounds: LatLngBounds, inside?: Boolean, padding?: Point): Number
3834 // Returns the maximum zoom level on which the given bounds fit to the map
3835 // view in its entirety. If `inside` (optional) is set to `true`, the method
3836 // instead returns the minimum zoom level on which the map view fits into
3837 // the given bounds in its entirety.
3838 getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number
3839 bounds = toLatLngBounds(bounds);
3840 padding = toPoint(padding || [0, 0]);
3841
3842 var zoom = this.getZoom() || 0,
3843 min = this.getMinZoom(),
3844 max = this.getMaxZoom(),
3845 nw = bounds.getNorthWest(),
3846 se = bounds.getSouthEast(),
3847 size = this.getSize().subtract(padding),
3848 boundsSize = toBounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
3849 snap = any3d ? this.options.zoomSnap : 1,
3850 scalex = size.x / boundsSize.x,
3851 scaley = size.y / boundsSize.y,
3852 scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
3853
3854 zoom = this.getScaleZoom(scale, zoom);
3855
3856 if (snap) {
3857 zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
3858 zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
3859 }
3860
3861 return Math.max(min, Math.min(max, zoom));
3862 },
3863
3864 // @method getSize(): Point
3865 // Returns the current size of the map container (in pixels).
3866 getSize: function () {
3867 if (!this._size || this._sizeChanged) {
3868 this._size = new Point(
3869 this._container.clientWidth || 0,
3870 this._container.clientHeight || 0);
3871
3872 this._sizeChanged = false;
3873 }
3874 return this._size.clone();
3875 },
3876
3877 // @method getPixelBounds(): Bounds
3878 // Returns the bounds of the current map view in projected pixel
3879 // coordinates (sometimes useful in layer and overlay implementations).
3880 getPixelBounds: function (center, zoom) {
3881 var topLeftPoint = this._getTopLeftPoint(center, zoom);
3882 return new Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
3883 },
3884
3885 // TODO: Check semantics - isn't the pixel origin the 0,0 coord relative to
3886 // the map pane? "left point of the map layer" can be confusing, specially
3887 // since there can be negative offsets.
3888 // @method getPixelOrigin(): Point
3889 // Returns the projected pixel coordinates of the top left point of
3890 // the map layer (useful in custom layer and overlay implementations).
3891 getPixelOrigin: function () {
3892 this._checkIfLoaded();
3893 return this._pixelOrigin;
3894 },
3895
3896 // @method getPixelWorldBounds(zoom?: Number): Bounds
3897 // Returns the world's bounds in pixel coordinates for zoom level `zoom`.
3898 // If `zoom` is omitted, the map's current zoom level is used.
3899 getPixelWorldBounds: function (zoom) {
3900 return this.options.crs.getProjectedBounds(zoom === undefined ? this.getZoom() : zoom);
3901 },
3902
3903 // @section Other Methods
3904
3905 // @method getPane(pane: String|HTMLElement): HTMLElement
3906 // Returns a [map pane](#map-pane), given its name or its HTML element (its identity).
3907 getPane: function (pane) {
3908 return typeof pane === 'string' ? this._panes[pane] : pane;
3909 },
3910
3911 // @method getPanes(): Object
3912 // Returns a plain object containing the names of all [panes](#map-pane) as keys and
3913 // the panes as values.
3914 getPanes: function () {
3915 return this._panes;
3916 },
3917
3918 // @method getContainer: HTMLElement
3919 // Returns the HTML element that contains the map.
3920 getContainer: function () {
3921 return this._container;
3922 },
3923
3924
3925 // @section Conversion Methods
3926
3927 // @method getZoomScale(toZoom: Number, fromZoom: Number): Number
3928 // Returns the scale factor to be applied to a map transition from zoom level
3929 // `fromZoom` to `toZoom`. Used internally to help with zoom animations.
3930 getZoomScale: function (toZoom, fromZoom) {
3931 // TODO replace with universal implementation after refactoring projections
3932 var crs = this.options.crs;
3933 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3934 return crs.scale(toZoom) / crs.scale(fromZoom);
3935 },
3936
3937 // @method getScaleZoom(scale: Number, fromZoom: Number): Number
3938 // Returns the zoom level that the map would end up at, if it is at `fromZoom`
3939 // level and everything is scaled by a factor of `scale`. Inverse of
3940 // [`getZoomScale`](#map-getZoomScale).
3941 getScaleZoom: function (scale, fromZoom) {
3942 var crs = this.options.crs;
3943 fromZoom = fromZoom === undefined ? this._zoom : fromZoom;
3944 var zoom = crs.zoom(scale * crs.scale(fromZoom));
3945 return isNaN(zoom) ? Infinity : zoom;
3946 },
3947
3948 // @method project(latlng: LatLng, zoom: Number): Point
3949 // Projects a geographical coordinate `LatLng` according to the projection
3950 // of the map's CRS, then scales it according to `zoom` and the CRS's
3951 // `Transformation`. The result is pixel coordinate relative to
3952 // the CRS origin.
3953 project: function (latlng, zoom) {
3954 zoom = zoom === undefined ? this._zoom : zoom;
3955 return this.options.crs.latLngToPoint(toLatLng(latlng), zoom);
3956 },
3957
3958 // @method unproject(point: Point, zoom: Number): LatLng
3959 // Inverse of [`project`](#map-project).
3960 unproject: function (point, zoom) {
3961 zoom = zoom === undefined ? this._zoom : zoom;
3962 return this.options.crs.pointToLatLng(toPoint(point), zoom);
3963 },
3964
3965 // @method layerPointToLatLng(point: Point): LatLng
3966 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
3967 // returns the corresponding geographical coordinate (for the current zoom level).
3968 layerPointToLatLng: function (point) {
3969 var projectedPoint = toPoint(point).add(this.getPixelOrigin());
3970 return this.unproject(projectedPoint);
3971 },
3972
3973 // @method latLngToLayerPoint(latlng: LatLng): Point
3974 // Given a geographical coordinate, returns the corresponding pixel coordinate
3975 // relative to the [origin pixel](#map-getpixelorigin).
3976 latLngToLayerPoint: function (latlng) {
3977 var projectedPoint = this.project(toLatLng(latlng))._round();
3978 return projectedPoint._subtract(this.getPixelOrigin());
3979 },
3980
3981 // @method wrapLatLng(latlng: LatLng): LatLng
3982 // Returns a `LatLng` where `lat` and `lng` has been wrapped according to the
3983 // map's CRS's `wrapLat` and `wrapLng` properties, if they are outside the
3984 // CRS's bounds.
3985 // By default this means longitude is wrapped around the dateline so its
3986 // value is between -180 and +180 degrees.
3987 wrapLatLng: function (latlng) {
3988 return this.options.crs.wrapLatLng(toLatLng(latlng));
3989 },
3990
3991 // @method wrapLatLngBounds(bounds: LatLngBounds): LatLngBounds
3992 // Returns a `LatLngBounds` with the same size as the given one, ensuring that
3993 // its center is within the CRS's bounds.
3994 // By default this means the center longitude is wrapped around the dateline so its
3995 // value is between -180 and +180 degrees, and the majority of the bounds
3996 // overlaps the CRS's bounds.
3997 wrapLatLngBounds: function (latlng) {
3998 return this.options.crs.wrapLatLngBounds(toLatLngBounds(latlng));
3999 },
4000
4001 // @method distance(latlng1: LatLng, latlng2: LatLng): Number
4002 // Returns the distance between two geographical coordinates according to
4003 // the map's CRS. By default this measures distance in meters.
4004 distance: function (latlng1, latlng2) {
4005 return this.options.crs.distance(toLatLng(latlng1), toLatLng(latlng2));
4006 },
4007
4008 // @method containerPointToLayerPoint(point: Point): Point
4009 // Given a pixel coordinate relative to the map container, returns the corresponding
4010 // pixel coordinate relative to the [origin pixel](#map-getpixelorigin).
4011 containerPointToLayerPoint: function (point) { // (Point)
4012 return toPoint(point).subtract(this._getMapPanePos());
4013 },
4014
4015 // @method layerPointToContainerPoint(point: Point): Point
4016 // Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
4017 // returns the corresponding pixel coordinate relative to the map container.
4018 layerPointToContainerPoint: function (point) { // (Point)
4019 return toPoint(point).add(this._getMapPanePos());
4020 },
4021
4022 // @method containerPointToLatLng(point: Point): LatLng
4023 // Given a pixel coordinate relative to the map container, returns
4024 // the corresponding geographical coordinate (for the current zoom level).
4025 containerPointToLatLng: function (point) {
4026 var layerPoint = this.containerPointToLayerPoint(toPoint(point));
4027 return this.layerPointToLatLng(layerPoint);
4028 },
4029
4030 // @method latLngToContainerPoint(latlng: LatLng): Point
4031 // Given a geographical coordinate, returns the corresponding pixel coordinate
4032 // relative to the map container.
4033 latLngToContainerPoint: function (latlng) {
4034 return this.layerPointToContainerPoint(this.latLngToLayerPoint(toLatLng(latlng)));
4035 },
4036
4037 // @method mouseEventToContainerPoint(ev: MouseEvent): Point
4038 // Given a MouseEvent object, returns the pixel coordinate relative to the
4039 // map container where the event took place.
4040 mouseEventToContainerPoint: function (e) {
4041 return getMousePosition(e, this._container);
4042 },
4043
4044 // @method mouseEventToLayerPoint(ev: MouseEvent): Point
4045 // Given a MouseEvent object, returns the pixel coordinate relative to
4046 // the [origin pixel](#map-getpixelorigin) where the event took place.
4047 mouseEventToLayerPoint: function (e) {
4048 return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e));
4049 },
4050
4051 // @method mouseEventToLatLng(ev: MouseEvent): LatLng
4052 // Given a MouseEvent object, returns geographical coordinate where the
4053 // event took place.
4054 mouseEventToLatLng: function (e) { // (MouseEvent)
4055 return this.layerPointToLatLng(this.mouseEventToLayerPoint(e));
4056 },
4057
4058
4059 // map initialization methods
4060
4061 _initContainer: function (id) {
4062 var container = this._container = get(id);
4063
4064 if (!container) {
4065 throw new Error('Map container not found.');
4066 } else if (container._leaflet_id) {
4067 throw new Error('Map container is already initialized.');
4068 }
4069
4070 on(container, 'scroll', this._onScroll, this);
4071 this._containerId = stamp(container);
4072 },
4073
4074 _initLayout: function () {
4075 var container = this._container;
4076
4077 this._fadeAnimated = this.options.fadeAnimation && any3d;
4078
4079 addClass(container, 'leaflet-container' +
4080 (touch ? ' leaflet-touch' : '') +
4081 (retina ? ' leaflet-retina' : '') +
4082 (ielt9 ? ' leaflet-oldie' : '') +
4083 (safari ? ' leaflet-safari' : '') +
4084 (this._fadeAnimated ? ' leaflet-fade-anim' : ''));
4085
4086 var position = getStyle(container, 'position');
4087
4088 if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') {
4089 container.style.position = 'relative';
4090 }
4091
4092 this._initPanes();
4093
4094 if (this._initControlPos) {
4095 this._initControlPos();
4096 }
4097 },
4098
4099 _initPanes: function () {
4100 var panes = this._panes = {};
4101 this._paneRenderers = {};
4102
4103 // @section
4104 //
4105 // Panes are DOM elements used to control the ordering of layers on the map. You
4106 // can access panes with [`map.getPane`](#map-getpane) or
4107 // [`map.getPanes`](#map-getpanes) methods. New panes can be created with the
4108 // [`map.createPane`](#map-createpane) method.
4109 //
4110 // Every map has the following default panes that differ only in zIndex.
4111 //
4112 // @pane mapPane: HTMLElement = 'auto'
4113 // Pane that contains all other map panes
4114
4115 this._mapPane = this.createPane('mapPane', this._container);
4116 setPosition(this._mapPane, new Point(0, 0));
4117
4118 // @pane tilePane: HTMLElement = 200
4119 // Pane for `GridLayer`s and `TileLayer`s
4120 this.createPane('tilePane');
4121 // @pane overlayPane: HTMLElement = 400
4122 // Pane for overlay shadows (e.g. `Marker` shadows)
4123 this.createPane('shadowPane');
4124 // @pane shadowPane: HTMLElement = 500
4125 // Pane for vectors (`Path`s, like `Polyline`s and `Polygon`s), `ImageOverlay`s and `VideoOverlay`s
4126 this.createPane('overlayPane');
4127 // @pane markerPane: HTMLElement = 600
4128 // Pane for `Icon`s of `Marker`s
4129 this.createPane('markerPane');
4130 // @pane tooltipPane: HTMLElement = 650
4131 // Pane for `Tooltip`s.
4132 this.createPane('tooltipPane');
4133 // @pane popupPane: HTMLElement = 700
4134 // Pane for `Popup`s.
4135 this.createPane('popupPane');
4136
4137 if (!this.options.markerZoomAnimation) {
4138 addClass(panes.markerPane, 'leaflet-zoom-hide');
4139 addClass(panes.shadowPane, 'leaflet-zoom-hide');
4140 }
4141 },
4142
4143
4144 // private methods that modify map state
4145
4146 // @section Map state change events
4147 _resetView: function (center, zoom) {
4148 setPosition(this._mapPane, new Point(0, 0));
4149
4150 var loading = !this._loaded;
4151 this._loaded = true;
4152 zoom = this._limitZoom(zoom);
4153
4154 this.fire('viewprereset');
4155
4156 var zoomChanged = this._zoom !== zoom;
4157 this
4158 ._moveStart(zoomChanged, false)
4159 ._move(center, zoom)
4160 ._moveEnd(zoomChanged);
4161
4162 // @event viewreset: Event
4163 // Fired when the map needs to redraw its content (this usually happens
4164 // on map zoom or load). Very useful for creating custom overlays.
4165 this.fire('viewreset');
4166
4167 // @event load: Event
4168 // Fired when the map is initialized (when its center and zoom are set
4169 // for the first time).
4170 if (loading) {
4171 this.fire('load');
4172 }
4173 },
4174
4175 _moveStart: function (zoomChanged, noMoveStart) {
4176 // @event zoomstart: Event
4177 // Fired when the map zoom is about to change (e.g. before zoom animation).
4178 // @event movestart: Event
4179 // Fired when the view of the map starts changing (e.g. user starts dragging the map).
4180 if (zoomChanged) {
4181 this.fire('zoomstart');
4182 }
4183 if (!noMoveStart) {
4184 this.fire('movestart');
4185 }
4186 return this;
4187 },
4188
4189 _move: function (center, zoom, data) {
4190 if (zoom === undefined) {
4191 zoom = this._zoom;
4192 }
4193 var zoomChanged = this._zoom !== zoom;
4194
4195 this._zoom = zoom;
4196 this._lastCenter = center;
4197 this._pixelOrigin = this._getNewPixelOrigin(center);
4198
4199 // @event zoom: Event
4200 // Fired repeatedly during any change in zoom level, including zoom
4201 // and fly animations.
4202 if (zoomChanged || (data && data.pinch)) { // Always fire 'zoom' if pinching because #3530
4203 this.fire('zoom', data);
4204 }
4205
4206 // @event move: Event
4207 // Fired repeatedly during any movement of the map, including pan and
4208 // fly animations.
4209 return this.fire('move', data);
4210 },
4211
4212 _moveEnd: function (zoomChanged) {
4213 // @event zoomend: Event
4214 // Fired when the map has changed, after any animations.
4215 if (zoomChanged) {
4216 this.fire('zoomend');
4217 }
4218
4219 // @event moveend: Event
4220 // Fired when the center of the map stops changing (e.g. user stopped
4221 // dragging the map).
4222 return this.fire('moveend');
4223 },
4224
4225 _stop: function () {
4226 cancelAnimFrame(this._flyToFrame);
4227 if (this._panAnim) {
4228 this._panAnim.stop();
4229 }
4230 return this;
4231 },
4232
4233 _rawPanBy: function (offset) {
4234 setPosition(this._mapPane, this._getMapPanePos().subtract(offset));
4235 },
4236
4237 _getZoomSpan: function () {
4238 return this.getMaxZoom() - this.getMinZoom();
4239 },
4240
4241 _panInsideMaxBounds: function () {
4242 if (!this._enforcingBounds) {
4243 this.panInsideBounds(this.options.maxBounds);
4244 }
4245 },
4246
4247 _checkIfLoaded: function () {
4248 if (!this._loaded) {
4249 throw new Error('Set map center and zoom first.');
4250 }
4251 },
4252
4253 // DOM event handling
4254
4255 // @section Interaction events
4256 _initEvents: function (remove$$1) {
4257 this._targets = {};
4258 this._targets[stamp(this._container)] = this;
4259
4260 var onOff = remove$$1 ? off : on;
4261
4262 // @event click: MouseEvent
4263 // Fired when the user clicks (or taps) the map.
4264 // @event dblclick: MouseEvent
4265 // Fired when the user double-clicks (or double-taps) the map.
4266 // @event mousedown: MouseEvent
4267 // Fired when the user pushes the mouse button on the map.
4268 // @event mouseup: MouseEvent
4269 // Fired when the user releases the mouse button on the map.
4270 // @event mouseover: MouseEvent
4271 // Fired when the mouse enters the map.
4272 // @event mouseout: MouseEvent
4273 // Fired when the mouse leaves the map.
4274 // @event mousemove: MouseEvent
4275 // Fired while the mouse moves over the map.
4276 // @event contextmenu: MouseEvent
4277 // Fired when the user pushes the right mouse button on the map, prevents
4278 // default browser context menu from showing if there are listeners on
4279 // this event. Also fired on mobile when the user holds a single touch
4280 // for a second (also called long press).
4281 // @event keypress: KeyboardEvent
4282 // Fired when the user presses a key from the keyboard that produces a character value while the map is focused.
4283 // @event keydown: KeyboardEvent
4284 // Fired when the user presses a key from the keyboard while the map is focused. Unlike the `keypress` event,
4285 // the `keydown` event is fired for keys that produce a character value and for keys
4286 // that do not produce a character value.
4287 // @event keyup: KeyboardEvent
4288 // Fired when the user releases a key from the keyboard while the map is focused.
4289 onOff(this._container, 'click dblclick mousedown mouseup ' +
4290 'mouseover mouseout mousemove contextmenu keypress keydown keyup', this._handleDOMEvent, this);
4291
4292 if (this.options.trackResize) {
4293 onOff(window, 'resize', this._onResize, this);
4294 }
4295
4296 if (any3d && this.options.transform3DLimit) {
4297 (remove$$1 ? this.off : this.on).call(this, 'moveend', this._onMoveEnd);
4298 }
4299 },
4300
4301 _onResize: function () {
4302 cancelAnimFrame(this._resizeRequest);
4303 this._resizeRequest = requestAnimFrame(
4304 function () { this.invalidateSize({debounceMoveend: true}); }, this);
4305 },
4306
4307 _onScroll: function () {
4308 this._container.scrollTop = 0;
4309 this._container.scrollLeft = 0;
4310 },
4311
4312 _onMoveEnd: function () {
4313 var pos = this._getMapPanePos();
4314 if (Math.max(Math.abs(pos.x), Math.abs(pos.y)) >= this.options.transform3DLimit) {
4315 // https://bugzilla.mozilla.org/show_bug.cgi?id=1203873 but Webkit also have
4316 // a pixel offset on very high values, see: http://jsfiddle.net/dg6r5hhb/
4317 this._resetView(this.getCenter(), this.getZoom());
4318 }
4319 },
4320
4321 _findEventTargets: function (e, type) {
4322 var targets = [],
4323 target,
4324 isHover = type === 'mouseout' || type === 'mouseover',
4325 src = e.target || e.srcElement,
4326 dragging = false;
4327
4328 while (src) {
4329 target = this._targets[stamp(src)];
4330 if (target && (type === 'click' || type === 'preclick') && !e._simulated && this._draggableMoved(target)) {
4331 // Prevent firing click after you just dragged an object.
4332 dragging = true;
4333 break;
4334 }
4335 if (target && target.listens(type, true)) {
4336 if (isHover && !isExternalTarget(src, e)) { break; }
4337 targets.push(target);
4338 if (isHover) { break; }
4339 }
4340 if (src === this._container) { break; }
4341 src = src.parentNode;
4342 }
4343 if (!targets.length && !dragging && !isHover && isExternalTarget(src, e)) {
4344 targets = [this];
4345 }
4346 return targets;
4347 },
4348
4349 _handleDOMEvent: function (e) {
4350 if (!this._loaded || skipped(e)) { return; }
4351
4352 var type = e.type;
4353
4354 if (type === 'mousedown' || type === 'keypress' || type === 'keyup' || type === 'keydown') {
4355 // prevents outline when clicking on keyboard-focusable element
4356 preventOutline(e.target || e.srcElement);
4357 }
4358
4359 this._fireDOMEvent(e, type);
4360 },
4361
4362 _mouseEvents: ['click', 'dblclick', 'mouseover', 'mouseout', 'contextmenu'],
4363
4364 _fireDOMEvent: function (e, type, targets) {
4365
4366 if (e.type === 'click') {
4367 // Fire a synthetic 'preclick' event which propagates up (mainly for closing popups).
4368 // @event preclick: MouseEvent
4369 // Fired before mouse click on the map (sometimes useful when you
4370 // want something to happen on click before any existing click
4371 // handlers start running).
4372 var synth = extend({}, e);
4373 synth.type = 'preclick';
4374 this._fireDOMEvent(synth, synth.type, targets);
4375 }
4376
4377 if (e._stopped) { return; }
4378
4379 // Find the layer the event is propagating from and its parents.
4380 targets = (targets || []).concat(this._findEventTargets(e, type));
4381
4382 if (!targets.length) { return; }
4383
4384 var target = targets[0];
4385 if (type === 'contextmenu' && target.listens(type, true)) {
4386 preventDefault(e);
4387 }
4388
4389 var data = {
4390 originalEvent: e
4391 };
4392
4393 if (e.type !== 'keypress' && e.type !== 'keydown' && e.type !== 'keyup') {
4394 var isMarker = target.getLatLng && (!target._radius || target._radius <= 10);
4395 data.containerPoint = isMarker ?
4396 this.latLngToContainerPoint(target.getLatLng()) : this.mouseEventToContainerPoint(e);
4397 data.layerPoint = this.containerPointToLayerPoint(data.containerPoint);
4398 data.latlng = isMarker ? target.getLatLng() : this.layerPointToLatLng(data.layerPoint);
4399 }
4400
4401 for (var i = 0; i < targets.length; i++) {
4402 targets[i].fire(type, data, true);
4403 if (data.originalEvent._stopped ||
4404 (targets[i].options.bubblingMouseEvents === false && indexOf(this._mouseEvents, type) !== -1)) { return; }
4405 }
4406 },
4407
4408 _draggableMoved: function (obj) {
4409 obj = obj.dragging && obj.dragging.enabled() ? obj : this;
4410 return (obj.dragging && obj.dragging.moved()) || (this.boxZoom && this.boxZoom.moved());
4411 },
4412
4413 _clearHandlers: function () {
4414 for (var i = 0, len = this._handlers.length; i < len; i++) {
4415 this._handlers[i].disable();
4416 }
4417 },
4418
4419 // @section Other Methods
4420
4421 // @method whenReady(fn: Function, context?: Object): this
4422 // Runs the given function `fn` when the map gets initialized with
4423 // a view (center and zoom) and at least one layer, or immediately
4424 // if it's already initialized, optionally passing a function context.
4425 whenReady: function (callback, context) {
4426 if (this._loaded) {
4427 callback.call(context || this, {target: this});
4428 } else {
4429 this.on('load', callback, context);
4430 }
4431 return this;
4432 },
4433
4434
4435 // private methods for getting map state
4436
4437 _getMapPanePos: function () {
4438 return getPosition(this._mapPane) || new Point(0, 0);
4439 },
4440
4441 _moved: function () {
4442 var pos = this._getMapPanePos();
4443 return pos && !pos.equals([0, 0]);
4444 },
4445
4446 _getTopLeftPoint: function (center, zoom) {
4447 var pixelOrigin = center && zoom !== undefined ?
4448 this._getNewPixelOrigin(center, zoom) :
4449 this.getPixelOrigin();
4450 return pixelOrigin.subtract(this._getMapPanePos());
4451 },
4452
4453 _getNewPixelOrigin: function (center, zoom) {
4454 var viewHalf = this.getSize()._divideBy(2);
4455 return this.project(center, zoom)._subtract(viewHalf)._add(this._getMapPanePos())._round();
4456 },
4457
4458 _latLngToNewLayerPoint: function (latlng, zoom, center) {
4459 var topLeft = this._getNewPixelOrigin(center, zoom);
4460 return this.project(latlng, zoom)._subtract(topLeft);
4461 },
4462
4463 _latLngBoundsToNewLayerBounds: function (latLngBounds, zoom, center) {
4464 var topLeft = this._getNewPixelOrigin(center, zoom);
4465 return toBounds([
4466 this.project(latLngBounds.getSouthWest(), zoom)._subtract(topLeft),
4467 this.project(latLngBounds.getNorthWest(), zoom)._subtract(topLeft),
4468 this.project(latLngBounds.getSouthEast(), zoom)._subtract(topLeft),
4469 this.project(latLngBounds.getNorthEast(), zoom)._subtract(topLeft)
4470 ]);
4471 },
4472
4473 // layer point of the current center
4474 _getCenterLayerPoint: function () {
4475 return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
4476 },
4477
4478 // offset of the specified place to the current center in pixels
4479 _getCenterOffset: function (latlng) {
4480 return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint());
4481 },
4482
4483 // adjust center for view to get inside bounds
4484 _limitCenter: function (center, zoom, bounds) {
4485
4486 if (!bounds) { return center; }
4487
4488 var centerPoint = this.project(center, zoom),
4489 viewHalf = this.getSize().divideBy(2),
4490 viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
4491 offset = this._getBoundsOffset(viewBounds, bounds, zoom);
4492
4493 // If offset is less than a pixel, ignore.
4494 // This prevents unstable projections from getting into
4495 // an infinite loop of tiny offsets.
4496 if (offset.round().equals([0, 0])) {
4497 return center;
4498 }
4499
4500 return this.unproject(centerPoint.add(offset), zoom);
4501 },
4502
4503 // adjust offset for view to get inside bounds
4504 _limitOffset: function (offset, bounds) {
4505 if (!bounds) { return offset; }
4506
4507 var viewBounds = this.getPixelBounds(),
4508 newBounds = new Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset));
4509
4510 return offset.add(this._getBoundsOffset(newBounds, bounds));
4511 },
4512
4513 // returns offset needed for pxBounds to get inside maxBounds at a specified zoom
4514 _getBoundsOffset: function (pxBounds, maxBounds, zoom) {
4515 var projectedMaxBounds = toBounds(
4516 this.project(maxBounds.getNorthEast(), zoom),
4517 this.project(maxBounds.getSouthWest(), zoom)
4518 ),
4519 minOffset = projectedMaxBounds.min.subtract(pxBounds.min),
4520 maxOffset = projectedMaxBounds.max.subtract(pxBounds.max),
4521
4522 dx = this._rebound(minOffset.x, -maxOffset.x),
4523 dy = this._rebound(minOffset.y, -maxOffset.y);
4524
4525 return new Point(dx, dy);
4526 },
4527
4528 _rebound: function (left, right) {
4529 return left + right > 0 ?
4530 Math.round(left - right) / 2 :
4531 Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right));
4532 },
4533
4534 _limitZoom: function (zoom) {
4535 var min = this.getMinZoom(),
4536 max = this.getMaxZoom(),
4537 snap = any3d ? this.options.zoomSnap : 1;
4538 if (snap) {
4539 zoom = Math.round(zoom / snap) * snap;
4540 }
4541 return Math.max(min, Math.min(max, zoom));
4542 },
4543
4544 _onPanTransitionStep: function () {
4545 this.fire('move');
4546 },
4547
4548 _onPanTransitionEnd: function () {
4549 removeClass(this._mapPane, 'leaflet-pan-anim');
4550 this.fire('moveend');
4551 },
4552
4553 _tryAnimatedPan: function (center, options) {
4554 // difference between the new and current centers in pixels
4555 var offset = this._getCenterOffset(center)._trunc();
4556
4557 // don't animate too far unless animate: true specified in options
4558 if ((options && options.animate) !== true && !this.getSize().contains(offset)) { return false; }
4559
4560 this.panBy(offset, options);
4561
4562 return true;
4563 },
4564
4565 _createAnimProxy: function () {
4566
4567 var proxy = this._proxy = create$1('div', 'leaflet-proxy leaflet-zoom-animated');
4568 this._panes.mapPane.appendChild(proxy);
4569
4570 this.on('zoomanim', function (e) {
4571 var prop = TRANSFORM,
4572 transform = this._proxy.style[prop];
4573
4574 setTransform(this._proxy, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1));
4575
4576 // workaround for case when transform is the same and so transitionend event is not fired
4577 if (transform === this._proxy.style[prop] && this._animatingZoom) {
4578 this._onZoomTransitionEnd();
4579 }
4580 }, this);
4581
4582 this.on('load moveend', this._animMoveEnd, this);
4583
4584 this._on('unload', this._destroyAnimProxy, this);
4585 },
4586
4587 _destroyAnimProxy: function () {
4588 remove(this._proxy);
4589 this.off('load moveend', this._animMoveEnd, this);
4590 delete this._proxy;
4591 },
4592
4593 _animMoveEnd: function () {
4594 var c = this.getCenter(),
4595 z = this.getZoom();
4596 setTransform(this._proxy, this.project(c, z), this.getZoomScale(z, 1));
4597 },
4598
4599 _catchTransitionEnd: function (e) {
4600 if (this._animatingZoom && e.propertyName.indexOf('transform') >= 0) {
4601 this._onZoomTransitionEnd();
4602 }
4603 },
4604
4605 _nothingToAnimate: function () {
4606 return !this._container.getElementsByClassName('leaflet-zoom-animated').length;
4607 },
4608
4609 _tryAnimatedZoom: function (center, zoom, options) {
4610
4611 if (this._animatingZoom) { return true; }
4612
4613 options = options || {};
4614
4615 // don't animate if disabled, not supported or zoom difference is too large
4616 if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() ||
4617 Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; }
4618
4619 // offset is the pixel coords of the zoom origin relative to the current center
4620 var scale = this.getZoomScale(zoom),
4621 offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale);
4622
4623 // don't animate if the zoom origin isn't within one screen from the current center, unless forced
4624 if (options.animate !== true && !this.getSize().contains(offset)) { return false; }
4625
4626 requestAnimFrame(function () {
4627 this
4628 ._moveStart(true, false)
4629 ._animateZoom(center, zoom, true);
4630 }, this);
4631
4632 return true;
4633 },
4634
4635 _animateZoom: function (center, zoom, startAnim, noUpdate) {
4636 if (!this._mapPane) { return; }
4637
4638 if (startAnim) {
4639 this._animatingZoom = true;
4640
4641 // remember what center/zoom to set after animation
4642 this._animateToCenter = center;
4643 this._animateToZoom = zoom;
4644
4645 addClass(this._mapPane, 'leaflet-zoom-anim');
4646 }
4647
4648 // @section Other Events
4649 // @event zoomanim: ZoomAnimEvent
4650 // Fired at least once per zoom animation. For continuous zoom, like pinch zooming, fired once per frame during zoom.
4651 this.fire('zoomanim', {
4652 center: center,
4653 zoom: zoom,
4654 noUpdate: noUpdate
4655 });
4656
4657 // Work around webkit not firing 'transitionend', see https://github.com/Leaflet/Leaflet/issues/3689, 2693
4658 setTimeout(bind(this._onZoomTransitionEnd, this), 250);
4659 },
4660
4661 _onZoomTransitionEnd: function () {
4662 if (!this._animatingZoom) { return; }
4663
4664 if (this._mapPane) {
4665 removeClass(this._mapPane, 'leaflet-zoom-anim');
4666 }
4667
4668 this._animatingZoom = false;
4669
4670 this._move(this._animateToCenter, this._animateToZoom);
4671
4672 // This anim frame should prevent an obscure iOS webkit tile loading race condition.
4673 requestAnimFrame(function () {
4674 this._moveEnd(true);
4675 }, this);
4676 }
4677 });
4678
4679 // @section
4680
4681 // @factory L.map(id: String, options?: Map options)
4682 // Instantiates a map object given the DOM ID of a `<div>` element
4683 // and optionally an object literal with `Map options`.
4684 //
4685 // @alternative
4686 // @factory L.map(el: HTMLElement, options?: Map options)
4687 // Instantiates a map object given an instance of a `<div>` HTML element
4688 // and optionally an object literal with `Map options`.
4689 function createMap(id, options) {
4690 return new Map(id, options);
4691 }
4692
4693 /*
4694 * @class Control
4695 * @aka L.Control
4696 * @inherits Class
4697 *
4698 * L.Control is a base class for implementing map controls. Handles positioning.
4699 * All other controls extend from this class.
4700 */
4701
4702 var Control = Class.extend({
4703 // @section
4704 // @aka Control options
4705 options: {
4706 // @option position: String = 'topright'
4707 // The position of the control (one of the map corners). Possible values are `'topleft'`,
4708 // `'topright'`, `'bottomleft'` or `'bottomright'`
4709 position: 'topright'
4710 },
4711
4712 initialize: function (options) {
4713 setOptions(this, options);
4714 },
4715
4716 /* @section
4717 * Classes extending L.Control will inherit the following methods:
4718 *
4719 * @method getPosition: string
4720 * Returns the position of the control.
4721 */
4722 getPosition: function () {
4723 return this.options.position;
4724 },
4725
4726 // @method setPosition(position: string): this
4727 // Sets the position of the control.
4728 setPosition: function (position) {
4729 var map = this._map;
4730
4731 if (map) {
4732 map.removeControl(this);
4733 }
4734
4735 this.options.position = position;
4736
4737 if (map) {
4738 map.addControl(this);
4739 }
4740
4741 return this;
4742 },
4743
4744 // @method getContainer: HTMLElement
4745 // Returns the HTMLElement that contains the control.
4746 getContainer: function () {
4747 return this._container;
4748 },
4749
4750 // @method addTo(map: Map): this
4751 // Adds the control to the given map.
4752 addTo: function (map) {
4753 this.remove();
4754 this._map = map;
4755
4756 var container = this._container = this.onAdd(map),
4757 pos = this.getPosition(),
4758 corner = map._controlCorners[pos];
4759
4760 addClass(container, 'leaflet-control');
4761
4762 if (pos.indexOf('bottom') !== -1) {
4763 corner.insertBefore(container, corner.firstChild);
4764 } else {
4765 corner.appendChild(container);
4766 }
4767
4768 this._map.on('unload', this.remove, this);
4769
4770 return this;
4771 },
4772
4773 // @method remove: this
4774 // Removes the control from the map it is currently active on.
4775 remove: function () {
4776 if (!this._map) {
4777 return this;
4778 }
4779
4780 remove(this._container);
4781
4782 if (this.onRemove) {
4783 this.onRemove(this._map);
4784 }
4785
4786 this._map.off('unload', this.remove, this);
4787 this._map = null;
4788
4789 return this;
4790 },
4791
4792 _refocusOnMap: function (e) {
4793 // if map exists and event is not a keyboard event
4794 if (this._map && e && e.screenX > 0 && e.screenY > 0) {
4795 this._map.getContainer().focus();
4796 }
4797 }
4798 });
4799
4800 var control = function (options) {
4801 return new Control(options);
4802 };
4803
4804 /* @section Extension methods
4805 * @uninheritable
4806 *
4807 * Every control should extend from `L.Control` and (re-)implement the following methods.
4808 *
4809 * @method onAdd(map: Map): HTMLElement
4810 * Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
4811 *
4812 * @method onRemove(map: Map)
4813 * Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
4814 */
4815
4816 /* @namespace Map
4817 * @section Methods for Layers and Controls
4818 */
4819 Map.include({
4820 // @method addControl(control: Control): this
4821 // Adds the given control to the map
4822 addControl: function (control) {
4823 control.addTo(this);
4824 return this;
4825 },
4826
4827 // @method removeControl(control: Control): this
4828 // Removes the given control from the map
4829 removeControl: function (control) {
4830 control.remove();
4831 return this;
4832 },
4833
4834 _initControlPos: function () {
4835 var corners = this._controlCorners = {},
4836 l = 'leaflet-',
4837 container = this._controlContainer =
4838 create$1('div', l + 'control-container', this._container);
4839
4840 function createCorner(vSide, hSide) {
4841 var className = l + vSide + ' ' + l + hSide;
4842
4843 corners[vSide + hSide] = create$1('div', className, container);
4844 }
4845
4846 createCorner('top', 'left');
4847 createCorner('top', 'right');
4848 createCorner('bottom', 'left');
4849 createCorner('bottom', 'right');
4850 },
4851
4852 _clearControlPos: function () {
4853 for (var i in this._controlCorners) {
4854 remove(this._controlCorners[i]);
4855 }
4856 remove(this._controlContainer);
4857 delete this._controlCorners;
4858 delete this._controlContainer;
4859 }
4860 });
4861
4862 /*
4863 * @class Control.Layers
4864 * @aka L.Control.Layers
4865 * @inherits Control
4866 *
4867 * The layers control gives users the ability to switch between different base layers and switch overlays on/off (check out the [detailed example](http://leafletjs.com/examples/layers-control/)). Extends `Control`.
4868 *
4869 * @example
4870 *
4871 * ```js
4872 * var baseLayers = {
4873 * "Mapbox": mapbox,
4874 * "OpenStreetMap": osm
4875 * };
4876 *
4877 * var overlays = {
4878 * "Marker": marker,
4879 * "Roads": roadsLayer
4880 * };
4881 *
4882 * L.control.layers(baseLayers, overlays).addTo(map);
4883 * ```
4884 *
4885 * The `baseLayers` and `overlays` parameters are object literals with layer names as keys and `Layer` objects as values:
4886 *
4887 * ```js
4888 * {
4889 * "<someName1>": layer1,
4890 * "<someName2>": layer2
4891 * }
4892 * ```
4893 *
4894 * The layer names can contain HTML, which allows you to add additional styling to the items:
4895 *
4896 * ```js
4897 * {"<img src='my-layer-icon' /> <span class='my-layer-item'>My Layer</span>": myLayer}
4898 * ```
4899 */
4900
4901 var Layers = Control.extend({
4902 // @section
4903 // @aka Control.Layers options
4904 options: {
4905 // @option collapsed: Boolean = true
4906 // If `true`, the control will be collapsed into an icon and expanded on mouse hover or touch.
4907 collapsed: true,
4908 position: 'topright',
4909
4910 // @option autoZIndex: Boolean = true
4911 // If `true`, the control will assign zIndexes in increasing order to all of its layers so that the order is preserved when switching them on/off.
4912 autoZIndex: true,
4913
4914 // @option hideSingleBase: Boolean = false
4915 // If `true`, the base layers in the control will be hidden when there is only one.
4916 hideSingleBase: false,
4917
4918 // @option sortLayers: Boolean = false
4919 // Whether to sort the layers. When `false`, layers will keep the order
4920 // in which they were added to the control.
4921 sortLayers: false,
4922
4923 // @option sortFunction: Function = *
4924 // A [compare function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)
4925 // that will be used for sorting the layers, when `sortLayers` is `true`.
4926 // The function receives both the `L.Layer` instances and their names, as in
4927 // `sortFunction(layerA, layerB, nameA, nameB)`.
4928 // By default, it sorts layers alphabetically by their name.
4929 sortFunction: function (layerA, layerB, nameA, nameB) {
4930 return nameA < nameB ? -1 : (nameB < nameA ? 1 : 0);
4931 }
4932 },
4933
4934 initialize: function (baseLayers, overlays, options) {
4935 setOptions(this, options);
4936
4937 this._layerControlInputs = [];
4938 this._layers = [];
4939 this._lastZIndex = 0;
4940 this._handlingClick = false;
4941
4942 for (var i in baseLayers) {
4943 this._addLayer(baseLayers[i], i);
4944 }
4945
4946 for (i in overlays) {
4947 this._addLayer(overlays[i], i, true);
4948 }
4949 },
4950
4951 onAdd: function (map) {
4952 this._initLayout();
4953 this._update();
4954
4955 this._map = map;
4956 map.on('zoomend', this._checkDisabledLayers, this);
4957
4958 for (var i = 0; i < this._layers.length; i++) {
4959 this._layers[i].layer.on('add remove', this._onLayerChange, this);
4960 }
4961
4962 return this._container;
4963 },
4964
4965 addTo: function (map) {
4966 Control.prototype.addTo.call(this, map);
4967 // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height.
4968 return this._expandIfNotCollapsed();
4969 },
4970
4971 onRemove: function () {
4972 this._map.off('zoomend', this._checkDisabledLayers, this);
4973
4974 for (var i = 0; i < this._layers.length; i++) {
4975 this._layers[i].layer.off('add remove', this._onLayerChange, this);
4976 }
4977 },
4978
4979 // @method addBaseLayer(layer: Layer, name: String): this
4980 // Adds a base layer (radio button entry) with the given name to the control.
4981 addBaseLayer: function (layer, name) {
4982 this._addLayer(layer, name);
4983 return (this._map) ? this._update() : this;
4984 },
4985
4986 // @method addOverlay(layer: Layer, name: String): this
4987 // Adds an overlay (checkbox entry) with the given name to the control.
4988 addOverlay: function (layer, name) {
4989 this._addLayer(layer, name, true);
4990 return (this._map) ? this._update() : this;
4991 },
4992
4993 // @method removeLayer(layer: Layer): this
4994 // Remove the given layer from the control.
4995 removeLayer: function (layer) {
4996 layer.off('add remove', this._onLayerChange, this);
4997
4998 var obj = this._getLayer(stamp(layer));
4999 if (obj) {
5000 this._layers.splice(this._layers.indexOf(obj), 1);
5001 }
5002 return (this._map) ? this._update() : this;
5003 },
5004
5005 // @method expand(): this
5006 // Expand the control container if collapsed.
5007 expand: function () {
5008 addClass(this._container, 'leaflet-control-layers-expanded');
5009 this._section.style.height = null;
5010 var acceptableHeight = this._map.getSize().y - (this._container.offsetTop + 50);
5011 if (acceptableHeight < this._section.clientHeight) {
5012 addClass(this._section, 'leaflet-control-layers-scrollbar');
5013 this._section.style.height = acceptableHeight + 'px';
5014 } else {
5015 removeClass(this._section, 'leaflet-control-layers-scrollbar');
5016 }
5017 this._checkDisabledLayers();
5018 return this;
5019 },
5020
5021 // @method collapse(): this
5022 // Collapse the control container if expanded.
5023 collapse: function () {
5024 removeClass(this._container, 'leaflet-control-layers-expanded');
5025 return this;
5026 },
5027
5028 _initLayout: function () {
5029 var className = 'leaflet-control-layers',
5030 container = this._container = create$1('div', className),
5031 collapsed = this.options.collapsed;
5032
5033 // makes this work on IE touch devices by stopping it from firing a mouseout event when the touch is released
5034 container.setAttribute('aria-haspopup', true);
5035
5036 disableClickPropagation(container);
5037 disableScrollPropagation(container);
5038
5039 var section = this._section = create$1('section', className + '-list');
5040
5041 if (collapsed) {
5042 this._map.on('click', this.collapse, this);
5043
5044 if (!android) {
5045 on(container, {
5046 mouseenter: this.expand,
5047 mouseleave: this.collapse
5048 }, this);
5049 }
5050 }
5051
5052 var link = this._layersLink = create$1('a', className + '-toggle', container);
5053 link.href = '#';
5054 link.title = 'Layers';
5055
5056 if (touch) {
5057 on(link, 'click', stop);
5058 on(link, 'click', this.expand, this);
5059 } else {
5060 on(link, 'focus', this.expand, this);
5061 }
5062
5063 if (!collapsed) {
5064 this.expand();
5065 }
5066
5067 this._baseLayersList = create$1('div', className + '-base', section);
5068 this._separator = create$1('div', className + '-separator', section);
5069 this._overlaysList = create$1('div', className + '-overlays', section);
5070
5071 container.appendChild(section);
5072 },
5073
5074 _getLayer: function (id) {
5075 for (var i = 0; i < this._layers.length; i++) {
5076
5077 if (this._layers[i] && stamp(this._layers[i].layer) === id) {
5078 return this._layers[i];
5079 }
5080 }
5081 },
5082
5083 _addLayer: function (layer, name, overlay) {
5084 if (this._map) {
5085 layer.on('add remove', this._onLayerChange, this);
5086 }
5087
5088 this._layers.push({
5089 layer: layer,
5090 name: name,
5091 overlay: overlay
5092 });
5093
5094 if (this.options.sortLayers) {
5095 this._layers.sort(bind(function (a, b) {
5096 return this.options.sortFunction(a.layer, b.layer, a.name, b.name);
5097 }, this));
5098 }
5099
5100 if (this.options.autoZIndex && layer.setZIndex) {
5101 this._lastZIndex++;
5102 layer.setZIndex(this._lastZIndex);
5103 }
5104
5105 this._expandIfNotCollapsed();
5106 },
5107
5108 _update: function () {
5109 if (!this._container) { return this; }
5110
5111 empty(this._baseLayersList);
5112 empty(this._overlaysList);
5113
5114 this._layerControlInputs = [];
5115 var baseLayersPresent, overlaysPresent, i, obj, baseLayersCount = 0;
5116
5117 for (i = 0; i < this._layers.length; i++) {
5118 obj = this._layers[i];
5119 this._addItem(obj);
5120 overlaysPresent = overlaysPresent || obj.overlay;
5121 baseLayersPresent = baseLayersPresent || !obj.overlay;
5122 baseLayersCount += !obj.overlay ? 1 : 0;
5123 }
5124
5125 // Hide base layers section if there's only one layer.
5126 if (this.options.hideSingleBase) {
5127 baseLayersPresent = baseLayersPresent && baseLayersCount > 1;
5128 this._baseLayersList.style.display = baseLayersPresent ? '' : 'none';
5129 }
5130
5131 this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none';
5132
5133 return this;
5134 },
5135
5136 _onLayerChange: function (e) {
5137 if (!this._handlingClick) {
5138 this._update();
5139 }
5140
5141 var obj = this._getLayer(stamp(e.target));
5142
5143 // @namespace Map
5144 // @section Layer events
5145 // @event baselayerchange: LayersControlEvent
5146 // Fired when the base layer is changed through the [layers control](#control-layers).
5147 // @event overlayadd: LayersControlEvent
5148 // Fired when an overlay is selected through the [layers control](#control-layers).
5149 // @event overlayremove: LayersControlEvent
5150 // Fired when an overlay is deselected through the [layers control](#control-layers).
5151 // @namespace Control.Layers
5152 var type = obj.overlay ?
5153 (e.type === 'add' ? 'overlayadd' : 'overlayremove') :
5154 (e.type === 'add' ? 'baselayerchange' : null);
5155
5156 if (type) {
5157 this._map.fire(type, obj);
5158 }
5159 },
5160
5161 // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe)
5162 _createRadioElement: function (name, checked) {
5163
5164 var radioHtml = '<input type="radio" class="leaflet-control-layers-selector" name="' +
5165 name + '"' + (checked ? ' checked="checked"' : '') + '/>';
5166
5167 var radioFragment = document.createElement('div');
5168 radioFragment.innerHTML = radioHtml;
5169
5170 return radioFragment.firstChild;
5171 },
5172
5173 _addItem: function (obj) {
5174 var label = document.createElement('label'),
5175 checked = this._map.hasLayer(obj.layer),
5176 input;
5177
5178 if (obj.overlay) {
5179 input = document.createElement('input');
5180 input.type = 'checkbox';
5181 input.className = 'leaflet-control-layers-selector';
5182 input.defaultChecked = checked;
5183 } else {
5184 input = this._createRadioElement('leaflet-base-layers_' + stamp(this), checked);
5185 }
5186
5187 this._layerControlInputs.push(input);
5188 input.layerId = stamp(obj.layer);
5189
5190 on(input, 'click', this._onInputClick, this);
5191
5192 var name = document.createElement('span');
5193 name.innerHTML = ' ' + obj.name;
5194
5195 // Helps from preventing layer control flicker when checkboxes are disabled
5196 // https://github.com/Leaflet/Leaflet/issues/2771
5197 var holder = document.createElement('div');
5198
5199 label.appendChild(holder);
5200 holder.appendChild(input);
5201 holder.appendChild(name);
5202
5203 var container = obj.overlay ? this._overlaysList : this._baseLayersList;
5204 container.appendChild(label);
5205
5206 this._checkDisabledLayers();
5207 return label;
5208 },
5209
5210 _onInputClick: function () {
5211 var inputs = this._layerControlInputs,
5212 input, layer;
5213 var addedLayers = [],
5214 removedLayers = [];
5215
5216 this._handlingClick = true;
5217
5218 for (var i = inputs.length - 1; i >= 0; i--) {
5219 input = inputs[i];
5220 layer = this._getLayer(input.layerId).layer;
5221
5222 if (input.checked) {
5223 addedLayers.push(layer);
5224 } else if (!input.checked) {
5225 removedLayers.push(layer);
5226 }
5227 }
5228
5229 // Bugfix issue 2318: Should remove all old layers before readding new ones
5230 for (i = 0; i < removedLayers.length; i++) {
5231 if (this._map.hasLayer(removedLayers[i])) {
5232 this._map.removeLayer(removedLayers[i]);
5233 }
5234 }
5235 for (i = 0; i < addedLayers.length; i++) {
5236 if (!this._map.hasLayer(addedLayers[i])) {
5237 this._map.addLayer(addedLayers[i]);
5238 }
5239 }
5240
5241 this._handlingClick = false;
5242
5243 this._refocusOnMap();
5244 },
5245
5246 _checkDisabledLayers: function () {
5247 var inputs = this._layerControlInputs,
5248 input,
5249 layer,
5250 zoom = this._map.getZoom();
5251
5252 for (var i = inputs.length - 1; i >= 0; i--) {
5253 input = inputs[i];
5254 layer = this._getLayer(input.layerId).layer;
5255 input.disabled = (layer.options.minZoom !== undefined && zoom < layer.options.minZoom) ||
5256 (layer.options.maxZoom !== undefined && zoom > layer.options.maxZoom);
5257
5258 }
5259 },
5260
5261 _expandIfNotCollapsed: function () {
5262 if (this._map && !this.options.collapsed) {
5263 this.expand();
5264 }
5265 return this;
5266 },
5267
5268 _expand: function () {
5269 // Backward compatibility, remove me in 1.1.
5270 return this.expand();
5271 },
5272
5273 _collapse: function () {
5274 // Backward compatibility, remove me in 1.1.
5275 return this.collapse();
5276 }
5277
5278 });
5279
5280
5281 // @factory L.control.layers(baselayers?: Object, overlays?: Object, options?: Control.Layers options)
5282 // Creates a layers control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. Note that all base layers should be passed in the base layers object, but only one should be added to the map during map instantiation.
5283 var layers = function (baseLayers, overlays, options) {
5284 return new Layers(baseLayers, overlays, options);
5285 };
5286
5287 /*
5288 * @class Control.Zoom
5289 * @aka L.Control.Zoom
5290 * @inherits Control
5291 *
5292 * A basic zoom control with two buttons (zoom in and zoom out). It is put on the map by default unless you set its [`zoomControl` option](#map-zoomcontrol) to `false`. Extends `Control`.
5293 */
5294
5295 var Zoom = Control.extend({
5296 // @section
5297 // @aka Control.Zoom options
5298 options: {
5299 position: 'topleft',
5300
5301 // @option zoomInText: String = '+'
5302 // The text set on the 'zoom in' button.
5303 zoomInText: '+',
5304
5305 // @option zoomInTitle: String = 'Zoom in'
5306 // The title set on the 'zoom in' button.
5307 zoomInTitle: 'Zoom in',
5308
5309 // @option zoomOutText: String = '&#x2212;'
5310 // The text set on the 'zoom out' button.
5311 zoomOutText: '&#x2212;',
5312
5313 // @option zoomOutTitle: String = 'Zoom out'
5314 // The title set on the 'zoom out' button.
5315 zoomOutTitle: 'Zoom out'
5316 },
5317
5318 onAdd: function (map) {
5319 var zoomName = 'leaflet-control-zoom',
5320 container = create$1('div', zoomName + ' leaflet-bar'),
5321 options = this.options;
5322
5323 this._zoomInButton = this._createButton(options.zoomInText, options.zoomInTitle,
5324 zoomName + '-in', container, this._zoomIn);
5325 this._zoomOutButton = this._createButton(options.zoomOutText, options.zoomOutTitle,
5326 zoomName + '-out', container, this._zoomOut);
5327
5328 this._updateDisabled();
5329 map.on('zoomend zoomlevelschange', this._updateDisabled, this);
5330
5331 return container;
5332 },
5333
5334 onRemove: function (map) {
5335 map.off('zoomend zoomlevelschange', this._updateDisabled, this);
5336 },
5337
5338 disable: function () {
5339 this._disabled = true;
5340 this._updateDisabled();
5341 return this;
5342 },
5343
5344 enable: function () {
5345 this._disabled = false;
5346 this._updateDisabled();
5347 return this;
5348 },
5349
5350 _zoomIn: function (e) {
5351 if (!this._disabled && this._map._zoom < this._map.getMaxZoom()) {
5352 this._map.zoomIn(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5353 }
5354 },
5355
5356 _zoomOut: function (e) {
5357 if (!this._disabled && this._map._zoom > this._map.getMinZoom()) {
5358 this._map.zoomOut(this._map.options.zoomDelta * (e.shiftKey ? 3 : 1));
5359 }
5360 },
5361
5362 _createButton: function (html, title, className, container, fn) {
5363 var link = create$1('a', className, container);
5364 link.innerHTML = html;
5365 link.href = '#';
5366 link.title = title;
5367
5368 /*
5369 * Will force screen readers like VoiceOver to read this as "Zoom in - button"
5370 */
5371 link.setAttribute('role', 'button');
5372 link.setAttribute('aria-label', title);
5373
5374 disableClickPropagation(link);
5375 on(link, 'click', stop);
5376 on(link, 'click', fn, this);
5377 on(link, 'click', this._refocusOnMap, this);
5378
5379 return link;
5380 },
5381
5382 _updateDisabled: function () {
5383 var map = this._map,
5384 className = 'leaflet-disabled';
5385
5386 removeClass(this._zoomInButton, className);
5387 removeClass(this._zoomOutButton, className);
5388
5389 if (this._disabled || map._zoom === map.getMinZoom()) {
5390 addClass(this._zoomOutButton, className);
5391 }
5392 if (this._disabled || map._zoom === map.getMaxZoom()) {
5393 addClass(this._zoomInButton, className);
5394 }
5395 }
5396 });
5397
5398 // @namespace Map
5399 // @section Control options
5400 // @option zoomControl: Boolean = true
5401 // Whether a [zoom control](#control-zoom) is added to the map by default.
5402 Map.mergeOptions({
5403 zoomControl: true
5404 });
5405
5406 Map.addInitHook(function () {
5407 if (this.options.zoomControl) {
5408 // @section Controls
5409 // @property zoomControl: Control.Zoom
5410 // The default zoom control (only available if the
5411 // [`zoomControl` option](#map-zoomcontrol) was `true` when creating the map).
5412 this.zoomControl = new Zoom();
5413 this.addControl(this.zoomControl);
5414 }
5415 });
5416
5417 // @namespace Control.Zoom
5418 // @factory L.control.zoom(options: Control.Zoom options)
5419 // Creates a zoom control
5420 var zoom = function (options) {
5421 return new Zoom(options);
5422 };
5423
5424 /*
5425 * @class Control.Scale
5426 * @aka L.Control.Scale
5427 * @inherits Control
5428 *
5429 * A simple scale control that shows the scale of the current center of screen in metric (m/km) and imperial (mi/ft) systems. Extends `Control`.
5430 *
5431 * @example
5432 *
5433 * ```js
5434 * L.control.scale().addTo(map);
5435 * ```
5436 */
5437
5438 var Scale = Control.extend({
5439 // @section
5440 // @aka Control.Scale options
5441 options: {
5442 position: 'bottomleft',
5443
5444 // @option maxWidth: Number = 100
5445 // Maximum width of the control in pixels. The width is set dynamically to show round values (e.g. 100, 200, 500).
5446 maxWidth: 100,
5447
5448 // @option metric: Boolean = True
5449 // Whether to show the metric scale line (m/km).
5450 metric: true,
5451
5452 // @option imperial: Boolean = True
5453 // Whether to show the imperial scale line (mi/ft).
5454 imperial: true
5455
5456 // @option updateWhenIdle: Boolean = false
5457 // If `true`, the control is updated on [`moveend`](#map-moveend), otherwise it's always up-to-date (updated on [`move`](#map-move)).
5458 },
5459
5460 onAdd: function (map) {
5461 var className = 'leaflet-control-scale',
5462 container = create$1('div', className),
5463 options = this.options;
5464
5465 this._addScales(options, className + '-line', container);
5466
5467 map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5468 map.whenReady(this._update, this);
5469
5470 return container;
5471 },
5472
5473 onRemove: function (map) {
5474 map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this);
5475 },
5476
5477 _addScales: function (options, className, container) {
5478 if (options.metric) {
5479 this._mScale = create$1('div', className, container);
5480 }
5481 if (options.imperial) {
5482 this._iScale = create$1('div', className, container);
5483 }
5484 },
5485
5486 _update: function () {
5487 var map = this._map,
5488 y = map.getSize().y / 2;
5489
5490 var maxMeters = map.distance(
5491 map.containerPointToLatLng([0, y]),
5492 map.containerPointToLatLng([this.options.maxWidth, y]));
5493
5494 this._updateScales(maxMeters);
5495 },
5496
5497 _updateScales: function (maxMeters) {
5498 if (this.options.metric && maxMeters) {
5499 this._updateMetric(maxMeters);
5500 }
5501 if (this.options.imperial && maxMeters) {
5502 this._updateImperial(maxMeters);
5503 }
5504 },
5505
5506 _updateMetric: function (maxMeters) {
5507 var meters = this._getRoundNum(maxMeters),
5508 label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';
5509
5510 this._updateScale(this._mScale, label, meters / maxMeters);
5511 },
5512
5513 _updateImperial: function (maxMeters) {
5514 var maxFeet = maxMeters * 3.2808399,
5515 maxMiles, miles, feet;
5516
5517 if (maxFeet > 5280) {
5518 maxMiles = maxFeet / 5280;
5519 miles = this._getRoundNum(maxMiles);
5520 this._updateScale(this._iScale, miles + ' mi', miles / maxMiles);
5521
5522 } else {
5523 feet = this._getRoundNum(maxFeet);
5524 this._updateScale(this._iScale, feet + ' ft', feet / maxFeet);
5525 }
5526 },
5527
5528 _updateScale: function (scale, text, ratio) {
5529 scale.style.width = Math.round(this.options.maxWidth * ratio) + 'px';
5530 scale.innerHTML = text;
5531 },
5532
5533 _getRoundNum: function (num) {
5534 var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1),
5535 d = num / pow10;
5536
5537 d = d >= 10 ? 10 :
5538 d >= 5 ? 5 :
5539 d >= 3 ? 3 :
5540 d >= 2 ? 2 : 1;
5541
5542 return pow10 * d;
5543 }
5544 });
5545
5546
5547 // @factory L.control.scale(options?: Control.Scale options)
5548 // Creates an scale control with the given options.
5549 var scale = function (options) {
5550 return new Scale(options);
5551 };
5552
5553 /*
5554 * @class Control.Attribution
5555 * @aka L.Control.Attribution
5556 * @inherits Control
5557 *
5558 * The attribution control allows you to display attribution data in a small text box on a map. It is put on the map by default unless you set its [`attributionControl` option](#map-attributioncontrol) to `false`, and it fetches attribution texts from layers with the [`getAttribution` method](#layer-getattribution) automatically. Extends Control.
5559 */
5560
5561 var Attribution = Control.extend({
5562 // @section
5563 // @aka Control.Attribution options
5564 options: {
5565 position: 'bottomright',
5566
5567 // @option prefix: String = 'Leaflet'
5568 // The HTML text shown before the attributions. Pass `false` to disable.
5569 prefix: '<a href="https://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'
5570 },
5571
5572 initialize: function (options) {
5573 setOptions(this, options);
5574
5575 this._attributions = {};
5576 },
5577
5578 onAdd: function (map) {
5579 map.attributionControl = this;
5580 this._container = create$1('div', 'leaflet-control-attribution');
5581 disableClickPropagation(this._container);
5582
5583 // TODO ugly, refactor
5584 for (var i in map._layers) {
5585 if (map._layers[i].getAttribution) {
5586 this.addAttribution(map._layers[i].getAttribution());
5587 }
5588 }
5589
5590 this._update();
5591
5592 return this._container;
5593 },
5594
5595 // @method setPrefix(prefix: String): this
5596 // Sets the text before the attributions.
5597 setPrefix: function (prefix) {
5598 this.options.prefix = prefix;
5599 this._update();
5600 return this;
5601 },
5602
5603 // @method addAttribution(text: String): this
5604 // Adds an attribution text (e.g. `'Vector data &copy; Mapbox'`).
5605 addAttribution: function (text) {
5606 if (!text) { return this; }
5607
5608 if (!this._attributions[text]) {
5609 this._attributions[text] = 0;
5610 }
5611 this._attributions[text]++;
5612
5613 this._update();
5614
5615 return this;
5616 },
5617
5618 // @method removeAttribution(text: String): this
5619 // Removes an attribution text.
5620 removeAttribution: function (text) {
5621 if (!text) { return this; }
5622
5623 if (this._attributions[text]) {
5624 this._attributions[text]--;
5625 this._update();
5626 }
5627
5628 return this;
5629 },
5630
5631 _update: function () {
5632 if (!this._map) { return; }
5633
5634 var attribs = [];
5635
5636 for (var i in this._attributions) {
5637 if (this._attributions[i]) {
5638 attribs.push(i);
5639 }
5640 }
5641
5642 var prefixAndAttribs = [];
5643
5644 if (this.options.prefix) {
5645 prefixAndAttribs.push(this.options.prefix);
5646 }
5647 if (attribs.length) {
5648 prefixAndAttribs.push(attribs.join(', '));
5649 }
5650
5651 this._container.innerHTML = prefixAndAttribs.join(' | ');
5652 }
5653 });
5654
5655 // @namespace Map
5656 // @section Control options
5657 // @option attributionControl: Boolean = true
5658 // Whether a [attribution control](#control-attribution) is added to the map by default.
5659 Map.mergeOptions({
5660 attributionControl: true
5661 });
5662
5663 Map.addInitHook(function () {
5664 if (this.options.attributionControl) {
5665 new Attribution().addTo(this);
5666 }
5667 });
5668
5669 // @namespace Control.Attribution
5670 // @factory L.control.attribution(options: Control.Attribution options)
5671 // Creates an attribution control.
5672 var attribution = function (options) {
5673 return new Attribution(options);
5674 };
5675
5676 Control.Layers = Layers;
5677 Control.Zoom = Zoom;
5678 Control.Scale = Scale;
5679 Control.Attribution = Attribution;
5680
5681 control.layers = layers;
5682 control.zoom = zoom;
5683 control.scale = scale;
5684 control.attribution = attribution;
5685
5686 /*
5687 L.Handler is a base class for handler classes that are used internally to inject
5688 interaction features like dragging to classes like Map and Marker.
5689 */
5690
5691 // @class Handler
5692 // @aka L.Handler
5693 // Abstract class for map interaction handlers
5694
5695 var Handler = Class.extend({
5696 initialize: function (map) {
5697 this._map = map;
5698 },
5699
5700 // @method enable(): this
5701 // Enables the handler
5702 enable: function () {
5703 if (this._enabled) { return this; }
5704
5705 this._enabled = true;
5706 this.addHooks();
5707 return this;
5708 },
5709
5710 // @method disable(): this
5711 // Disables the handler
5712 disable: function () {
5713 if (!this._enabled) { return this; }
5714
5715 this._enabled = false;
5716 this.removeHooks();
5717 return this;
5718 },
5719
5720 // @method enabled(): Boolean
5721 // Returns `true` if the handler is enabled
5722 enabled: function () {
5723 return !!this._enabled;
5724 }
5725
5726 // @section Extension methods
5727 // Classes inheriting from `Handler` must implement the two following methods:
5728 // @method addHooks()
5729 // Called when the handler is enabled, should add event hooks.
5730 // @method removeHooks()
5731 // Called when the handler is disabled, should remove the event hooks added previously.
5732 });
5733
5734 // @section There is static function which can be called without instantiating L.Handler:
5735 // @function addTo(map: Map, name: String): this
5736 // Adds a new Handler to the given map with the given name.
5737 Handler.addTo = function (map, name) {
5738 map.addHandler(name, this);
5739 return this;
5740 };
5741
5742 var Mixin = {Events: Events};
5743
5744 /*
5745 * @class Draggable
5746 * @aka L.Draggable
5747 * @inherits Evented
5748 *
5749 * A class for making DOM elements draggable (including touch support).
5750 * Used internally for map and marker dragging. Only works for elements
5751 * that were positioned with [`L.DomUtil.setPosition`](#domutil-setposition).
5752 *
5753 * @example
5754 * ```js
5755 * var draggable = new L.Draggable(elementToDrag);
5756 * draggable.enable();
5757 * ```
5758 */
5759
5760 var START = touch ? 'touchstart mousedown' : 'mousedown';
5761 var END = {
5762 mousedown: 'mouseup',
5763 touchstart: 'touchend',
5764 pointerdown: 'touchend',
5765 MSPointerDown: 'touchend'
5766 };
5767 var MOVE = {
5768 mousedown: 'mousemove',
5769 touchstart: 'touchmove',
5770 pointerdown: 'touchmove',
5771 MSPointerDown: 'touchmove'
5772 };
5773
5774
5775 var Draggable = Evented.extend({
5776
5777 options: {
5778 // @section
5779 // @aka Draggable options
5780 // @option clickTolerance: Number = 3
5781 // The max number of pixels a user can shift the mouse pointer during a click
5782 // for it to be considered a valid click (as opposed to a mouse drag).
5783 clickTolerance: 3
5784 },
5785
5786 // @constructor L.Draggable(el: HTMLElement, dragHandle?: HTMLElement, preventOutline?: Boolean, options?: Draggable options)
5787 // Creates a `Draggable` object for moving `el` when you start dragging the `dragHandle` element (equals `el` itself by default).
5788 initialize: function (element, dragStartTarget, preventOutline$$1, options) {
5789 setOptions(this, options);
5790
5791 this._element = element;
5792 this._dragStartTarget = dragStartTarget || element;
5793 this._preventOutline = preventOutline$$1;
5794 },
5795
5796 // @method enable()
5797 // Enables the dragging ability
5798 enable: function () {
5799 if (this._enabled) { return; }
5800
5801 on(this._dragStartTarget, START, this._onDown, this);
5802
5803 this._enabled = true;
5804 },
5805
5806 // @method disable()
5807 // Disables the dragging ability
5808 disable: function () {
5809 if (!this._enabled) { return; }
5810
5811 // If we're currently dragging this draggable,
5812 // disabling it counts as first ending the drag.
5813 if (Draggable._dragging === this) {
5814 this.finishDrag();
5815 }
5816
5817 off(this._dragStartTarget, START, this._onDown, this);
5818
5819 this._enabled = false;
5820 this._moved = false;
5821 },
5822
5823 _onDown: function (e) {
5824 // Ignore simulated events, since we handle both touch and
5825 // mouse explicitly; otherwise we risk getting duplicates of
5826 // touch events, see #4315.
5827 // Also ignore the event if disabled; this happens in IE11
5828 // under some circumstances, see #3666.
5829 if (e._simulated || !this._enabled) { return; }
5830
5831 this._moved = false;
5832
5833 if (hasClass(this._element, 'leaflet-zoom-anim')) { return; }
5834
5835 if (Draggable._dragging || e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; }
5836 Draggable._dragging = this; // Prevent dragging multiple objects at once.
5837
5838 if (this._preventOutline) {
5839 preventOutline(this._element);
5840 }
5841
5842 disableImageDrag();
5843 disableTextSelection();
5844
5845 if (this._moving) { return; }
5846
5847 // @event down: Event
5848 // Fired when a drag is about to start.
5849 this.fire('down');
5850
5851 var first = e.touches ? e.touches[0] : e,
5852 sizedParent = getSizedParentNode(this._element);
5853
5854 this._startPoint = new Point(first.clientX, first.clientY);
5855
5856 // Cache the scale, so that we can continuously compensate for it during drag (_onMove).
5857 this._parentScale = getScale(sizedParent);
5858
5859 on(document, MOVE[e.type], this._onMove, this);
5860 on(document, END[e.type], this._onUp, this);
5861 },
5862
5863 _onMove: function (e) {
5864 // Ignore simulated events, since we handle both touch and
5865 // mouse explicitly; otherwise we risk getting duplicates of
5866 // touch events, see #4315.
5867 // Also ignore the event if disabled; this happens in IE11
5868 // under some circumstances, see #3666.
5869 if (e._simulated || !this._enabled) { return; }
5870
5871 if (e.touches && e.touches.length > 1) {
5872 this._moved = true;
5873 return;
5874 }
5875
5876 var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e),
5877 offset = new Point(first.clientX, first.clientY)._subtract(this._startPoint);
5878
5879 if (!offset.x && !offset.y) { return; }
5880 if (Math.abs(offset.x) + Math.abs(offset.y) < this.options.clickTolerance) { return; }
5881
5882 // We assume that the parent container's position, border and scale do not change for the duration of the drag.
5883 // Therefore there is no need to account for the position and border (they are eliminated by the subtraction)
5884 // and we can use the cached value for the scale.
5885 offset.x /= this._parentScale.x;
5886 offset.y /= this._parentScale.y;
5887
5888 preventDefault(e);
5889
5890 if (!this._moved) {
5891 // @event dragstart: Event
5892 // Fired when a drag starts
5893 this.fire('dragstart');
5894
5895 this._moved = true;
5896 this._startPos = getPosition(this._element).subtract(offset);
5897
5898 addClass(document.body, 'leaflet-dragging');
5899
5900 this._lastTarget = e.target || e.srcElement;
5901 // IE and Edge do not give the <use> element, so fetch it
5902 // if necessary
5903 if (window.SVGElementInstance && this._lastTarget instanceof window.SVGElementInstance) {
5904 this._lastTarget = this._lastTarget.correspondingUseElement;
5905 }
5906 addClass(this._lastTarget, 'leaflet-drag-target');
5907 }
5908
5909 this._newPos = this._startPos.add(offset);
5910 this._moving = true;
5911
5912 cancelAnimFrame(this._animRequest);
5913 this._lastEvent = e;
5914 this._animRequest = requestAnimFrame(this._updatePosition, this, true);
5915 },
5916
5917 _updatePosition: function () {
5918 var e = {originalEvent: this._lastEvent};
5919
5920 // @event predrag: Event
5921 // Fired continuously during dragging *before* each corresponding
5922 // update of the element's position.
5923 this.fire('predrag', e);
5924 setPosition(this._element, this._newPos);
5925
5926 // @event drag: Event
5927 // Fired continuously during dragging.
5928 this.fire('drag', e);
5929 },
5930
5931 _onUp: function (e) {
5932 // Ignore simulated events, since we handle both touch and
5933 // mouse explicitly; otherwise we risk getting duplicates of
5934 // touch events, see #4315.
5935 // Also ignore the event if disabled; this happens in IE11
5936 // under some circumstances, see #3666.
5937 if (e._simulated || !this._enabled) { return; }
5938 this.finishDrag();
5939 },
5940
5941 finishDrag: function () {
5942 removeClass(document.body, 'leaflet-dragging');
5943
5944 if (this._lastTarget) {
5945 removeClass(this._lastTarget, 'leaflet-drag-target');
5946 this._lastTarget = null;
5947 }
5948
5949 for (var i in MOVE) {
5950 off(document, MOVE[i], this._onMove, this);
5951 off(document, END[i], this._onUp, this);
5952 }
5953
5954 enableImageDrag();
5955 enableTextSelection();
5956
5957 if (this._moved && this._moving) {
5958 // ensure drag is not fired after dragend
5959 cancelAnimFrame(this._animRequest);
5960
5961 // @event dragend: DragEndEvent
5962 // Fired when the drag ends.
5963 this.fire('dragend', {
5964 distance: this._newPos.distanceTo(this._startPos)
5965 });
5966 }
5967
5968 this._moving = false;
5969 Draggable._dragging = false;
5970 }
5971
5972 });
5973
5974 /*
5975 * @namespace LineUtil
5976 *
5977 * Various utility functions for polyline points processing, used by Leaflet internally to make polylines lightning-fast.
5978 */
5979
5980 // Simplify polyline with vertex reduction and Douglas-Peucker simplification.
5981 // Improves rendering performance dramatically by lessening the number of points to draw.
5982
5983 // @function simplify(points: Point[], tolerance: Number): Point[]
5984 // Dramatically reduces the number of points in a polyline while retaining
5985 // its shape and returns a new array of simplified points, using the
5986 // [Douglas-Peucker algorithm](http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm).
5987 // Used for a huge performance boost when processing/displaying Leaflet polylines for
5988 // each zoom level and also reducing visual noise. tolerance affects the amount of
5989 // simplification (lesser value means higher quality but slower and with more points).
5990 // Also released as a separated micro-library [Simplify.js](http://mourner.github.com/simplify-js/).
5991 function simplify(points, tolerance) {
5992 if (!tolerance || !points.length) {
5993 return points.slice();
5994 }
5995
5996 var sqTolerance = tolerance * tolerance;
5997
5998 // stage 1: vertex reduction
5999 points = _reducePoints(points, sqTolerance);
6000
6001 // stage 2: Douglas-Peucker simplification
6002 points = _simplifyDP(points, sqTolerance);
6003
6004 return points;
6005 }
6006
6007 // @function pointToSegmentDistance(p: Point, p1: Point, p2: Point): Number
6008 // Returns the distance between point `p` and segment `p1` to `p2`.
6009 function pointToSegmentDistance(p, p1, p2) {
6010 return Math.sqrt(_sqClosestPointOnSegment(p, p1, p2, true));
6011 }
6012
6013 // @function closestPointOnSegment(p: Point, p1: Point, p2: Point): Number
6014 // Returns the closest point from a point `p` on a segment `p1` to `p2`.
6015 function closestPointOnSegment(p, p1, p2) {
6016 return _sqClosestPointOnSegment(p, p1, p2);
6017 }
6018
6019 // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm
6020 function _simplifyDP(points, sqTolerance) {
6021
6022 var len = points.length,
6023 ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array,
6024 markers = new ArrayConstructor(len);
6025
6026 markers[0] = markers[len - 1] = 1;
6027
6028 _simplifyDPStep(points, markers, sqTolerance, 0, len - 1);
6029
6030 var i,
6031 newPoints = [];
6032
6033 for (i = 0; i < len; i++) {
6034 if (markers[i]) {
6035 newPoints.push(points[i]);
6036 }
6037 }
6038
6039 return newPoints;
6040 }
6041
6042 function _simplifyDPStep(points, markers, sqTolerance, first, last) {
6043
6044 var maxSqDist = 0,
6045 index, i, sqDist;
6046
6047 for (i = first + 1; i <= last - 1; i++) {
6048 sqDist = _sqClosestPointOnSegment(points[i], points[first], points[last], true);
6049
6050 if (sqDist > maxSqDist) {
6051 index = i;
6052 maxSqDist = sqDist;
6053 }
6054 }
6055
6056 if (maxSqDist > sqTolerance) {
6057 markers[index] = 1;
6058
6059 _simplifyDPStep(points, markers, sqTolerance, first, index);
6060 _simplifyDPStep(points, markers, sqTolerance, index, last);
6061 }
6062 }
6063
6064 // reduce points that are too close to each other to a single point
6065 function _reducePoints(points, sqTolerance) {
6066 var reducedPoints = [points[0]];
6067
6068 for (var i = 1, prev = 0, len = points.length; i < len; i++) {
6069 if (_sqDist(points[i], points[prev]) > sqTolerance) {
6070 reducedPoints.push(points[i]);
6071 prev = i;
6072 }
6073 }
6074 if (prev < len - 1) {
6075 reducedPoints.push(points[len - 1]);
6076 }
6077 return reducedPoints;
6078 }
6079
6080 var _lastCode;
6081
6082 // @function clipSegment(a: Point, b: Point, bounds: Bounds, useLastCode?: Boolean, round?: Boolean): Point[]|Boolean
6083 // Clips the segment a to b by rectangular bounds with the
6084 // [Cohen-Sutherland algorithm](https://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm)
6085 // (modifying the segment points directly!). Used by Leaflet to only show polyline
6086 // points that are on the screen or near, increasing performance.
6087 function clipSegment(a, b, bounds, useLastCode, round) {
6088 var codeA = useLastCode ? _lastCode : _getBitCode(a, bounds),
6089 codeB = _getBitCode(b, bounds),
6090
6091 codeOut, p, newCode;
6092
6093 // save 2nd code to avoid calculating it on the next segment
6094 _lastCode = codeB;
6095
6096 while (true) {
6097 // if a,b is inside the clip window (trivial accept)
6098 if (!(codeA | codeB)) {
6099 return [a, b];
6100 }
6101
6102 // if a,b is outside the clip window (trivial reject)
6103 if (codeA & codeB) {
6104 return false;
6105 }
6106
6107 // other cases
6108 codeOut = codeA || codeB;
6109 p = _getEdgeIntersection(a, b, codeOut, bounds, round);
6110 newCode = _getBitCode(p, bounds);
6111
6112 if (codeOut === codeA) {
6113 a = p;
6114 codeA = newCode;
6115 } else {
6116 b = p;
6117 codeB = newCode;
6118 }
6119 }
6120 }
6121
6122 function _getEdgeIntersection(a, b, code, bounds, round) {
6123 var dx = b.x - a.x,
6124 dy = b.y - a.y,
6125 min = bounds.min,
6126 max = bounds.max,
6127 x, y;
6128
6129 if (code & 8) { // top
6130 x = a.x + dx * (max.y - a.y) / dy;
6131 y = max.y;
6132
6133 } else if (code & 4) { // bottom
6134 x = a.x + dx * (min.y - a.y) / dy;
6135 y = min.y;
6136
6137 } else if (code & 2) { // right
6138 x = max.x;
6139 y = a.y + dy * (max.x - a.x) / dx;
6140
6141 } else if (code & 1) { // left
6142 x = min.x;
6143 y = a.y + dy * (min.x - a.x) / dx;
6144 }
6145
6146 return new Point(x, y, round);
6147 }
6148
6149 function _getBitCode(p, bounds) {
6150 var code = 0;
6151
6152 if (p.x < bounds.min.x) { // left
6153 code |= 1;
6154 } else if (p.x > bounds.max.x) { // right
6155 code |= 2;
6156 }
6157
6158 if (p.y < bounds.min.y) { // bottom
6159 code |= 4;
6160 } else if (p.y > bounds.max.y) { // top
6161 code |= 8;
6162 }
6163
6164 return code;
6165 }
6166
6167 // square distance (to avoid unnecessary Math.sqrt calls)
6168 function _sqDist(p1, p2) {
6169 var dx = p2.x - p1.x,
6170 dy = p2.y - p1.y;
6171 return dx * dx + dy * dy;
6172 }
6173
6174 // return closest point on segment or distance to that point
6175 function _sqClosestPointOnSegment(p, p1, p2, sqDist) {
6176 var x = p1.x,
6177 y = p1.y,
6178 dx = p2.x - x,
6179 dy = p2.y - y,
6180 dot = dx * dx + dy * dy,
6181 t;
6182
6183 if (dot > 0) {
6184 t = ((p.x - x) * dx + (p.y - y) * dy) / dot;
6185
6186 if (t > 1) {
6187 x = p2.x;
6188 y = p2.y;
6189 } else if (t > 0) {
6190 x += dx * t;
6191 y += dy * t;
6192 }
6193 }
6194
6195 dx = p.x - x;
6196 dy = p.y - y;
6197
6198 return sqDist ? dx * dx + dy * dy : new Point(x, y);
6199 }
6200
6201
6202 // @function isFlat(latlngs: LatLng[]): Boolean
6203 // Returns true if `latlngs` is a flat array, false is nested.
6204 function isFlat(latlngs) {
6205 return !isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined');
6206 }
6207
6208 function _flat(latlngs) {
6209 console.warn('Deprecated use of _flat, please use L.LineUtil.isFlat instead.');
6210 return isFlat(latlngs);
6211 }
6212
6213 var LineUtil = ({
6214 simplify: simplify,
6215 pointToSegmentDistance: pointToSegmentDistance,
6216 closestPointOnSegment: closestPointOnSegment,
6217 clipSegment: clipSegment,
6218 _getEdgeIntersection: _getEdgeIntersection,
6219 _getBitCode: _getBitCode,
6220 _sqClosestPointOnSegment: _sqClosestPointOnSegment,
6221 isFlat: isFlat,
6222 _flat: _flat
6223 });
6224
6225 /*
6226 * @namespace PolyUtil
6227 * Various utility functions for polygon geometries.
6228 */
6229
6230 /* @function clipPolygon(points: Point[], bounds: Bounds, round?: Boolean): Point[]
6231 * Clips the polygon geometry defined by the given `points` by the given bounds (using the [Sutherland-Hodgman algorithm](https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm)).
6232 * Used by Leaflet to only show polygon points that are on the screen or near, increasing
6233 * performance. Note that polygon points needs different algorithm for clipping
6234 * than polyline, so there's a separate method for it.
6235 */
6236 function clipPolygon(points, bounds, round) {
6237 var clippedPoints,
6238 edges = [1, 4, 2, 8],
6239 i, j, k,
6240 a, b,
6241 len, edge, p;
6242
6243 for (i = 0, len = points.length; i < len; i++) {
6244 points[i]._code = _getBitCode(points[i], bounds);
6245 }
6246
6247 // for each edge (left, bottom, right, top)
6248 for (k = 0; k < 4; k++) {
6249 edge = edges[k];
6250 clippedPoints = [];
6251
6252 for (i = 0, len = points.length, j = len - 1; i < len; j = i++) {
6253 a = points[i];
6254 b = points[j];
6255
6256 // if a is inside the clip window
6257 if (!(a._code & edge)) {
6258 // if b is outside the clip window (a->b goes out of screen)
6259 if (b._code & edge) {
6260 p = _getEdgeIntersection(b, a, edge, bounds, round);
6261 p._code = _getBitCode(p, bounds);
6262 clippedPoints.push(p);
6263 }
6264 clippedPoints.push(a);
6265
6266 // else if b is inside the clip window (a->b enters the screen)
6267 } else if (!(b._code & edge)) {
6268 p = _getEdgeIntersection(b, a, edge, bounds, round);
6269 p._code = _getBitCode(p, bounds);
6270 clippedPoints.push(p);
6271 }
6272 }
6273 points = clippedPoints;
6274 }
6275
6276 return points;
6277 }
6278
6279 var PolyUtil = ({
6280 clipPolygon: clipPolygon
6281 });
6282
6283 /*
6284 * @namespace Projection
6285 * @section
6286 * Leaflet comes with a set of already defined Projections out of the box:
6287 *
6288 * @projection L.Projection.LonLat
6289 *
6290 * Equirectangular, or Plate Carree projection — the most simple projection,
6291 * mostly used by GIS enthusiasts. Directly maps `x` as longitude, and `y` as
6292 * latitude. Also suitable for flat worlds, e.g. game maps. Used by the
6293 * `EPSG:4326` and `Simple` CRS.
6294 */
6295
6296 var LonLat = {
6297 project: function (latlng) {
6298 return new Point(latlng.lng, latlng.lat);
6299 },
6300
6301 unproject: function (point) {
6302 return new LatLng(point.y, point.x);
6303 },
6304
6305 bounds: new Bounds([-180, -90], [180, 90])
6306 };
6307
6308 /*
6309 * @namespace Projection
6310 * @projection L.Projection.Mercator
6311 *
6312 * Elliptical Mercator projection — more complex than Spherical Mercator. Assumes that Earth is an ellipsoid. Used by the EPSG:3395 CRS.
6313 */
6314
6315 var Mercator = {
6316 R: 6378137,
6317 R_MINOR: 6356752.314245179,
6318
6319 bounds: new Bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]),
6320
6321 project: function (latlng) {
6322 var d = Math.PI / 180,
6323 r = this.R,
6324 y = latlng.lat * d,
6325 tmp = this.R_MINOR / r,
6326 e = Math.sqrt(1 - tmp * tmp),
6327 con = e * Math.sin(y);
6328
6329 var ts = Math.tan(Math.PI / 4 - y / 2) / Math.pow((1 - con) / (1 + con), e / 2);
6330 y = -r * Math.log(Math.max(ts, 1E-10));
6331
6332 return new Point(latlng.lng * d * r, y);
6333 },
6334
6335 unproject: function (point) {
6336 var d = 180 / Math.PI,
6337 r = this.R,
6338 tmp = this.R_MINOR / r,
6339 e = Math.sqrt(1 - tmp * tmp),
6340 ts = Math.exp(-point.y / r),
6341 phi = Math.PI / 2 - 2 * Math.atan(ts);
6342
6343 for (var i = 0, dphi = 0.1, con; i < 15 && Math.abs(dphi) > 1e-7; i++) {
6344 con = e * Math.sin(phi);
6345 con = Math.pow((1 - con) / (1 + con), e / 2);
6346 dphi = Math.PI / 2 - 2 * Math.atan(ts * con) - phi;
6347 phi += dphi;
6348 }
6349
6350 return new LatLng(phi * d, point.x * d / r);
6351 }
6352 };
6353
6354 /*
6355 * @class Projection
6356
6357 * An object with methods for projecting geographical coordinates of the world onto
6358 * a flat surface (and back). See [Map projection](http://en.wikipedia.org/wiki/Map_projection).
6359
6360 * @property bounds: Bounds
6361 * The bounds (specified in CRS units) where the projection is valid
6362
6363 * @method project(latlng: LatLng): Point
6364 * Projects geographical coordinates into a 2D point.
6365 * Only accepts actual `L.LatLng` instances, not arrays.
6366
6367 * @method unproject(point: Point): LatLng
6368 * The inverse of `project`. Projects a 2D point into a geographical location.
6369 * Only accepts actual `L.Point` instances, not arrays.
6370
6371 * Note that the projection instances do not inherit from Leaflet's `Class` object,
6372 * and can't be instantiated. Also, new classes can't inherit from them,
6373 * and methods can't be added to them with the `include` function.
6374
6375 */
6376
6377 var index = ({
6378 LonLat: LonLat,
6379 Mercator: Mercator,
6380 SphericalMercator: SphericalMercator
6381 });
6382
6383 /*
6384 * @namespace CRS
6385 * @crs L.CRS.EPSG3395
6386 *
6387 * Rarely used by some commercial tile providers. Uses Elliptical Mercator projection.
6388 */
6389 var EPSG3395 = extend({}, Earth, {
6390 code: 'EPSG:3395',
6391 projection: Mercator,
6392
6393 transformation: (function () {
6394 var scale = 0.5 / (Math.PI * Mercator.R);
6395 return toTransformation(scale, 0.5, -scale, 0.5);
6396 }())
6397 });
6398
6399 /*
6400 * @namespace CRS
6401 * @crs L.CRS.EPSG4326
6402 *
6403 * A common CRS among GIS enthusiasts. Uses simple Equirectangular projection.
6404 *
6405 * Leaflet 1.0.x complies with the [TMS coordinate scheme for EPSG:4326](https://wiki.osgeo.org/wiki/Tile_Map_Service_Specification#global-geodetic),
6406 * which is a breaking change from 0.7.x behaviour. If you are using a `TileLayer`
6407 * with this CRS, ensure that there are two 256x256 pixel tiles covering the
6408 * whole earth at zoom level zero, and that the tile coordinate origin is (-180,+90),
6409 * or (-180,-90) for `TileLayer`s with [the `tms` option](#tilelayer-tms) set.
6410 */
6411
6412 var EPSG4326 = extend({}, Earth, {
6413 code: 'EPSG:4326',
6414 projection: LonLat,
6415 transformation: toTransformation(1 / 180, 1, -1 / 180, 0.5)
6416 });
6417
6418 /*
6419 * @namespace CRS
6420 * @crs L.CRS.Simple
6421 *
6422 * A simple CRS that maps longitude and latitude into `x` and `y` directly.
6423 * May be used for maps of flat surfaces (e.g. game maps). Note that the `y`
6424 * axis should still be inverted (going from bottom to top). `distance()` returns
6425 * simple euclidean distance.
6426 */
6427
6428 var Simple = extend({}, CRS, {
6429 projection: LonLat,
6430 transformation: toTransformation(1, 0, -1, 0),
6431
6432 scale: function (zoom) {
6433 return Math.pow(2, zoom);
6434 },
6435
6436 zoom: function (scale) {
6437 return Math.log(scale) / Math.LN2;
6438 },
6439
6440 distance: function (latlng1, latlng2) {
6441 var dx = latlng2.lng - latlng1.lng,
6442 dy = latlng2.lat - latlng1.lat;
6443
6444 return Math.sqrt(dx * dx + dy * dy);
6445 },
6446
6447 infinite: true
6448 });
6449
6450 CRS.Earth = Earth;
6451 CRS.EPSG3395 = EPSG3395;
6452 CRS.EPSG3857 = EPSG3857;
6453 CRS.EPSG900913 = EPSG900913;
6454 CRS.EPSG4326 = EPSG4326;
6455 CRS.Simple = Simple;
6456
6457 /*
6458 * @class Layer
6459 * @inherits Evented
6460 * @aka L.Layer
6461 * @aka ILayer
6462 *
6463 * A set of methods from the Layer base class that all Leaflet layers use.
6464 * Inherits all methods, options and events from `L.Evented`.
6465 *
6466 * @example
6467 *
6468 * ```js
6469 * var layer = L.marker(latlng).addTo(map);
6470 * layer.addTo(map);
6471 * layer.remove();
6472 * ```
6473 *
6474 * @event add: Event
6475 * Fired after the layer is added to a map
6476 *
6477 * @event remove: Event
6478 * Fired after the layer is removed from a map
6479 */
6480
6481
6482 var Layer = Evented.extend({
6483
6484 // Classes extending `L.Layer` will inherit the following options:
6485 options: {
6486 // @option pane: String = 'overlayPane'
6487 // By default the layer will be added to the map's [overlay pane](#map-overlaypane). Overriding this option will cause the layer to be placed on another pane by default.
6488 pane: 'overlayPane',
6489
6490 // @option attribution: String = null
6491 // String to be shown in the attribution control, e.g. "© OpenStreetMap contributors". It describes the layer data and is often a legal obligation towards copyright holders and tile providers.
6492 attribution: null,
6493
6494 bubblingMouseEvents: true
6495 },
6496
6497 /* @section
6498 * Classes extending `L.Layer` will inherit the following methods:
6499 *
6500 * @method addTo(map: Map|LayerGroup): this
6501 * Adds the layer to the given map or layer group.
6502 */
6503 addTo: function (map) {
6504 map.addLayer(this);
6505 return this;
6506 },
6507
6508 // @method remove: this
6509 // Removes the layer from the map it is currently active on.
6510 remove: function () {
6511 return this.removeFrom(this._map || this._mapToAdd);
6512 },
6513
6514 // @method removeFrom(map: Map): this
6515 // Removes the layer from the given map
6516 //
6517 // @alternative
6518 // @method removeFrom(group: LayerGroup): this
6519 // Removes the layer from the given `LayerGroup`
6520 removeFrom: function (obj) {
6521 if (obj) {
6522 obj.removeLayer(this);
6523 }
6524 return this;
6525 },
6526
6527 // @method getPane(name? : String): HTMLElement
6528 // Returns the `HTMLElement` representing the named pane on the map. If `name` is omitted, returns the pane for this layer.
6529 getPane: function (name) {
6530 return this._map.getPane(name ? (this.options[name] || name) : this.options.pane);
6531 },
6532
6533 addInteractiveTarget: function (targetEl) {
6534 this._map._targets[stamp(targetEl)] = this;
6535 return this;
6536 },
6537
6538 removeInteractiveTarget: function (targetEl) {
6539 delete this._map._targets[stamp(targetEl)];
6540 return this;
6541 },
6542
6543 // @method getAttribution: String
6544 // Used by the `attribution control`, returns the [attribution option](#gridlayer-attribution).
6545 getAttribution: function () {
6546 return this.options.attribution;
6547 },
6548
6549 _layerAdd: function (e) {
6550 var map = e.target;
6551
6552 // check in case layer gets added and then removed before the map is ready
6553 if (!map.hasLayer(this)) { return; }
6554
6555 this._map = map;
6556 this._zoomAnimated = map._zoomAnimated;
6557
6558 if (this.getEvents) {
6559 var events = this.getEvents();
6560 map.on(events, this);
6561 this.once('remove', function () {
6562 map.off(events, this);
6563 }, this);
6564 }
6565
6566 this.onAdd(map);
6567
6568 if (this.getAttribution && map.attributionControl) {
6569 map.attributionControl.addAttribution(this.getAttribution());
6570 }
6571
6572 this.fire('add');
6573 map.fire('layeradd', {layer: this});
6574 }
6575 });
6576
6577 /* @section Extension methods
6578 * @uninheritable
6579 *
6580 * Every layer should extend from `L.Layer` and (re-)implement the following methods.
6581 *
6582 * @method onAdd(map: Map): this
6583 * Should contain code that creates DOM elements for the layer, adds them to `map panes` where they should belong and puts listeners on relevant map events. Called on [`map.addLayer(layer)`](#map-addlayer).
6584 *
6585 * @method onRemove(map: Map): this
6586 * Should contain all clean up code that removes the layer's elements from the DOM and removes listeners previously added in [`onAdd`](#layer-onadd). Called on [`map.removeLayer(layer)`](#map-removelayer).
6587 *
6588 * @method getEvents(): Object
6589 * This optional method should return an object like `{ viewreset: this._reset }` for [`addEventListener`](#evented-addeventlistener). The event handlers in this object will be automatically added and removed from the map with your layer.
6590 *
6591 * @method getAttribution(): String
6592 * This optional method should return a string containing HTML to be shown on the `Attribution control` whenever the layer is visible.
6593 *
6594 * @method beforeAdd(map: Map): this
6595 * Optional method. Called on [`map.addLayer(layer)`](#map-addlayer), before the layer is added to the map, before events are initialized, without waiting until the map is in a usable state. Use for early initialization only.
6596 */
6597
6598
6599 /* @namespace Map
6600 * @section Layer events
6601 *
6602 * @event layeradd: LayerEvent
6603 * Fired when a new layer is added to the map.
6604 *
6605 * @event layerremove: LayerEvent
6606 * Fired when some layer is removed from the map
6607 *
6608 * @section Methods for Layers and Controls
6609 */
6610 Map.include({
6611 // @method addLayer(layer: Layer): this
6612 // Adds the given layer to the map
6613 addLayer: function (layer) {
6614 if (!layer._layerAdd) {
6615 throw new Error('The provided object is not a Layer.');
6616 }
6617
6618 var id = stamp(layer);
6619 if (this._layers[id]) { return this; }
6620 this._layers[id] = layer;
6621
6622 layer._mapToAdd = this;
6623
6624 if (layer.beforeAdd) {
6625 layer.beforeAdd(this);
6626 }
6627
6628 this.whenReady(layer._layerAdd, layer);
6629
6630 return this;
6631 },
6632
6633 // @method removeLayer(layer: Layer): this
6634 // Removes the given layer from the map.
6635 removeLayer: function (layer) {
6636 var id = stamp(layer);
6637
6638 if (!this._layers[id]) { return this; }
6639
6640 if (this._loaded) {
6641 layer.onRemove(this);
6642 }
6643
6644 if (layer.getAttribution && this.attributionControl) {
6645 this.attributionControl.removeAttribution(layer.getAttribution());
6646 }
6647
6648 delete this._layers[id];
6649
6650 if (this._loaded) {
6651 this.fire('layerremove', {layer: layer});
6652 layer.fire('remove');
6653 }
6654
6655 layer._map = layer._mapToAdd = null;
6656
6657 return this;
6658 },
6659
6660 // @method hasLayer(layer: Layer): Boolean
6661 // Returns `true` if the given layer is currently added to the map
6662 hasLayer: function (layer) {
6663 return !!layer && (stamp(layer) in this._layers);
6664 },
6665
6666 /* @method eachLayer(fn: Function, context?: Object): this
6667 * Iterates over the layers of the map, optionally specifying context of the iterator function.
6668 * ```
6669 * map.eachLayer(function(layer){
6670 * layer.bindPopup('Hello');
6671 * });
6672 * ```
6673 */
6674 eachLayer: function (method, context) {
6675 for (var i in this._layers) {
6676 method.call(context, this._layers[i]);
6677 }
6678 return this;
6679 },
6680
6681 _addLayers: function (layers) {
6682 layers = layers ? (isArray(layers) ? layers : [layers]) : [];
6683
6684 for (var i = 0, len = layers.length; i < len; i++) {
6685 this.addLayer(layers[i]);
6686 }
6687 },
6688
6689 _addZoomLimit: function (layer) {
6690 if (isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom)) {
6691 this._zoomBoundLayers[stamp(layer)] = layer;
6692 this._updateZoomLevels();
6693 }
6694 },
6695
6696 _removeZoomLimit: function (layer) {
6697 var id = stamp(layer);
6698
6699 if (this._zoomBoundLayers[id]) {
6700 delete this._zoomBoundLayers[id];
6701 this._updateZoomLevels();
6702 }
6703 },
6704
6705 _updateZoomLevels: function () {
6706 var minZoom = Infinity,
6707 maxZoom = -Infinity,
6708 oldZoomSpan = this._getZoomSpan();
6709
6710 for (var i in this._zoomBoundLayers) {
6711 var options = this._zoomBoundLayers[i].options;
6712
6713 minZoom = options.minZoom === undefined ? minZoom : Math.min(minZoom, options.minZoom);
6714 maxZoom = options.maxZoom === undefined ? maxZoom : Math.max(maxZoom, options.maxZoom);
6715 }
6716
6717 this._layersMaxZoom = maxZoom === -Infinity ? undefined : maxZoom;
6718 this._layersMinZoom = minZoom === Infinity ? undefined : minZoom;
6719
6720 // @section Map state change events
6721 // @event zoomlevelschange: Event
6722 // Fired when the number of zoomlevels on the map is changed due
6723 // to adding or removing a layer.
6724 if (oldZoomSpan !== this._getZoomSpan()) {
6725 this.fire('zoomlevelschange');
6726 }
6727
6728 if (this.options.maxZoom === undefined && this._layersMaxZoom && this.getZoom() > this._layersMaxZoom) {
6729 this.setZoom(this._layersMaxZoom);
6730 }
6731 if (this.options.minZoom === undefined && this._layersMinZoom && this.getZoom() < this._layersMinZoom) {
6732 this.setZoom(this._layersMinZoom);
6733 }
6734 }
6735 });
6736
6737 /*
6738 * @class LayerGroup
6739 * @aka L.LayerGroup
6740 * @inherits Layer
6741 *
6742 * Used to group several layers and handle them as one. If you add it to the map,
6743 * any layers added or removed from the group will be added/removed on the map as
6744 * well. Extends `Layer`.
6745 *
6746 * @example
6747 *
6748 * ```js
6749 * L.layerGroup([marker1, marker2])
6750 * .addLayer(polyline)
6751 * .addTo(map);
6752 * ```
6753 */
6754
6755 var LayerGroup = Layer.extend({
6756
6757 initialize: function (layers, options) {
6758 setOptions(this, options);
6759
6760 this._layers = {};
6761
6762 var i, len;
6763
6764 if (layers) {
6765 for (i = 0, len = layers.length; i < len; i++) {
6766 this.addLayer(layers[i]);
6767 }
6768 }
6769 },
6770
6771 // @method addLayer(layer: Layer): this
6772 // Adds the given layer to the group.
6773 addLayer: function (layer) {
6774 var id = this.getLayerId(layer);
6775
6776 this._layers[id] = layer;
6777
6778 if (this._map) {
6779 this._map.addLayer(layer);
6780 }
6781
6782 return this;
6783 },
6784
6785 // @method removeLayer(layer: Layer): this
6786 // Removes the given layer from the group.
6787 // @alternative
6788 // @method removeLayer(id: Number): this
6789 // Removes the layer with the given internal ID from the group.
6790 removeLayer: function (layer) {
6791 var id = layer in this._layers ? layer : this.getLayerId(layer);
6792
6793 if (this._map && this._layers[id]) {
6794 this._map.removeLayer(this._layers[id]);
6795 }
6796
6797 delete this._layers[id];
6798
6799 return this;
6800 },
6801
6802 // @method hasLayer(layer: Layer): Boolean
6803 // Returns `true` if the given layer is currently added to the group.
6804 // @alternative
6805 // @method hasLayer(id: Number): Boolean
6806 // Returns `true` if the given internal ID is currently added to the group.
6807 hasLayer: function (layer) {
6808 if (!layer) { return false; }
6809 var layerId = typeof layer === 'number' ? layer : this.getLayerId(layer);
6810 return layerId in this._layers;
6811 },
6812
6813 // @method clearLayers(): this
6814 // Removes all the layers from the group.
6815 clearLayers: function () {
6816 return this.eachLayer(this.removeLayer, this);
6817 },
6818
6819 // @method invoke(methodName: String, …): this
6820 // Calls `methodName` on every layer contained in this group, passing any
6821 // additional parameters. Has no effect if the layers contained do not
6822 // implement `methodName`.
6823 invoke: function (methodName) {
6824 var args = Array.prototype.slice.call(arguments, 1),
6825 i, layer;
6826
6827 for (i in this._layers) {
6828 layer = this._layers[i];
6829
6830 if (layer[methodName]) {
6831 layer[methodName].apply(layer, args);
6832 }
6833 }
6834
6835 return this;
6836 },
6837
6838 onAdd: function (map) {
6839 this.eachLayer(map.addLayer, map);
6840 },
6841
6842 onRemove: function (map) {
6843 this.eachLayer(map.removeLayer, map);
6844 },
6845
6846 // @method eachLayer(fn: Function, context?: Object): this
6847 // Iterates over the layers of the group, optionally specifying context of the iterator function.
6848 // ```js
6849 // group.eachLayer(function (layer) {
6850 // layer.bindPopup('Hello');
6851 // });
6852 // ```
6853 eachLayer: function (method, context) {
6854 for (var i in this._layers) {
6855 method.call(context, this._layers[i]);
6856 }
6857 return this;
6858 },
6859
6860 // @method getLayer(id: Number): Layer
6861 // Returns the layer with the given internal ID.
6862 getLayer: function (id) {
6863 return this._layers[id];
6864 },
6865
6866 // @method getLayers(): Layer[]
6867 // Returns an array of all the layers added to the group.
6868 getLayers: function () {
6869 var layers = [];
6870 this.eachLayer(layers.push, layers);
6871 return layers;
6872 },
6873
6874 // @method setZIndex(zIndex: Number): this
6875 // Calls `setZIndex` on every layer contained in this group, passing the z-index.
6876 setZIndex: function (zIndex) {
6877 return this.invoke('setZIndex', zIndex);
6878 },
6879
6880 // @method getLayerId(layer: Layer): Number
6881 // Returns the internal ID for a layer
6882 getLayerId: function (layer) {
6883 return stamp(layer);
6884 }
6885 });
6886
6887
6888 // @factory L.layerGroup(layers?: Layer[], options?: Object)
6889 // Create a layer group, optionally given an initial set of layers and an `options` object.
6890 var layerGroup = function (layers, options) {
6891 return new LayerGroup(layers, options);
6892 };
6893
6894 /*
6895 * @class FeatureGroup
6896 * @aka L.FeatureGroup
6897 * @inherits LayerGroup
6898 *
6899 * Extended `LayerGroup` that makes it easier to do the same thing to all its member layers:
6900 * * [`bindPopup`](#layer-bindpopup) binds a popup to all of the layers at once (likewise with [`bindTooltip`](#layer-bindtooltip))
6901 * * Events are propagated to the `FeatureGroup`, so if the group has an event
6902 * handler, it will handle events from any of the layers. This includes mouse events
6903 * and custom events.
6904 * * Has `layeradd` and `layerremove` events
6905 *
6906 * @example
6907 *
6908 * ```js
6909 * L.featureGroup([marker1, marker2, polyline])
6910 * .bindPopup('Hello world!')
6911 * .on('click', function() { alert('Clicked on a member of the group!'); })
6912 * .addTo(map);
6913 * ```
6914 */
6915
6916 var FeatureGroup = LayerGroup.extend({
6917
6918 addLayer: function (layer) {
6919 if (this.hasLayer(layer)) {
6920 return this;
6921 }
6922
6923 layer.addEventParent(this);
6924
6925 LayerGroup.prototype.addLayer.call(this, layer);
6926
6927 // @event layeradd: LayerEvent
6928 // Fired when a layer is added to this `FeatureGroup`
6929 return this.fire('layeradd', {layer: layer});
6930 },
6931
6932 removeLayer: function (layer) {
6933 if (!this.hasLayer(layer)) {
6934 return this;
6935 }
6936 if (layer in this._layers) {
6937 layer = this._layers[layer];
6938 }
6939
6940 layer.removeEventParent(this);
6941
6942 LayerGroup.prototype.removeLayer.call(this, layer);
6943
6944 // @event layerremove: LayerEvent
6945 // Fired when a layer is removed from this `FeatureGroup`
6946 return this.fire('layerremove', {layer: layer});
6947 },
6948
6949 // @method setStyle(style: Path options): this
6950 // Sets the given path options to each layer of the group that has a `setStyle` method.
6951 setStyle: function (style) {
6952 return this.invoke('setStyle', style);
6953 },
6954
6955 // @method bringToFront(): this
6956 // Brings the layer group to the top of all other layers
6957 bringToFront: function () {
6958 return this.invoke('bringToFront');
6959 },
6960
6961 // @method bringToBack(): this
6962 // Brings the layer group to the back of all other layers
6963 bringToBack: function () {
6964 return this.invoke('bringToBack');
6965 },
6966
6967 // @method getBounds(): LatLngBounds
6968 // Returns the LatLngBounds of the Feature Group (created from bounds and coordinates of its children).
6969 getBounds: function () {
6970 var bounds = new LatLngBounds();
6971
6972 for (var id in this._layers) {
6973 var layer = this._layers[id];
6974 bounds.extend(layer.getBounds ? layer.getBounds() : layer.getLatLng());
6975 }
6976 return bounds;
6977 }
6978 });
6979
6980 // @factory L.featureGroup(layers?: Layer[], options?: Object)
6981 // Create a feature group, optionally given an initial set of layers and an `options` object.
6982 var featureGroup = function (layers, options) {
6983 return new FeatureGroup(layers, options);
6984 };
6985
6986 /*
6987 * @class Icon
6988 * @aka L.Icon
6989 *
6990 * Represents an icon to provide when creating a marker.
6991 *
6992 * @example
6993 *
6994 * ```js
6995 * var myIcon = L.icon({
6996 * iconUrl: 'my-icon.png',
6997 * iconRetinaUrl: 'my-icon@2x.png',
6998 * iconSize: [38, 95],
6999 * iconAnchor: [22, 94],
7000 * popupAnchor: [-3, -76],
7001 * shadowUrl: 'my-icon-shadow.png',
7002 * shadowRetinaUrl: 'my-icon-shadow@2x.png',
7003 * shadowSize: [68, 95],
7004 * shadowAnchor: [22, 94]
7005 * });
7006 *
7007 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
7008 * ```
7009 *
7010 * `L.Icon.Default` extends `L.Icon` and is the blue icon Leaflet uses for markers by default.
7011 *
7012 */
7013
7014 var Icon = Class.extend({
7015
7016 /* @section
7017 * @aka Icon options
7018 *
7019 * @option iconUrl: String = null
7020 * **(required)** The URL to the icon image (absolute or relative to your script path).
7021 *
7022 * @option iconRetinaUrl: String = null
7023 * The URL to a retina sized version of the icon image (absolute or relative to your
7024 * script path). Used for Retina screen devices.
7025 *
7026 * @option iconSize: Point = null
7027 * Size of the icon image in pixels.
7028 *
7029 * @option iconAnchor: Point = null
7030 * The coordinates of the "tip" of the icon (relative to its top left corner). The icon
7031 * will be aligned so that this point is at the marker's geographical location. Centered
7032 * by default if size is specified, also can be set in CSS with negative margins.
7033 *
7034 * @option popupAnchor: Point = [0, 0]
7035 * The coordinates of the point from which popups will "open", relative to the icon anchor.
7036 *
7037 * @option tooltipAnchor: Point = [0, 0]
7038 * The coordinates of the point from which tooltips will "open", relative to the icon anchor.
7039 *
7040 * @option shadowUrl: String = null
7041 * The URL to the icon shadow image. If not specified, no shadow image will be created.
7042 *
7043 * @option shadowRetinaUrl: String = null
7044 *
7045 * @option shadowSize: Point = null
7046 * Size of the shadow image in pixels.
7047 *
7048 * @option shadowAnchor: Point = null
7049 * The coordinates of the "tip" of the shadow (relative to its top left corner) (the same
7050 * as iconAnchor if not specified).
7051 *
7052 * @option className: String = ''
7053 * A custom class name to assign to both icon and shadow images. Empty by default.
7054 */
7055
7056 options: {
7057 popupAnchor: [0, 0],
7058 tooltipAnchor: [0, 0]
7059 },
7060
7061 initialize: function (options) {
7062 setOptions(this, options);
7063 },
7064
7065 // @method createIcon(oldIcon?: HTMLElement): HTMLElement
7066 // Called internally when the icon has to be shown, returns a `<img>` HTML element
7067 // styled according to the options.
7068 createIcon: function (oldIcon) {
7069 return this._createIcon('icon', oldIcon);
7070 },
7071
7072 // @method createShadow(oldIcon?: HTMLElement): HTMLElement
7073 // As `createIcon`, but for the shadow beneath it.
7074 createShadow: function (oldIcon) {
7075 return this._createIcon('shadow', oldIcon);
7076 },
7077
7078 _createIcon: function (name, oldIcon) {
7079 var src = this._getIconUrl(name);
7080
7081 if (!src) {
7082 if (name === 'icon') {
7083 throw new Error('iconUrl not set in Icon options (see the docs).');
7084 }
7085 return null;
7086 }
7087
7088 var img = this._createImg(src, oldIcon && oldIcon.tagName === 'IMG' ? oldIcon : null);
7089 this._setIconStyles(img, name);
7090
7091 return img;
7092 },
7093
7094 _setIconStyles: function (img, name) {
7095 var options = this.options;
7096 var sizeOption = options[name + 'Size'];
7097
7098 if (typeof sizeOption === 'number') {
7099 sizeOption = [sizeOption, sizeOption];
7100 }
7101
7102 var size = toPoint(sizeOption),
7103 anchor = toPoint(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
7104 size && size.divideBy(2, true));
7105
7106 img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
7107
7108 if (anchor) {
7109 img.style.marginLeft = (-anchor.x) + 'px';
7110 img.style.marginTop = (-anchor.y) + 'px';
7111 }
7112
7113 if (size) {
7114 img.style.width = size.x + 'px';
7115 img.style.height = size.y + 'px';
7116 }
7117 },
7118
7119 _createImg: function (src, el) {
7120 el = el || document.createElement('img');
7121 el.src = src;
7122 return el;
7123 },
7124
7125 _getIconUrl: function (name) {
7126 return retina && this.options[name + 'RetinaUrl'] || this.options[name + 'Url'];
7127 }
7128 });
7129
7130
7131 // @factory L.icon(options: Icon options)
7132 // Creates an icon instance with the given options.
7133 function icon(options) {
7134 return new Icon(options);
7135 }
7136
7137 /*
7138 * @miniclass Icon.Default (Icon)
7139 * @aka L.Icon.Default
7140 * @section
7141 *
7142 * A trivial subclass of `Icon`, represents the icon to use in `Marker`s when
7143 * no icon is specified. Points to the blue marker image distributed with Leaflet
7144 * releases.
7145 *
7146 * In order to customize the default icon, just change the properties of `L.Icon.Default.prototype.options`
7147 * (which is a set of `Icon options`).
7148 *
7149 * If you want to _completely_ replace the default icon, override the
7150 * `L.Marker.prototype.options.icon` with your own icon instead.
7151 */
7152
7153 var IconDefault = Icon.extend({
7154
7155 options: {
7156 iconUrl: 'marker-icon.png',
7157 iconRetinaUrl: 'marker-icon-2x.png',
7158 shadowUrl: 'marker-shadow.png',
7159 iconSize: [25, 41],
7160 iconAnchor: [12, 41],
7161 popupAnchor: [1, -34],
7162 tooltipAnchor: [16, -28],
7163 shadowSize: [41, 41]
7164 },
7165
7166 _getIconUrl: function (name) {
7167 if (!IconDefault.imagePath) { // Deprecated, backwards-compatibility only
7168 IconDefault.imagePath = this._detectIconPath();
7169 }
7170
7171 // @option imagePath: String
7172 // `Icon.Default` will try to auto-detect the location of the
7173 // blue icon images. If you are placing these images in a non-standard
7174 // way, set this option to point to the right path.
7175 return (this.options.imagePath || IconDefault.imagePath) + Icon.prototype._getIconUrl.call(this, name);
7176 },
7177
7178 _detectIconPath: function () {
7179 var el = create$1('div', 'leaflet-default-icon-path', document.body);
7180 var path = getStyle(el, 'background-image') ||
7181 getStyle(el, 'backgroundImage'); // IE8
7182
7183 document.body.removeChild(el);
7184
7185 if (path === null || path.indexOf('url') !== 0) {
7186 path = '';
7187 } else {
7188 path = path.replace(/^url\(["']?/, '').replace(/marker-icon\.png["']?\)$/, '');
7189 }
7190
7191 return path;
7192 }
7193 });
7194
7195 /*
7196 * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable.
7197 */
7198
7199
7200 /* @namespace Marker
7201 * @section Interaction handlers
7202 *
7203 * Interaction handlers are properties of a marker instance that allow you to control interaction behavior in runtime, enabling or disabling certain features such as dragging (see `Handler` methods). Example:
7204 *
7205 * ```js
7206 * marker.dragging.disable();
7207 * ```
7208 *
7209 * @property dragging: Handler
7210 * Marker dragging handler (by both mouse and touch). Only valid when the marker is on the map (Otherwise set [`marker.options.draggable`](#marker-draggable)).
7211 */
7212
7213 var MarkerDrag = Handler.extend({
7214 initialize: function (marker) {
7215 this._marker = marker;
7216 },
7217
7218 addHooks: function () {
7219 var icon = this._marker._icon;
7220
7221 if (!this._draggable) {
7222 this._draggable = new Draggable(icon, icon, true);
7223 }
7224
7225 this._draggable.on({
7226 dragstart: this._onDragStart,
7227 predrag: this._onPreDrag,
7228 drag: this._onDrag,
7229 dragend: this._onDragEnd
7230 }, this).enable();
7231
7232 addClass(icon, 'leaflet-marker-draggable');
7233 },
7234
7235 removeHooks: function () {
7236 this._draggable.off({
7237 dragstart: this._onDragStart,
7238 predrag: this._onPreDrag,
7239 drag: this._onDrag,
7240 dragend: this._onDragEnd
7241 }, this).disable();
7242
7243 if (this._marker._icon) {
7244 removeClass(this._marker._icon, 'leaflet-marker-draggable');
7245 }
7246 },
7247
7248 moved: function () {
7249 return this._draggable && this._draggable._moved;
7250 },
7251
7252 _adjustPan: function (e) {
7253 var marker = this._marker,
7254 map = marker._map,
7255 speed = this._marker.options.autoPanSpeed,
7256 padding = this._marker.options.autoPanPadding,
7257 iconPos = getPosition(marker._icon),
7258 bounds = map.getPixelBounds(),
7259 origin = map.getPixelOrigin();
7260
7261 var panBounds = toBounds(
7262 bounds.min._subtract(origin).add(padding),
7263 bounds.max._subtract(origin).subtract(padding)
7264 );
7265
7266 if (!panBounds.contains(iconPos)) {
7267 // Compute incremental movement
7268 var movement = toPoint(
7269 (Math.max(panBounds.max.x, iconPos.x) - panBounds.max.x) / (bounds.max.x - panBounds.max.x) -
7270 (Math.min(panBounds.min.x, iconPos.x) - panBounds.min.x) / (bounds.min.x - panBounds.min.x),
7271
7272 (Math.max(panBounds.max.y, iconPos.y) - panBounds.max.y) / (bounds.max.y - panBounds.max.y) -
7273 (Math.min(panBounds.min.y, iconPos.y) - panBounds.min.y) / (bounds.min.y - panBounds.min.y)
7274 ).multiplyBy(speed);
7275
7276 map.panBy(movement, {animate: false});
7277
7278 this._draggable._newPos._add(movement);
7279 this._draggable._startPos._add(movement);
7280
7281 setPosition(marker._icon, this._draggable._newPos);
7282 this._onDrag(e);
7283
7284 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7285 }
7286 },
7287
7288 _onDragStart: function () {
7289 // @section Dragging events
7290 // @event dragstart: Event
7291 // Fired when the user starts dragging the marker.
7292
7293 // @event movestart: Event
7294 // Fired when the marker starts moving (because of dragging).
7295
7296 this._oldLatLng = this._marker.getLatLng();
7297
7298 // When using ES6 imports it could not be set when `Popup` was not imported as well
7299 this._marker.closePopup && this._marker.closePopup();
7300
7301 this._marker
7302 .fire('movestart')
7303 .fire('dragstart');
7304 },
7305
7306 _onPreDrag: function (e) {
7307 if (this._marker.options.autoPan) {
7308 cancelAnimFrame(this._panRequest);
7309 this._panRequest = requestAnimFrame(this._adjustPan.bind(this, e));
7310 }
7311 },
7312
7313 _onDrag: function (e) {
7314 var marker = this._marker,
7315 shadow = marker._shadow,
7316 iconPos = getPosition(marker._icon),
7317 latlng = marker._map.layerPointToLatLng(iconPos);
7318
7319 // update shadow position
7320 if (shadow) {
7321 setPosition(shadow, iconPos);
7322 }
7323
7324 marker._latlng = latlng;
7325 e.latlng = latlng;
7326 e.oldLatLng = this._oldLatLng;
7327
7328 // @event drag: Event
7329 // Fired repeatedly while the user drags the marker.
7330 marker
7331 .fire('move', e)
7332 .fire('drag', e);
7333 },
7334
7335 _onDragEnd: function (e) {
7336 // @event dragend: DragEndEvent
7337 // Fired when the user stops dragging the marker.
7338
7339 cancelAnimFrame(this._panRequest);
7340
7341 // @event moveend: Event
7342 // Fired when the marker stops moving (because of dragging).
7343 delete this._oldLatLng;
7344 this._marker
7345 .fire('moveend')
7346 .fire('dragend', e);
7347 }
7348 });
7349
7350 /*
7351 * @class Marker
7352 * @inherits Interactive layer
7353 * @aka L.Marker
7354 * L.Marker is used to display clickable/draggable icons on the map. Extends `Layer`.
7355 *
7356 * @example
7357 *
7358 * ```js
7359 * L.marker([50.5, 30.5]).addTo(map);
7360 * ```
7361 */
7362
7363 var Marker = Layer.extend({
7364
7365 // @section
7366 // @aka Marker options
7367 options: {
7368 // @option icon: Icon = *
7369 // Icon instance to use for rendering the marker.
7370 // See [Icon documentation](#L.Icon) for details on how to customize the marker icon.
7371 // If not specified, a common instance of `L.Icon.Default` is used.
7372 icon: new IconDefault(),
7373
7374 // Option inherited from "Interactive layer" abstract class
7375 interactive: true,
7376
7377 // @option keyboard: Boolean = true
7378 // Whether the marker can be tabbed to with a keyboard and clicked by pressing enter.
7379 keyboard: true,
7380
7381 // @option title: String = ''
7382 // Text for the browser tooltip that appear on marker hover (no tooltip by default).
7383 title: '',
7384
7385 // @option alt: String = ''
7386 // Text for the `alt` attribute of the icon image (useful for accessibility).
7387 alt: '',
7388
7389 // @option zIndexOffset: Number = 0
7390 // By default, marker images zIndex is set automatically based on its latitude. Use this option if you want to put the marker on top of all others (or below), specifying a high value like `1000` (or high negative value, respectively).
7391 zIndexOffset: 0,
7392
7393 // @option opacity: Number = 1.0
7394 // The opacity of the marker.
7395 opacity: 1,
7396
7397 // @option riseOnHover: Boolean = false
7398 // If `true`, the marker will get on top of others when you hover the mouse over it.
7399 riseOnHover: false,
7400
7401 // @option riseOffset: Number = 250
7402 // The z-index offset used for the `riseOnHover` feature.
7403 riseOffset: 250,
7404
7405 // @option pane: String = 'markerPane'
7406 // `Map pane` where the markers icon will be added.
7407 pane: 'markerPane',
7408
7409 // @option shadowPane: String = 'shadowPane'
7410 // `Map pane` where the markers shadow will be added.
7411 shadowPane: 'shadowPane',
7412
7413 // @option bubblingMouseEvents: Boolean = false
7414 // When `true`, a mouse event on this marker will trigger the same event on the map
7415 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7416 bubblingMouseEvents: false,
7417
7418 // @section Draggable marker options
7419 // @option draggable: Boolean = false
7420 // Whether the marker is draggable with mouse/touch or not.
7421 draggable: false,
7422
7423 // @option autoPan: Boolean = false
7424 // Whether to pan the map when dragging this marker near its edge or not.
7425 autoPan: false,
7426
7427 // @option autoPanPadding: Point = Point(50, 50)
7428 // Distance (in pixels to the left/right and to the top/bottom) of the
7429 // map edge to start panning the map.
7430 autoPanPadding: [50, 50],
7431
7432 // @option autoPanSpeed: Number = 10
7433 // Number of pixels the map should pan by.
7434 autoPanSpeed: 10
7435 },
7436
7437 /* @section
7438 *
7439 * In addition to [shared layer methods](#Layer) like `addTo()` and `remove()` and [popup methods](#Popup) like bindPopup() you can also use the following methods:
7440 */
7441
7442 initialize: function (latlng, options) {
7443 setOptions(this, options);
7444 this._latlng = toLatLng(latlng);
7445 },
7446
7447 onAdd: function (map) {
7448 this._zoomAnimated = this._zoomAnimated && map.options.markerZoomAnimation;
7449
7450 if (this._zoomAnimated) {
7451 map.on('zoomanim', this._animateZoom, this);
7452 }
7453
7454 this._initIcon();
7455 this.update();
7456 },
7457
7458 onRemove: function (map) {
7459 if (this.dragging && this.dragging.enabled()) {
7460 this.options.draggable = true;
7461 this.dragging.removeHooks();
7462 }
7463 delete this.dragging;
7464
7465 if (this._zoomAnimated) {
7466 map.off('zoomanim', this._animateZoom, this);
7467 }
7468
7469 this._removeIcon();
7470 this._removeShadow();
7471 },
7472
7473 getEvents: function () {
7474 return {
7475 zoom: this.update,
7476 viewreset: this.update
7477 };
7478 },
7479
7480 // @method getLatLng: LatLng
7481 // Returns the current geographical position of the marker.
7482 getLatLng: function () {
7483 return this._latlng;
7484 },
7485
7486 // @method setLatLng(latlng: LatLng): this
7487 // Changes the marker position to the given point.
7488 setLatLng: function (latlng) {
7489 var oldLatLng = this._latlng;
7490 this._latlng = toLatLng(latlng);
7491 this.update();
7492
7493 // @event move: Event
7494 // Fired when the marker is moved via [`setLatLng`](#marker-setlatlng) or by [dragging](#marker-dragging). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7495 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7496 },
7497
7498 // @method setZIndexOffset(offset: Number): this
7499 // Changes the [zIndex offset](#marker-zindexoffset) of the marker.
7500 setZIndexOffset: function (offset) {
7501 this.options.zIndexOffset = offset;
7502 return this.update();
7503 },
7504
7505 // @method getIcon: Icon
7506 // Returns the current icon used by the marker
7507 getIcon: function () {
7508 return this.options.icon;
7509 },
7510
7511 // @method setIcon(icon: Icon): this
7512 // Changes the marker icon.
7513 setIcon: function (icon) {
7514
7515 this.options.icon = icon;
7516
7517 if (this._map) {
7518 this._initIcon();
7519 this.update();
7520 }
7521
7522 if (this._popup) {
7523 this.bindPopup(this._popup, this._popup.options);
7524 }
7525
7526 return this;
7527 },
7528
7529 getElement: function () {
7530 return this._icon;
7531 },
7532
7533 update: function () {
7534
7535 if (this._icon && this._map) {
7536 var pos = this._map.latLngToLayerPoint(this._latlng).round();
7537 this._setPos(pos);
7538 }
7539
7540 return this;
7541 },
7542
7543 _initIcon: function () {
7544 var options = this.options,
7545 classToAdd = 'leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
7546
7547 var icon = options.icon.createIcon(this._icon),
7548 addIcon = false;
7549
7550 // if we're not reusing the icon, remove the old one and init new one
7551 if (icon !== this._icon) {
7552 if (this._icon) {
7553 this._removeIcon();
7554 }
7555 addIcon = true;
7556
7557 if (options.title) {
7558 icon.title = options.title;
7559 }
7560
7561 if (icon.tagName === 'IMG') {
7562 icon.alt = options.alt || '';
7563 }
7564 }
7565
7566 addClass(icon, classToAdd);
7567
7568 if (options.keyboard) {
7569 icon.tabIndex = '0';
7570 }
7571
7572 this._icon = icon;
7573
7574 if (options.riseOnHover) {
7575 this.on({
7576 mouseover: this._bringToFront,
7577 mouseout: this._resetZIndex
7578 });
7579 }
7580
7581 var newShadow = options.icon.createShadow(this._shadow),
7582 addShadow = false;
7583
7584 if (newShadow !== this._shadow) {
7585 this._removeShadow();
7586 addShadow = true;
7587 }
7588
7589 if (newShadow) {
7590 addClass(newShadow, classToAdd);
7591 newShadow.alt = '';
7592 }
7593 this._shadow = newShadow;
7594
7595
7596 if (options.opacity < 1) {
7597 this._updateOpacity();
7598 }
7599
7600
7601 if (addIcon) {
7602 this.getPane().appendChild(this._icon);
7603 }
7604 this._initInteraction();
7605 if (newShadow && addShadow) {
7606 this.getPane(options.shadowPane).appendChild(this._shadow);
7607 }
7608 },
7609
7610 _removeIcon: function () {
7611 if (this.options.riseOnHover) {
7612 this.off({
7613 mouseover: this._bringToFront,
7614 mouseout: this._resetZIndex
7615 });
7616 }
7617
7618 remove(this._icon);
7619 this.removeInteractiveTarget(this._icon);
7620
7621 this._icon = null;
7622 },
7623
7624 _removeShadow: function () {
7625 if (this._shadow) {
7626 remove(this._shadow);
7627 }
7628 this._shadow = null;
7629 },
7630
7631 _setPos: function (pos) {
7632
7633 if (this._icon) {
7634 setPosition(this._icon, pos);
7635 }
7636
7637 if (this._shadow) {
7638 setPosition(this._shadow, pos);
7639 }
7640
7641 this._zIndex = pos.y + this.options.zIndexOffset;
7642
7643 this._resetZIndex();
7644 },
7645
7646 _updateZIndex: function (offset) {
7647 if (this._icon) {
7648 this._icon.style.zIndex = this._zIndex + offset;
7649 }
7650 },
7651
7652 _animateZoom: function (opt) {
7653 var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
7654
7655 this._setPos(pos);
7656 },
7657
7658 _initInteraction: function () {
7659
7660 if (!this.options.interactive) { return; }
7661
7662 addClass(this._icon, 'leaflet-interactive');
7663
7664 this.addInteractiveTarget(this._icon);
7665
7666 if (MarkerDrag) {
7667 var draggable = this.options.draggable;
7668 if (this.dragging) {
7669 draggable = this.dragging.enabled();
7670 this.dragging.disable();
7671 }
7672
7673 this.dragging = new MarkerDrag(this);
7674
7675 if (draggable) {
7676 this.dragging.enable();
7677 }
7678 }
7679 },
7680
7681 // @method setOpacity(opacity: Number): this
7682 // Changes the opacity of the marker.
7683 setOpacity: function (opacity) {
7684 this.options.opacity = opacity;
7685 if (this._map) {
7686 this._updateOpacity();
7687 }
7688
7689 return this;
7690 },
7691
7692 _updateOpacity: function () {
7693 var opacity = this.options.opacity;
7694
7695 if (this._icon) {
7696 setOpacity(this._icon, opacity);
7697 }
7698
7699 if (this._shadow) {
7700 setOpacity(this._shadow, opacity);
7701 }
7702 },
7703
7704 _bringToFront: function () {
7705 this._updateZIndex(this.options.riseOffset);
7706 },
7707
7708 _resetZIndex: function () {
7709 this._updateZIndex(0);
7710 },
7711
7712 _getPopupAnchor: function () {
7713 return this.options.icon.options.popupAnchor;
7714 },
7715
7716 _getTooltipAnchor: function () {
7717 return this.options.icon.options.tooltipAnchor;
7718 }
7719 });
7720
7721
7722 // factory L.marker(latlng: LatLng, options? : Marker options)
7723
7724 // @factory L.marker(latlng: LatLng, options? : Marker options)
7725 // Instantiates a Marker object given a geographical point and optionally an options object.
7726 function marker(latlng, options) {
7727 return new Marker(latlng, options);
7728 }
7729
7730 /*
7731 * @class Path
7732 * @aka L.Path
7733 * @inherits Interactive layer
7734 *
7735 * An abstract class that contains options and constants shared between vector
7736 * overlays (Polygon, Polyline, Circle). Do not use it directly. Extends `Layer`.
7737 */
7738
7739 var Path = Layer.extend({
7740
7741 // @section
7742 // @aka Path options
7743 options: {
7744 // @option stroke: Boolean = true
7745 // Whether to draw stroke along the path. Set it to `false` to disable borders on polygons or circles.
7746 stroke: true,
7747
7748 // @option color: String = '#3388ff'
7749 // Stroke color
7750 color: '#3388ff',
7751
7752 // @option weight: Number = 3
7753 // Stroke width in pixels
7754 weight: 3,
7755
7756 // @option opacity: Number = 1.0
7757 // Stroke opacity
7758 opacity: 1,
7759
7760 // @option lineCap: String= 'round'
7761 // A string that defines [shape to be used at the end](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linecap) of the stroke.
7762 lineCap: 'round',
7763
7764 // @option lineJoin: String = 'round'
7765 // A string that defines [shape to be used at the corners](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-linejoin) of the stroke.
7766 lineJoin: 'round',
7767
7768 // @option dashArray: String = null
7769 // A string that defines the stroke [dash pattern](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dasharray). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7770 dashArray: null,
7771
7772 // @option dashOffset: String = null
7773 // A string that defines the [distance into the dash pattern to start the dash](https://developer.mozilla.org/docs/Web/SVG/Attribute/stroke-dashoffset). Doesn't work on `Canvas`-powered layers in [some old browsers](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility).
7774 dashOffset: null,
7775
7776 // @option fill: Boolean = depends
7777 // Whether to fill the path with color. Set it to `false` to disable filling on polygons or circles.
7778 fill: false,
7779
7780 // @option fillColor: String = *
7781 // Fill color. Defaults to the value of the [`color`](#path-color) option
7782 fillColor: null,
7783
7784 // @option fillOpacity: Number = 0.2
7785 // Fill opacity.
7786 fillOpacity: 0.2,
7787
7788 // @option fillRule: String = 'evenodd'
7789 // A string that defines [how the inside of a shape](https://developer.mozilla.org/docs/Web/SVG/Attribute/fill-rule) is determined.
7790 fillRule: 'evenodd',
7791
7792 // className: '',
7793
7794 // Option inherited from "Interactive layer" abstract class
7795 interactive: true,
7796
7797 // @option bubblingMouseEvents: Boolean = true
7798 // When `true`, a mouse event on this path will trigger the same event on the map
7799 // (unless [`L.DomEvent.stopPropagation`](#domevent-stoppropagation) is used).
7800 bubblingMouseEvents: true
7801 },
7802
7803 beforeAdd: function (map) {
7804 // Renderer is set here because we need to call renderer.getEvents
7805 // before this.getEvents.
7806 this._renderer = map.getRenderer(this);
7807 },
7808
7809 onAdd: function () {
7810 this._renderer._initPath(this);
7811 this._reset();
7812 this._renderer._addPath(this);
7813 },
7814
7815 onRemove: function () {
7816 this._renderer._removePath(this);
7817 },
7818
7819 // @method redraw(): this
7820 // Redraws the layer. Sometimes useful after you changed the coordinates that the path uses.
7821 redraw: function () {
7822 if (this._map) {
7823 this._renderer._updatePath(this);
7824 }
7825 return this;
7826 },
7827
7828 // @method setStyle(style: Path options): this
7829 // Changes the appearance of a Path based on the options in the `Path options` object.
7830 setStyle: function (style) {
7831 setOptions(this, style);
7832 if (this._renderer) {
7833 this._renderer._updateStyle(this);
7834 if (this.options.stroke && style && Object.prototype.hasOwnProperty.call(style, 'weight')) {
7835 this._updateBounds();
7836 }
7837 }
7838 return this;
7839 },
7840
7841 // @method bringToFront(): this
7842 // Brings the layer to the top of all path layers.
7843 bringToFront: function () {
7844 if (this._renderer) {
7845 this._renderer._bringToFront(this);
7846 }
7847 return this;
7848 },
7849
7850 // @method bringToBack(): this
7851 // Brings the layer to the bottom of all path layers.
7852 bringToBack: function () {
7853 if (this._renderer) {
7854 this._renderer._bringToBack(this);
7855 }
7856 return this;
7857 },
7858
7859 getElement: function () {
7860 return this._path;
7861 },
7862
7863 _reset: function () {
7864 // defined in child classes
7865 this._project();
7866 this._update();
7867 },
7868
7869 _clickTolerance: function () {
7870 // used when doing hit detection for Canvas layers
7871 return (this.options.stroke ? this.options.weight / 2 : 0) + this._renderer.options.tolerance;
7872 }
7873 });
7874
7875 /*
7876 * @class CircleMarker
7877 * @aka L.CircleMarker
7878 * @inherits Path
7879 *
7880 * A circle of a fixed size with radius specified in pixels. Extends `Path`.
7881 */
7882
7883 var CircleMarker = Path.extend({
7884
7885 // @section
7886 // @aka CircleMarker options
7887 options: {
7888 fill: true,
7889
7890 // @option radius: Number = 10
7891 // Radius of the circle marker, in pixels
7892 radius: 10
7893 },
7894
7895 initialize: function (latlng, options) {
7896 setOptions(this, options);
7897 this._latlng = toLatLng(latlng);
7898 this._radius = this.options.radius;
7899 },
7900
7901 // @method setLatLng(latLng: LatLng): this
7902 // Sets the position of a circle marker to a new location.
7903 setLatLng: function (latlng) {
7904 var oldLatLng = this._latlng;
7905 this._latlng = toLatLng(latlng);
7906 this.redraw();
7907
7908 // @event move: Event
7909 // Fired when the marker is moved via [`setLatLng`](#circlemarker-setlatlng). Old and new coordinates are included in event arguments as `oldLatLng`, `latlng`.
7910 return this.fire('move', {oldLatLng: oldLatLng, latlng: this._latlng});
7911 },
7912
7913 // @method getLatLng(): LatLng
7914 // Returns the current geographical position of the circle marker
7915 getLatLng: function () {
7916 return this._latlng;
7917 },
7918
7919 // @method setRadius(radius: Number): this
7920 // Sets the radius of a circle marker. Units are in pixels.
7921 setRadius: function (radius) {
7922 this.options.radius = this._radius = radius;
7923 return this.redraw();
7924 },
7925
7926 // @method getRadius(): Number
7927 // Returns the current radius of the circle
7928 getRadius: function () {
7929 return this._radius;
7930 },
7931
7932 setStyle : function (options) {
7933 var radius = options && options.radius || this._radius;
7934 Path.prototype.setStyle.call(this, options);
7935 this.setRadius(radius);
7936 return this;
7937 },
7938
7939 _project: function () {
7940 this._point = this._map.latLngToLayerPoint(this._latlng);
7941 this._updateBounds();
7942 },
7943
7944 _updateBounds: function () {
7945 var r = this._radius,
7946 r2 = this._radiusY || r,
7947 w = this._clickTolerance(),
7948 p = [r + w, r2 + w];
7949 this._pxBounds = new Bounds(this._point.subtract(p), this._point.add(p));
7950 },
7951
7952 _update: function () {
7953 if (this._map) {
7954 this._updatePath();
7955 }
7956 },
7957
7958 _updatePath: function () {
7959 this._renderer._updateCircle(this);
7960 },
7961
7962 _empty: function () {
7963 return this._radius && !this._renderer._bounds.intersects(this._pxBounds);
7964 },
7965
7966 // Needed by the `Canvas` renderer for interactivity
7967 _containsPoint: function (p) {
7968 return p.distanceTo(this._point) <= this._radius + this._clickTolerance();
7969 }
7970 });
7971
7972
7973 // @factory L.circleMarker(latlng: LatLng, options?: CircleMarker options)
7974 // Instantiates a circle marker object given a geographical point, and an optional options object.
7975 function circleMarker(latlng, options) {
7976 return new CircleMarker(latlng, options);
7977 }
7978
7979 /*
7980 * @class Circle
7981 * @aka L.Circle
7982 * @inherits CircleMarker
7983 *
7984 * A class for drawing circle overlays on a map. Extends `CircleMarker`.
7985 *
7986 * It's an approximation and starts to diverge from a real circle closer to poles (due to projection distortion).
7987 *
7988 * @example
7989 *
7990 * ```js
7991 * L.circle([50.5, 30.5], {radius: 200}).addTo(map);
7992 * ```
7993 */
7994
7995 var Circle = CircleMarker.extend({
7996
7997 initialize: function (latlng, options, legacyOptions) {
7998 if (typeof options === 'number') {
7999 // Backwards compatibility with 0.7.x factory (latlng, radius, options?)
8000 options = extend({}, legacyOptions, {radius: options});
8001 }
8002 setOptions(this, options);
8003 this._latlng = toLatLng(latlng);
8004
8005 if (isNaN(this.options.radius)) { throw new Error('Circle radius cannot be NaN'); }
8006
8007 // @section
8008 // @aka Circle options
8009 // @option radius: Number; Radius of the circle, in meters.
8010 this._mRadius = this.options.radius;
8011 },
8012
8013 // @method setRadius(radius: Number): this
8014 // Sets the radius of a circle. Units are in meters.
8015 setRadius: function (radius) {
8016 this._mRadius = radius;
8017 return this.redraw();
8018 },
8019
8020 // @method getRadius(): Number
8021 // Returns the current radius of a circle. Units are in meters.
8022 getRadius: function () {
8023 return this._mRadius;
8024 },
8025
8026 // @method getBounds(): LatLngBounds
8027 // Returns the `LatLngBounds` of the path.
8028 getBounds: function () {
8029 var half = [this._radius, this._radiusY || this._radius];
8030
8031 return new LatLngBounds(
8032 this._map.layerPointToLatLng(this._point.subtract(half)),
8033 this._map.layerPointToLatLng(this._point.add(half)));
8034 },
8035
8036 setStyle: Path.prototype.setStyle,
8037
8038 _project: function () {
8039
8040 var lng = this._latlng.lng,
8041 lat = this._latlng.lat,
8042 map = this._map,
8043 crs = map.options.crs;
8044
8045 if (crs.distance === Earth.distance) {
8046 var d = Math.PI / 180,
8047 latR = (this._mRadius / Earth.R) / d,
8048 top = map.project([lat + latR, lng]),
8049 bottom = map.project([lat - latR, lng]),
8050 p = top.add(bottom).divideBy(2),
8051 lat2 = map.unproject(p).lat,
8052 lngR = Math.acos((Math.cos(latR * d) - Math.sin(lat * d) * Math.sin(lat2 * d)) /
8053 (Math.cos(lat * d) * Math.cos(lat2 * d))) / d;
8054
8055 if (isNaN(lngR) || lngR === 0) {
8056 lngR = latR / Math.cos(Math.PI / 180 * lat); // Fallback for edge case, #2425
8057 }
8058
8059 this._point = p.subtract(map.getPixelOrigin());
8060 this._radius = isNaN(lngR) ? 0 : p.x - map.project([lat2, lng - lngR]).x;
8061 this._radiusY = p.y - top.y;
8062
8063 } else {
8064 var latlng2 = crs.unproject(crs.project(this._latlng).subtract([this._mRadius, 0]));
8065
8066 this._point = map.latLngToLayerPoint(this._latlng);
8067 this._radius = this._point.x - map.latLngToLayerPoint(latlng2).x;
8068 }
8069
8070 this._updateBounds();
8071 }
8072 });
8073
8074 // @factory L.circle(latlng: LatLng, options?: Circle options)
8075 // Instantiates a circle object given a geographical point, and an options object
8076 // which contains the circle radius.
8077 // @alternative
8078 // @factory L.circle(latlng: LatLng, radius: Number, options?: Circle options)
8079 // Obsolete way of instantiating a circle, for compatibility with 0.7.x code.
8080 // Do not use in new applications or plugins.
8081 function circle(latlng, options, legacyOptions) {
8082 return new Circle(latlng, options, legacyOptions);
8083 }
8084
8085 /*
8086 * @class Polyline
8087 * @aka L.Polyline
8088 * @inherits Path
8089 *
8090 * A class for drawing polyline overlays on a map. Extends `Path`.
8091 *
8092 * @example
8093 *
8094 * ```js
8095 * // create a red polyline from an array of LatLng points
8096 * var latlngs = [
8097 * [45.51, -122.68],
8098 * [37.77, -122.43],
8099 * [34.04, -118.2]
8100 * ];
8101 *
8102 * var polyline = L.polyline(latlngs, {color: 'red'}).addTo(map);
8103 *
8104 * // zoom the map to the polyline
8105 * map.fitBounds(polyline.getBounds());
8106 * ```
8107 *
8108 * You can also pass a multi-dimensional array to represent a `MultiPolyline` shape:
8109 *
8110 * ```js
8111 * // create a red polyline from an array of arrays of LatLng points
8112 * var latlngs = [
8113 * [[45.51, -122.68],
8114 * [37.77, -122.43],
8115 * [34.04, -118.2]],
8116 * [[40.78, -73.91],
8117 * [41.83, -87.62],
8118 * [32.76, -96.72]]
8119 * ];
8120 * ```
8121 */
8122
8123
8124 var Polyline = Path.extend({
8125
8126 // @section
8127 // @aka Polyline options
8128 options: {
8129 // @option smoothFactor: Number = 1.0
8130 // How much to simplify the polyline on each zoom level. More means
8131 // better performance and smoother look, and less means more accurate representation.
8132 smoothFactor: 1.0,
8133
8134 // @option noClip: Boolean = false
8135 // Disable polyline clipping.
8136 noClip: false
8137 },
8138
8139 initialize: function (latlngs, options) {
8140 setOptions(this, options);
8141 this._setLatLngs(latlngs);
8142 },
8143
8144 // @method getLatLngs(): LatLng[]
8145 // Returns an array of the points in the path, or nested arrays of points in case of multi-polyline.
8146 getLatLngs: function () {
8147 return this._latlngs;
8148 },
8149
8150 // @method setLatLngs(latlngs: LatLng[]): this
8151 // Replaces all the points in the polyline with the given array of geographical points.
8152 setLatLngs: function (latlngs) {
8153 this._setLatLngs(latlngs);
8154 return this.redraw();
8155 },
8156
8157 // @method isEmpty(): Boolean
8158 // Returns `true` if the Polyline has no LatLngs.
8159 isEmpty: function () {
8160 return !this._latlngs.length;
8161 },
8162
8163 // @method closestLayerPoint(p: Point): Point
8164 // Returns the point closest to `p` on the Polyline.
8165 closestLayerPoint: function (p) {
8166 var minDistance = Infinity,
8167 minPoint = null,
8168 closest = _sqClosestPointOnSegment,
8169 p1, p2;
8170
8171 for (var j = 0, jLen = this._parts.length; j < jLen; j++) {
8172 var points = this._parts[j];
8173
8174 for (var i = 1, len = points.length; i < len; i++) {
8175 p1 = points[i - 1];
8176 p2 = points[i];
8177
8178 var sqDist = closest(p, p1, p2, true);
8179
8180 if (sqDist < minDistance) {
8181 minDistance = sqDist;
8182 minPoint = closest(p, p1, p2);
8183 }
8184 }
8185 }
8186 if (minPoint) {
8187 minPoint.distance = Math.sqrt(minDistance);
8188 }
8189 return minPoint;
8190 },
8191
8192 // @method getCenter(): LatLng
8193 // Returns the center ([centroid](http://en.wikipedia.org/wiki/Centroid)) of the polyline.
8194 getCenter: function () {
8195 // throws error when not yet added to map as this center calculation requires projected coordinates
8196 if (!this._map) {
8197 throw new Error('Must add layer to map before using getCenter()');
8198 }
8199
8200 var i, halfDist, segDist, dist, p1, p2, ratio,
8201 points = this._rings[0],
8202 len = points.length;
8203
8204 if (!len) { return null; }
8205
8206 // polyline centroid algorithm; only uses the first ring if there are multiple
8207
8208 for (i = 0, halfDist = 0; i < len - 1; i++) {
8209 halfDist += points[i].distanceTo(points[i + 1]) / 2;
8210 }
8211
8212 // The line is so small in the current view that all points are on the same pixel.
8213 if (halfDist === 0) {
8214 return this._map.layerPointToLatLng(points[0]);
8215 }
8216
8217 for (i = 0, dist = 0; i < len - 1; i++) {
8218 p1 = points[i];
8219 p2 = points[i + 1];
8220 segDist = p1.distanceTo(p2);
8221 dist += segDist;
8222
8223 if (dist > halfDist) {
8224 ratio = (dist - halfDist) / segDist;
8225 return this._map.layerPointToLatLng([
8226 p2.x - ratio * (p2.x - p1.x),
8227 p2.y - ratio * (p2.y - p1.y)
8228 ]);
8229 }
8230 }
8231 },
8232
8233 // @method getBounds(): LatLngBounds
8234 // Returns the `LatLngBounds` of the path.
8235 getBounds: function () {
8236 return this._bounds;
8237 },
8238
8239 // @method addLatLng(latlng: LatLng, latlngs?: LatLng[]): this
8240 // Adds a given point to the polyline. By default, adds to the first ring of
8241 // the polyline in case of a multi-polyline, but can be overridden by passing
8242 // a specific ring as a LatLng array (that you can earlier access with [`getLatLngs`](#polyline-getlatlngs)).
8243 addLatLng: function (latlng, latlngs) {
8244 latlngs = latlngs || this._defaultShape();
8245 latlng = toLatLng(latlng);
8246 latlngs.push(latlng);
8247 this._bounds.extend(latlng);
8248 return this.redraw();
8249 },
8250
8251 _setLatLngs: function (latlngs) {
8252 this._bounds = new LatLngBounds();
8253 this._latlngs = this._convertLatLngs(latlngs);
8254 },
8255
8256 _defaultShape: function () {
8257 return isFlat(this._latlngs) ? this._latlngs : this._latlngs[0];
8258 },
8259
8260 // recursively convert latlngs input into actual LatLng instances; calculate bounds along the way
8261 _convertLatLngs: function (latlngs) {
8262 var result = [],
8263 flat = isFlat(latlngs);
8264
8265 for (var i = 0, len = latlngs.length; i < len; i++) {
8266 if (flat) {
8267 result[i] = toLatLng(latlngs[i]);
8268 this._bounds.extend(result[i]);
8269 } else {
8270 result[i] = this._convertLatLngs(latlngs[i]);
8271 }
8272 }
8273
8274 return result;
8275 },
8276
8277 _project: function () {
8278 var pxBounds = new Bounds();
8279 this._rings = [];
8280 this._projectLatlngs(this._latlngs, this._rings, pxBounds);
8281
8282 if (this._bounds.isValid() && pxBounds.isValid()) {
8283 this._rawPxBounds = pxBounds;
8284 this._updateBounds();
8285 }
8286 },
8287
8288 _updateBounds: function () {
8289 var w = this._clickTolerance(),
8290 p = new Point(w, w);
8291 this._pxBounds = new Bounds([
8292 this._rawPxBounds.min.subtract(p),
8293 this._rawPxBounds.max.add(p)
8294 ]);
8295 },
8296
8297 // recursively turns latlngs into a set of rings with projected coordinates
8298 _projectLatlngs: function (latlngs, result, projectedBounds) {
8299 var flat = latlngs[0] instanceof LatLng,
8300 len = latlngs.length,
8301 i, ring;
8302
8303 if (flat) {
8304 ring = [];
8305 for (i = 0; i < len; i++) {
8306 ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
8307 projectedBounds.extend(ring[i]);
8308 }
8309 result.push(ring);
8310 } else {
8311 for (i = 0; i < len; i++) {
8312 this._projectLatlngs(latlngs[i], result, projectedBounds);
8313 }
8314 }
8315 },
8316
8317 // clip polyline by renderer bounds so that we have less to render for performance
8318 _clipPoints: function () {
8319 var bounds = this._renderer._bounds;
8320
8321 this._parts = [];
8322 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8323 return;
8324 }
8325
8326 if (this.options.noClip) {
8327 this._parts = this._rings;
8328 return;
8329 }
8330
8331 var parts = this._parts,
8332 i, j, k, len, len2, segment, points;
8333
8334 for (i = 0, k = 0, len = this._rings.length; i < len; i++) {
8335 points = this._rings[i];
8336
8337 for (j = 0, len2 = points.length; j < len2 - 1; j++) {
8338 segment = clipSegment(points[j], points[j + 1], bounds, j, true);
8339
8340 if (!segment) { continue; }
8341
8342 parts[k] = parts[k] || [];
8343 parts[k].push(segment[0]);
8344
8345 // if segment goes out of screen, or it's the last one, it's the end of the line part
8346 if ((segment[1] !== points[j + 1]) || (j === len2 - 2)) {
8347 parts[k].push(segment[1]);
8348 k++;
8349 }
8350 }
8351 }
8352 },
8353
8354 // simplify each clipped part of the polyline for performance
8355 _simplifyPoints: function () {
8356 var parts = this._parts,
8357 tolerance = this.options.smoothFactor;
8358
8359 for (var i = 0, len = parts.length; i < len; i++) {
8360 parts[i] = simplify(parts[i], tolerance);
8361 }
8362 },
8363
8364 _update: function () {
8365 if (!this._map) { return; }
8366
8367 this._clipPoints();
8368 this._simplifyPoints();
8369 this._updatePath();
8370 },
8371
8372 _updatePath: function () {
8373 this._renderer._updatePoly(this);
8374 },
8375
8376 // Needed by the `Canvas` renderer for interactivity
8377 _containsPoint: function (p, closed) {
8378 var i, j, k, len, len2, part,
8379 w = this._clickTolerance();
8380
8381 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8382
8383 // hit detection for polylines
8384 for (i = 0, len = this._parts.length; i < len; i++) {
8385 part = this._parts[i];
8386
8387 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8388 if (!closed && (j === 0)) { continue; }
8389
8390 if (pointToSegmentDistance(p, part[k], part[j]) <= w) {
8391 return true;
8392 }
8393 }
8394 }
8395 return false;
8396 }
8397 });
8398
8399 // @factory L.polyline(latlngs: LatLng[], options?: Polyline options)
8400 // Instantiates a polyline object given an array of geographical points and
8401 // optionally an options object. You can create a `Polyline` object with
8402 // multiple separate lines (`MultiPolyline`) by passing an array of arrays
8403 // of geographic points.
8404 function polyline(latlngs, options) {
8405 return new Polyline(latlngs, options);
8406 }
8407
8408 // Retrocompat. Allow plugins to support Leaflet versions before and after 1.1.
8409 Polyline._flat = _flat;
8410
8411 /*
8412 * @class Polygon
8413 * @aka L.Polygon
8414 * @inherits Polyline
8415 *
8416 * A class for drawing polygon overlays on a map. Extends `Polyline`.
8417 *
8418 * Note that points you pass when creating a polygon shouldn't have an additional last point equal to the first one — it's better to filter out such points.
8419 *
8420 *
8421 * @example
8422 *
8423 * ```js
8424 * // create a red polygon from an array of LatLng points
8425 * var latlngs = [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]];
8426 *
8427 * var polygon = L.polygon(latlngs, {color: 'red'}).addTo(map);
8428 *
8429 * // zoom the map to the polygon
8430 * map.fitBounds(polygon.getBounds());
8431 * ```
8432 *
8433 * You can also pass an array of arrays of latlngs, with the first array representing the outer shape and the other arrays representing holes in the outer shape:
8434 *
8435 * ```js
8436 * var latlngs = [
8437 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8438 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8439 * ];
8440 * ```
8441 *
8442 * Additionally, you can pass a multi-dimensional array to represent a MultiPolygon shape.
8443 *
8444 * ```js
8445 * var latlngs = [
8446 * [ // first polygon
8447 * [[37, -109.05],[41, -109.03],[41, -102.05],[37, -102.04]], // outer ring
8448 * [[37.29, -108.58],[40.71, -108.58],[40.71, -102.50],[37.29, -102.50]] // hole
8449 * ],
8450 * [ // second polygon
8451 * [[41, -111.03],[45, -111.04],[45, -104.05],[41, -104.05]]
8452 * ]
8453 * ];
8454 * ```
8455 */
8456
8457 var Polygon = Polyline.extend({
8458
8459 options: {
8460 fill: true
8461 },
8462
8463 isEmpty: function () {
8464 return !this._latlngs.length || !this._latlngs[0].length;
8465 },
8466
8467 getCenter: function () {
8468 // throws error when not yet added to map as this center calculation requires projected coordinates
8469 if (!this._map) {
8470 throw new Error('Must add layer to map before using getCenter()');
8471 }
8472
8473 var i, j, p1, p2, f, area, x, y, center,
8474 points = this._rings[0],
8475 len = points.length;
8476
8477 if (!len) { return null; }
8478
8479 // polygon centroid algorithm; only uses the first ring if there are multiple
8480
8481 area = x = y = 0;
8482
8483 for (i = 0, j = len - 1; i < len; j = i++) {
8484 p1 = points[i];
8485 p2 = points[j];
8486
8487 f = p1.y * p2.x - p2.y * p1.x;
8488 x += (p1.x + p2.x) * f;
8489 y += (p1.y + p2.y) * f;
8490 area += f * 3;
8491 }
8492
8493 if (area === 0) {
8494 // Polygon is so small that all points are on same pixel.
8495 center = points[0];
8496 } else {
8497 center = [x / area, y / area];
8498 }
8499 return this._map.layerPointToLatLng(center);
8500 },
8501
8502 _convertLatLngs: function (latlngs) {
8503 var result = Polyline.prototype._convertLatLngs.call(this, latlngs),
8504 len = result.length;
8505
8506 // remove last point if it equals first one
8507 if (len >= 2 && result[0] instanceof LatLng && result[0].equals(result[len - 1])) {
8508 result.pop();
8509 }
8510 return result;
8511 },
8512
8513 _setLatLngs: function (latlngs) {
8514 Polyline.prototype._setLatLngs.call(this, latlngs);
8515 if (isFlat(this._latlngs)) {
8516 this._latlngs = [this._latlngs];
8517 }
8518 },
8519
8520 _defaultShape: function () {
8521 return isFlat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0];
8522 },
8523
8524 _clipPoints: function () {
8525 // polygons need a different clipping algorithm so we redefine that
8526
8527 var bounds = this._renderer._bounds,
8528 w = this.options.weight,
8529 p = new Point(w, w);
8530
8531 // increase clip padding by stroke width to avoid stroke on clip edges
8532 bounds = new Bounds(bounds.min.subtract(p), bounds.max.add(p));
8533
8534 this._parts = [];
8535 if (!this._pxBounds || !this._pxBounds.intersects(bounds)) {
8536 return;
8537 }
8538
8539 if (this.options.noClip) {
8540 this._parts = this._rings;
8541 return;
8542 }
8543
8544 for (var i = 0, len = this._rings.length, clipped; i < len; i++) {
8545 clipped = clipPolygon(this._rings[i], bounds, true);
8546 if (clipped.length) {
8547 this._parts.push(clipped);
8548 }
8549 }
8550 },
8551
8552 _updatePath: function () {
8553 this._renderer._updatePoly(this, true);
8554 },
8555
8556 // Needed by the `Canvas` renderer for interactivity
8557 _containsPoint: function (p) {
8558 var inside = false,
8559 part, p1, p2, i, j, k, len, len2;
8560
8561 if (!this._pxBounds || !this._pxBounds.contains(p)) { return false; }
8562
8563 // ray casting algorithm for detecting if point is in polygon
8564 for (i = 0, len = this._parts.length; i < len; i++) {
8565 part = this._parts[i];
8566
8567 for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) {
8568 p1 = part[j];
8569 p2 = part[k];
8570
8571 if (((p1.y > p.y) !== (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) {
8572 inside = !inside;
8573 }
8574 }
8575 }
8576
8577 // also check if it's on polygon stroke
8578 return inside || Polyline.prototype._containsPoint.call(this, p, true);
8579 }
8580
8581 });
8582
8583
8584 // @factory L.polygon(latlngs: LatLng[], options?: Polyline options)
8585 function polygon(latlngs, options) {
8586 return new Polygon(latlngs, options);
8587 }
8588
8589 /*
8590 * @class GeoJSON
8591 * @aka L.GeoJSON
8592 * @inherits FeatureGroup
8593 *
8594 * Represents a GeoJSON object or an array of GeoJSON objects. Allows you to parse
8595 * GeoJSON data and display it on the map. Extends `FeatureGroup`.
8596 *
8597 * @example
8598 *
8599 * ```js
8600 * L.geoJSON(data, {
8601 * style: function (feature) {
8602 * return {color: feature.properties.color};
8603 * }
8604 * }).bindPopup(function (layer) {
8605 * return layer.feature.properties.description;
8606 * }).addTo(map);
8607 * ```
8608 */
8609
8610 var GeoJSON = FeatureGroup.extend({
8611
8612 /* @section
8613 * @aka GeoJSON options
8614 *
8615 * @option pointToLayer: Function = *
8616 * A `Function` defining how GeoJSON points spawn Leaflet layers. It is internally
8617 * called when data is added, passing the GeoJSON point feature and its `LatLng`.
8618 * The default is to spawn a default `Marker`:
8619 * ```js
8620 * function(geoJsonPoint, latlng) {
8621 * return L.marker(latlng);
8622 * }
8623 * ```
8624 *
8625 * @option style: Function = *
8626 * A `Function` defining the `Path options` for styling GeoJSON lines and polygons,
8627 * called internally when data is added.
8628 * The default value is to not override any defaults:
8629 * ```js
8630 * function (geoJsonFeature) {
8631 * return {}
8632 * }
8633 * ```
8634 *
8635 * @option onEachFeature: Function = *
8636 * A `Function` that will be called once for each created `Feature`, after it has
8637 * been created and styled. Useful for attaching events and popups to features.
8638 * The default is to do nothing with the newly created layers:
8639 * ```js
8640 * function (feature, layer) {}
8641 * ```
8642 *
8643 * @option filter: Function = *
8644 * A `Function` that will be used to decide whether to include a feature or not.
8645 * The default is to include all features:
8646 * ```js
8647 * function (geoJsonFeature) {
8648 * return true;
8649 * }
8650 * ```
8651 * Note: dynamically changing the `filter` option will have effect only on newly
8652 * added data. It will _not_ re-evaluate already included features.
8653 *
8654 * @option coordsToLatLng: Function = *
8655 * A `Function` that will be used for converting GeoJSON coordinates to `LatLng`s.
8656 * The default is the `coordsToLatLng` static method.
8657 *
8658 * @option markersInheritOptions: Boolean = false
8659 * Whether default Markers for "Point" type Features inherit from group options.
8660 */
8661
8662 initialize: function (geojson, options) {
8663 setOptions(this, options);
8664
8665 this._layers = {};
8666
8667 if (geojson) {
8668 this.addData(geojson);
8669 }
8670 },
8671
8672 // @method addData( <GeoJSON> data ): this
8673 // Adds a GeoJSON object to the layer.
8674 addData: function (geojson) {
8675 var features = isArray(geojson) ? geojson : geojson.features,
8676 i, len, feature;
8677
8678 if (features) {
8679 for (i = 0, len = features.length; i < len; i++) {
8680 // only add this if geometry or geometries are set and not null
8681 feature = features[i];
8682 if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
8683 this.addData(feature);
8684 }
8685 }
8686 return this;
8687 }
8688
8689 var options = this.options;
8690
8691 if (options.filter && !options.filter(geojson)) { return this; }
8692
8693 var layer = geometryToLayer(geojson, options);
8694 if (!layer) {
8695 return this;
8696 }
8697 layer.feature = asFeature(geojson);
8698
8699 layer.defaultOptions = layer.options;
8700 this.resetStyle(layer);
8701
8702 if (options.onEachFeature) {
8703 options.onEachFeature(geojson, layer);
8704 }
8705
8706 return this.addLayer(layer);
8707 },
8708
8709 // @method resetStyle( <Path> layer? ): this
8710 // Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
8711 // If `layer` is omitted, the style of all features in the current layer is reset.
8712 resetStyle: function (layer) {
8713 if (layer === undefined) {
8714 return this.eachLayer(this.resetStyle, this);
8715 }
8716 // reset any custom styles
8717 layer.options = extend({}, layer.defaultOptions);
8718 this._setLayerStyle(layer, this.options.style);
8719 return this;
8720 },
8721
8722 // @method setStyle( <Function> style ): this
8723 // Changes styles of GeoJSON vector layers with the given style function.
8724 setStyle: function (style) {
8725 return this.eachLayer(function (layer) {
8726 this._setLayerStyle(layer, style);
8727 }, this);
8728 },
8729
8730 _setLayerStyle: function (layer, style) {
8731 if (layer.setStyle) {
8732 if (typeof style === 'function') {
8733 style = style(layer.feature);
8734 }
8735 layer.setStyle(style);
8736 }
8737 }
8738 });
8739
8740 // @section
8741 // There are several static functions which can be called without instantiating L.GeoJSON:
8742
8743 // @function geometryToLayer(featureData: Object, options?: GeoJSON options): Layer
8744 // Creates a `Layer` from a given GeoJSON feature. Can use a custom
8745 // [`pointToLayer`](#geojson-pointtolayer) and/or [`coordsToLatLng`](#geojson-coordstolatlng)
8746 // functions if provided as options.
8747 function geometryToLayer(geojson, options) {
8748
8749 var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
8750 coords = geometry ? geometry.coordinates : null,
8751 layers = [],
8752 pointToLayer = options && options.pointToLayer,
8753 _coordsToLatLng = options && options.coordsToLatLng || coordsToLatLng,
8754 latlng, latlngs, i, len;
8755
8756 if (!coords && !geometry) {
8757 return null;
8758 }
8759
8760 switch (geometry.type) {
8761 case 'Point':
8762 latlng = _coordsToLatLng(coords);
8763 return _pointToLayer(pointToLayer, geojson, latlng, options);
8764
8765 case 'MultiPoint':
8766 for (i = 0, len = coords.length; i < len; i++) {
8767 latlng = _coordsToLatLng(coords[i]);
8768 layers.push(_pointToLayer(pointToLayer, geojson, latlng, options));
8769 }
8770 return new FeatureGroup(layers);
8771
8772 case 'LineString':
8773 case 'MultiLineString':
8774 latlngs = coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
8775 return new Polyline(latlngs, options);
8776
8777 case 'Polygon':
8778 case 'MultiPolygon':
8779 latlngs = coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
8780 return new Polygon(latlngs, options);
8781
8782 case 'GeometryCollection':
8783 for (i = 0, len = geometry.geometries.length; i < len; i++) {
8784 var layer = geometryToLayer({
8785 geometry: geometry.geometries[i],
8786 type: 'Feature',
8787 properties: geojson.properties
8788 }, options);
8789
8790 if (layer) {
8791 layers.push(layer);
8792 }
8793 }
8794 return new FeatureGroup(layers);
8795
8796 default:
8797 throw new Error('Invalid GeoJSON object.');
8798 }
8799 }
8800
8801 function _pointToLayer(pointToLayerFn, geojson, latlng, options) {
8802 return pointToLayerFn ?
8803 pointToLayerFn(geojson, latlng) :
8804 new Marker(latlng, options && options.markersInheritOptions && options);
8805 }
8806
8807 // @function coordsToLatLng(coords: Array): LatLng
8808 // Creates a `LatLng` object from an array of 2 numbers (longitude, latitude)
8809 // or 3 numbers (longitude, latitude, altitude) used in GeoJSON for points.
8810 function coordsToLatLng(coords) {
8811 return new LatLng(coords[1], coords[0], coords[2]);
8812 }
8813
8814 // @function coordsToLatLngs(coords: Array, levelsDeep?: Number, coordsToLatLng?: Function): Array
8815 // Creates a multidimensional array of `LatLng`s from a GeoJSON coordinates array.
8816 // `levelsDeep` specifies the nesting level (0 is for an array of points, 1 for an array of arrays of points, etc., 0 by default).
8817 // Can use a custom [`coordsToLatLng`](#geojson-coordstolatlng) function.
8818 function coordsToLatLngs(coords, levelsDeep, _coordsToLatLng) {
8819 var latlngs = [];
8820
8821 for (var i = 0, len = coords.length, latlng; i < len; i++) {
8822 latlng = levelsDeep ?
8823 coordsToLatLngs(coords[i], levelsDeep - 1, _coordsToLatLng) :
8824 (_coordsToLatLng || coordsToLatLng)(coords[i]);
8825
8826 latlngs.push(latlng);
8827 }
8828
8829 return latlngs;
8830 }
8831
8832 // @function latLngToCoords(latlng: LatLng, precision?: Number): Array
8833 // Reverse of [`coordsToLatLng`](#geojson-coordstolatlng)
8834 function latLngToCoords(latlng, precision) {
8835 precision = typeof precision === 'number' ? precision : 6;
8836 return latlng.alt !== undefined ?
8837 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision), formatNum(latlng.alt, precision)] :
8838 [formatNum(latlng.lng, precision), formatNum(latlng.lat, precision)];
8839 }
8840
8841 // @function latLngsToCoords(latlngs: Array, levelsDeep?: Number, closed?: Boolean): Array
8842 // Reverse of [`coordsToLatLngs`](#geojson-coordstolatlngs)
8843 // `closed` determines whether the first point should be appended to the end of the array to close the feature, only used when `levelsDeep` is 0. False by default.
8844 function latLngsToCoords(latlngs, levelsDeep, closed, precision) {
8845 var coords = [];
8846
8847 for (var i = 0, len = latlngs.length; i < len; i++) {
8848 coords.push(levelsDeep ?
8849 latLngsToCoords(latlngs[i], levelsDeep - 1, closed, precision) :
8850 latLngToCoords(latlngs[i], precision));
8851 }
8852
8853 if (!levelsDeep && closed) {
8854 coords.push(coords[0]);
8855 }
8856
8857 return coords;
8858 }
8859
8860 function getFeature(layer, newGeometry) {
8861 return layer.feature ?
8862 extend({}, layer.feature, {geometry: newGeometry}) :
8863 asFeature(newGeometry);
8864 }
8865
8866 // @function asFeature(geojson: Object): Object
8867 // Normalize GeoJSON geometries/features into GeoJSON features.
8868 function asFeature(geojson) {
8869 if (geojson.type === 'Feature' || geojson.type === 'FeatureCollection') {
8870 return geojson;
8871 }
8872
8873 return {
8874 type: 'Feature',
8875 properties: {},
8876 geometry: geojson
8877 };
8878 }
8879
8880 var PointToGeoJSON = {
8881 toGeoJSON: function (precision) {
8882 return getFeature(this, {
8883 type: 'Point',
8884 coordinates: latLngToCoords(this.getLatLng(), precision)
8885 });
8886 }
8887 };
8888
8889 // @namespace Marker
8890 // @section Other methods
8891 // @method toGeoJSON(precision?: Number): Object
8892 // `precision` is the number of decimal places for coordinates.
8893 // The default value is 6 places.
8894 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the marker (as a GeoJSON `Point` Feature).
8895 Marker.include(PointToGeoJSON);
8896
8897 // @namespace CircleMarker
8898 // @method toGeoJSON(precision?: Number): Object
8899 // `precision` is the number of decimal places for coordinates.
8900 // The default value is 6 places.
8901 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the circle marker (as a GeoJSON `Point` Feature).
8902 Circle.include(PointToGeoJSON);
8903 CircleMarker.include(PointToGeoJSON);
8904
8905
8906 // @namespace Polyline
8907 // @method toGeoJSON(precision?: Number): Object
8908 // `precision` is the number of decimal places for coordinates.
8909 // The default value is 6 places.
8910 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polyline (as a GeoJSON `LineString` or `MultiLineString` Feature).
8911 Polyline.include({
8912 toGeoJSON: function (precision) {
8913 var multi = !isFlat(this._latlngs);
8914
8915 var coords = latLngsToCoords(this._latlngs, multi ? 1 : 0, false, precision);
8916
8917 return getFeature(this, {
8918 type: (multi ? 'Multi' : '') + 'LineString',
8919 coordinates: coords
8920 });
8921 }
8922 });
8923
8924 // @namespace Polygon
8925 // @method toGeoJSON(precision?: Number): Object
8926 // `precision` is the number of decimal places for coordinates.
8927 // The default value is 6 places.
8928 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the polygon (as a GeoJSON `Polygon` or `MultiPolygon` Feature).
8929 Polygon.include({
8930 toGeoJSON: function (precision) {
8931 var holes = !isFlat(this._latlngs),
8932 multi = holes && !isFlat(this._latlngs[0]);
8933
8934 var coords = latLngsToCoords(this._latlngs, multi ? 2 : holes ? 1 : 0, true, precision);
8935
8936 if (!holes) {
8937 coords = [coords];
8938 }
8939
8940 return getFeature(this, {
8941 type: (multi ? 'Multi' : '') + 'Polygon',
8942 coordinates: coords
8943 });
8944 }
8945 });
8946
8947
8948 // @namespace LayerGroup
8949 LayerGroup.include({
8950 toMultiPoint: function (precision) {
8951 var coords = [];
8952
8953 this.eachLayer(function (layer) {
8954 coords.push(layer.toGeoJSON(precision).geometry.coordinates);
8955 });
8956
8957 return getFeature(this, {
8958 type: 'MultiPoint',
8959 coordinates: coords
8960 });
8961 },
8962
8963 // @method toGeoJSON(precision?: Number): Object
8964 // `precision` is the number of decimal places for coordinates.
8965 // The default value is 6 places.
8966 // Returns a [`GeoJSON`](http://en.wikipedia.org/wiki/GeoJSON) representation of the layer group (as a GeoJSON `FeatureCollection`, `GeometryCollection`, or `MultiPoint`).
8967 toGeoJSON: function (precision) {
8968
8969 var type = this.feature && this.feature.geometry && this.feature.geometry.type;
8970
8971 if (type === 'MultiPoint') {
8972 return this.toMultiPoint(precision);
8973 }
8974
8975 var isGeometryCollection = type === 'GeometryCollection',
8976 jsons = [];
8977
8978 this.eachLayer(function (layer) {
8979 if (layer.toGeoJSON) {
8980 var json = layer.toGeoJSON(precision);
8981 if (isGeometryCollection) {
8982 jsons.push(json.geometry);
8983 } else {
8984 var feature = asFeature(json);
8985 // Squash nested feature collections
8986 if (feature.type === 'FeatureCollection') {
8987 jsons.push.apply(jsons, feature.features);
8988 } else {
8989 jsons.push(feature);
8990 }
8991 }
8992 }
8993 });
8994
8995 if (isGeometryCollection) {
8996 return getFeature(this, {
8997 geometries: jsons,
8998 type: 'GeometryCollection'
8999 });
9000 }
9001
9002 return {
9003 type: 'FeatureCollection',
9004 features: jsons
9005 };
9006 }
9007 });
9008
9009 // @namespace GeoJSON
9010 // @factory L.geoJSON(geojson?: Object, options?: GeoJSON options)
9011 // Creates a GeoJSON layer. Optionally accepts an object in
9012 // [GeoJSON format](https://tools.ietf.org/html/rfc7946) to display on the map
9013 // (you can alternatively add it later with `addData` method) and an `options` object.
9014 function geoJSON(geojson, options) {
9015 return new GeoJSON(geojson, options);
9016 }
9017
9018 // Backward compatibility.
9019 var geoJson = geoJSON;
9020
9021 /*
9022 * @class ImageOverlay
9023 * @aka L.ImageOverlay
9024 * @inherits Interactive layer
9025 *
9026 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
9027 *
9028 * @example
9029 *
9030 * ```js
9031 * var imageUrl = 'http://www.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
9032 * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
9033 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
9034 * ```
9035 */
9036
9037 var ImageOverlay = Layer.extend({
9038
9039 // @section
9040 // @aka ImageOverlay options
9041 options: {
9042 // @option opacity: Number = 1.0
9043 // The opacity of the image overlay.
9044 opacity: 1,
9045
9046 // @option alt: String = ''
9047 // Text for the `alt` attribute of the image (useful for accessibility).
9048 alt: '',
9049
9050 // @option interactive: Boolean = false
9051 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
9052 interactive: false,
9053
9054 // @option crossOrigin: Boolean|String = false
9055 // Whether the crossOrigin attribute will be added to the image.
9056 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
9057 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
9058 crossOrigin: false,
9059
9060 // @option errorOverlayUrl: String = ''
9061 // URL to the overlay image to show in place of the overlay that failed to load.
9062 errorOverlayUrl: '',
9063
9064 // @option zIndex: Number = 1
9065 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
9066 zIndex: 1,
9067
9068 // @option className: String = ''
9069 // A custom class name to assign to the image. Empty by default.
9070 className: ''
9071 },
9072
9073 initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
9074 this._url = url;
9075 this._bounds = toLatLngBounds(bounds);
9076
9077 setOptions(this, options);
9078 },
9079
9080 onAdd: function () {
9081 if (!this._image) {
9082 this._initImage();
9083
9084 if (this.options.opacity < 1) {
9085 this._updateOpacity();
9086 }
9087 }
9088
9089 if (this.options.interactive) {
9090 addClass(this._image, 'leaflet-interactive');
9091 this.addInteractiveTarget(this._image);
9092 }
9093
9094 this.getPane().appendChild(this._image);
9095 this._reset();
9096 },
9097
9098 onRemove: function () {
9099 remove(this._image);
9100 if (this.options.interactive) {
9101 this.removeInteractiveTarget(this._image);
9102 }
9103 },
9104
9105 // @method setOpacity(opacity: Number): this
9106 // Sets the opacity of the overlay.
9107 setOpacity: function (opacity) {
9108 this.options.opacity = opacity;
9109
9110 if (this._image) {
9111 this._updateOpacity();
9112 }
9113 return this;
9114 },
9115
9116 setStyle: function (styleOpts) {
9117 if (styleOpts.opacity) {
9118 this.setOpacity(styleOpts.opacity);
9119 }
9120 return this;
9121 },
9122
9123 // @method bringToFront(): this
9124 // Brings the layer to the top of all overlays.
9125 bringToFront: function () {
9126 if (this._map) {
9127 toFront(this._image);
9128 }
9129 return this;
9130 },
9131
9132 // @method bringToBack(): this
9133 // Brings the layer to the bottom of all overlays.
9134 bringToBack: function () {
9135 if (this._map) {
9136 toBack(this._image);
9137 }
9138 return this;
9139 },
9140
9141 // @method setUrl(url: String): this
9142 // Changes the URL of the image.
9143 setUrl: function (url) {
9144 this._url = url;
9145
9146 if (this._image) {
9147 this._image.src = url;
9148 }
9149 return this;
9150 },
9151
9152 // @method setBounds(bounds: LatLngBounds): this
9153 // Update the bounds that this ImageOverlay covers
9154 setBounds: function (bounds) {
9155 this._bounds = toLatLngBounds(bounds);
9156
9157 if (this._map) {
9158 this._reset();
9159 }
9160 return this;
9161 },
9162
9163 getEvents: function () {
9164 var events = {
9165 zoom: this._reset,
9166 viewreset: this._reset
9167 };
9168
9169 if (this._zoomAnimated) {
9170 events.zoomanim = this._animateZoom;
9171 }
9172
9173 return events;
9174 },
9175
9176 // @method setZIndex(value: Number): this
9177 // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
9178 setZIndex: function (value) {
9179 this.options.zIndex = value;
9180 this._updateZIndex();
9181 return this;
9182 },
9183
9184 // @method getBounds(): LatLngBounds
9185 // Get the bounds that this ImageOverlay covers
9186 getBounds: function () {
9187 return this._bounds;
9188 },
9189
9190 // @method getElement(): HTMLElement
9191 // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
9192 // used by this overlay.
9193 getElement: function () {
9194 return this._image;
9195 },
9196
9197 _initImage: function () {
9198 var wasElementSupplied = this._url.tagName === 'IMG';
9199 var img = this._image = wasElementSupplied ? this._url : create$1('img');
9200
9201 addClass(img, 'leaflet-image-layer');
9202 if (this._zoomAnimated) { addClass(img, 'leaflet-zoom-animated'); }
9203 if (this.options.className) { addClass(img, this.options.className); }
9204
9205 img.onselectstart = falseFn;
9206 img.onmousemove = falseFn;
9207
9208 // @event load: Event
9209 // Fired when the ImageOverlay layer has loaded its image
9210 img.onload = bind(this.fire, this, 'load');
9211 img.onerror = bind(this._overlayOnError, this, 'error');
9212
9213 if (this.options.crossOrigin || this.options.crossOrigin === '') {
9214 img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
9215 }
9216
9217 if (this.options.zIndex) {
9218 this._updateZIndex();
9219 }
9220
9221 if (wasElementSupplied) {
9222 this._url = img.src;
9223 return;
9224 }
9225
9226 img.src = this._url;
9227 img.alt = this.options.alt;
9228 },
9229
9230 _animateZoom: function (e) {
9231 var scale = this._map.getZoomScale(e.zoom),
9232 offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
9233
9234 setTransform(this._image, offset, scale);
9235 },
9236
9237 _reset: function () {
9238 var image = this._image,
9239 bounds = new Bounds(
9240 this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
9241 this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
9242 size = bounds.getSize();
9243
9244 setPosition(image, bounds.min);
9245
9246 image.style.width = size.x + 'px';
9247 image.style.height = size.y + 'px';
9248 },
9249
9250 _updateOpacity: function () {
9251 setOpacity(this._image, this.options.opacity);
9252 },
9253
9254 _updateZIndex: function () {
9255 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
9256 this._image.style.zIndex = this.options.zIndex;
9257 }
9258 },
9259
9260 _overlayOnError: function () {
9261 // @event error: Event
9262 // Fired when the ImageOverlay layer fails to load its image
9263 this.fire('error');
9264
9265 var errorUrl = this.options.errorOverlayUrl;
9266 if (errorUrl && this._url !== errorUrl) {
9267 this._url = errorUrl;
9268 this._image.src = errorUrl;
9269 }
9270 }
9271 });
9272
9273 // @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
9274 // Instantiates an image overlay object given the URL of the image and the
9275 // geographical bounds it is tied to.
9276 var imageOverlay = function (url, bounds, options) {
9277 return new ImageOverlay(url, bounds, options);
9278 };
9279
9280 /*
9281 * @class VideoOverlay
9282 * @aka L.VideoOverlay
9283 * @inherits ImageOverlay
9284 *
9285 * Used to load and display a video player over specific bounds of the map. Extends `ImageOverlay`.
9286 *
9287 * A video overlay uses the [`<video>`](https://developer.mozilla.org/docs/Web/HTML/Element/video)
9288 * HTML5 element.
9289 *
9290 * @example
9291 *
9292 * ```js
9293 * var videoUrl = 'https://www.mapbox.com/bites/00188/patricia_nasa.webm',
9294 * videoBounds = [[ 32, -130], [ 13, -100]];
9295 * L.videoOverlay(videoUrl, videoBounds ).addTo(map);
9296 * ```
9297 */
9298
9299 var VideoOverlay = ImageOverlay.extend({
9300
9301 // @section
9302 // @aka VideoOverlay options
9303 options: {
9304 // @option autoplay: Boolean = true
9305 // Whether the video starts playing automatically when loaded.
9306 autoplay: true,
9307
9308 // @option loop: Boolean = true
9309 // Whether the video will loop back to the beginning when played.
9310 loop: true,
9311
9312 // @option keepAspectRatio: Boolean = true
9313 // Whether the video will save aspect ratio after the projection.
9314 // Relevant for supported browsers. Browser compatibility- https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
9315 keepAspectRatio: true,
9316
9317 // @option muted: Boolean = false
9318 // Whether the video starts on mute when loaded.
9319 muted: false
9320 },
9321
9322 _initImage: function () {
9323 var wasElementSupplied = this._url.tagName === 'VIDEO';
9324 var vid = this._image = wasElementSupplied ? this._url : create$1('video');
9325
9326 addClass(vid, 'leaflet-image-layer');
9327 if (this._zoomAnimated) { addClass(vid, 'leaflet-zoom-animated'); }
9328 if (this.options.className) { addClass(vid, this.options.className); }
9329
9330 vid.onselectstart = falseFn;
9331 vid.onmousemove = falseFn;
9332
9333 // @event load: Event
9334 // Fired when the video has finished loading the first frame
9335 vid.onloadeddata = bind(this.fire, this, 'load');
9336
9337 if (wasElementSupplied) {
9338 var sourceElements = vid.getElementsByTagName('source');
9339 var sources = [];
9340 for (var j = 0; j < sourceElements.length; j++) {
9341 sources.push(sourceElements[j].src);
9342 }
9343
9344 this._url = (sourceElements.length > 0) ? sources : [vid.src];
9345 return;
9346 }
9347
9348 if (!isArray(this._url)) { this._url = [this._url]; }
9349
9350 if (!this.options.keepAspectRatio && Object.prototype.hasOwnProperty.call(vid.style, 'objectFit')) {
9351 vid.style['objectFit'] = 'fill';
9352 }
9353 vid.autoplay = !!this.options.autoplay;
9354 vid.loop = !!this.options.loop;
9355 vid.muted = !!this.options.muted;
9356 for (var i = 0; i < this._url.length; i++) {
9357 var source = create$1('source');
9358 source.src = this._url[i];
9359 vid.appendChild(source);
9360 }
9361 }
9362
9363 // @method getElement(): HTMLVideoElement
9364 // Returns the instance of [`HTMLVideoElement`](https://developer.mozilla.org/docs/Web/API/HTMLVideoElement)
9365 // used by this overlay.
9366 });
9367
9368
9369 // @factory L.videoOverlay(video: String|Array|HTMLVideoElement, bounds: LatLngBounds, options?: VideoOverlay options)
9370 // Instantiates an image overlay object given the URL of the video (or array of URLs, or even a video element) and the
9371 // geographical bounds it is tied to.
9372
9373 function videoOverlay(video, bounds, options) {
9374 return new VideoOverlay(video, bounds, options);
9375 }
9376
9377 /*
9378 * @class SVGOverlay
9379 * @aka L.SVGOverlay
9380 * @inherits ImageOverlay
9381 *
9382 * Used to load, display and provide DOM access to an SVG file over specific bounds of the map. Extends `ImageOverlay`.
9383 *
9384 * An SVG overlay uses the [`<svg>`](https://developer.mozilla.org/docs/Web/SVG/Element/svg) element.
9385 *
9386 * @example
9387 *
9388 * ```js
9389 * var svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
9390 * svgElement.setAttribute('xmlns', "http://www.w3.org/2000/svg");
9391 * svgElement.setAttribute('viewBox', "0 0 200 200");
9392 * svgElement.innerHTML = '<rect width="200" height="200"/><rect x="75" y="23" width="50" height="50" style="fill:red"/><rect x="75" y="123" width="50" height="50" style="fill:#0013ff"/>';
9393 * var svgElementBounds = [ [ 32, -130 ], [ 13, -100 ] ];
9394 * L.svgOverlay(svgElement, svgElementBounds).addTo(map);
9395 * ```
9396 */
9397
9398 var SVGOverlay = ImageOverlay.extend({
9399 _initImage: function () {
9400 var el = this._image = this._url;
9401
9402 addClass(el, 'leaflet-image-layer');
9403 if (this._zoomAnimated) { addClass(el, 'leaflet-zoom-animated'); }
9404 if (this.options.className) { addClass(el, this.options.className); }
9405
9406 el.onselectstart = falseFn;
9407 el.onmousemove = falseFn;
9408 }
9409
9410 // @method getElement(): SVGElement
9411 // Returns the instance of [`SVGElement`](https://developer.mozilla.org/docs/Web/API/SVGElement)
9412 // used by this overlay.
9413 });
9414
9415
9416 // @factory L.svgOverlay(svg: String|SVGElement, bounds: LatLngBounds, options?: SVGOverlay options)
9417 // Instantiates an image overlay object given an SVG element and the geographical bounds it is tied to.
9418 // A viewBox attribute is required on the SVG element to zoom in and out properly.
9419
9420 function svgOverlay(el, bounds, options) {
9421 return new SVGOverlay(el, bounds, options);
9422 }
9423
9424 /*
9425 * @class DivOverlay
9426 * @inherits Layer
9427 * @aka L.DivOverlay
9428 * Base model for L.Popup and L.Tooltip. Inherit from it for custom popup like plugins.
9429 */
9430
9431 // @namespace DivOverlay
9432 var DivOverlay = Layer.extend({
9433
9434 // @section
9435 // @aka DivOverlay options
9436 options: {
9437 // @option offset: Point = Point(0, 7)
9438 // The offset of the popup position. Useful to control the anchor
9439 // of the popup when opening it on some overlays.
9440 offset: [0, 7],
9441
9442 // @option className: String = ''
9443 // A custom CSS class name to assign to the popup.
9444 className: '',
9445
9446 // @option pane: String = 'popupPane'
9447 // `Map pane` where the popup will be added.
9448 pane: 'popupPane'
9449 },
9450
9451 initialize: function (options, source) {
9452 setOptions(this, options);
9453
9454 this._source = source;
9455 },
9456
9457 onAdd: function (map) {
9458 this._zoomAnimated = map._zoomAnimated;
9459
9460 if (!this._container) {
9461 this._initLayout();
9462 }
9463
9464 if (map._fadeAnimated) {
9465 setOpacity(this._container, 0);
9466 }
9467
9468 clearTimeout(this._removeTimeout);
9469 this.getPane().appendChild(this._container);
9470 this.update();
9471
9472 if (map._fadeAnimated) {
9473 setOpacity(this._container, 1);
9474 }
9475
9476 this.bringToFront();
9477 },
9478
9479 onRemove: function (map) {
9480 if (map._fadeAnimated) {
9481 setOpacity(this._container, 0);
9482 this._removeTimeout = setTimeout(bind(remove, undefined, this._container), 200);
9483 } else {
9484 remove(this._container);
9485 }
9486 },
9487
9488 // @namespace Popup
9489 // @method getLatLng: LatLng
9490 // Returns the geographical point of popup.
9491 getLatLng: function () {
9492 return this._latlng;
9493 },
9494
9495 // @method setLatLng(latlng: LatLng): this
9496 // Sets the geographical point where the popup will open.
9497 setLatLng: function (latlng) {
9498 this._latlng = toLatLng(latlng);
9499 if (this._map) {
9500 this._updatePosition();
9501 this._adjustPan();
9502 }
9503 return this;
9504 },
9505
9506 // @method getContent: String|HTMLElement
9507 // Returns the content of the popup.
9508 getContent: function () {
9509 return this._content;
9510 },
9511
9512 // @method setContent(htmlContent: String|HTMLElement|Function): this
9513 // Sets the HTML content of the popup. If a function is passed the source layer will be passed to the function. The function should return a `String` or `HTMLElement` to be used in the popup.
9514 setContent: function (content) {
9515 this._content = content;
9516 this.update();
9517 return this;
9518 },
9519
9520 // @method getElement: String|HTMLElement
9521 // Returns the HTML container of the popup.
9522 getElement: function () {
9523 return this._container;
9524 },
9525
9526 // @method update: null
9527 // Updates the popup content, layout and position. Useful for updating the popup after something inside changed, e.g. image loaded.
9528 update: function () {
9529 if (!this._map) { return; }
9530
9531 this._container.style.visibility = 'hidden';
9532
9533 this._updateContent();
9534 this._updateLayout();
9535 this._updatePosition();
9536
9537 this._container.style.visibility = '';
9538
9539 this._adjustPan();
9540 },
9541
9542 getEvents: function () {
9543 var events = {
9544 zoom: this._updatePosition,
9545 viewreset: this._updatePosition
9546 };
9547
9548 if (this._zoomAnimated) {
9549 events.zoomanim = this._animateZoom;
9550 }
9551 return events;
9552 },
9553
9554 // @method isOpen: Boolean
9555 // Returns `true` when the popup is visible on the map.
9556 isOpen: function () {
9557 return !!this._map && this._map.hasLayer(this);
9558 },
9559
9560 // @method bringToFront: this
9561 // Brings this popup in front of other popups (in the same map pane).
9562 bringToFront: function () {
9563 if (this._map) {
9564 toFront(this._container);
9565 }
9566 return this;
9567 },
9568
9569 // @method bringToBack: this
9570 // Brings this popup to the back of other popups (in the same map pane).
9571 bringToBack: function () {
9572 if (this._map) {
9573 toBack(this._container);
9574 }
9575 return this;
9576 },
9577
9578 _prepareOpen: function (parent, layer, latlng) {
9579 if (!(layer instanceof Layer)) {
9580 latlng = layer;
9581 layer = parent;
9582 }
9583
9584 if (layer instanceof FeatureGroup) {
9585 for (var id in parent._layers) {
9586 layer = parent._layers[id];
9587 break;
9588 }
9589 }
9590
9591 if (!latlng) {
9592 if (layer.getCenter) {
9593 latlng = layer.getCenter();
9594 } else if (layer.getLatLng) {
9595 latlng = layer.getLatLng();
9596 } else {
9597 throw new Error('Unable to get source layer LatLng.');
9598 }
9599 }
9600
9601 // set overlay source to this layer
9602 this._source = layer;
9603
9604 // update the overlay (content, layout, ect...)
9605 this.update();
9606
9607 return latlng;
9608 },
9609
9610 _updateContent: function () {
9611 if (!this._content) { return; }
9612
9613 var node = this._contentNode;
9614 var content = (typeof this._content === 'function') ? this._content(this._source || this) : this._content;
9615
9616 if (typeof content === 'string') {
9617 node.innerHTML = content;
9618 } else {
9619 while (node.hasChildNodes()) {
9620 node.removeChild(node.firstChild);
9621 }
9622 node.appendChild(content);
9623 }
9624 this.fire('contentupdate');
9625 },
9626
9627 _updatePosition: function () {
9628 if (!this._map) { return; }
9629
9630 var pos = this._map.latLngToLayerPoint(this._latlng),
9631 offset = toPoint(this.options.offset),
9632 anchor = this._getAnchor();
9633
9634 if (this._zoomAnimated) {
9635 setPosition(this._container, pos.add(anchor));
9636 } else {
9637 offset = offset.add(pos).add(anchor);
9638 }
9639
9640 var bottom = this._containerBottom = -offset.y,
9641 left = this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x;
9642
9643 // bottom position the popup in case the height of the popup changes (images loading etc)
9644 this._container.style.bottom = bottom + 'px';
9645 this._container.style.left = left + 'px';
9646 },
9647
9648 _getAnchor: function () {
9649 return [0, 0];
9650 }
9651
9652 });
9653
9654 /*
9655 * @class Popup
9656 * @inherits DivOverlay
9657 * @aka L.Popup
9658 * Used to open popups in certain places of the map. Use [Map.openPopup](#map-openpopup) to
9659 * open popups while making sure that only one popup is open at one time
9660 * (recommended for usability), or use [Map.addLayer](#map-addlayer) to open as many as you want.
9661 *
9662 * @example
9663 *
9664 * If you want to just bind a popup to marker click and then open it, it's really easy:
9665 *
9666 * ```js
9667 * marker.bindPopup(popupContent).openPopup();
9668 * ```
9669 * Path overlays like polylines also have a `bindPopup` method.
9670 * Here's a more complicated way to open a popup on a map:
9671 *
9672 * ```js
9673 * var popup = L.popup()
9674 * .setLatLng(latlng)
9675 * .setContent('<p>Hello world!<br />This is a nice popup.</p>')
9676 * .openOn(map);
9677 * ```
9678 */
9679
9680
9681 // @namespace Popup
9682 var Popup = DivOverlay.extend({
9683
9684 // @section
9685 // @aka Popup options
9686 options: {
9687 // @option maxWidth: Number = 300
9688 // Max width of the popup, in pixels.
9689 maxWidth: 300,
9690
9691 // @option minWidth: Number = 50
9692 // Min width of the popup, in pixels.
9693 minWidth: 50,
9694
9695 // @option maxHeight: Number = null
9696 // If set, creates a scrollable container of the given height
9697 // inside a popup if its content exceeds it.
9698 maxHeight: null,
9699
9700 // @option autoPan: Boolean = true
9701 // Set it to `false` if you don't want the map to do panning animation
9702 // to fit the opened popup.
9703 autoPan: true,
9704
9705 // @option autoPanPaddingTopLeft: Point = null
9706 // The margin between the popup and the top left corner of the map
9707 // view after autopanning was performed.
9708 autoPanPaddingTopLeft: null,
9709
9710 // @option autoPanPaddingBottomRight: Point = null
9711 // The margin between the popup and the bottom right corner of the map
9712 // view after autopanning was performed.
9713 autoPanPaddingBottomRight: null,
9714
9715 // @option autoPanPadding: Point = Point(5, 5)
9716 // Equivalent of setting both top left and bottom right autopan padding to the same value.
9717 autoPanPadding: [5, 5],
9718
9719 // @option keepInView: Boolean = false
9720 // Set it to `true` if you want to prevent users from panning the popup
9721 // off of the screen while it is open.
9722 keepInView: false,
9723
9724 // @option closeButton: Boolean = true
9725 // Controls the presence of a close button in the popup.
9726 closeButton: true,
9727
9728 // @option autoClose: Boolean = true
9729 // Set it to `false` if you want to override the default behavior of
9730 // the popup closing when another popup is opened.
9731 autoClose: true,
9732
9733 // @option closeOnEscapeKey: Boolean = true
9734 // Set it to `false` if you want to override the default behavior of
9735 // the ESC key for closing of the popup.
9736 closeOnEscapeKey: true,
9737
9738 // @option closeOnClick: Boolean = *
9739 // Set it if you want to override the default behavior of the popup closing when user clicks
9740 // on the map. Defaults to the map's [`closePopupOnClick`](#map-closepopuponclick) option.
9741
9742 // @option className: String = ''
9743 // A custom CSS class name to assign to the popup.
9744 className: ''
9745 },
9746
9747 // @namespace Popup
9748 // @method openOn(map: Map): this
9749 // Adds the popup to the map and closes the previous one. The same as `map.openPopup(popup)`.
9750 openOn: function (map) {
9751 map.openPopup(this);
9752 return this;
9753 },
9754
9755 onAdd: function (map) {
9756 DivOverlay.prototype.onAdd.call(this, map);
9757
9758 // @namespace Map
9759 // @section Popup events
9760 // @event popupopen: PopupEvent
9761 // Fired when a popup is opened in the map
9762 map.fire('popupopen', {popup: this});
9763
9764 if (this._source) {
9765 // @namespace Layer
9766 // @section Popup events
9767 // @event popupopen: PopupEvent
9768 // Fired when a popup bound to this layer is opened
9769 this._source.fire('popupopen', {popup: this}, true);
9770 // For non-path layers, we toggle the popup when clicking
9771 // again the layer, so prevent the map to reopen it.
9772 if (!(this._source instanceof Path)) {
9773 this._source.on('preclick', stopPropagation);
9774 }
9775 }
9776 },
9777
9778 onRemove: function (map) {
9779 DivOverlay.prototype.onRemove.call(this, map);
9780
9781 // @namespace Map
9782 // @section Popup events
9783 // @event popupclose: PopupEvent
9784 // Fired when a popup in the map is closed
9785 map.fire('popupclose', {popup: this});
9786
9787 if (this._source) {
9788 // @namespace Layer
9789 // @section Popup events
9790 // @event popupclose: PopupEvent
9791 // Fired when a popup bound to this layer is closed
9792 this._source.fire('popupclose', {popup: this}, true);
9793 if (!(this._source instanceof Path)) {
9794 this._source.off('preclick', stopPropagation);
9795 }
9796 }
9797 },
9798
9799 getEvents: function () {
9800 var events = DivOverlay.prototype.getEvents.call(this);
9801
9802 if (this.options.closeOnClick !== undefined ? this.options.closeOnClick : this._map.options.closePopupOnClick) {
9803 events.preclick = this._close;
9804 }
9805
9806 if (this.options.keepInView) {
9807 events.moveend = this._adjustPan;
9808 }
9809
9810 return events;
9811 },
9812
9813 _close: function () {
9814 if (this._map) {
9815 this._map.closePopup(this);
9816 }
9817 },
9818
9819 _initLayout: function () {
9820 var prefix = 'leaflet-popup',
9821 container = this._container = create$1('div',
9822 prefix + ' ' + (this.options.className || '') +
9823 ' leaflet-zoom-animated');
9824
9825 var wrapper = this._wrapper = create$1('div', prefix + '-content-wrapper', container);
9826 this._contentNode = create$1('div', prefix + '-content', wrapper);
9827
9828 disableClickPropagation(container);
9829 disableScrollPropagation(this._contentNode);
9830 on(container, 'contextmenu', stopPropagation);
9831
9832 this._tipContainer = create$1('div', prefix + '-tip-container', container);
9833 this._tip = create$1('div', prefix + '-tip', this._tipContainer);
9834
9835 if (this.options.closeButton) {
9836 var closeButton = this._closeButton = create$1('a', prefix + '-close-button', container);
9837 closeButton.href = '#close';
9838 closeButton.innerHTML = '&#215;';
9839
9840 on(closeButton, 'click', this._onCloseButtonClick, this);
9841 }
9842 },
9843
9844 _updateLayout: function () {
9845 var container = this._contentNode,
9846 style = container.style;
9847
9848 style.width = '';
9849 style.whiteSpace = 'nowrap';
9850
9851 var width = container.offsetWidth;
9852 width = Math.min(width, this.options.maxWidth);
9853 width = Math.max(width, this.options.minWidth);
9854
9855 style.width = (width + 1) + 'px';
9856 style.whiteSpace = '';
9857
9858 style.height = '';
9859
9860 var height = container.offsetHeight,
9861 maxHeight = this.options.maxHeight,
9862 scrolledClass = 'leaflet-popup-scrolled';
9863
9864 if (maxHeight && height > maxHeight) {
9865 style.height = maxHeight + 'px';
9866 addClass(container, scrolledClass);
9867 } else {
9868 removeClass(container, scrolledClass);
9869 }
9870
9871 this._containerWidth = this._container.offsetWidth;
9872 },
9873
9874 _animateZoom: function (e) {
9875 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center),
9876 anchor = this._getAnchor();
9877 setPosition(this._container, pos.add(anchor));
9878 },
9879
9880 _adjustPan: function () {
9881 if (!this.options.autoPan) { return; }
9882 if (this._map._panAnim) { this._map._panAnim.stop(); }
9883
9884 var map = this._map,
9885 marginBottom = parseInt(getStyle(this._container, 'marginBottom'), 10) || 0,
9886 containerHeight = this._container.offsetHeight + marginBottom,
9887 containerWidth = this._containerWidth,
9888 layerPos = new Point(this._containerLeft, -containerHeight - this._containerBottom);
9889
9890 layerPos._add(getPosition(this._container));
9891
9892 var containerPos = map.layerPointToContainerPoint(layerPos),
9893 padding = toPoint(this.options.autoPanPadding),
9894 paddingTL = toPoint(this.options.autoPanPaddingTopLeft || padding),
9895 paddingBR = toPoint(this.options.autoPanPaddingBottomRight || padding),
9896 size = map.getSize(),
9897 dx = 0,
9898 dy = 0;
9899
9900 if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
9901 dx = containerPos.x + containerWidth - size.x + paddingBR.x;
9902 }
9903 if (containerPos.x - dx - paddingTL.x < 0) { // left
9904 dx = containerPos.x - paddingTL.x;
9905 }
9906 if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
9907 dy = containerPos.y + containerHeight - size.y + paddingBR.y;
9908 }
9909 if (containerPos.y - dy - paddingTL.y < 0) { // top
9910 dy = containerPos.y - paddingTL.y;
9911 }
9912
9913 // @namespace Map
9914 // @section Popup events
9915 // @event autopanstart: Event
9916 // Fired when the map starts autopanning when opening a popup.
9917 if (dx || dy) {
9918 map
9919 .fire('autopanstart')
9920 .panBy([dx, dy]);
9921 }
9922 },
9923
9924 _onCloseButtonClick: function (e) {
9925 this._close();
9926 stop(e);
9927 },
9928
9929 _getAnchor: function () {
9930 // Where should we anchor the popup on the source layer?
9931 return toPoint(this._source && this._source._getPopupAnchor ? this._source._getPopupAnchor() : [0, 0]);
9932 }
9933
9934 });
9935
9936 // @namespace Popup
9937 // @factory L.popup(options?: Popup options, source?: Layer)
9938 // Instantiates a `Popup` object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the popup with a reference to the Layer to which it refers.
9939 var popup = function (options, source) {
9940 return new Popup(options, source);
9941 };
9942
9943
9944 /* @namespace Map
9945 * @section Interaction Options
9946 * @option closePopupOnClick: Boolean = true
9947 * Set it to `false` if you don't want popups to close when user clicks the map.
9948 */
9949 Map.mergeOptions({
9950 closePopupOnClick: true
9951 });
9952
9953
9954 // @namespace Map
9955 // @section Methods for Layers and Controls
9956 Map.include({
9957 // @method openPopup(popup: Popup): this
9958 // Opens the specified popup while closing the previously opened (to make sure only one is opened at one time for usability).
9959 // @alternative
9960 // @method openPopup(content: String|HTMLElement, latlng: LatLng, options?: Popup options): this
9961 // Creates a popup with the specified content and options and opens it in the given point on a map.
9962 openPopup: function (popup, latlng, options) {
9963 if (!(popup instanceof Popup)) {
9964 popup = new Popup(options).setContent(popup);
9965 }
9966
9967 if (latlng) {
9968 popup.setLatLng(latlng);
9969 }
9970
9971 if (this.hasLayer(popup)) {
9972 return this;
9973 }
9974
9975 if (this._popup && this._popup.options.autoClose) {
9976 this.closePopup();
9977 }
9978
9979 this._popup = popup;
9980 return this.addLayer(popup);
9981 },
9982
9983 // @method closePopup(popup?: Popup): this
9984 // Closes the popup previously opened with [openPopup](#map-openpopup) (or the given one).
9985 closePopup: function (popup) {
9986 if (!popup || popup === this._popup) {
9987 popup = this._popup;
9988 this._popup = null;
9989 }
9990 if (popup) {
9991 this.removeLayer(popup);
9992 }
9993 return this;
9994 }
9995 });
9996
9997 /*
9998 * @namespace Layer
9999 * @section Popup methods example
10000 *
10001 * All layers share a set of methods convenient for binding popups to it.
10002 *
10003 * ```js
10004 * var layer = L.Polygon(latlngs).bindPopup('Hi There!').addTo(map);
10005 * layer.openPopup();
10006 * layer.closePopup();
10007 * ```
10008 *
10009 * Popups will also be automatically opened when the layer is clicked on and closed when the layer is removed from the map or another popup is opened.
10010 */
10011
10012 // @section Popup methods
10013 Layer.include({
10014
10015 // @method bindPopup(content: String|HTMLElement|Function|Popup, options?: Popup options): this
10016 // Binds a popup to the layer with the passed `content` and sets up the
10017 // necessary event listeners. If a `Function` is passed it will receive
10018 // the layer as the first argument and should return a `String` or `HTMLElement`.
10019 bindPopup: function (content, options) {
10020
10021 if (content instanceof Popup) {
10022 setOptions(content, options);
10023 this._popup = content;
10024 content._source = this;
10025 } else {
10026 if (!this._popup || options) {
10027 this._popup = new Popup(options, this);
10028 }
10029 this._popup.setContent(content);
10030 }
10031
10032 if (!this._popupHandlersAdded) {
10033 this.on({
10034 click: this._openPopup,
10035 keypress: this._onKeyPress,
10036 remove: this.closePopup,
10037 move: this._movePopup
10038 });
10039 this._popupHandlersAdded = true;
10040 }
10041
10042 return this;
10043 },
10044
10045 // @method unbindPopup(): this
10046 // Removes the popup previously bound with `bindPopup`.
10047 unbindPopup: function () {
10048 if (this._popup) {
10049 this.off({
10050 click: this._openPopup,
10051 keypress: this._onKeyPress,
10052 remove: this.closePopup,
10053 move: this._movePopup
10054 });
10055 this._popupHandlersAdded = false;
10056 this._popup = null;
10057 }
10058 return this;
10059 },
10060
10061 // @method openPopup(latlng?: LatLng): this
10062 // Opens the bound popup at the specified `latlng` or at the default popup anchor if no `latlng` is passed.
10063 openPopup: function (layer, latlng) {
10064 if (this._popup && this._map) {
10065 latlng = this._popup._prepareOpen(this, layer, latlng);
10066
10067 // open the popup on the map
10068 this._map.openPopup(this._popup, latlng);
10069 }
10070
10071 return this;
10072 },
10073
10074 // @method closePopup(): this
10075 // Closes the popup bound to this layer if it is open.
10076 closePopup: function () {
10077 if (this._popup) {
10078 this._popup._close();
10079 }
10080 return this;
10081 },
10082
10083 // @method togglePopup(): this
10084 // Opens or closes the popup bound to this layer depending on its current state.
10085 togglePopup: function (target) {
10086 if (this._popup) {
10087 if (this._popup._map) {
10088 this.closePopup();
10089 } else {
10090 this.openPopup(target);
10091 }
10092 }
10093 return this;
10094 },
10095
10096 // @method isPopupOpen(): boolean
10097 // Returns `true` if the popup bound to this layer is currently open.
10098 isPopupOpen: function () {
10099 return (this._popup ? this._popup.isOpen() : false);
10100 },
10101
10102 // @method setPopupContent(content: String|HTMLElement|Popup): this
10103 // Sets the content of the popup bound to this layer.
10104 setPopupContent: function (content) {
10105 if (this._popup) {
10106 this._popup.setContent(content);
10107 }
10108 return this;
10109 },
10110
10111 // @method getPopup(): Popup
10112 // Returns the popup bound to this layer.
10113 getPopup: function () {
10114 return this._popup;
10115 },
10116
10117 _openPopup: function (e) {
10118 var layer = e.layer || e.target;
10119
10120 if (!this._popup) {
10121 return;
10122 }
10123
10124 if (!this._map) {
10125 return;
10126 }
10127
10128 // prevent map click
10129 stop(e);
10130
10131 // if this inherits from Path its a vector and we can just
10132 // open the popup at the new location
10133 if (layer instanceof Path) {
10134 this.openPopup(e.layer || e.target, e.latlng);
10135 return;
10136 }
10137
10138 // otherwise treat it like a marker and figure out
10139 // if we should toggle it open/closed
10140 if (this._map.hasLayer(this._popup) && this._popup._source === layer) {
10141 this.closePopup();
10142 } else {
10143 this.openPopup(layer, e.latlng);
10144 }
10145 },
10146
10147 _movePopup: function (e) {
10148 this._popup.setLatLng(e.latlng);
10149 },
10150
10151 _onKeyPress: function (e) {
10152 if (e.originalEvent.keyCode === 13) {
10153 this._openPopup(e);
10154 }
10155 }
10156 });
10157
10158 /*
10159 * @class Tooltip
10160 * @inherits DivOverlay
10161 * @aka L.Tooltip
10162 * Used to display small texts on top of map layers.
10163 *
10164 * @example
10165 *
10166 * ```js
10167 * marker.bindTooltip("my tooltip text").openTooltip();
10168 * ```
10169 * Note about tooltip offset. Leaflet takes two options in consideration
10170 * for computing tooltip offsetting:
10171 * - the `offset` Tooltip option: it defaults to [0, 0], and it's specific to one tooltip.
10172 * Add a positive x offset to move the tooltip to the right, and a positive y offset to
10173 * move it to the bottom. Negatives will move to the left and top.
10174 * - the `tooltipAnchor` Icon option: this will only be considered for Marker. You
10175 * should adapt this value if you use a custom icon.
10176 */
10177
10178
10179 // @namespace Tooltip
10180 var Tooltip = DivOverlay.extend({
10181
10182 // @section
10183 // @aka Tooltip options
10184 options: {
10185 // @option pane: String = 'tooltipPane'
10186 // `Map pane` where the tooltip will be added.
10187 pane: 'tooltipPane',
10188
10189 // @option offset: Point = Point(0, 0)
10190 // Optional offset of the tooltip position.
10191 offset: [0, 0],
10192
10193 // @option direction: String = 'auto'
10194 // Direction where to open the tooltip. Possible values are: `right`, `left`,
10195 // `top`, `bottom`, `center`, `auto`.
10196 // `auto` will dynamically switch between `right` and `left` according to the tooltip
10197 // position on the map.
10198 direction: 'auto',
10199
10200 // @option permanent: Boolean = false
10201 // Whether to open the tooltip permanently or only on mouseover.
10202 permanent: false,
10203
10204 // @option sticky: Boolean = false
10205 // If true, the tooltip will follow the mouse instead of being fixed at the feature center.
10206 sticky: false,
10207
10208 // @option interactive: Boolean = false
10209 // If true, the tooltip will listen to the feature events.
10210 interactive: false,
10211
10212 // @option opacity: Number = 0.9
10213 // Tooltip container opacity.
10214 opacity: 0.9
10215 },
10216
10217 onAdd: function (map) {
10218 DivOverlay.prototype.onAdd.call(this, map);
10219 this.setOpacity(this.options.opacity);
10220
10221 // @namespace Map
10222 // @section Tooltip events
10223 // @event tooltipopen: TooltipEvent
10224 // Fired when a tooltip is opened in the map.
10225 map.fire('tooltipopen', {tooltip: this});
10226
10227 if (this._source) {
10228 // @namespace Layer
10229 // @section Tooltip events
10230 // @event tooltipopen: TooltipEvent
10231 // Fired when a tooltip bound to this layer is opened.
10232 this._source.fire('tooltipopen', {tooltip: this}, true);
10233 }
10234 },
10235
10236 onRemove: function (map) {
10237 DivOverlay.prototype.onRemove.call(this, map);
10238
10239 // @namespace Map
10240 // @section Tooltip events
10241 // @event tooltipclose: TooltipEvent
10242 // Fired when a tooltip in the map is closed.
10243 map.fire('tooltipclose', {tooltip: this});
10244
10245 if (this._source) {
10246 // @namespace Layer
10247 // @section Tooltip events
10248 // @event tooltipclose: TooltipEvent
10249 // Fired when a tooltip bound to this layer is closed.
10250 this._source.fire('tooltipclose', {tooltip: this}, true);
10251 }
10252 },
10253
10254 getEvents: function () {
10255 var events = DivOverlay.prototype.getEvents.call(this);
10256
10257 if (touch && !this.options.permanent) {
10258 events.preclick = this._close;
10259 }
10260
10261 return events;
10262 },
10263
10264 _close: function () {
10265 if (this._map) {
10266 this._map.closeTooltip(this);
10267 }
10268 },
10269
10270 _initLayout: function () {
10271 var prefix = 'leaflet-tooltip',
10272 className = prefix + ' ' + (this.options.className || '') + ' leaflet-zoom-' + (this._zoomAnimated ? 'animated' : 'hide');
10273
10274 this._contentNode = this._container = create$1('div', className);
10275 },
10276
10277 _updateLayout: function () {},
10278
10279 _adjustPan: function () {},
10280
10281 _setPosition: function (pos) {
10282 var subX, subY,
10283 map = this._map,
10284 container = this._container,
10285 centerPoint = map.latLngToContainerPoint(map.getCenter()),
10286 tooltipPoint = map.layerPointToContainerPoint(pos),
10287 direction = this.options.direction,
10288 tooltipWidth = container.offsetWidth,
10289 tooltipHeight = container.offsetHeight,
10290 offset = toPoint(this.options.offset),
10291 anchor = this._getAnchor();
10292
10293 if (direction === 'top') {
10294 subX = tooltipWidth / 2;
10295 subY = tooltipHeight;
10296 } else if (direction === 'bottom') {
10297 subX = tooltipWidth / 2;
10298 subY = 0;
10299 } else if (direction === 'center') {
10300 subX = tooltipWidth / 2;
10301 subY = tooltipHeight / 2;
10302 } else if (direction === 'right') {
10303 subX = 0;
10304 subY = tooltipHeight / 2;
10305 } else if (direction === 'left') {
10306 subX = tooltipWidth;
10307 subY = tooltipHeight / 2;
10308 } else if (tooltipPoint.x < centerPoint.x) {
10309 direction = 'right';
10310 subX = 0;
10311 subY = tooltipHeight / 2;
10312 } else {
10313 direction = 'left';
10314 subX = tooltipWidth + (offset.x + anchor.x) * 2;
10315 subY = tooltipHeight / 2;
10316 }
10317
10318 pos = pos.subtract(toPoint(subX, subY, true)).add(offset).add(anchor);
10319
10320 removeClass(container, 'leaflet-tooltip-right');
10321 removeClass(container, 'leaflet-tooltip-left');
10322 removeClass(container, 'leaflet-tooltip-top');
10323 removeClass(container, 'leaflet-tooltip-bottom');
10324 addClass(container, 'leaflet-tooltip-' + direction);
10325 setPosition(container, pos);
10326 },
10327
10328 _updatePosition: function () {
10329 var pos = this._map.latLngToLayerPoint(this._latlng);
10330 this._setPosition(pos);
10331 },
10332
10333 setOpacity: function (opacity) {
10334 this.options.opacity = opacity;
10335
10336 if (this._container) {
10337 setOpacity(this._container, opacity);
10338 }
10339 },
10340
10341 _animateZoom: function (e) {
10342 var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
10343 this._setPosition(pos);
10344 },
10345
10346 _getAnchor: function () {
10347 // Where should we anchor the tooltip on the source layer?
10348 return toPoint(this._source && this._source._getTooltipAnchor && !this.options.sticky ? this._source._getTooltipAnchor() : [0, 0]);
10349 }
10350
10351 });
10352
10353 // @namespace Tooltip
10354 // @factory L.tooltip(options?: Tooltip options, source?: Layer)
10355 // Instantiates a Tooltip object given an optional `options` object that describes its appearance and location and an optional `source` object that is used to tag the tooltip with a reference to the Layer to which it refers.
10356 var tooltip = function (options, source) {
10357 return new Tooltip(options, source);
10358 };
10359
10360 // @namespace Map
10361 // @section Methods for Layers and Controls
10362 Map.include({
10363
10364 // @method openTooltip(tooltip: Tooltip): this
10365 // Opens the specified tooltip.
10366 // @alternative
10367 // @method openTooltip(content: String|HTMLElement, latlng: LatLng, options?: Tooltip options): this
10368 // Creates a tooltip with the specified content and options and open it.
10369 openTooltip: function (tooltip, latlng, options) {
10370 if (!(tooltip instanceof Tooltip)) {
10371 tooltip = new Tooltip(options).setContent(tooltip);
10372 }
10373
10374 if (latlng) {
10375 tooltip.setLatLng(latlng);
10376 }
10377
10378 if (this.hasLayer(tooltip)) {
10379 return this;
10380 }
10381
10382 return this.addLayer(tooltip);
10383 },
10384
10385 // @method closeTooltip(tooltip?: Tooltip): this
10386 // Closes the tooltip given as parameter.
10387 closeTooltip: function (tooltip) {
10388 if (tooltip) {
10389 this.removeLayer(tooltip);
10390 }
10391 return this;
10392 }
10393
10394 });
10395
10396 /*
10397 * @namespace Layer
10398 * @section Tooltip methods example
10399 *
10400 * All layers share a set of methods convenient for binding tooltips to it.
10401 *
10402 * ```js
10403 * var layer = L.Polygon(latlngs).bindTooltip('Hi There!').addTo(map);
10404 * layer.openTooltip();
10405 * layer.closeTooltip();
10406 * ```
10407 */
10408
10409 // @section Tooltip methods
10410 Layer.include({
10411
10412 // @method bindTooltip(content: String|HTMLElement|Function|Tooltip, options?: Tooltip options): this
10413 // Binds a tooltip to the layer with the passed `content` and sets up the
10414 // necessary event listeners. If a `Function` is passed it will receive
10415 // the layer as the first argument and should return a `String` or `HTMLElement`.
10416 bindTooltip: function (content, options) {
10417
10418 if (content instanceof Tooltip) {
10419 setOptions(content, options);
10420 this._tooltip = content;
10421 content._source = this;
10422 } else {
10423 if (!this._tooltip || options) {
10424 this._tooltip = new Tooltip(options, this);
10425 }
10426 this._tooltip.setContent(content);
10427
10428 }
10429
10430 this._initTooltipInteractions();
10431
10432 if (this._tooltip.options.permanent && this._map && this._map.hasLayer(this)) {
10433 this.openTooltip();
10434 }
10435
10436 return this;
10437 },
10438
10439 // @method unbindTooltip(): this
10440 // Removes the tooltip previously bound with `bindTooltip`.
10441 unbindTooltip: function () {
10442 if (this._tooltip) {
10443 this._initTooltipInteractions(true);
10444 this.closeTooltip();
10445 this._tooltip = null;
10446 }
10447 return this;
10448 },
10449
10450 _initTooltipInteractions: function (remove$$1) {
10451 if (!remove$$1 && this._tooltipHandlersAdded) { return; }
10452 var onOff = remove$$1 ? 'off' : 'on',
10453 events = {
10454 remove: this.closeTooltip,
10455 move: this._moveTooltip
10456 };
10457 if (!this._tooltip.options.permanent) {
10458 events.mouseover = this._openTooltip;
10459 events.mouseout = this.closeTooltip;
10460 if (this._tooltip.options.sticky) {
10461 events.mousemove = this._moveTooltip;
10462 }
10463 if (touch) {
10464 events.click = this._openTooltip;
10465 }
10466 } else {
10467 events.add = this._openTooltip;
10468 }
10469 this[onOff](events);
10470 this._tooltipHandlersAdded = !remove$$1;
10471 },
10472
10473 // @method openTooltip(latlng?: LatLng): this
10474 // Opens the bound tooltip at the specified `latlng` or at the default tooltip anchor if no `latlng` is passed.
10475 openTooltip: function (layer, latlng) {
10476 if (this._tooltip && this._map) {
10477 latlng = this._tooltip._prepareOpen(this, layer, latlng);
10478
10479 // open the tooltip on the map
10480 this._map.openTooltip(this._tooltip, latlng);
10481
10482 // Tooltip container may not be defined if not permanent and never
10483 // opened.
10484 if (this._tooltip.options.interactive && this._tooltip._container) {
10485 addClass(this._tooltip._container, 'leaflet-clickable');
10486 this.addInteractiveTarget(this._tooltip._container);
10487 }
10488 }
10489
10490 return this;
10491 },
10492
10493 // @method closeTooltip(): this
10494 // Closes the tooltip bound to this layer if it is open.
10495 closeTooltip: function () {
10496 if (this._tooltip) {
10497 this._tooltip._close();
10498 if (this._tooltip.options.interactive && this._tooltip._container) {
10499 removeClass(this._tooltip._container, 'leaflet-clickable');
10500 this.removeInteractiveTarget(this._tooltip._container);
10501 }
10502 }
10503 return this;
10504 },
10505
10506 // @method toggleTooltip(): this
10507 // Opens or closes the tooltip bound to this layer depending on its current state.
10508 toggleTooltip: function (target) {
10509 if (this._tooltip) {
10510 if (this._tooltip._map) {
10511 this.closeTooltip();
10512 } else {
10513 this.openTooltip(target);
10514 }
10515 }
10516 return this;
10517 },
10518
10519 // @method isTooltipOpen(): boolean
10520 // Returns `true` if the tooltip bound to this layer is currently open.
10521 isTooltipOpen: function () {
10522 return this._tooltip.isOpen();
10523 },
10524
10525 // @method setTooltipContent(content: String|HTMLElement|Tooltip): this
10526 // Sets the content of the tooltip bound to this layer.
10527 setTooltipContent: function (content) {
10528 if (this._tooltip) {
10529 this._tooltip.setContent(content);
10530 }
10531 return this;
10532 },
10533
10534 // @method getTooltip(): Tooltip
10535 // Returns the tooltip bound to this layer.
10536 getTooltip: function () {
10537 return this._tooltip;
10538 },
10539
10540 _openTooltip: function (e) {
10541 var layer = e.layer || e.target;
10542
10543 if (!this._tooltip || !this._map) {
10544 return;
10545 }
10546 this.openTooltip(layer, this._tooltip.options.sticky ? e.latlng : undefined);
10547 },
10548
10549 _moveTooltip: function (e) {
10550 var latlng = e.latlng, containerPoint, layerPoint;
10551 if (this._tooltip.options.sticky && e.originalEvent) {
10552 containerPoint = this._map.mouseEventToContainerPoint(e.originalEvent);
10553 layerPoint = this._map.containerPointToLayerPoint(containerPoint);
10554 latlng = this._map.layerPointToLatLng(layerPoint);
10555 }
10556 this._tooltip.setLatLng(latlng);
10557 }
10558 });
10559
10560 /*
10561 * @class DivIcon
10562 * @aka L.DivIcon
10563 * @inherits Icon
10564 *
10565 * Represents a lightweight icon for markers that uses a simple `<div>`
10566 * element instead of an image. Inherits from `Icon` but ignores the `iconUrl` and shadow options.
10567 *
10568 * @example
10569 * ```js
10570 * var myIcon = L.divIcon({className: 'my-div-icon'});
10571 * // you can set .my-div-icon styles in CSS
10572 *
10573 * L.marker([50.505, 30.57], {icon: myIcon}).addTo(map);
10574 * ```
10575 *
10576 * By default, it has a 'leaflet-div-icon' CSS class and is styled as a little white square with a shadow.
10577 */
10578
10579 var DivIcon = Icon.extend({
10580 options: {
10581 // @section
10582 // @aka DivIcon options
10583 iconSize: [12, 12], // also can be set through CSS
10584
10585 // iconAnchor: (Point),
10586 // popupAnchor: (Point),
10587
10588 // @option html: String|HTMLElement = ''
10589 // Custom HTML code to put inside the div element, empty by default. Alternatively,
10590 // an instance of `HTMLElement`.
10591 html: false,
10592
10593 // @option bgPos: Point = [0, 0]
10594 // Optional relative position of the background, in pixels
10595 bgPos: null,
10596
10597 className: 'leaflet-div-icon'
10598 },
10599
10600 createIcon: function (oldIcon) {
10601 var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'),
10602 options = this.options;
10603
10604 if (options.html instanceof Element) {
10605 empty(div);
10606 div.appendChild(options.html);
10607 } else {
10608 div.innerHTML = options.html !== false ? options.html : '';
10609 }
10610
10611 if (options.bgPos) {
10612 var bgPos = toPoint(options.bgPos);
10613 div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px';
10614 }
10615 this._setIconStyles(div, 'icon');
10616
10617 return div;
10618 },
10619
10620 createShadow: function () {
10621 return null;
10622 }
10623 });
10624
10625 // @factory L.divIcon(options: DivIcon options)
10626 // Creates a `DivIcon` instance with the given options.
10627 function divIcon(options) {
10628 return new DivIcon(options);
10629 }
10630
10631 Icon.Default = IconDefault;
10632
10633 /*
10634 * @class GridLayer
10635 * @inherits Layer
10636 * @aka L.GridLayer
10637 *
10638 * Generic class for handling a tiled grid of HTML elements. This is the base class for all tile layers and replaces `TileLayer.Canvas`.
10639 * GridLayer can be extended to create a tiled grid of HTML elements like `<canvas>`, `<img>` or `<div>`. GridLayer will handle creating and animating these DOM elements for you.
10640 *
10641 *
10642 * @section Synchronous usage
10643 * @example
10644 *
10645 * To create a custom layer, extend GridLayer and implement the `createTile()` method, which will be passed a `Point` object with the `x`, `y`, and `z` (zoom level) coordinates to draw your tile.
10646 *
10647 * ```js
10648 * var CanvasLayer = L.GridLayer.extend({
10649 * createTile: function(coords){
10650 * // create a <canvas> element for drawing
10651 * var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10652 *
10653 * // setup tile width and height according to the options
10654 * var size = this.getTileSize();
10655 * tile.width = size.x;
10656 * tile.height = size.y;
10657 *
10658 * // get a canvas context and draw something on it using coords.x, coords.y and coords.z
10659 * var ctx = tile.getContext('2d');
10660 *
10661 * // return the tile so it can be rendered on screen
10662 * return tile;
10663 * }
10664 * });
10665 * ```
10666 *
10667 * @section Asynchronous usage
10668 * @example
10669 *
10670 * Tile creation can also be asynchronous, this is useful when using a third-party drawing library. Once the tile is finished drawing it can be passed to the `done()` callback.
10671 *
10672 * ```js
10673 * var CanvasLayer = L.GridLayer.extend({
10674 * createTile: function(coords, done){
10675 * var error;
10676 *
10677 * // create a <canvas> element for drawing
10678 * var tile = L.DomUtil.create('canvas', 'leaflet-tile');
10679 *
10680 * // setup tile width and height according to the options
10681 * var size = this.getTileSize();
10682 * tile.width = size.x;
10683 * tile.height = size.y;
10684 *
10685 * // draw something asynchronously and pass the tile to the done() callback
10686 * setTimeout(function() {
10687 * done(error, tile);
10688 * }, 1000);
10689 *
10690 * return tile;
10691 * }
10692 * });
10693 * ```
10694 *
10695 * @section
10696 */
10697
10698
10699 var GridLayer = Layer.extend({
10700
10701 // @section
10702 // @aka GridLayer options
10703 options: {
10704 // @option tileSize: Number|Point = 256
10705 // Width and height of tiles in the grid. Use a number if width and height are equal, or `L.point(width, height)` otherwise.
10706 tileSize: 256,
10707
10708 // @option opacity: Number = 1.0
10709 // Opacity of the tiles. Can be used in the `createTile()` function.
10710 opacity: 1,
10711
10712 // @option updateWhenIdle: Boolean = (depends)
10713 // Load new tiles only when panning ends.
10714 // `true` by default on mobile browsers, in order to avoid too many requests and keep smooth navigation.
10715 // `false` otherwise in order to display new tiles _during_ panning, since it is easy to pan outside the
10716 // [`keepBuffer`](#gridlayer-keepbuffer) option in desktop browsers.
10717 updateWhenIdle: mobile,
10718
10719 // @option updateWhenZooming: Boolean = true
10720 // By default, a smooth zoom animation (during a [touch zoom](#map-touchzoom) or a [`flyTo()`](#map-flyto)) will update grid layers every integer zoom level. Setting this option to `false` will update the grid layer only when the smooth animation ends.
10721 updateWhenZooming: true,
10722
10723 // @option updateInterval: Number = 200
10724 // Tiles will not update more than once every `updateInterval` milliseconds when panning.
10725 updateInterval: 200,
10726
10727 // @option zIndex: Number = 1
10728 // The explicit zIndex of the tile layer.
10729 zIndex: 1,
10730
10731 // @option bounds: LatLngBounds = undefined
10732 // If set, tiles will only be loaded inside the set `LatLngBounds`.
10733 bounds: null,
10734
10735 // @option minZoom: Number = 0
10736 // The minimum zoom level down to which this layer will be displayed (inclusive).
10737 minZoom: 0,
10738
10739 // @option maxZoom: Number = undefined
10740 // The maximum zoom level up to which this layer will be displayed (inclusive).
10741 maxZoom: undefined,
10742
10743 // @option maxNativeZoom: Number = undefined
10744 // Maximum zoom number the tile source has available. If it is specified,
10745 // the tiles on all zoom levels higher than `maxNativeZoom` will be loaded
10746 // from `maxNativeZoom` level and auto-scaled.
10747 maxNativeZoom: undefined,
10748
10749 // @option minNativeZoom: Number = undefined
10750 // Minimum zoom number the tile source has available. If it is specified,
10751 // the tiles on all zoom levels lower than `minNativeZoom` will be loaded
10752 // from `minNativeZoom` level and auto-scaled.
10753 minNativeZoom: undefined,
10754
10755 // @option noWrap: Boolean = false
10756 // Whether the layer is wrapped around the antimeridian. If `true`, the
10757 // GridLayer will only be displayed once at low zoom levels. Has no
10758 // effect when the [map CRS](#map-crs) doesn't wrap around. Can be used
10759 // in combination with [`bounds`](#gridlayer-bounds) to prevent requesting
10760 // tiles outside the CRS limits.
10761 noWrap: false,
10762
10763 // @option pane: String = 'tilePane'
10764 // `Map pane` where the grid layer will be added.
10765 pane: 'tilePane',
10766
10767 // @option className: String = ''
10768 // A custom class name to assign to the tile layer. Empty by default.
10769 className: '',
10770
10771 // @option keepBuffer: Number = 2
10772 // When panning the map, keep this many rows and columns of tiles before unloading them.
10773 keepBuffer: 2
10774 },
10775
10776 initialize: function (options) {
10777 setOptions(this, options);
10778 },
10779
10780 onAdd: function () {
10781 this._initContainer();
10782
10783 this._levels = {};
10784 this._tiles = {};
10785
10786 this._resetView();
10787 this._update();
10788 },
10789
10790 beforeAdd: function (map) {
10791 map._addZoomLimit(this);
10792 },
10793
10794 onRemove: function (map) {
10795 this._removeAllTiles();
10796 remove(this._container);
10797 map._removeZoomLimit(this);
10798 this._container = null;
10799 this._tileZoom = undefined;
10800 },
10801
10802 // @method bringToFront: this
10803 // Brings the tile layer to the top of all tile layers.
10804 bringToFront: function () {
10805 if (this._map) {
10806 toFront(this._container);
10807 this._setAutoZIndex(Math.max);
10808 }
10809 return this;
10810 },
10811
10812 // @method bringToBack: this
10813 // Brings the tile layer to the bottom of all tile layers.
10814 bringToBack: function () {
10815 if (this._map) {
10816 toBack(this._container);
10817 this._setAutoZIndex(Math.min);
10818 }
10819 return this;
10820 },
10821
10822 // @method getContainer: HTMLElement
10823 // Returns the HTML element that contains the tiles for this layer.
10824 getContainer: function () {
10825 return this._container;
10826 },
10827
10828 // @method setOpacity(opacity: Number): this
10829 // Changes the [opacity](#gridlayer-opacity) of the grid layer.
10830 setOpacity: function (opacity) {
10831 this.options.opacity = opacity;
10832 this._updateOpacity();
10833 return this;
10834 },
10835
10836 // @method setZIndex(zIndex: Number): this
10837 // Changes the [zIndex](#gridlayer-zindex) of the grid layer.
10838 setZIndex: function (zIndex) {
10839 this.options.zIndex = zIndex;
10840 this._updateZIndex();
10841
10842 return this;
10843 },
10844
10845 // @method isLoading: Boolean
10846 // Returns `true` if any tile in the grid layer has not finished loading.
10847 isLoading: function () {
10848 return this._loading;
10849 },
10850
10851 // @method redraw: this
10852 // Causes the layer to clear all the tiles and request them again.
10853 redraw: function () {
10854 if (this._map) {
10855 this._removeAllTiles();
10856 this._update();
10857 }
10858 return this;
10859 },
10860
10861 getEvents: function () {
10862 var events = {
10863 viewprereset: this._invalidateAll,
10864 viewreset: this._resetView,
10865 zoom: this._resetView,
10866 moveend: this._onMoveEnd
10867 };
10868
10869 if (!this.options.updateWhenIdle) {
10870 // update tiles on move, but not more often than once per given interval
10871 if (!this._onMove) {
10872 this._onMove = throttle(this._onMoveEnd, this.options.updateInterval, this);
10873 }
10874
10875 events.move = this._onMove;
10876 }
10877
10878 if (this._zoomAnimated) {
10879 events.zoomanim = this._animateZoom;
10880 }
10881
10882 return events;
10883 },
10884
10885 // @section Extension methods
10886 // Layers extending `GridLayer` shall reimplement the following method.
10887 // @method createTile(coords: Object, done?: Function): HTMLElement
10888 // Called only internally, must be overridden by classes extending `GridLayer`.
10889 // Returns the `HTMLElement` corresponding to the given `coords`. If the `done` callback
10890 // is specified, it must be called when the tile has finished loading and drawing.
10891 createTile: function () {
10892 return document.createElement('div');
10893 },
10894
10895 // @section
10896 // @method getTileSize: Point
10897 // Normalizes the [tileSize option](#gridlayer-tilesize) into a point. Used by the `createTile()` method.
10898 getTileSize: function () {
10899 var s = this.options.tileSize;
10900 return s instanceof Point ? s : new Point(s, s);
10901 },
10902
10903 _updateZIndex: function () {
10904 if (this._container && this.options.zIndex !== undefined && this.options.zIndex !== null) {
10905 this._container.style.zIndex = this.options.zIndex;
10906 }
10907 },
10908
10909 _setAutoZIndex: function (compare) {
10910 // go through all other layers of the same pane, set zIndex to max + 1 (front) or min - 1 (back)
10911
10912 var layers = this.getPane().children,
10913 edgeZIndex = -compare(-Infinity, Infinity); // -Infinity for max, Infinity for min
10914
10915 for (var i = 0, len = layers.length, zIndex; i < len; i++) {
10916
10917 zIndex = layers[i].style.zIndex;
10918
10919 if (layers[i] !== this._container && zIndex) {
10920 edgeZIndex = compare(edgeZIndex, +zIndex);
10921 }
10922 }
10923
10924 if (isFinite(edgeZIndex)) {
10925 this.options.zIndex = edgeZIndex + compare(-1, 1);
10926 this._updateZIndex();
10927 }
10928 },
10929
10930 _updateOpacity: function () {
10931 if (!this._map) { return; }
10932
10933 // IE doesn't inherit filter opacity properly, so we're forced to set it on tiles
10934 if (ielt9) { return; }
10935
10936 setOpacity(this._container, this.options.opacity);
10937
10938 var now = +new Date(),
10939 nextFrame = false,
10940 willPrune = false;
10941
10942 for (var key in this._tiles) {
10943 var tile = this._tiles[key];
10944 if (!tile.current || !tile.loaded) { continue; }
10945
10946 var fade = Math.min(1, (now - tile.loaded) / 200);
10947
10948 setOpacity(tile.el, fade);
10949 if (fade < 1) {
10950 nextFrame = true;
10951 } else {
10952 if (tile.active) {
10953 willPrune = true;
10954 } else {
10955 this._onOpaqueTile(tile);
10956 }
10957 tile.active = true;
10958 }
10959 }
10960
10961 if (willPrune && !this._noPrune) { this._pruneTiles(); }
10962
10963 if (nextFrame) {
10964 cancelAnimFrame(this._fadeFrame);
10965 this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
10966 }
10967 },
10968
10969 _onOpaqueTile: falseFn,
10970
10971 _initContainer: function () {
10972 if (this._container) { return; }
10973
10974 this._container = create$1('div', 'leaflet-layer ' + (this.options.className || ''));
10975 this._updateZIndex();
10976
10977 if (this.options.opacity < 1) {
10978 this._updateOpacity();
10979 }
10980
10981 this.getPane().appendChild(this._container);
10982 },
10983
10984 _updateLevels: function () {
10985
10986 var zoom = this._tileZoom,
10987 maxZoom = this.options.maxZoom;
10988
10989 if (zoom === undefined) { return undefined; }
10990
10991 for (var z in this._levels) {
10992 z = Number(z);
10993 if (this._levels[z].el.children.length || z === zoom) {
10994 this._levels[z].el.style.zIndex = maxZoom - Math.abs(zoom - z);
10995 this._onUpdateLevel(z);
10996 } else {
10997 remove(this._levels[z].el);
10998 this._removeTilesAtZoom(z);
10999 this._onRemoveLevel(z);
11000 delete this._levels[z];
11001 }
11002 }
11003
11004 var level = this._levels[zoom],
11005 map = this._map;
11006
11007 if (!level) {
11008 level = this._levels[zoom] = {};
11009
11010 level.el = create$1('div', 'leaflet-tile-container leaflet-zoom-animated', this._container);
11011 level.el.style.zIndex = maxZoom;
11012
11013 level.origin = map.project(map.unproject(map.getPixelOrigin()), zoom).round();
11014 level.zoom = zoom;
11015
11016 this._setZoomTransform(level, map.getCenter(), map.getZoom());
11017
11018 // force the browser to consider the newly added element for transition
11019 falseFn(level.el.offsetWidth);
11020
11021 this._onCreateLevel(level);
11022 }
11023
11024 this._level = level;
11025
11026 return level;
11027 },
11028
11029 _onUpdateLevel: falseFn,
11030
11031 _onRemoveLevel: falseFn,
11032
11033 _onCreateLevel: falseFn,
11034
11035 _pruneTiles: function () {
11036 if (!this._map) {
11037 return;
11038 }
11039
11040 var key, tile;
11041
11042 var zoom = this._map.getZoom();
11043 if (zoom > this.options.maxZoom ||
11044 zoom < this.options.minZoom) {
11045 this._removeAllTiles();
11046 return;
11047 }
11048
11049 for (key in this._tiles) {
11050 tile = this._tiles[key];
11051 tile.retain = tile.current;
11052 }
11053
11054 for (key in this._tiles) {
11055 tile = this._tiles[key];
11056 if (tile.current && !tile.active) {
11057 var coords = tile.coords;
11058 if (!this._retainParent(coords.x, coords.y, coords.z, coords.z - 5)) {
11059 this._retainChildren(coords.x, coords.y, coords.z, coords.z + 2);
11060 }
11061 }
11062 }
11063
11064 for (key in this._tiles) {
11065 if (!this._tiles[key].retain) {
11066 this._removeTile(key);
11067 }
11068 }
11069 },
11070
11071 _removeTilesAtZoom: function (zoom) {
11072 for (var key in this._tiles) {
11073 if (this._tiles[key].coords.z !== zoom) {
11074 continue;
11075 }
11076 this._removeTile(key);
11077 }
11078 },
11079
11080 _removeAllTiles: function () {
11081 for (var key in this._tiles) {
11082 this._removeTile(key);
11083 }
11084 },
11085
11086 _invalidateAll: function () {
11087 for (var z in this._levels) {
11088 remove(this._levels[z].el);
11089 this._onRemoveLevel(Number(z));
11090 delete this._levels[z];
11091 }
11092 this._removeAllTiles();
11093
11094 this._tileZoom = undefined;
11095 },
11096
11097 _retainParent: function (x, y, z, minZoom) {
11098 var x2 = Math.floor(x / 2),
11099 y2 = Math.floor(y / 2),
11100 z2 = z - 1,
11101 coords2 = new Point(+x2, +y2);
11102 coords2.z = +z2;
11103
11104 var key = this._tileCoordsToKey(coords2),
11105 tile = this._tiles[key];
11106
11107 if (tile && tile.active) {
11108 tile.retain = true;
11109 return true;
11110
11111 } else if (tile && tile.loaded) {
11112 tile.retain = true;
11113 }
11114
11115 if (z2 > minZoom) {
11116 return this._retainParent(x2, y2, z2, minZoom);
11117 }
11118
11119 return false;
11120 },
11121
11122 _retainChildren: function (x, y, z, maxZoom) {
11123
11124 for (var i = 2 * x; i < 2 * x + 2; i++) {
11125 for (var j = 2 * y; j < 2 * y + 2; j++) {
11126
11127 var coords = new Point(i, j);
11128 coords.z = z + 1;
11129
11130 var key = this._tileCoordsToKey(coords),
11131 tile = this._tiles[key];
11132
11133 if (tile && tile.active) {
11134 tile.retain = true;
11135 continue;
11136
11137 } else if (tile && tile.loaded) {
11138 tile.retain = true;
11139 }
11140
11141 if (z + 1 < maxZoom) {
11142 this._retainChildren(i, j, z + 1, maxZoom);
11143 }
11144 }
11145 }
11146 },
11147
11148 _resetView: function (e) {
11149 var animating = e && (e.pinch || e.flyTo);
11150 this._setView(this._map.getCenter(), this._map.getZoom(), animating, animating);
11151 },
11152
11153 _animateZoom: function (e) {
11154 this._setView(e.center, e.zoom, true, e.noUpdate);
11155 },
11156
11157 _clampZoom: function (zoom) {
11158 var options = this.options;
11159
11160 if (undefined !== options.minNativeZoom && zoom < options.minNativeZoom) {
11161 return options.minNativeZoom;
11162 }
11163
11164 if (undefined !== options.maxNativeZoom && options.maxNativeZoom < zoom) {
11165 return options.maxNativeZoom;
11166 }
11167
11168 return zoom;
11169 },
11170
11171 _setView: function (center, zoom, noPrune, noUpdate) {
11172 var tileZoom = Math.round(zoom);
11173 if ((this.options.maxZoom !== undefined && tileZoom > this.options.maxZoom) ||
11174 (this.options.minZoom !== undefined && tileZoom < this.options.minZoom)) {
11175 tileZoom = undefined;
11176 } else {
11177 tileZoom = this._clampZoom(tileZoom);
11178 }
11179
11180 var tileZoomChanged = this.options.updateWhenZooming && (tileZoom !== this._tileZoom);
11181
11182 if (!noUpdate || tileZoomChanged) {
11183
11184 this._tileZoom = tileZoom;
11185
11186 if (this._abortLoading) {
11187 this._abortLoading();
11188 }
11189
11190 this._updateLevels();
11191 this._resetGrid();
11192
11193 if (tileZoom !== undefined) {
11194 this._update(center);
11195 }
11196
11197 if (!noPrune) {
11198 this._pruneTiles();
11199 }
11200
11201 // Flag to prevent _updateOpacity from pruning tiles during
11202 // a zoom anim or a pinch gesture
11203 this._noPrune = !!noPrune;
11204 }
11205
11206 this._setZoomTransforms(center, zoom);
11207 },
11208
11209 _setZoomTransforms: function (center, zoom) {
11210 for (var i in this._levels) {
11211 this._setZoomTransform(this._levels[i], center, zoom);
11212 }
11213 },
11214
11215 _setZoomTransform: function (level, center, zoom) {
11216 var scale = this._map.getZoomScale(zoom, level.zoom),
11217 translate = level.origin.multiplyBy(scale)
11218 .subtract(this._map._getNewPixelOrigin(center, zoom)).round();
11219
11220 if (any3d) {
11221 setTransform(level.el, translate, scale);
11222 } else {
11223 setPosition(level.el, translate);
11224 }
11225 },
11226
11227 _resetGrid: function () {
11228 var map = this._map,
11229 crs = map.options.crs,
11230 tileSize = this._tileSize = this.getTileSize(),
11231 tileZoom = this._tileZoom;
11232
11233 var bounds = this._map.getPixelWorldBounds(this._tileZoom);
11234 if (bounds) {
11235 this._globalTileRange = this._pxBoundsToTileRange(bounds);
11236 }
11237
11238 this._wrapX = crs.wrapLng && !this.options.noWrap && [
11239 Math.floor(map.project([0, crs.wrapLng[0]], tileZoom).x / tileSize.x),
11240 Math.ceil(map.project([0, crs.wrapLng[1]], tileZoom).x / tileSize.y)
11241 ];
11242 this._wrapY = crs.wrapLat && !this.options.noWrap && [
11243 Math.floor(map.project([crs.wrapLat[0], 0], tileZoom).y / tileSize.x),
11244 Math.ceil(map.project([crs.wrapLat[1], 0], tileZoom).y / tileSize.y)
11245 ];
11246 },
11247
11248 _onMoveEnd: function () {
11249 if (!this._map || this._map._animatingZoom) { return; }
11250
11251 this._update();
11252 },
11253
11254 _getTiledPixelBounds: function (center) {
11255 var map = this._map,
11256 mapZoom = map._animatingZoom ? Math.max(map._animateToZoom, map.getZoom()) : map.getZoom(),
11257 scale = map.getZoomScale(mapZoom, this._tileZoom),
11258 pixelCenter = map.project(center, this._tileZoom).floor(),
11259 halfSize = map.getSize().divideBy(scale * 2);
11260
11261 return new Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
11262 },
11263
11264 // Private method to load tiles in the grid's active zoom level according to map bounds
11265 _update: function (center) {
11266 var map = this._map;
11267 if (!map) { return; }
11268 var zoom = this._clampZoom(map.getZoom());
11269
11270 if (center === undefined) { center = map.getCenter(); }
11271 if (this._tileZoom === undefined) { return; } // if out of minzoom/maxzoom
11272
11273 var pixelBounds = this._getTiledPixelBounds(center),
11274 tileRange = this._pxBoundsToTileRange(pixelBounds),
11275 tileCenter = tileRange.getCenter(),
11276 queue = [],
11277 margin = this.options.keepBuffer,
11278 noPruneRange = new Bounds(tileRange.getBottomLeft().subtract([margin, -margin]),
11279 tileRange.getTopRight().add([margin, -margin]));
11280
11281 // Sanity check: panic if the tile range contains Infinity somewhere.
11282 if (!(isFinite(tileRange.min.x) &&
11283 isFinite(tileRange.min.y) &&
11284 isFinite(tileRange.max.x) &&
11285 isFinite(tileRange.max.y))) { throw new Error('Attempted to load an infinite number of tiles'); }
11286
11287 for (var key in this._tiles) {
11288 var c = this._tiles[key].coords;
11289 if (c.z !== this._tileZoom || !noPruneRange.contains(new Point(c.x, c.y))) {
11290 this._tiles[key].current = false;
11291 }
11292 }
11293
11294 // _update just loads more tiles. If the tile zoom level differs too much
11295 // from the map's, let _setView reset levels and prune old tiles.
11296 if (Math.abs(zoom - this._tileZoom) > 1) { this._setView(center, zoom); return; }
11297
11298 // create a queue of coordinates to load tiles from
11299 for (var j = tileRange.min.y; j <= tileRange.max.y; j++) {
11300 for (var i = tileRange.min.x; i <= tileRange.max.x; i++) {
11301 var coords = new Point(i, j);
11302 coords.z = this._tileZoom;
11303
11304 if (!this._isValidTile(coords)) { continue; }
11305
11306 var tile = this._tiles[this._tileCoordsToKey(coords)];
11307 if (tile) {
11308 tile.current = true;
11309 } else {
11310 queue.push(coords);
11311 }
11312 }
11313 }
11314
11315 // sort tile queue to load tiles in order of their distance to center
11316 queue.sort(function (a, b) {
11317 return a.distanceTo(tileCenter) - b.distanceTo(tileCenter);
11318 });
11319
11320 if (queue.length !== 0) {
11321 // if it's the first batch of tiles to load
11322 if (!this._loading) {
11323 this._loading = true;
11324 // @event loading: Event
11325 // Fired when the grid layer starts loading tiles.
11326 this.fire('loading');
11327 }
11328
11329 // create DOM fragment to append tiles in one batch
11330 var fragment = document.createDocumentFragment();
11331
11332 for (i = 0; i < queue.length; i++) {
11333 this._addTile(queue[i], fragment);
11334 }
11335
11336 this._level.el.appendChild(fragment);
11337 }
11338 },
11339
11340 _isValidTile: function (coords) {
11341 var crs = this._map.options.crs;
11342
11343 if (!crs.infinite) {
11344 // don't load tile if it's out of bounds and not wrapped
11345 var bounds = this._globalTileRange;
11346 if ((!crs.wrapLng && (coords.x < bounds.min.x || coords.x > bounds.max.x)) ||
11347 (!crs.wrapLat && (coords.y < bounds.min.y || coords.y > bounds.max.y))) { return false; }
11348 }
11349
11350 if (!this.options.bounds) { return true; }
11351
11352 // don't load tile if it doesn't intersect the bounds in options
11353 var tileBounds = this._tileCoordsToBounds(coords);
11354 return toLatLngBounds(this.options.bounds).overlaps(tileBounds);
11355 },
11356
11357 _keyToBounds: function (key) {
11358 return this._tileCoordsToBounds(this._keyToTileCoords(key));
11359 },
11360
11361 _tileCoordsToNwSe: function (coords) {
11362 var map = this._map,
11363 tileSize = this.getTileSize(),
11364 nwPoint = coords.scaleBy(tileSize),
11365 sePoint = nwPoint.add(tileSize),
11366 nw = map.unproject(nwPoint, coords.z),
11367 se = map.unproject(sePoint, coords.z);
11368 return [nw, se];
11369 },
11370
11371 // converts tile coordinates to its geographical bounds
11372 _tileCoordsToBounds: function (coords) {
11373 var bp = this._tileCoordsToNwSe(coords),
11374 bounds = new LatLngBounds(bp[0], bp[1]);
11375
11376 if (!this.options.noWrap) {
11377 bounds = this._map.wrapLatLngBounds(bounds);
11378 }
11379 return bounds;
11380 },
11381 // converts tile coordinates to key for the tile cache
11382 _tileCoordsToKey: function (coords) {
11383 return coords.x + ':' + coords.y + ':' + coords.z;
11384 },
11385
11386 // converts tile cache key to coordinates
11387 _keyToTileCoords: function (key) {
11388 var k = key.split(':'),
11389 coords = new Point(+k[0], +k[1]);
11390 coords.z = +k[2];
11391 return coords;
11392 },
11393
11394 _removeTile: function (key) {
11395 var tile = this._tiles[key];
11396 if (!tile) { return; }
11397
11398 remove(tile.el);
11399
11400 delete this._tiles[key];
11401
11402 // @event tileunload: TileEvent
11403 // Fired when a tile is removed (e.g. when a tile goes off the screen).
11404 this.fire('tileunload', {
11405 tile: tile.el,
11406 coords: this._keyToTileCoords(key)
11407 });
11408 },
11409
11410 _initTile: function (tile) {
11411 addClass(tile, 'leaflet-tile');
11412
11413 var tileSize = this.getTileSize();
11414 tile.style.width = tileSize.x + 'px';
11415 tile.style.height = tileSize.y + 'px';
11416
11417 tile.onselectstart = falseFn;
11418 tile.onmousemove = falseFn;
11419
11420 // update opacity on tiles in IE7-8 because of filter inheritance problems
11421 if (ielt9 && this.options.opacity < 1) {
11422 setOpacity(tile, this.options.opacity);
11423 }
11424
11425 // without this hack, tiles disappear after zoom on Chrome for Android
11426 // https://github.com/Leaflet/Leaflet/issues/2078
11427 if (android && !android23) {
11428 tile.style.WebkitBackfaceVisibility = 'hidden';
11429 }
11430 },
11431
11432 _addTile: function (coords, container) {
11433 var tilePos = this._getTilePos(coords),
11434 key = this._tileCoordsToKey(coords);
11435
11436 var tile = this.createTile(this._wrapCoords(coords), bind(this._tileReady, this, coords));
11437
11438 this._initTile(tile);
11439
11440 // if createTile is defined with a second argument ("done" callback),
11441 // we know that tile is async and will be ready later; otherwise
11442 if (this.createTile.length < 2) {
11443 // mark tile as ready, but delay one frame for opacity animation to happen
11444 requestAnimFrame(bind(this._tileReady, this, coords, null, tile));
11445 }
11446
11447 setPosition(tile, tilePos);
11448
11449 // save tile in cache
11450 this._tiles[key] = {
11451 el: tile,
11452 coords: coords,
11453 current: true
11454 };
11455
11456 container.appendChild(tile);
11457 // @event tileloadstart: TileEvent
11458 // Fired when a tile is requested and starts loading.
11459 this.fire('tileloadstart', {
11460 tile: tile,
11461 coords: coords
11462 });
11463 },
11464
11465 _tileReady: function (coords, err, tile) {
11466 if (err) {
11467 // @event tileerror: TileErrorEvent
11468 // Fired when there is an error loading a tile.
11469 this.fire('tileerror', {
11470 error: err,
11471 tile: tile,
11472 coords: coords
11473 });
11474 }
11475
11476 var key = this._tileCoordsToKey(coords);
11477
11478 tile = this._tiles[key];
11479 if (!tile) { return; }
11480
11481 tile.loaded = +new Date();
11482 if (this._map._fadeAnimated) {
11483 setOpacity(tile.el, 0);
11484 cancelAnimFrame(this._fadeFrame);
11485 this._fadeFrame = requestAnimFrame(this._updateOpacity, this);
11486 } else {
11487 tile.active = true;
11488 this._pruneTiles();
11489 }
11490
11491 if (!err) {
11492 addClass(tile.el, 'leaflet-tile-loaded');
11493
11494 // @event tileload: TileEvent
11495 // Fired when a tile loads.
11496 this.fire('tileload', {
11497 tile: tile.el,
11498 coords: coords
11499 });
11500 }
11501
11502 if (this._noTilesToLoad()) {
11503 this._loading = false;
11504 // @event load: Event
11505 // Fired when the grid layer loaded all visible tiles.
11506 this.fire('load');
11507
11508 if (ielt9 || !this._map._fadeAnimated) {
11509 requestAnimFrame(this._pruneTiles, this);
11510 } else {
11511 // Wait a bit more than 0.2 secs (the duration of the tile fade-in)
11512 // to trigger a pruning.
11513 setTimeout(bind(this._pruneTiles, this), 250);
11514 }
11515 }
11516 },
11517
11518 _getTilePos: function (coords) {
11519 return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
11520 },
11521
11522 _wrapCoords: function (coords) {
11523 var newCoords = new Point(
11524 this._wrapX ? wrapNum(coords.x, this._wrapX) : coords.x,
11525 this._wrapY ? wrapNum(coords.y, this._wrapY) : coords.y);
11526 newCoords.z = coords.z;
11527 return newCoords;
11528 },
11529
11530 _pxBoundsToTileRange: function (bounds) {
11531 var tileSize = this.getTileSize();
11532 return new Bounds(
11533 bounds.min.unscaleBy(tileSize).floor(),
11534 bounds.max.unscaleBy(tileSize).ceil().subtract([1, 1]));
11535 },
11536
11537 _noTilesToLoad: function () {
11538 for (var key in this._tiles) {
11539 if (!this._tiles[key].loaded) { return false; }
11540 }
11541 return true;
11542 }
11543 });
11544
11545 // @factory L.gridLayer(options?: GridLayer options)
11546 // Creates a new instance of GridLayer with the supplied options.
11547 function gridLayer(options) {
11548 return new GridLayer(options);
11549 }
11550
11551 /*
11552 * @class TileLayer
11553 * @inherits GridLayer
11554 * @aka L.TileLayer
11555 * Used to load and display tile layers on the map. Note that most tile servers require attribution, which you can set under `Layer`. Extends `GridLayer`.
11556 *
11557 * @example
11558 *
11559 * ```js
11560 * L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?{foo}', {foo: 'bar', attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>'}).addTo(map);
11561 * ```
11562 *
11563 * @section URL template
11564 * @example
11565 *
11566 * A string of the following form:
11567 *
11568 * ```
11569 * 'http://{s}.somedomain.com/blabla/{z}/{x}/{y}{r}.png'
11570 * ```
11571 *
11572 * `{s}` means one of the available subdomains (used sequentially to help with browser parallel requests per domain limitation; subdomain values are specified in options; `a`, `b` or `c` by default, can be omitted), `{z}` — zoom level, `{x}` and `{y}` — tile coordinates. `{r}` can be used to add "&commat;2x" to the URL to load retina tiles.
11573 *
11574 * You can use custom keys in the template, which will be [evaluated](#util-template) from TileLayer options, like this:
11575 *
11576 * ```
11577 * L.tileLayer('http://{s}.somedomain.com/{foo}/{z}/{x}/{y}.png', {foo: 'bar'});
11578 * ```
11579 */
11580
11581
11582 var TileLayer = GridLayer.extend({
11583
11584 // @section
11585 // @aka TileLayer options
11586 options: {
11587 // @option minZoom: Number = 0
11588 // The minimum zoom level down to which this layer will be displayed (inclusive).
11589 minZoom: 0,
11590
11591 // @option maxZoom: Number = 18
11592 // The maximum zoom level up to which this layer will be displayed (inclusive).
11593 maxZoom: 18,
11594
11595 // @option subdomains: String|String[] = 'abc'
11596 // Subdomains of the tile service. Can be passed in the form of one string (where each letter is a subdomain name) or an array of strings.
11597 subdomains: 'abc',
11598
11599 // @option errorTileUrl: String = ''
11600 // URL to the tile image to show in place of the tile that failed to load.
11601 errorTileUrl: '',
11602
11603 // @option zoomOffset: Number = 0
11604 // The zoom number used in tile URLs will be offset with this value.
11605 zoomOffset: 0,
11606
11607 // @option tms: Boolean = false
11608 // If `true`, inverses Y axis numbering for tiles (turn this on for [TMS](https://en.wikipedia.org/wiki/Tile_Map_Service) services).
11609 tms: false,
11610
11611 // @option zoomReverse: Boolean = false
11612 // If set to true, the zoom number used in tile URLs will be reversed (`maxZoom - zoom` instead of `zoom`)
11613 zoomReverse: false,
11614
11615 // @option detectRetina: Boolean = false
11616 // If `true` and user is on a retina display, it will request four tiles of half the specified size and a bigger zoom level in place of one to utilize the high resolution.
11617 detectRetina: false,
11618
11619 // @option crossOrigin: Boolean|String = false
11620 // Whether the crossOrigin attribute will be added to the tiles.
11621 // If a String is provided, all tiles will have their crossOrigin attribute set to the String provided. This is needed if you want to access tile pixel data.
11622 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
11623 crossOrigin: false
11624 },
11625
11626 initialize: function (url, options) {
11627
11628 this._url = url;
11629
11630 options = setOptions(this, options);
11631
11632 // detecting retina displays, adjusting tileSize and zoom levels
11633 if (options.detectRetina && retina && options.maxZoom > 0) {
11634
11635 options.tileSize = Math.floor(options.tileSize / 2);
11636
11637 if (!options.zoomReverse) {
11638 options.zoomOffset++;
11639 options.maxZoom--;
11640 } else {
11641 options.zoomOffset--;
11642 options.minZoom++;
11643 }
11644
11645 options.minZoom = Math.max(0, options.minZoom);
11646 }
11647
11648 if (typeof options.subdomains === 'string') {
11649 options.subdomains = options.subdomains.split('');
11650 }
11651
11652 // for https://github.com/Leaflet/Leaflet/issues/137
11653 if (!android) {
11654 this.on('tileunload', this._onTileRemove);
11655 }
11656 },
11657
11658 // @method setUrl(url: String, noRedraw?: Boolean): this
11659 // Updates the layer's URL template and redraws it (unless `noRedraw` is set to `true`).
11660 // If the URL does not change, the layer will not be redrawn unless
11661 // the noRedraw parameter is set to false.
11662 setUrl: function (url, noRedraw) {
11663 if (this._url === url && noRedraw === undefined) {
11664 noRedraw = true;
11665 }
11666
11667 this._url = url;
11668
11669 if (!noRedraw) {
11670 this.redraw();
11671 }
11672 return this;
11673 },
11674
11675 // @method createTile(coords: Object, done?: Function): HTMLElement
11676 // Called only internally, overrides GridLayer's [`createTile()`](#gridlayer-createtile)
11677 // to return an `<img>` HTML element with the appropriate image URL given `coords`. The `done`
11678 // callback is called when the tile has been loaded.
11679 createTile: function (coords, done) {
11680 var tile = document.createElement('img');
11681
11682 on(tile, 'load', bind(this._tileOnLoad, this, done, tile));
11683 on(tile, 'error', bind(this._tileOnError, this, done, tile));
11684
11685 if (this.options.crossOrigin || this.options.crossOrigin === '') {
11686 tile.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
11687 }
11688
11689 /*
11690 Alt tag is set to empty string to keep screen readers from reading URL and for compliance reasons
11691 http://www.w3.org/TR/WCAG20-TECHS/H67
11692 */
11693 tile.alt = '';
11694
11695 /*
11696 Set role="presentation" to force screen readers to ignore this
11697 https://www.w3.org/TR/wai-aria/roles#textalternativecomputation
11698 */
11699 tile.setAttribute('role', 'presentation');
11700
11701 tile.src = this.getTileUrl(coords);
11702
11703 return tile;
11704 },
11705
11706 // @section Extension methods
11707 // @uninheritable
11708 // Layers extending `TileLayer` might reimplement the following method.
11709 // @method getTileUrl(coords: Object): String
11710 // Called only internally, returns the URL for a tile given its coordinates.
11711 // Classes extending `TileLayer` can override this function to provide custom tile URL naming schemes.
11712 getTileUrl: function (coords) {
11713 var data = {
11714 r: retina ? '@2x' : '',
11715 s: this._getSubdomain(coords),
11716 x: coords.x,
11717 y: coords.y,
11718 z: this._getZoomForUrl()
11719 };
11720 if (this._map && !this._map.options.crs.infinite) {
11721 var invertedY = this._globalTileRange.max.y - coords.y;
11722 if (this.options.tms) {
11723 data['y'] = invertedY;
11724 }
11725 data['-y'] = invertedY;
11726 }
11727
11728 return template(this._url, extend(data, this.options));
11729 },
11730
11731 _tileOnLoad: function (done, tile) {
11732 // For https://github.com/Leaflet/Leaflet/issues/3332
11733 if (ielt9) {
11734 setTimeout(bind(done, this, null, tile), 0);
11735 } else {
11736 done(null, tile);
11737 }
11738 },
11739
11740 _tileOnError: function (done, tile, e) {
11741 var errorUrl = this.options.errorTileUrl;
11742 if (errorUrl && tile.getAttribute('src') !== errorUrl) {
11743 tile.src = errorUrl;
11744 }
11745 done(e, tile);
11746 },
11747
11748 _onTileRemove: function (e) {
11749 e.tile.onload = null;
11750 },
11751
11752 _getZoomForUrl: function () {
11753 var zoom = this._tileZoom,
11754 maxZoom = this.options.maxZoom,
11755 zoomReverse = this.options.zoomReverse,
11756 zoomOffset = this.options.zoomOffset;
11757
11758 if (zoomReverse) {
11759 zoom = maxZoom - zoom;
11760 }
11761
11762 return zoom + zoomOffset;
11763 },
11764
11765 _getSubdomain: function (tilePoint) {
11766 var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length;
11767 return this.options.subdomains[index];
11768 },
11769
11770 // stops loading all tiles in the background layer
11771 _abortLoading: function () {
11772 var i, tile;
11773 for (i in this._tiles) {
11774 if (this._tiles[i].coords.z !== this._tileZoom) {
11775 tile = this._tiles[i].el;
11776
11777 tile.onload = falseFn;
11778 tile.onerror = falseFn;
11779
11780 if (!tile.complete) {
11781 tile.src = emptyImageUrl;
11782 remove(tile);
11783 delete this._tiles[i];
11784 }
11785 }
11786 }
11787 },
11788
11789 _removeTile: function (key) {
11790 var tile = this._tiles[key];
11791 if (!tile) { return; }
11792
11793 // Cancels any pending http requests associated with the tile
11794 // unless we're on Android's stock browser,
11795 // see https://github.com/Leaflet/Leaflet/issues/137
11796 if (!androidStock) {
11797 tile.el.setAttribute('src', emptyImageUrl);
11798 }
11799
11800 return GridLayer.prototype._removeTile.call(this, key);
11801 },
11802
11803 _tileReady: function (coords, err, tile) {
11804 if (!this._map || (tile && tile.getAttribute('src') === emptyImageUrl)) {
11805 return;
11806 }
11807
11808 return GridLayer.prototype._tileReady.call(this, coords, err, tile);
11809 }
11810 });
11811
11812
11813 // @factory L.tilelayer(urlTemplate: String, options?: TileLayer options)
11814 // Instantiates a tile layer object given a `URL template` and optionally an options object.
11815
11816 function tileLayer(url, options) {
11817 return new TileLayer(url, options);
11818 }
11819
11820 /*
11821 * @class TileLayer.WMS
11822 * @inherits TileLayer
11823 * @aka L.TileLayer.WMS
11824 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
11825 *
11826 * @example
11827 *
11828 * ```js
11829 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
11830 * layers: 'nexrad-n0r-900913',
11831 * format: 'image/png',
11832 * transparent: true,
11833 * attribution: "Weather data © 2012 IEM Nexrad"
11834 * });
11835 * ```
11836 */
11837
11838 var TileLayerWMS = TileLayer.extend({
11839
11840 // @section
11841 // @aka TileLayer.WMS options
11842 // If any custom options not documented here are used, they will be sent to the
11843 // WMS server as extra parameters in each request URL. This can be useful for
11844 // [non-standard vendor WMS parameters](http://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
11845 defaultWmsParams: {
11846 service: 'WMS',
11847 request: 'GetMap',
11848
11849 // @option layers: String = ''
11850 // **(required)** Comma-separated list of WMS layers to show.
11851 layers: '',
11852
11853 // @option styles: String = ''
11854 // Comma-separated list of WMS styles.
11855 styles: '',
11856
11857 // @option format: String = 'image/jpeg'
11858 // WMS image format (use `'image/png'` for layers with transparency).
11859 format: 'image/jpeg',
11860
11861 // @option transparent: Boolean = false
11862 // If `true`, the WMS service will return images with transparency.
11863 transparent: false,
11864
11865 // @option version: String = '1.1.1'
11866 // Version of the WMS service to use
11867 version: '1.1.1'
11868 },
11869
11870 options: {
11871 // @option crs: CRS = null
11872 // Coordinate Reference System to use for the WMS requests, defaults to
11873 // map CRS. Don't change this if you're not sure what it means.
11874 crs: null,
11875
11876 // @option uppercase: Boolean = false
11877 // If `true`, WMS request parameter keys will be uppercase.
11878 uppercase: false
11879 },
11880
11881 initialize: function (url, options) {
11882
11883 this._url = url;
11884
11885 var wmsParams = extend({}, this.defaultWmsParams);
11886
11887 // all keys that are not TileLayer options go to WMS params
11888 for (var i in options) {
11889 if (!(i in this.options)) {
11890 wmsParams[i] = options[i];
11891 }
11892 }
11893
11894 options = setOptions(this, options);
11895
11896 var realRetina = options.detectRetina && retina ? 2 : 1;
11897 var tileSize = this.getTileSize();
11898 wmsParams.width = tileSize.x * realRetina;
11899 wmsParams.height = tileSize.y * realRetina;
11900
11901 this.wmsParams = wmsParams;
11902 },
11903
11904 onAdd: function (map) {
11905
11906 this._crs = this.options.crs || map.options.crs;
11907 this._wmsVersion = parseFloat(this.wmsParams.version);
11908
11909 var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
11910 this.wmsParams[projectionKey] = this._crs.code;
11911
11912 TileLayer.prototype.onAdd.call(this, map);
11913 },
11914
11915 getTileUrl: function (coords) {
11916
11917 var tileBounds = this._tileCoordsToNwSe(coords),
11918 crs = this._crs,
11919 bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
11920 min = bounds.min,
11921 max = bounds.max,
11922 bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326 ?
11923 [min.y, min.x, max.y, max.x] :
11924 [min.x, min.y, max.x, max.y]).join(','),
11925 url = TileLayer.prototype.getTileUrl.call(this, coords);
11926 return url +
11927 getParamString(this.wmsParams, url, this.options.uppercase) +
11928 (this.options.uppercase ? '&BBOX=' : '&bbox=') + bbox;
11929 },
11930
11931 // @method setParams(params: Object, noRedraw?: Boolean): this
11932 // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
11933 setParams: function (params, noRedraw) {
11934
11935 extend(this.wmsParams, params);
11936
11937 if (!noRedraw) {
11938 this.redraw();
11939 }
11940
11941 return this;
11942 }
11943 });
11944
11945
11946 // @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
11947 // Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
11948 function tileLayerWMS(url, options) {
11949 return new TileLayerWMS(url, options);
11950 }
11951
11952 TileLayer.WMS = TileLayerWMS;
11953 tileLayer.wms = tileLayerWMS;
11954
11955 /*
11956 * @class Renderer
11957 * @inherits Layer
11958 * @aka L.Renderer
11959 *
11960 * Base class for vector renderer implementations (`SVG`, `Canvas`). Handles the
11961 * DOM container of the renderer, its bounds, and its zoom animation.
11962 *
11963 * A `Renderer` works as an implicit layer group for all `Path`s - the renderer
11964 * itself can be added or removed to the map. All paths use a renderer, which can
11965 * be implicit (the map will decide the type of renderer and use it automatically)
11966 * or explicit (using the [`renderer`](#path-renderer) option of the path).
11967 *
11968 * Do not use this class directly, use `SVG` and `Canvas` instead.
11969 *
11970 * @event update: Event
11971 * Fired when the renderer updates its bounds, center and zoom, for example when
11972 * its map has moved
11973 */
11974
11975 var Renderer = Layer.extend({
11976
11977 // @section
11978 // @aka Renderer options
11979 options: {
11980 // @option padding: Number = 0.1
11981 // How much to extend the clip area around the map view (relative to its size)
11982 // e.g. 0.1 would be 10% of map view in each direction
11983 padding: 0.1,
11984
11985 // @option tolerance: Number = 0
11986 // How much to extend click tolerance round a path/object on the map
11987 tolerance : 0
11988 },
11989
11990 initialize: function (options) {
11991 setOptions(this, options);
11992 stamp(this);
11993 this._layers = this._layers || {};
11994 },
11995
11996 onAdd: function () {
11997 if (!this._container) {
11998 this._initContainer(); // defined by renderer implementations
11999
12000 if (this._zoomAnimated) {
12001 addClass(this._container, 'leaflet-zoom-animated');
12002 }
12003 }
12004
12005 this.getPane().appendChild(this._container);
12006 this._update();
12007 this.on('update', this._updatePaths, this);
12008 },
12009
12010 onRemove: function () {
12011 this.off('update', this._updatePaths, this);
12012 this._destroyContainer();
12013 },
12014
12015 getEvents: function () {
12016 var events = {
12017 viewreset: this._reset,
12018 zoom: this._onZoom,
12019 moveend: this._update,
12020 zoomend: this._onZoomEnd
12021 };
12022 if (this._zoomAnimated) {
12023 events.zoomanim = this._onAnimZoom;
12024 }
12025 return events;
12026 },
12027
12028 _onAnimZoom: function (ev) {
12029 this._updateTransform(ev.center, ev.zoom);
12030 },
12031
12032 _onZoom: function () {
12033 this._updateTransform(this._map.getCenter(), this._map.getZoom());
12034 },
12035
12036 _updateTransform: function (center, zoom) {
12037 var scale = this._map.getZoomScale(zoom, this._zoom),
12038 position = getPosition(this._container),
12039 viewHalf = this._map.getSize().multiplyBy(0.5 + this.options.padding),
12040 currentCenterPoint = this._map.project(this._center, zoom),
12041 destCenterPoint = this._map.project(center, zoom),
12042 centerOffset = destCenterPoint.subtract(currentCenterPoint),
12043
12044 topLeftOffset = viewHalf.multiplyBy(-scale).add(position).add(viewHalf).subtract(centerOffset);
12045
12046 if (any3d) {
12047 setTransform(this._container, topLeftOffset, scale);
12048 } else {
12049 setPosition(this._container, topLeftOffset);
12050 }
12051 },
12052
12053 _reset: function () {
12054 this._update();
12055 this._updateTransform(this._center, this._zoom);
12056
12057 for (var id in this._layers) {
12058 this._layers[id]._reset();
12059 }
12060 },
12061
12062 _onZoomEnd: function () {
12063 for (var id in this._layers) {
12064 this._layers[id]._project();
12065 }
12066 },
12067
12068 _updatePaths: function () {
12069 for (var id in this._layers) {
12070 this._layers[id]._update();
12071 }
12072 },
12073
12074 _update: function () {
12075 // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
12076 // Subclasses are responsible of firing the 'update' event.
12077 var p = this.options.padding,
12078 size = this._map.getSize(),
12079 min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
12080
12081 this._bounds = new Bounds(min, min.add(size.multiplyBy(1 + p * 2)).round());
12082
12083 this._center = this._map.getCenter();
12084 this._zoom = this._map.getZoom();
12085 }
12086 });
12087
12088 /*
12089 * @class Canvas
12090 * @inherits Renderer
12091 * @aka L.Canvas
12092 *
12093 * Allows vector layers to be displayed with [`<canvas>`](https://developer.mozilla.org/docs/Web/API/Canvas_API).
12094 * Inherits `Renderer`.
12095 *
12096 * Due to [technical limitations](http://caniuse.com/#search=canvas), Canvas is not
12097 * available in all web browsers, notably IE8, and overlapping geometries might
12098 * not display properly in some edge cases.
12099 *
12100 * @example
12101 *
12102 * Use Canvas by default for all paths in the map:
12103 *
12104 * ```js
12105 * var map = L.map('map', {
12106 * renderer: L.canvas()
12107 * });
12108 * ```
12109 *
12110 * Use a Canvas renderer with extra padding for specific vector geometries:
12111 *
12112 * ```js
12113 * var map = L.map('map');
12114 * var myRenderer = L.canvas({ padding: 0.5 });
12115 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12116 * var circle = L.circle( center, { renderer: myRenderer } );
12117 * ```
12118 */
12119
12120 var Canvas = Renderer.extend({
12121 getEvents: function () {
12122 var events = Renderer.prototype.getEvents.call(this);
12123 events.viewprereset = this._onViewPreReset;
12124 return events;
12125 },
12126
12127 _onViewPreReset: function () {
12128 // Set a flag so that a viewprereset+moveend+viewreset only updates&redraws once
12129 this._postponeUpdatePaths = true;
12130 },
12131
12132 onAdd: function () {
12133 Renderer.prototype.onAdd.call(this);
12134
12135 // Redraw vectors since canvas is cleared upon removal,
12136 // in case of removing the renderer itself from the map.
12137 this._draw();
12138 },
12139
12140 _initContainer: function () {
12141 var container = this._container = document.createElement('canvas');
12142
12143 on(container, 'mousemove', this._onMouseMove, this);
12144 on(container, 'click dblclick mousedown mouseup contextmenu', this._onClick, this);
12145 on(container, 'mouseout', this._handleMouseOut, this);
12146
12147 this._ctx = container.getContext('2d');
12148 },
12149
12150 _destroyContainer: function () {
12151 cancelAnimFrame(this._redrawRequest);
12152 delete this._ctx;
12153 remove(this._container);
12154 off(this._container);
12155 delete this._container;
12156 },
12157
12158 _updatePaths: function () {
12159 if (this._postponeUpdatePaths) { return; }
12160
12161 var layer;
12162 this._redrawBounds = null;
12163 for (var id in this._layers) {
12164 layer = this._layers[id];
12165 layer._update();
12166 }
12167 this._redraw();
12168 },
12169
12170 _update: function () {
12171 if (this._map._animatingZoom && this._bounds) { return; }
12172
12173 Renderer.prototype._update.call(this);
12174
12175 var b = this._bounds,
12176 container = this._container,
12177 size = b.getSize(),
12178 m = retina ? 2 : 1;
12179
12180 setPosition(container, b.min);
12181
12182 // set canvas size (also clearing it); use double size on retina
12183 container.width = m * size.x;
12184 container.height = m * size.y;
12185 container.style.width = size.x + 'px';
12186 container.style.height = size.y + 'px';
12187
12188 if (retina) {
12189 this._ctx.scale(2, 2);
12190 }
12191
12192 // translate so we use the same path coordinates after canvas element moves
12193 this._ctx.translate(-b.min.x, -b.min.y);
12194
12195 // Tell paths to redraw themselves
12196 this.fire('update');
12197 },
12198
12199 _reset: function () {
12200 Renderer.prototype._reset.call(this);
12201
12202 if (this._postponeUpdatePaths) {
12203 this._postponeUpdatePaths = false;
12204 this._updatePaths();
12205 }
12206 },
12207
12208 _initPath: function (layer) {
12209 this._updateDashArray(layer);
12210 this._layers[stamp(layer)] = layer;
12211
12212 var order = layer._order = {
12213 layer: layer,
12214 prev: this._drawLast,
12215 next: null
12216 };
12217 if (this._drawLast) { this._drawLast.next = order; }
12218 this._drawLast = order;
12219 this._drawFirst = this._drawFirst || this._drawLast;
12220 },
12221
12222 _addPath: function (layer) {
12223 this._requestRedraw(layer);
12224 },
12225
12226 _removePath: function (layer) {
12227 var order = layer._order;
12228 var next = order.next;
12229 var prev = order.prev;
12230
12231 if (next) {
12232 next.prev = prev;
12233 } else {
12234 this._drawLast = prev;
12235 }
12236 if (prev) {
12237 prev.next = next;
12238 } else {
12239 this._drawFirst = next;
12240 }
12241
12242 delete layer._order;
12243
12244 delete this._layers[stamp(layer)];
12245
12246 this._requestRedraw(layer);
12247 },
12248
12249 _updatePath: function (layer) {
12250 // Redraw the union of the layer's old pixel
12251 // bounds and the new pixel bounds.
12252 this._extendRedrawBounds(layer);
12253 layer._project();
12254 layer._update();
12255 // The redraw will extend the redraw bounds
12256 // with the new pixel bounds.
12257 this._requestRedraw(layer);
12258 },
12259
12260 _updateStyle: function (layer) {
12261 this._updateDashArray(layer);
12262 this._requestRedraw(layer);
12263 },
12264
12265 _updateDashArray: function (layer) {
12266 if (typeof layer.options.dashArray === 'string') {
12267 var parts = layer.options.dashArray.split(/[, ]+/),
12268 dashArray = [],
12269 dashValue,
12270 i;
12271 for (i = 0; i < parts.length; i++) {
12272 dashValue = Number(parts[i]);
12273 // Ignore dash array containing invalid lengths
12274 if (isNaN(dashValue)) { return; }
12275 dashArray.push(dashValue);
12276 }
12277 layer.options._dashArray = dashArray;
12278 } else {
12279 layer.options._dashArray = layer.options.dashArray;
12280 }
12281 },
12282
12283 _requestRedraw: function (layer) {
12284 if (!this._map) { return; }
12285
12286 this._extendRedrawBounds(layer);
12287 this._redrawRequest = this._redrawRequest || requestAnimFrame(this._redraw, this);
12288 },
12289
12290 _extendRedrawBounds: function (layer) {
12291 if (layer._pxBounds) {
12292 var padding = (layer.options.weight || 0) + 1;
12293 this._redrawBounds = this._redrawBounds || new Bounds();
12294 this._redrawBounds.extend(layer._pxBounds.min.subtract([padding, padding]));
12295 this._redrawBounds.extend(layer._pxBounds.max.add([padding, padding]));
12296 }
12297 },
12298
12299 _redraw: function () {
12300 this._redrawRequest = null;
12301
12302 if (this._redrawBounds) {
12303 this._redrawBounds.min._floor();
12304 this._redrawBounds.max._ceil();
12305 }
12306
12307 this._clear(); // clear layers in redraw bounds
12308 this._draw(); // draw layers
12309
12310 this._redrawBounds = null;
12311 },
12312
12313 _clear: function () {
12314 var bounds = this._redrawBounds;
12315 if (bounds) {
12316 var size = bounds.getSize();
12317 this._ctx.clearRect(bounds.min.x, bounds.min.y, size.x, size.y);
12318 } else {
12319 this._ctx.save();
12320 this._ctx.setTransform(1, 0, 0, 1, 0, 0);
12321 this._ctx.clearRect(0, 0, this._container.width, this._container.height);
12322 this._ctx.restore();
12323 }
12324 },
12325
12326 _draw: function () {
12327 var layer, bounds = this._redrawBounds;
12328 this._ctx.save();
12329 if (bounds) {
12330 var size = bounds.getSize();
12331 this._ctx.beginPath();
12332 this._ctx.rect(bounds.min.x, bounds.min.y, size.x, size.y);
12333 this._ctx.clip();
12334 }
12335
12336 this._drawing = true;
12337
12338 for (var order = this._drawFirst; order; order = order.next) {
12339 layer = order.layer;
12340 if (!bounds || (layer._pxBounds && layer._pxBounds.intersects(bounds))) {
12341 layer._updatePath();
12342 }
12343 }
12344
12345 this._drawing = false;
12346
12347 this._ctx.restore(); // Restore state before clipping.
12348 },
12349
12350 _updatePoly: function (layer, closed) {
12351 if (!this._drawing) { return; }
12352
12353 var i, j, len2, p,
12354 parts = layer._parts,
12355 len = parts.length,
12356 ctx = this._ctx;
12357
12358 if (!len) { return; }
12359
12360 ctx.beginPath();
12361
12362 for (i = 0; i < len; i++) {
12363 for (j = 0, len2 = parts[i].length; j < len2; j++) {
12364 p = parts[i][j];
12365 ctx[j ? 'lineTo' : 'moveTo'](p.x, p.y);
12366 }
12367 if (closed) {
12368 ctx.closePath();
12369 }
12370 }
12371
12372 this._fillStroke(ctx, layer);
12373
12374 // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature
12375 },
12376
12377 _updateCircle: function (layer) {
12378
12379 if (!this._drawing || layer._empty()) { return; }
12380
12381 var p = layer._point,
12382 ctx = this._ctx,
12383 r = Math.max(Math.round(layer._radius), 1),
12384 s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
12385
12386 if (s !== 1) {
12387 ctx.save();
12388 ctx.scale(1, s);
12389 }
12390
12391 ctx.beginPath();
12392 ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
12393
12394 if (s !== 1) {
12395 ctx.restore();
12396 }
12397
12398 this._fillStroke(ctx, layer);
12399 },
12400
12401 _fillStroke: function (ctx, layer) {
12402 var options = layer.options;
12403
12404 if (options.fill) {
12405 ctx.globalAlpha = options.fillOpacity;
12406 ctx.fillStyle = options.fillColor || options.color;
12407 ctx.fill(options.fillRule || 'evenodd');
12408 }
12409
12410 if (options.stroke && options.weight !== 0) {
12411 if (ctx.setLineDash) {
12412 ctx.setLineDash(layer.options && layer.options._dashArray || []);
12413 }
12414 ctx.globalAlpha = options.opacity;
12415 ctx.lineWidth = options.weight;
12416 ctx.strokeStyle = options.color;
12417 ctx.lineCap = options.lineCap;
12418 ctx.lineJoin = options.lineJoin;
12419 ctx.stroke();
12420 }
12421 },
12422
12423 // Canvas obviously doesn't have mouse events for individual drawn objects,
12424 // so we emulate that by calculating what's under the mouse on mousemove/click manually
12425
12426 _onClick: function (e) {
12427 var point = this._map.mouseEventToLayerPoint(e), layer, clickedLayer;
12428
12429 for (var order = this._drawFirst; order; order = order.next) {
12430 layer = order.layer;
12431 if (layer.options.interactive && layer._containsPoint(point)) {
12432 if (!(e.type === 'click' || e.type !== 'preclick') || !this._map._draggableMoved(layer)) {
12433 clickedLayer = layer;
12434 }
12435 }
12436 }
12437 if (clickedLayer) {
12438 fakeStop(e);
12439 this._fireEvent([clickedLayer], e);
12440 }
12441 },
12442
12443 _onMouseMove: function (e) {
12444 if (!this._map || this._map.dragging.moving() || this._map._animatingZoom) { return; }
12445
12446 var point = this._map.mouseEventToLayerPoint(e);
12447 this._handleMouseHover(e, point);
12448 },
12449
12450
12451 _handleMouseOut: function (e) {
12452 var layer = this._hoveredLayer;
12453 if (layer) {
12454 // if we're leaving the layer, fire mouseout
12455 removeClass(this._container, 'leaflet-interactive');
12456 this._fireEvent([layer], e, 'mouseout');
12457 this._hoveredLayer = null;
12458 this._mouseHoverThrottled = false;
12459 }
12460 },
12461
12462 _handleMouseHover: function (e, point) {
12463 if (this._mouseHoverThrottled) {
12464 return;
12465 }
12466
12467 var layer, candidateHoveredLayer;
12468
12469 for (var order = this._drawFirst; order; order = order.next) {
12470 layer = order.layer;
12471 if (layer.options.interactive && layer._containsPoint(point)) {
12472 candidateHoveredLayer = layer;
12473 }
12474 }
12475
12476 if (candidateHoveredLayer !== this._hoveredLayer) {
12477 this._handleMouseOut(e);
12478
12479 if (candidateHoveredLayer) {
12480 addClass(this._container, 'leaflet-interactive'); // change cursor
12481 this._fireEvent([candidateHoveredLayer], e, 'mouseover');
12482 this._hoveredLayer = candidateHoveredLayer;
12483 }
12484 }
12485
12486 if (this._hoveredLayer) {
12487 this._fireEvent([this._hoveredLayer], e);
12488 }
12489
12490 this._mouseHoverThrottled = true;
12491 setTimeout(bind(function () {
12492 this._mouseHoverThrottled = false;
12493 }, this), 32);
12494 },
12495
12496 _fireEvent: function (layers, e, type) {
12497 this._map._fireDOMEvent(e, type || e.type, layers);
12498 },
12499
12500 _bringToFront: function (layer) {
12501 var order = layer._order;
12502
12503 if (!order) { return; }
12504
12505 var next = order.next;
12506 var prev = order.prev;
12507
12508 if (next) {
12509 next.prev = prev;
12510 } else {
12511 // Already last
12512 return;
12513 }
12514 if (prev) {
12515 prev.next = next;
12516 } else if (next) {
12517 // Update first entry unless this is the
12518 // single entry
12519 this._drawFirst = next;
12520 }
12521
12522 order.prev = this._drawLast;
12523 this._drawLast.next = order;
12524
12525 order.next = null;
12526 this._drawLast = order;
12527
12528 this._requestRedraw(layer);
12529 },
12530
12531 _bringToBack: function (layer) {
12532 var order = layer._order;
12533
12534 if (!order) { return; }
12535
12536 var next = order.next;
12537 var prev = order.prev;
12538
12539 if (prev) {
12540 prev.next = next;
12541 } else {
12542 // Already first
12543 return;
12544 }
12545 if (next) {
12546 next.prev = prev;
12547 } else if (prev) {
12548 // Update last entry unless this is the
12549 // single entry
12550 this._drawLast = prev;
12551 }
12552
12553 order.prev = null;
12554
12555 order.next = this._drawFirst;
12556 this._drawFirst.prev = order;
12557 this._drawFirst = order;
12558
12559 this._requestRedraw(layer);
12560 }
12561 });
12562
12563 // @factory L.canvas(options?: Renderer options)
12564 // Creates a Canvas renderer with the given options.
12565 function canvas$1(options) {
12566 return canvas ? new Canvas(options) : null;
12567 }
12568
12569 /*
12570 * Thanks to Dmitry Baranovsky and his Raphael library for inspiration!
12571 */
12572
12573
12574 var vmlCreate = (function () {
12575 try {
12576 document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml');
12577 return function (name) {
12578 return document.createElement('<lvml:' + name + ' class="lvml">');
12579 };
12580 } catch (e) {
12581 return function (name) {
12582 return document.createElement('<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">');
12583 };
12584 }
12585 })();
12586
12587
12588 /*
12589 * @class SVG
12590 *
12591 *
12592 * VML was deprecated in 2012, which means VML functionality exists only for backwards compatibility
12593 * with old versions of Internet Explorer.
12594 */
12595
12596 // mixin to redefine some SVG methods to handle VML syntax which is similar but with some differences
12597 var vmlMixin = {
12598
12599 _initContainer: function () {
12600 this._container = create$1('div', 'leaflet-vml-container');
12601 },
12602
12603 _update: function () {
12604 if (this._map._animatingZoom) { return; }
12605 Renderer.prototype._update.call(this);
12606 this.fire('update');
12607 },
12608
12609 _initPath: function (layer) {
12610 var container = layer._container = vmlCreate('shape');
12611
12612 addClass(container, 'leaflet-vml-shape ' + (this.options.className || ''));
12613
12614 container.coordsize = '1 1';
12615
12616 layer._path = vmlCreate('path');
12617 container.appendChild(layer._path);
12618
12619 this._updateStyle(layer);
12620 this._layers[stamp(layer)] = layer;
12621 },
12622
12623 _addPath: function (layer) {
12624 var container = layer._container;
12625 this._container.appendChild(container);
12626
12627 if (layer.options.interactive) {
12628 layer.addInteractiveTarget(container);
12629 }
12630 },
12631
12632 _removePath: function (layer) {
12633 var container = layer._container;
12634 remove(container);
12635 layer.removeInteractiveTarget(container);
12636 delete this._layers[stamp(layer)];
12637 },
12638
12639 _updateStyle: function (layer) {
12640 var stroke = layer._stroke,
12641 fill = layer._fill,
12642 options = layer.options,
12643 container = layer._container;
12644
12645 container.stroked = !!options.stroke;
12646 container.filled = !!options.fill;
12647
12648 if (options.stroke) {
12649 if (!stroke) {
12650 stroke = layer._stroke = vmlCreate('stroke');
12651 }
12652 container.appendChild(stroke);
12653 stroke.weight = options.weight + 'px';
12654 stroke.color = options.color;
12655 stroke.opacity = options.opacity;
12656
12657 if (options.dashArray) {
12658 stroke.dashStyle = isArray(options.dashArray) ?
12659 options.dashArray.join(' ') :
12660 options.dashArray.replace(/( *, *)/g, ' ');
12661 } else {
12662 stroke.dashStyle = '';
12663 }
12664 stroke.endcap = options.lineCap.replace('butt', 'flat');
12665 stroke.joinstyle = options.lineJoin;
12666
12667 } else if (stroke) {
12668 container.removeChild(stroke);
12669 layer._stroke = null;
12670 }
12671
12672 if (options.fill) {
12673 if (!fill) {
12674 fill = layer._fill = vmlCreate('fill');
12675 }
12676 container.appendChild(fill);
12677 fill.color = options.fillColor || options.color;
12678 fill.opacity = options.fillOpacity;
12679
12680 } else if (fill) {
12681 container.removeChild(fill);
12682 layer._fill = null;
12683 }
12684 },
12685
12686 _updateCircle: function (layer) {
12687 var p = layer._point.round(),
12688 r = Math.round(layer._radius),
12689 r2 = Math.round(layer._radiusY || r);
12690
12691 this._setPath(layer, layer._empty() ? 'M0 0' :
12692 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r2 + ' 0,' + (65535 * 360));
12693 },
12694
12695 _setPath: function (layer, path) {
12696 layer._path.v = path;
12697 },
12698
12699 _bringToFront: function (layer) {
12700 toFront(layer._container);
12701 },
12702
12703 _bringToBack: function (layer) {
12704 toBack(layer._container);
12705 }
12706 };
12707
12708 var create$2 = vml ? vmlCreate : svgCreate;
12709
12710 /*
12711 * @class SVG
12712 * @inherits Renderer
12713 * @aka L.SVG
12714 *
12715 * Allows vector layers to be displayed with [SVG](https://developer.mozilla.org/docs/Web/SVG).
12716 * Inherits `Renderer`.
12717 *
12718 * Due to [technical limitations](http://caniuse.com/#search=svg), SVG is not
12719 * available in all web browsers, notably Android 2.x and 3.x.
12720 *
12721 * Although SVG is not available on IE7 and IE8, these browsers support
12722 * [VML](https://en.wikipedia.org/wiki/Vector_Markup_Language)
12723 * (a now deprecated technology), and the SVG renderer will fall back to VML in
12724 * this case.
12725 *
12726 * @example
12727 *
12728 * Use SVG by default for all paths in the map:
12729 *
12730 * ```js
12731 * var map = L.map('map', {
12732 * renderer: L.svg()
12733 * });
12734 * ```
12735 *
12736 * Use a SVG renderer with extra padding for specific vector geometries:
12737 *
12738 * ```js
12739 * var map = L.map('map');
12740 * var myRenderer = L.svg({ padding: 0.5 });
12741 * var line = L.polyline( coordinates, { renderer: myRenderer } );
12742 * var circle = L.circle( center, { renderer: myRenderer } );
12743 * ```
12744 */
12745
12746 var SVG = Renderer.extend({
12747
12748 getEvents: function () {
12749 var events = Renderer.prototype.getEvents.call(this);
12750 events.zoomstart = this._onZoomStart;
12751 return events;
12752 },
12753
12754 _initContainer: function () {
12755 this._container = create$2('svg');
12756
12757 // makes it possible to click through svg root; we'll reset it back in individual paths
12758 this._container.setAttribute('pointer-events', 'none');
12759
12760 this._rootGroup = create$2('g');
12761 this._container.appendChild(this._rootGroup);
12762 },
12763
12764 _destroyContainer: function () {
12765 remove(this._container);
12766 off(this._container);
12767 delete this._container;
12768 delete this._rootGroup;
12769 delete this._svgSize;
12770 },
12771
12772 _onZoomStart: function () {
12773 // Drag-then-pinch interactions might mess up the center and zoom.
12774 // In this case, the easiest way to prevent this is re-do the renderer
12775 // bounds and padding when the zooming starts.
12776 this._update();
12777 },
12778
12779 _update: function () {
12780 if (this._map._animatingZoom && this._bounds) { return; }
12781
12782 Renderer.prototype._update.call(this);
12783
12784 var b = this._bounds,
12785 size = b.getSize(),
12786 container = this._container;
12787
12788 // set size of svg-container if changed
12789 if (!this._svgSize || !this._svgSize.equals(size)) {
12790 this._svgSize = size;
12791 container.setAttribute('width', size.x);
12792 container.setAttribute('height', size.y);
12793 }
12794
12795 // movement: update container viewBox so that we don't have to change coordinates of individual layers
12796 setPosition(container, b.min);
12797 container.setAttribute('viewBox', [b.min.x, b.min.y, size.x, size.y].join(' '));
12798
12799 this.fire('update');
12800 },
12801
12802 // methods below are called by vector layers implementations
12803
12804 _initPath: function (layer) {
12805 var path = layer._path = create$2('path');
12806
12807 // @namespace Path
12808 // @option className: String = null
12809 // Custom class name set on an element. Only for SVG renderer.
12810 if (layer.options.className) {
12811 addClass(path, layer.options.className);
12812 }
12813
12814 if (layer.options.interactive) {
12815 addClass(path, 'leaflet-interactive');
12816 }
12817
12818 this._updateStyle(layer);
12819 this._layers[stamp(layer)] = layer;
12820 },
12821
12822 _addPath: function (layer) {
12823 if (!this._rootGroup) { this._initContainer(); }
12824 this._rootGroup.appendChild(layer._path);
12825 layer.addInteractiveTarget(layer._path);
12826 },
12827
12828 _removePath: function (layer) {
12829 remove(layer._path);
12830 layer.removeInteractiveTarget(layer._path);
12831 delete this._layers[stamp(layer)];
12832 },
12833
12834 _updatePath: function (layer) {
12835 layer._project();
12836 layer._update();
12837 },
12838
12839 _updateStyle: function (layer) {
12840 var path = layer._path,
12841 options = layer.options;
12842
12843 if (!path) { return; }
12844
12845 if (options.stroke) {
12846 path.setAttribute('stroke', options.color);
12847 path.setAttribute('stroke-opacity', options.opacity);
12848 path.setAttribute('stroke-width', options.weight);
12849 path.setAttribute('stroke-linecap', options.lineCap);
12850 path.setAttribute('stroke-linejoin', options.lineJoin);
12851
12852 if (options.dashArray) {
12853 path.setAttribute('stroke-dasharray', options.dashArray);
12854 } else {
12855 path.removeAttribute('stroke-dasharray');
12856 }
12857
12858 if (options.dashOffset) {
12859 path.setAttribute('stroke-dashoffset', options.dashOffset);
12860 } else {
12861 path.removeAttribute('stroke-dashoffset');
12862 }
12863 } else {
12864 path.setAttribute('stroke', 'none');
12865 }
12866
12867 if (options.fill) {
12868 path.setAttribute('fill', options.fillColor || options.color);
12869 path.setAttribute('fill-opacity', options.fillOpacity);
12870 path.setAttribute('fill-rule', options.fillRule || 'evenodd');
12871 } else {
12872 path.setAttribute('fill', 'none');
12873 }
12874 },
12875
12876 _updatePoly: function (layer, closed) {
12877 this._setPath(layer, pointsToPath(layer._parts, closed));
12878 },
12879
12880 _updateCircle: function (layer) {
12881 var p = layer._point,
12882 r = Math.max(Math.round(layer._radius), 1),
12883 r2 = Math.max(Math.round(layer._radiusY), 1) || r,
12884 arc = 'a' + r + ',' + r2 + ' 0 1,0 ';
12885
12886 // drawing a circle with two half-arcs
12887 var d = layer._empty() ? 'M0 0' :
12888 'M' + (p.x - r) + ',' + p.y +
12889 arc + (r * 2) + ',0 ' +
12890 arc + (-r * 2) + ',0 ';
12891
12892 this._setPath(layer, d);
12893 },
12894
12895 _setPath: function (layer, path) {
12896 layer._path.setAttribute('d', path);
12897 },
12898
12899 // SVG does not have the concept of zIndex so we resort to changing the DOM order of elements
12900 _bringToFront: function (layer) {
12901 toFront(layer._path);
12902 },
12903
12904 _bringToBack: function (layer) {
12905 toBack(layer._path);
12906 }
12907 });
12908
12909 if (vml) {
12910 SVG.include(vmlMixin);
12911 }
12912
12913 // @namespace SVG
12914 // @factory L.svg(options?: Renderer options)
12915 // Creates a SVG renderer with the given options.
12916 function svg$1(options) {
12917 return svg || vml ? new SVG(options) : null;
12918 }
12919
12920 Map.include({
12921 // @namespace Map; @method getRenderer(layer: Path): Renderer
12922 // Returns the instance of `Renderer` that should be used to render the given
12923 // `Path`. It will ensure that the `renderer` options of the map and paths
12924 // are respected, and that the renderers do exist on the map.
12925 getRenderer: function (layer) {
12926 // @namespace Path; @option renderer: Renderer
12927 // Use this specific instance of `Renderer` for this path. Takes
12928 // precedence over the map's [default renderer](#map-renderer).
12929 var renderer = layer.options.renderer || this._getPaneRenderer(layer.options.pane) || this.options.renderer || this._renderer;
12930
12931 if (!renderer) {
12932 renderer = this._renderer = this._createRenderer();
12933 }
12934
12935 if (!this.hasLayer(renderer)) {
12936 this.addLayer(renderer);
12937 }
12938 return renderer;
12939 },
12940
12941 _getPaneRenderer: function (name) {
12942 if (name === 'overlayPane' || name === undefined) {
12943 return false;
12944 }
12945
12946 var renderer = this._paneRenderers[name];
12947 if (renderer === undefined) {
12948 renderer = this._createRenderer({pane: name});
12949 this._paneRenderers[name] = renderer;
12950 }
12951 return renderer;
12952 },
12953
12954 _createRenderer: function (options) {
12955 // @namespace Map; @option preferCanvas: Boolean = false
12956 // Whether `Path`s should be rendered on a `Canvas` renderer.
12957 // By default, all `Path`s are rendered in a `SVG` renderer.
12958 return (this.options.preferCanvas && canvas$1(options)) || svg$1(options);
12959 }
12960 });
12961
12962 /*
12963 * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object.
12964 */
12965
12966 /*
12967 * @class Rectangle
12968 * @aka L.Rectangle
12969 * @inherits Polygon
12970 *
12971 * A class for drawing rectangle overlays on a map. Extends `Polygon`.
12972 *
12973 * @example
12974 *
12975 * ```js
12976 * // define rectangle geographical bounds
12977 * var bounds = [[54.559322, -5.767822], [56.1210604, -3.021240]];
12978 *
12979 * // create an orange rectangle
12980 * L.rectangle(bounds, {color: "#ff7800", weight: 1}).addTo(map);
12981 *
12982 * // zoom the map to the rectangle bounds
12983 * map.fitBounds(bounds);
12984 * ```
12985 *
12986 */
12987
12988
12989 var Rectangle = Polygon.extend({
12990 initialize: function (latLngBounds, options) {
12991 Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options);
12992 },
12993
12994 // @method setBounds(latLngBounds: LatLngBounds): this
12995 // Redraws the rectangle with the passed bounds.
12996 setBounds: function (latLngBounds) {
12997 return this.setLatLngs(this._boundsToLatLngs(latLngBounds));
12998 },
12999
13000 _boundsToLatLngs: function (latLngBounds) {
13001 latLngBounds = toLatLngBounds(latLngBounds);
13002 return [
13003 latLngBounds.getSouthWest(),
13004 latLngBounds.getNorthWest(),
13005 latLngBounds.getNorthEast(),
13006 latLngBounds.getSouthEast()
13007 ];
13008 }
13009 });
13010
13011
13012 // @factory L.rectangle(latLngBounds: LatLngBounds, options?: Polyline options)
13013 function rectangle(latLngBounds, options) {
13014 return new Rectangle(latLngBounds, options);
13015 }
13016
13017 SVG.create = create$2;
13018 SVG.pointsToPath = pointsToPath;
13019
13020 GeoJSON.geometryToLayer = geometryToLayer;
13021 GeoJSON.coordsToLatLng = coordsToLatLng;
13022 GeoJSON.coordsToLatLngs = coordsToLatLngs;
13023 GeoJSON.latLngToCoords = latLngToCoords;
13024 GeoJSON.latLngsToCoords = latLngsToCoords;
13025 GeoJSON.getFeature = getFeature;
13026 GeoJSON.asFeature = asFeature;
13027
13028 /*
13029 * L.Handler.BoxZoom is used to add shift-drag zoom interaction to the map
13030 * (zoom to a selected bounding box), enabled by default.
13031 */
13032
13033 // @namespace Map
13034 // @section Interaction Options
13035 Map.mergeOptions({
13036 // @option boxZoom: Boolean = true
13037 // Whether the map can be zoomed to a rectangular area specified by
13038 // dragging the mouse while pressing the shift key.
13039 boxZoom: true
13040 });
13041
13042 var BoxZoom = Handler.extend({
13043 initialize: function (map) {
13044 this._map = map;
13045 this._container = map._container;
13046 this._pane = map._panes.overlayPane;
13047 this._resetStateTimeout = 0;
13048 map.on('unload', this._destroy, this);
13049 },
13050
13051 addHooks: function () {
13052 on(this._container, 'mousedown', this._onMouseDown, this);
13053 },
13054
13055 removeHooks: function () {
13056 off(this._container, 'mousedown', this._onMouseDown, this);
13057 },
13058
13059 moved: function () {
13060 return this._moved;
13061 },
13062
13063 _destroy: function () {
13064 remove(this._pane);
13065 delete this._pane;
13066 },
13067
13068 _resetState: function () {
13069 this._resetStateTimeout = 0;
13070 this._moved = false;
13071 },
13072
13073 _clearDeferredResetState: function () {
13074 if (this._resetStateTimeout !== 0) {
13075 clearTimeout(this._resetStateTimeout);
13076 this._resetStateTimeout = 0;
13077 }
13078 },
13079
13080 _onMouseDown: function (e) {
13081 if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; }
13082
13083 // Clear the deferred resetState if it hasn't executed yet, otherwise it
13084 // will interrupt the interaction and orphan a box element in the container.
13085 this._clearDeferredResetState();
13086 this._resetState();
13087
13088 disableTextSelection();
13089 disableImageDrag();
13090
13091 this._startPoint = this._map.mouseEventToContainerPoint(e);
13092
13093 on(document, {
13094 contextmenu: stop,
13095 mousemove: this._onMouseMove,
13096 mouseup: this._onMouseUp,
13097 keydown: this._onKeyDown
13098 }, this);
13099 },
13100
13101 _onMouseMove: function (e) {
13102 if (!this._moved) {
13103 this._moved = true;
13104
13105 this._box = create$1('div', 'leaflet-zoom-box', this._container);
13106 addClass(this._container, 'leaflet-crosshair');
13107
13108 this._map.fire('boxzoomstart');
13109 }
13110
13111 this._point = this._map.mouseEventToContainerPoint(e);
13112
13113 var bounds = new Bounds(this._point, this._startPoint),
13114 size = bounds.getSize();
13115
13116 setPosition(this._box, bounds.min);
13117
13118 this._box.style.width = size.x + 'px';
13119 this._box.style.height = size.y + 'px';
13120 },
13121
13122 _finish: function () {
13123 if (this._moved) {
13124 remove(this._box);
13125 removeClass(this._container, 'leaflet-crosshair');
13126 }
13127
13128 enableTextSelection();
13129 enableImageDrag();
13130
13131 off(document, {
13132 contextmenu: stop,
13133 mousemove: this._onMouseMove,
13134 mouseup: this._onMouseUp,
13135 keydown: this._onKeyDown
13136 }, this);
13137 },
13138
13139 _onMouseUp: function (e) {
13140 if ((e.which !== 1) && (e.button !== 1)) { return; }
13141
13142 this._finish();
13143
13144 if (!this._moved) { return; }
13145 // Postpone to next JS tick so internal click event handling
13146 // still see it as "moved".
13147 this._clearDeferredResetState();
13148 this._resetStateTimeout = setTimeout(bind(this._resetState, this), 0);
13149
13150 var bounds = new LatLngBounds(
13151 this._map.containerPointToLatLng(this._startPoint),
13152 this._map.containerPointToLatLng(this._point));
13153
13154 this._map
13155 .fitBounds(bounds)
13156 .fire('boxzoomend', {boxZoomBounds: bounds});
13157 },
13158
13159 _onKeyDown: function (e) {
13160 if (e.keyCode === 27) {
13161 this._finish();
13162 }
13163 }
13164 });
13165
13166 // @section Handlers
13167 // @property boxZoom: Handler
13168 // Box (shift-drag with mouse) zoom handler.
13169 Map.addInitHook('addHandler', 'boxZoom', BoxZoom);
13170
13171 /*
13172 * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default.
13173 */
13174
13175 // @namespace Map
13176 // @section Interaction Options
13177
13178 Map.mergeOptions({
13179 // @option doubleClickZoom: Boolean|String = true
13180 // Whether the map can be zoomed in by double clicking on it and
13181 // zoomed out by double clicking while holding shift. If passed
13182 // `'center'`, double-click zoom will zoom to the center of the
13183 // view regardless of where the mouse was.
13184 doubleClickZoom: true
13185 });
13186
13187 var DoubleClickZoom = Handler.extend({
13188 addHooks: function () {
13189 this._map.on('dblclick', this._onDoubleClick, this);
13190 },
13191
13192 removeHooks: function () {
13193 this._map.off('dblclick', this._onDoubleClick, this);
13194 },
13195
13196 _onDoubleClick: function (e) {
13197 var map = this._map,
13198 oldZoom = map.getZoom(),
13199 delta = map.options.zoomDelta,
13200 zoom = e.originalEvent.shiftKey ? oldZoom - delta : oldZoom + delta;
13201
13202 if (map.options.doubleClickZoom === 'center') {
13203 map.setZoom(zoom);
13204 } else {
13205 map.setZoomAround(e.containerPoint, zoom);
13206 }
13207 }
13208 });
13209
13210 // @section Handlers
13211 //
13212 // Map properties include interaction handlers that allow you to control
13213 // interaction behavior in runtime, enabling or disabling certain features such
13214 // as dragging or touch zoom (see `Handler` methods). For example:
13215 //
13216 // ```js
13217 // map.doubleClickZoom.disable();
13218 // ```
13219 //
13220 // @property doubleClickZoom: Handler
13221 // Double click zoom handler.
13222 Map.addInitHook('addHandler', 'doubleClickZoom', DoubleClickZoom);
13223
13224 /*
13225 * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default.
13226 */
13227
13228 // @namespace Map
13229 // @section Interaction Options
13230 Map.mergeOptions({
13231 // @option dragging: Boolean = true
13232 // Whether the map be draggable with mouse/touch or not.
13233 dragging: true,
13234
13235 // @section Panning Inertia Options
13236 // @option inertia: Boolean = *
13237 // If enabled, panning of the map will have an inertia effect where
13238 // the map builds momentum while dragging and continues moving in
13239 // the same direction for some time. Feels especially nice on touch
13240 // devices. Enabled by default unless running on old Android devices.
13241 inertia: !android23,
13242
13243 // @option inertiaDeceleration: Number = 3000
13244 // The rate with which the inertial movement slows down, in pixels/second².
13245 inertiaDeceleration: 3400, // px/s^2
13246
13247 // @option inertiaMaxSpeed: Number = Infinity
13248 // Max speed of the inertial movement, in pixels/second.
13249 inertiaMaxSpeed: Infinity, // px/s
13250
13251 // @option easeLinearity: Number = 0.2
13252 easeLinearity: 0.2,
13253
13254 // TODO refactor, move to CRS
13255 // @option worldCopyJump: Boolean = false
13256 // With this option enabled, the map tracks when you pan to another "copy"
13257 // of the world and seamlessly jumps to the original one so that all overlays
13258 // like markers and vector layers are still visible.
13259 worldCopyJump: false,
13260
13261 // @option maxBoundsViscosity: Number = 0.0
13262 // If `maxBounds` is set, this option will control how solid the bounds
13263 // are when dragging the map around. The default value of `0.0` allows the
13264 // user to drag outside the bounds at normal speed, higher values will
13265 // slow down map dragging outside bounds, and `1.0` makes the bounds fully
13266 // solid, preventing the user from dragging outside the bounds.
13267 maxBoundsViscosity: 0.0
13268 });
13269
13270 var Drag = Handler.extend({
13271 addHooks: function () {
13272 if (!this._draggable) {
13273 var map = this._map;
13274
13275 this._draggable = new Draggable(map._mapPane, map._container);
13276
13277 this._draggable.on({
13278 dragstart: this._onDragStart,
13279 drag: this._onDrag,
13280 dragend: this._onDragEnd
13281 }, this);
13282
13283 this._draggable.on('predrag', this._onPreDragLimit, this);
13284 if (map.options.worldCopyJump) {
13285 this._draggable.on('predrag', this._onPreDragWrap, this);
13286 map.on('zoomend', this._onZoomEnd, this);
13287
13288 map.whenReady(this._onZoomEnd, this);
13289 }
13290 }
13291 addClass(this._map._container, 'leaflet-grab leaflet-touch-drag');
13292 this._draggable.enable();
13293 this._positions = [];
13294 this._times = [];
13295 },
13296
13297 removeHooks: function () {
13298 removeClass(this._map._container, 'leaflet-grab');
13299 removeClass(this._map._container, 'leaflet-touch-drag');
13300 this._draggable.disable();
13301 },
13302
13303 moved: function () {
13304 return this._draggable && this._draggable._moved;
13305 },
13306
13307 moving: function () {
13308 return this._draggable && this._draggable._moving;
13309 },
13310
13311 _onDragStart: function () {
13312 var map = this._map;
13313
13314 map._stop();
13315 if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) {
13316 var bounds = toLatLngBounds(this._map.options.maxBounds);
13317
13318 this._offsetLimit = toBounds(
13319 this._map.latLngToContainerPoint(bounds.getNorthWest()).multiplyBy(-1),
13320 this._map.latLngToContainerPoint(bounds.getSouthEast()).multiplyBy(-1)
13321 .add(this._map.getSize()));
13322
13323 this._viscosity = Math.min(1.0, Math.max(0.0, this._map.options.maxBoundsViscosity));
13324 } else {
13325 this._offsetLimit = null;
13326 }
13327
13328 map
13329 .fire('movestart')
13330 .fire('dragstart');
13331
13332 if (map.options.inertia) {
13333 this._positions = [];
13334 this._times = [];
13335 }
13336 },
13337
13338 _onDrag: function (e) {
13339 if (this._map.options.inertia) {
13340 var time = this._lastTime = +new Date(),
13341 pos = this._lastPos = this._draggable._absPos || this._draggable._newPos;
13342
13343 this._positions.push(pos);
13344 this._times.push(time);
13345
13346 this._prunePositions(time);
13347 }
13348
13349 this._map
13350 .fire('move', e)
13351 .fire('drag', e);
13352 },
13353
13354 _prunePositions: function (time) {
13355 while (this._positions.length > 1 && time - this._times[0] > 50) {
13356 this._positions.shift();
13357 this._times.shift();
13358 }
13359 },
13360
13361 _onZoomEnd: function () {
13362 var pxCenter = this._map.getSize().divideBy(2),
13363 pxWorldCenter = this._map.latLngToLayerPoint([0, 0]);
13364
13365 this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x;
13366 this._worldWidth = this._map.getPixelWorldBounds().getSize().x;
13367 },
13368
13369 _viscousLimit: function (value, threshold) {
13370 return value - (value - threshold) * this._viscosity;
13371 },
13372
13373 _onPreDragLimit: function () {
13374 if (!this._viscosity || !this._offsetLimit) { return; }
13375
13376 var offset = this._draggable._newPos.subtract(this._draggable._startPos);
13377
13378 var limit = this._offsetLimit;
13379 if (offset.x < limit.min.x) { offset.x = this._viscousLimit(offset.x, limit.min.x); }
13380 if (offset.y < limit.min.y) { offset.y = this._viscousLimit(offset.y, limit.min.y); }
13381 if (offset.x > limit.max.x) { offset.x = this._viscousLimit(offset.x, limit.max.x); }
13382 if (offset.y > limit.max.y) { offset.y = this._viscousLimit(offset.y, limit.max.y); }
13383
13384 this._draggable._newPos = this._draggable._startPos.add(offset);
13385 },
13386
13387 _onPreDragWrap: function () {
13388 // TODO refactor to be able to adjust map pane position after zoom
13389 var worldWidth = this._worldWidth,
13390 halfWidth = Math.round(worldWidth / 2),
13391 dx = this._initialWorldOffset,
13392 x = this._draggable._newPos.x,
13393 newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx,
13394 newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx,
13395 newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2;
13396
13397 this._draggable._absPos = this._draggable._newPos.clone();
13398 this._draggable._newPos.x = newX;
13399 },
13400
13401 _onDragEnd: function (e) {
13402 var map = this._map,
13403 options = map.options,
13404
13405 noInertia = !options.inertia || this._times.length < 2;
13406
13407 map.fire('dragend', e);
13408
13409 if (noInertia) {
13410 map.fire('moveend');
13411
13412 } else {
13413 this._prunePositions(+new Date());
13414
13415 var direction = this._lastPos.subtract(this._positions[0]),
13416 duration = (this._lastTime - this._times[0]) / 1000,
13417 ease = options.easeLinearity,
13418
13419 speedVector = direction.multiplyBy(ease / duration),
13420 speed = speedVector.distanceTo([0, 0]),
13421
13422 limitedSpeed = Math.min(options.inertiaMaxSpeed, speed),
13423 limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed),
13424
13425 decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease),
13426 offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round();
13427
13428 if (!offset.x && !offset.y) {
13429 map.fire('moveend');
13430
13431 } else {
13432 offset = map._limitOffset(offset, map.options.maxBounds);
13433
13434 requestAnimFrame(function () {
13435 map.panBy(offset, {
13436 duration: decelerationDuration,
13437 easeLinearity: ease,
13438 noMoveStart: true,
13439 animate: true
13440 });
13441 });
13442 }
13443 }
13444 }
13445 });
13446
13447 // @section Handlers
13448 // @property dragging: Handler
13449 // Map dragging handler (by both mouse and touch).
13450 Map.addInitHook('addHandler', 'dragging', Drag);
13451
13452 /*
13453 * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default.
13454 */
13455
13456 // @namespace Map
13457 // @section Keyboard Navigation Options
13458 Map.mergeOptions({
13459 // @option keyboard: Boolean = true
13460 // Makes the map focusable and allows users to navigate the map with keyboard
13461 // arrows and `+`/`-` keys.
13462 keyboard: true,
13463
13464 // @option keyboardPanDelta: Number = 80
13465 // Amount of pixels to pan when pressing an arrow key.
13466 keyboardPanDelta: 80
13467 });
13468
13469 var Keyboard = Handler.extend({
13470
13471 keyCodes: {
13472 left: [37],
13473 right: [39],
13474 down: [40],
13475 up: [38],
13476 zoomIn: [187, 107, 61, 171],
13477 zoomOut: [189, 109, 54, 173]
13478 },
13479
13480 initialize: function (map) {
13481 this._map = map;
13482
13483 this._setPanDelta(map.options.keyboardPanDelta);
13484 this._setZoomDelta(map.options.zoomDelta);
13485 },
13486
13487 addHooks: function () {
13488 var container = this._map._container;
13489
13490 // make the container focusable by tabbing
13491 if (container.tabIndex <= 0) {
13492 container.tabIndex = '0';
13493 }
13494
13495 on(container, {
13496 focus: this._onFocus,
13497 blur: this._onBlur,
13498 mousedown: this._onMouseDown
13499 }, this);
13500
13501 this._map.on({
13502 focus: this._addHooks,
13503 blur: this._removeHooks
13504 }, this);
13505 },
13506
13507 removeHooks: function () {
13508 this._removeHooks();
13509
13510 off(this._map._container, {
13511 focus: this._onFocus,
13512 blur: this._onBlur,
13513 mousedown: this._onMouseDown
13514 }, this);
13515
13516 this._map.off({
13517 focus: this._addHooks,
13518 blur: this._removeHooks
13519 }, this);
13520 },
13521
13522 _onMouseDown: function () {
13523 if (this._focused) { return; }
13524
13525 var body = document.body,
13526 docEl = document.documentElement,
13527 top = body.scrollTop || docEl.scrollTop,
13528 left = body.scrollLeft || docEl.scrollLeft;
13529
13530 this._map._container.focus();
13531
13532 window.scrollTo(left, top);
13533 },
13534
13535 _onFocus: function () {
13536 this._focused = true;
13537 this._map.fire('focus');
13538 },
13539
13540 _onBlur: function () {
13541 this._focused = false;
13542 this._map.fire('blur');
13543 },
13544
13545 _setPanDelta: function (panDelta) {
13546 var keys = this._panKeys = {},
13547 codes = this.keyCodes,
13548 i, len;
13549
13550 for (i = 0, len = codes.left.length; i < len; i++) {
13551 keys[codes.left[i]] = [-1 * panDelta, 0];
13552 }
13553 for (i = 0, len = codes.right.length; i < len; i++) {
13554 keys[codes.right[i]] = [panDelta, 0];
13555 }
13556 for (i = 0, len = codes.down.length; i < len; i++) {
13557 keys[codes.down[i]] = [0, panDelta];
13558 }
13559 for (i = 0, len = codes.up.length; i < len; i++) {
13560 keys[codes.up[i]] = [0, -1 * panDelta];
13561 }
13562 },
13563
13564 _setZoomDelta: function (zoomDelta) {
13565 var keys = this._zoomKeys = {},
13566 codes = this.keyCodes,
13567 i, len;
13568
13569 for (i = 0, len = codes.zoomIn.length; i < len; i++) {
13570 keys[codes.zoomIn[i]] = zoomDelta;
13571 }
13572 for (i = 0, len = codes.zoomOut.length; i < len; i++) {
13573 keys[codes.zoomOut[i]] = -zoomDelta;
13574 }
13575 },
13576
13577 _addHooks: function () {
13578 on(document, 'keydown', this._onKeyDown, this);
13579 },
13580
13581 _removeHooks: function () {
13582 off(document, 'keydown', this._onKeyDown, this);
13583 },
13584
13585 _onKeyDown: function (e) {
13586 if (e.altKey || e.ctrlKey || e.metaKey) { return; }
13587
13588 var key = e.keyCode,
13589 map = this._map,
13590 offset;
13591
13592 if (key in this._panKeys) {
13593 if (!map._panAnim || !map._panAnim._inProgress) {
13594 offset = this._panKeys[key];
13595 if (e.shiftKey) {
13596 offset = toPoint(offset).multiplyBy(3);
13597 }
13598
13599 map.panBy(offset);
13600
13601 if (map.options.maxBounds) {
13602 map.panInsideBounds(map.options.maxBounds);
13603 }
13604 }
13605 } else if (key in this._zoomKeys) {
13606 map.setZoom(map.getZoom() + (e.shiftKey ? 3 : 1) * this._zoomKeys[key]);
13607
13608 } else if (key === 27 && map._popup && map._popup.options.closeOnEscapeKey) {
13609 map.closePopup();
13610
13611 } else {
13612 return;
13613 }
13614
13615 stop(e);
13616 }
13617 });
13618
13619 // @section Handlers
13620 // @section Handlers
13621 // @property keyboard: Handler
13622 // Keyboard navigation handler.
13623 Map.addInitHook('addHandler', 'keyboard', Keyboard);
13624
13625 /*
13626 * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map.
13627 */
13628
13629 // @namespace Map
13630 // @section Interaction Options
13631 Map.mergeOptions({
13632 // @section Mouse wheel options
13633 // @option scrollWheelZoom: Boolean|String = true
13634 // Whether the map can be zoomed by using the mouse wheel. If passed `'center'`,
13635 // it will zoom to the center of the view regardless of where the mouse was.
13636 scrollWheelZoom: true,
13637
13638 // @option wheelDebounceTime: Number = 40
13639 // Limits the rate at which a wheel can fire (in milliseconds). By default
13640 // user can't zoom via wheel more often than once per 40 ms.
13641 wheelDebounceTime: 40,
13642
13643 // @option wheelPxPerZoomLevel: Number = 60
13644 // How many scroll pixels (as reported by [L.DomEvent.getWheelDelta](#domevent-getwheeldelta))
13645 // mean a change of one full zoom level. Smaller values will make wheel-zooming
13646 // faster (and vice versa).
13647 wheelPxPerZoomLevel: 60
13648 });
13649
13650 var ScrollWheelZoom = Handler.extend({
13651 addHooks: function () {
13652 on(this._map._container, 'wheel', this._onWheelScroll, this);
13653
13654 this._delta = 0;
13655 },
13656
13657 removeHooks: function () {
13658 off(this._map._container, 'wheel', this._onWheelScroll, this);
13659 },
13660
13661 _onWheelScroll: function (e) {
13662 var delta = getWheelDelta(e);
13663
13664 var debounce = this._map.options.wheelDebounceTime;
13665
13666 this._delta += delta;
13667 this._lastMousePos = this._map.mouseEventToContainerPoint(e);
13668
13669 if (!this._startTime) {
13670 this._startTime = +new Date();
13671 }
13672
13673 var left = Math.max(debounce - (+new Date() - this._startTime), 0);
13674
13675 clearTimeout(this._timer);
13676 this._timer = setTimeout(bind(this._performZoom, this), left);
13677
13678 stop(e);
13679 },
13680
13681 _performZoom: function () {
13682 var map = this._map,
13683 zoom = map.getZoom(),
13684 snap = this._map.options.zoomSnap || 0;
13685
13686 map._stop(); // stop panning and fly animations if any
13687
13688 // map the delta with a sigmoid function to -4..4 range leaning on -1..1
13689 var d2 = this._delta / (this._map.options.wheelPxPerZoomLevel * 4),
13690 d3 = 4 * Math.log(2 / (1 + Math.exp(-Math.abs(d2)))) / Math.LN2,
13691 d4 = snap ? Math.ceil(d3 / snap) * snap : d3,
13692 delta = map._limitZoom(zoom + (this._delta > 0 ? d4 : -d4)) - zoom;
13693
13694 this._delta = 0;
13695 this._startTime = null;
13696
13697 if (!delta) { return; }
13698
13699 if (map.options.scrollWheelZoom === 'center') {
13700 map.setZoom(zoom + delta);
13701 } else {
13702 map.setZoomAround(this._lastMousePos, zoom + delta);
13703 }
13704 }
13705 });
13706
13707 // @section Handlers
13708 // @property scrollWheelZoom: Handler
13709 // Scroll wheel zoom handler.
13710 Map.addInitHook('addHandler', 'scrollWheelZoom', ScrollWheelZoom);
13711
13712 /*
13713 * L.Map.Tap is used to enable mobile hacks like quick taps and long hold.
13714 */
13715
13716 // @namespace Map
13717 // @section Interaction Options
13718 Map.mergeOptions({
13719 // @section Touch interaction options
13720 // @option tap: Boolean = true
13721 // Enables mobile hacks for supporting instant taps (fixing 200ms click
13722 // delay on iOS/Android) and touch holds (fired as `contextmenu` events).
13723 tap: true,
13724
13725 // @option tapTolerance: Number = 15
13726 // The max number of pixels a user can shift his finger during touch
13727 // for it to be considered a valid tap.
13728 tapTolerance: 15
13729 });
13730
13731 var Tap = Handler.extend({
13732 addHooks: function () {
13733 on(this._map._container, 'touchstart', this._onDown, this);
13734 },
13735
13736 removeHooks: function () {
13737 off(this._map._container, 'touchstart', this._onDown, this);
13738 },
13739
13740 _onDown: function (e) {
13741 if (!e.touches) { return; }
13742
13743 preventDefault(e);
13744
13745 this._fireClick = true;
13746
13747 // don't simulate click or track longpress if more than 1 touch
13748 if (e.touches.length > 1) {
13749 this._fireClick = false;
13750 clearTimeout(this._holdTimeout);
13751 return;
13752 }
13753
13754 var first = e.touches[0],
13755 el = first.target;
13756
13757 this._startPos = this._newPos = new Point(first.clientX, first.clientY);
13758
13759 // if touching a link, highlight it
13760 if (el.tagName && el.tagName.toLowerCase() === 'a') {
13761 addClass(el, 'leaflet-active');
13762 }
13763
13764 // simulate long hold but setting a timeout
13765 this._holdTimeout = setTimeout(bind(function () {
13766 if (this._isTapValid()) {
13767 this._fireClick = false;
13768 this._onUp();
13769 this._simulateEvent('contextmenu', first);
13770 }
13771 }, this), 1000);
13772
13773 this._simulateEvent('mousedown', first);
13774
13775 on(document, {
13776 touchmove: this._onMove,
13777 touchend: this._onUp
13778 }, this);
13779 },
13780
13781 _onUp: function (e) {
13782 clearTimeout(this._holdTimeout);
13783
13784 off(document, {
13785 touchmove: this._onMove,
13786 touchend: this._onUp
13787 }, this);
13788
13789 if (this._fireClick && e && e.changedTouches) {
13790
13791 var first = e.changedTouches[0],
13792 el = first.target;
13793
13794 if (el && el.tagName && el.tagName.toLowerCase() === 'a') {
13795 removeClass(el, 'leaflet-active');
13796 }
13797
13798 this._simulateEvent('mouseup', first);
13799
13800 // simulate click if the touch didn't move too much
13801 if (this._isTapValid()) {
13802 this._simulateEvent('click', first);
13803 }
13804 }
13805 },
13806
13807 _isTapValid: function () {
13808 return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance;
13809 },
13810
13811 _onMove: function (e) {
13812 var first = e.touches[0];
13813 this._newPos = new Point(first.clientX, first.clientY);
13814 this._simulateEvent('mousemove', first);
13815 },
13816
13817 _simulateEvent: function (type, e) {
13818 var simulatedEvent = document.createEvent('MouseEvents');
13819
13820 simulatedEvent._simulated = true;
13821 e.target._simulatedClick = true;
13822
13823 simulatedEvent.initMouseEvent(
13824 type, true, true, window, 1,
13825 e.screenX, e.screenY,
13826 e.clientX, e.clientY,
13827 false, false, false, false, 0, null);
13828
13829 e.target.dispatchEvent(simulatedEvent);
13830 }
13831 });
13832
13833 // @section Handlers
13834 // @property tap: Handler
13835 // Mobile touch hacks (quick tap and touch hold) handler.
13836 if (touch && (!pointer || safari)) {
13837 Map.addInitHook('addHandler', 'tap', Tap);
13838 }
13839
13840 /*
13841 * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers.
13842 */
13843
13844 // @namespace Map
13845 // @section Interaction Options
13846 Map.mergeOptions({
13847 // @section Touch interaction options
13848 // @option touchZoom: Boolean|String = *
13849 // Whether the map can be zoomed by touch-dragging with two fingers. If
13850 // passed `'center'`, it will zoom to the center of the view regardless of
13851 // where the touch events (fingers) were. Enabled for touch-capable web
13852 // browsers except for old Androids.
13853 touchZoom: touch && !android23,
13854
13855 // @option bounceAtZoomLimits: Boolean = true
13856 // Set it to false if you don't want the map to zoom beyond min/max zoom
13857 // and then bounce back when pinch-zooming.
13858 bounceAtZoomLimits: true
13859 });
13860
13861 var TouchZoom = Handler.extend({
13862 addHooks: function () {
13863 addClass(this._map._container, 'leaflet-touch-zoom');
13864 on(this._map._container, 'touchstart', this._onTouchStart, this);
13865 },
13866
13867 removeHooks: function () {
13868 removeClass(this._map._container, 'leaflet-touch-zoom');
13869 off(this._map._container, 'touchstart', this._onTouchStart, this);
13870 },
13871
13872 _onTouchStart: function (e) {
13873 var map = this._map;
13874 if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; }
13875
13876 var p1 = map.mouseEventToContainerPoint(e.touches[0]),
13877 p2 = map.mouseEventToContainerPoint(e.touches[1]);
13878
13879 this._centerPoint = map.getSize()._divideBy(2);
13880 this._startLatLng = map.containerPointToLatLng(this._centerPoint);
13881 if (map.options.touchZoom !== 'center') {
13882 this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
13883 }
13884
13885 this._startDist = p1.distanceTo(p2);
13886 this._startZoom = map.getZoom();
13887
13888 this._moved = false;
13889 this._zooming = true;
13890
13891 map._stop();
13892
13893 on(document, 'touchmove', this._onTouchMove, this);
13894 on(document, 'touchend', this._onTouchEnd, this);
13895
13896 preventDefault(e);
13897 },
13898
13899 _onTouchMove: function (e) {
13900 if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; }
13901
13902 var map = this._map,
13903 p1 = map.mouseEventToContainerPoint(e.touches[0]),
13904 p2 = map.mouseEventToContainerPoint(e.touches[1]),
13905 scale = p1.distanceTo(p2) / this._startDist;
13906
13907 this._zoom = map.getScaleZoom(scale, this._startZoom);
13908
13909 if (!map.options.bounceAtZoomLimits && (
13910 (this._zoom < map.getMinZoom() && scale < 1) ||
13911 (this._zoom > map.getMaxZoom() && scale > 1))) {
13912 this._zoom = map._limitZoom(this._zoom);
13913 }
13914
13915 if (map.options.touchZoom === 'center') {
13916 this._center = this._startLatLng;
13917 if (scale === 1) { return; }
13918 } else {
13919 // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
13920 var delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
13921 if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
13922 this._center = map.unproject(map.project(this._pinchStartLatLng, this._zoom).subtract(delta), this._zoom);
13923 }
13924
13925 if (!this._moved) {
13926 map._moveStart(true, false);
13927 this._moved = true;
13928 }
13929
13930 cancelAnimFrame(this._animRequest);
13931
13932 var moveFn = bind(map._move, map, this._center, this._zoom, {pinch: true, round: false});
13933 this._animRequest = requestAnimFrame(moveFn, this, true);
13934
13935 preventDefault(e);
13936 },
13937
13938 _onTouchEnd: function () {
13939 if (!this._moved || !this._zooming) {
13940 this._zooming = false;
13941 return;
13942 }
13943
13944 this._zooming = false;
13945 cancelAnimFrame(this._animRequest);
13946
13947 off(document, 'touchmove', this._onTouchMove, this);
13948 off(document, 'touchend', this._onTouchEnd, this);
13949
13950 // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
13951 if (this._map.options.zoomAnimation) {
13952 this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
13953 } else {
13954 this._map._resetView(this._center, this._map._limitZoom(this._zoom));
13955 }
13956 }
13957 });
13958
13959 // @section Handlers
13960 // @property touchZoom: Handler
13961 // Touch zoom handler.
13962 Map.addInitHook('addHandler', 'touchZoom', TouchZoom);
13963
13964 Map.BoxZoom = BoxZoom;
13965 Map.DoubleClickZoom = DoubleClickZoom;
13966 Map.Drag = Drag;
13967 Map.Keyboard = Keyboard;
13968 Map.ScrollWheelZoom = ScrollWheelZoom;
13969 Map.Tap = Tap;
13970 Map.TouchZoom = TouchZoom;
13971
13972 exports.version = version;
13973 exports.Control = Control;
13974 exports.control = control;
13975 exports.Browser = Browser;
13976 exports.Evented = Evented;
13977 exports.Mixin = Mixin;
13978 exports.Util = Util;
13979 exports.Class = Class;
13980 exports.Handler = Handler;
13981 exports.extend = extend;
13982 exports.bind = bind;
13983 exports.stamp = stamp;
13984 exports.setOptions = setOptions;
13985 exports.DomEvent = DomEvent;
13986 exports.DomUtil = DomUtil;
13987 exports.PosAnimation = PosAnimation;
13988 exports.Draggable = Draggable;
13989 exports.LineUtil = LineUtil;
13990 exports.PolyUtil = PolyUtil;
13991 exports.Point = Point;
13992 exports.point = toPoint;
13993 exports.Bounds = Bounds;
13994 exports.bounds = toBounds;
13995 exports.Transformation = Transformation;
13996 exports.transformation = toTransformation;
13997 exports.Projection = index;
13998 exports.LatLng = LatLng;
13999 exports.latLng = toLatLng;
14000 exports.LatLngBounds = LatLngBounds;
14001 exports.latLngBounds = toLatLngBounds;
14002 exports.CRS = CRS;
14003 exports.GeoJSON = GeoJSON;
14004 exports.geoJSON = geoJSON;
14005 exports.geoJson = geoJson;
14006 exports.Layer = Layer;
14007 exports.LayerGroup = LayerGroup;
14008 exports.layerGroup = layerGroup;
14009 exports.FeatureGroup = FeatureGroup;
14010 exports.featureGroup = featureGroup;
14011 exports.ImageOverlay = ImageOverlay;
14012 exports.imageOverlay = imageOverlay;
14013 exports.VideoOverlay = VideoOverlay;
14014 exports.videoOverlay = videoOverlay;
14015 exports.SVGOverlay = SVGOverlay;
14016 exports.svgOverlay = svgOverlay;
14017 exports.DivOverlay = DivOverlay;
14018 exports.Popup = Popup;
14019 exports.popup = popup;
14020 exports.Tooltip = Tooltip;
14021 exports.tooltip = tooltip;
14022 exports.Icon = Icon;
14023 exports.icon = icon;
14024 exports.DivIcon = DivIcon;
14025 exports.divIcon = divIcon;
14026 exports.Marker = Marker;
14027 exports.marker = marker;
14028 exports.TileLayer = TileLayer;
14029 exports.tileLayer = tileLayer;
14030 exports.GridLayer = GridLayer;
14031 exports.gridLayer = gridLayer;
14032 exports.SVG = SVG;
14033 exports.svg = svg$1;
14034 exports.Renderer = Renderer;
14035 exports.Canvas = Canvas;
14036 exports.canvas = canvas$1;
14037 exports.Path = Path;
14038 exports.CircleMarker = CircleMarker;
14039 exports.circleMarker = circleMarker;
14040 exports.Circle = Circle;
14041 exports.circle = circle;
14042 exports.Polyline = Polyline;
14043 exports.polyline = polyline;
14044 exports.Polygon = Polygon;
14045 exports.polygon = polygon;
14046 exports.Rectangle = Rectangle;
14047 exports.rectangle = rectangle;
14048 exports.Map = Map;
14049 exports.map = createMap;
14050
14051 var oldL = window.L;
14052 exports.noConflict = function() {
14053 window.L = oldL;
14054 return this;
14055 }
14056
14057 // Always export us to window global (see #2364)
14058 window.L = exports;
1379914059
1380014060 })));
1380114061 //# sourceMappingURL=leaflet-src.js.map
00 /* @preserve
1 * Leaflet 1.3.1+Detached: ba6f97fff8647e724e4dfe66d2ed7da11f908989.ba6f97f, a JS library for interactive maps. http://leafletjs.com
2 * (c) 2010-2017 Vladimir Agafonkin, (c) 2010-2011 CloudMade
1 * Leaflet 1.4.0+Detached: 3337f36d2a2d2b33946779057619b31f674ff5dc.3337f36, a JS library for interactive maps. http://leafletjs.com
2 * (c) 2010-2018 Vladimir Agafonkin, (c) 2010-2011 CloudMade
33 */
4 !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e<n;e++){o=arguments[e];for(i in o)t[i]=o[i]}return t}function e(t,i){var e=Array.prototype.slice;if(t.bind)return t.bind.apply(t,e.call(arguments,1));var n=e.call(arguments,2);return function(){return t.apply(i,n.length?n.concat(e.call(arguments)):arguments)}}function n(t){return t._leaflet_id=t._leaflet_id||++ti,t._leaflet_id}function o(t,i,e){var n,o,s,r;return r=function(){n=!1,o&&(s.apply(e,o),o=!1)},s=function(){n?o=arguments:(t.apply(e,arguments),setTimeout(r,i),n=!0)}}function s(t,i,e){var n=i[1],o=i[0],s=n-o;return t===n&&e?t:((t-o)%s+s)%s+o}function r(){return!1}function a(t,i){var e=Math.pow(10,void 0===i?6:i);return Math.round(t*e)/e}function h(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}function u(t){return h(t).split(/\s+/)}function l(t,i){t.hasOwnProperty("options")||(t.options=t.options?Qt(t.options):{});for(var e in i)t.options[e]=i[e];return t.options}function c(t,i,e){var n=[];for(var o in t)n.push(encodeURIComponent(e?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(i&&-1!==i.indexOf("?")?"&":"?")+n.join("&")}function _(t,i){return t.replace(ii,function(t,e){var n=i[e];if(void 0===n)throw new Error("No value provided for variable "+t);return"function"==typeof n&&(n=n(i)),n})}function d(t,i){for(var e=0;e<t.length;e++)if(t[e]===i)return e;return-1}function p(t){return window["webkit"+t]||window["moz"+t]||window["ms"+t]}function m(t){var i=+new Date,e=Math.max(0,16-(i-oi));return oi=i+e,window.setTimeout(t,e)}function f(t,i,n){if(!n||si!==m)return si.call(window,e(t,i));t.call(i)}function g(t){t&&ri.call(window,t)}function v(){}function y(t){if("undefined"!=typeof L&&L&&L.Mixin){t=ei(t)?t:[t];for(var i=0;i<t.length;i++)t[i]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",(new Error).stack)}}function x(t,i,e){this.x=e?Math.round(t):t,this.y=e?Math.round(i):i}function w(t,i,e){return t instanceof x?t:ei(t)?new x(t[0],t[1]):void 0===t||null===t?t:"object"==typeof t&&"x"in t&&"y"in t?new x(t.x,t.y):new x(t,i,e)}function P(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function b(t,i){return!t||t instanceof P?t:new P(t,i)}function T(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function z(t,i){return t instanceof T?t:new T(t,i)}function M(t,i,e){if(isNaN(t)||isNaN(i))throw new Error("Invalid LatLng object: ("+t+", "+i+")");this.lat=+t,this.lng=+i,void 0!==e&&(this.alt=+e)}function C(t,i,e){return t instanceof M?t:ei(t)&&"object"!=typeof t[0]?3===t.length?new M(t[0],t[1],t[2]):2===t.length?new M(t[0],t[1]):null:void 0===t||null===t?t:"object"==typeof t&&"lat"in t?new M(t.lat,"lng"in t?t.lng:t.lon,t.alt):void 0===i?null:new M(t,i,e)}function Z(t,i,e,n){if(ei(t))return this._a=t[0],this._b=t[1],this._c=t[2],void(this._d=t[3]);this._a=t,this._b=i,this._c=e,this._d=n}function S(t,i,e,n){return new Z(t,i,e,n)}function E(t){return document.createElementNS("http://www.w3.org/2000/svg",t)}function k(t,i){var e,n,o,s,r,a,h="";for(e=0,o=t.length;e<o;e++){for(n=0,s=(r=t[e]).length;n<s;n++)a=r[n],h+=(n?"L":"M")+a.x+" "+a.y;h+=i?Xi?"z":"x":""}return h||"M0 0"}function A(t){return navigator.userAgent.toLowerCase().indexOf(t)>=0}function I(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function B(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(Qi,n,!1):"touchmove"===i?t.removeEventListener(te,n,!1):"touchend"===i&&(t.removeEventListener(ie,n,!1),t.removeEventListener(ee,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(ne.indexOf(t.target.tagName)<0))return;$(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(Qi,o,!1),se||(document.documentElement.addEventListener(Qi,R,!0),document.documentElement.addEventListener(te,D,!0),document.documentElement.addEventListener(ie,N,!0),document.documentElement.addEventListener(ee,N,!0),se=!0)}function R(t){oe[t.pointerId]=t,re++}function D(t){oe[t.pointerId]&&(oe[t.pointerId]=t)}function N(t){delete oe[t.pointerId],re--}function j(t,i){t.touches=[];for(var e in oe)t.touches.push(oe[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(te,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ie,n,!1),t.addEventListener(ee,n,!1)}function F(t,i,e){function n(t){var i;if(Ui){if(!Pi||"mouse"===t.pointerType)return;i=re}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(Ui){if(!Pi||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[ue+ae+e]=n,t[ue+he+e]=o,t[ue+"dblclick"+e]=i,t.addEventListener(ae,n,!1),t.addEventListener(he,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[ue+ae+i],n=t[ue+he+i],o=t[ue+"dblclick"+i];return t.removeEventListener(ae,e,!1),t.removeEventListener(he,n,!1),Pi||t.removeEventListener("dblclick",o,!1),this}function V(t,i,e,n){if("object"==typeof i)for(var o in i)G(t,o,i[o],e);else for(var s=0,r=(i=u(i)).length;s<r;s++)G(t,i[s],e,n);return this}function q(t,i,e,n){if("object"==typeof i)for(var o in i)K(t,o,i[o],e);else if(i)for(var s=0,r=(i=u(i)).length;s<r;s++)K(t,i[s],e,n);else{for(var a in t[le])K(t,a,t[le][a]);delete t[le]}return this}function G(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):"");if(t[le]&&t[le][s])return this;var r=function(i){return e.call(o||t,i||window.event)},a=r;Ui&&0===i.indexOf("touch")?I(t,i,r,s):!Vi||"dblclick"!==i||!F||Ui&&Si?"addEventListener"in t?"mousewheel"===i?t.addEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):"mouseenter"===i||"mouseleave"===i?(r=function(i){i=i||window.event,ot(t,i)&&a(i)},t.addEventListener("mouseenter"===i?"mouseover":"mouseout",r,!1)):("click"===i&&Ti&&(r=function(t){st(t,a)}),t.addEventListener(i,r,!1)):"attachEvent"in t&&t.attachEvent("on"+i,r):F(t,r,s),t[le]=t[le]||{},t[le][s]=r}function K(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):""),r=t[le]&&t[le][s];if(!r)return this;Ui&&0===i.indexOf("touch")?B(t,i,s):!Vi||"dblclick"!==i||!U||Ui&&Si?"removeEventListener"in t?"mousewheel"===i?t.removeEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):t.removeEventListener("mouseenter"===i?"mouseover":"mouseleave"===i?"mouseout":i,r,!1):"detachEvent"in t&&t.detachEvent("on"+i,r):U(t,s),t[le][s]=null}function Y(t){return t.stopPropagation?t.stopPropagation():t.originalEvent?t.originalEvent._stopped=!0:t.cancelBubble=!0,nt(t),this}function X(t){return G(t,"mousewheel",Y),this}function J(t){return V(t,"mousedown touchstart dblclick",Y),G(t,"click",et),this}function $(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this}function Q(t){return $(t),Y(t),this}function tt(t,i){if(!i)return new x(t.clientX,t.clientY);var e=i.getBoundingClientRect(),n=e.width/i.offsetWidth||1,o=e.height/i.offsetHeight||1;return new x(t.clientX/n-e.left-i.clientLeft,t.clientY/o-e.top-i.clientTop)}function it(t){return Pi?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/ce:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function et(t){_e[t.type]=!0}function nt(t){var i=_e[t.type];return _e[t.type]=!1,i}function ot(t,i){var e=i.relatedTarget;if(!e)return!0;try{for(;e&&e!==t;)e=e.parentNode}catch(t){return!1}return e!==t}function st(t,i){var e=t.timeStamp||t.originalEvent&&t.originalEvent.timeStamp,n=pi&&e-pi;n&&n>100&&n<500||t.target._simulatedClick&&!t._simulated?Q(t):(pi=e,i(t))}function rt(t){return"string"==typeof t?document.getElementById(t):t}function at(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function ht(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function ut(t){var i=t.parentNode;i&&i.removeChild(t)}function lt(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function ct(t){var i=t.parentNode;i.lastChild!==t&&i.appendChild(t)}function _t(t){var i=t.parentNode;i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function dt(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=gt(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function pt(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;n<o;n++)t.classList.add(e[n]);else if(!dt(t,i)){var s=gt(t);ft(t,(s?s+" ":"")+i)}}function mt(t,i){void 0!==t.classList?t.classList.remove(i):ft(t,h((" "+gt(t)+" ").replace(" "+i+" "," ")))}function ft(t,i){void 0===t.className.baseVal?t.className=i:t.className.baseVal=i}function gt(t){return void 0===t.className.baseVal?t.className:t.className.baseVal}function vt(t,i){"opacity"in t.style?t.style.opacity=i:"filter"in t.style&&yt(t,i)}function yt(t,i){var e=!1,n="DXImageTransform.Microsoft.Alpha";try{e=t.filters.item(n)}catch(t){if(1===i)return}i=Math.round(100*i),e?(e.Enabled=100!==i,e.Opacity=i):t.style.filter+=" progid:"+n+"(opacity="+i+")"}function xt(t){for(var i=document.documentElement.style,e=0;e<t.length;e++)if(t[e]in i)return t[e];return!1}function wt(t,i,e){var n=i||new x(0,0);t.style[pe]=(Oi?"translate("+n.x+"px,"+n.y+"px)":"translate3d("+n.x+"px,"+n.y+"px,0)")+(e?" scale("+e+")":"")}function Lt(t,i){t._leaflet_pos=i,Ni?wt(t,i):(t.style.left=i.x+"px",t.style.top=i.y+"px")}function Pt(t){return t._leaflet_pos||new x(0,0)}function bt(){V(window,"dragstart",$)}function Tt(){q(window,"dragstart",$)}function zt(t){for(;-1===t.tabIndex;)t=t.parentNode;t.style&&(Mt(),ve=t,ye=t.style.outline,t.style.outline="none",V(window,"keydown",Mt))}function Mt(){ve&&(ve.style.outline=ye,ve=void 0,ye=void 0,q(window,"keydown",Mt))}function Ct(t,i){if(!i||!t.length)return t.slice();var e=i*i;return t=kt(t,e),t=St(t,e)}function Zt(t,i,e){return Math.sqrt(Rt(t,i,e,!0))}function St(t,i){var e=t.length,n=new(typeof Uint8Array!=void 0+""?Uint8Array:Array)(e);n[0]=n[e-1]=1,Et(t,n,i,0,e-1);var o,s=[];for(o=0;o<e;o++)n[o]&&s.push(t[o]);return s}function Et(t,i,e,n,o){var s,r,a,h=0;for(r=n+1;r<=o-1;r++)(a=Rt(t[r],t[n],t[o],!0))>h&&(s=r,h=a);h>e&&(i[s]=1,Et(t,i,e,n,s),Et(t,i,e,s,o))}function kt(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;n<s;n++)Ot(t[n],t[o])>i&&(e.push(t[n]),o=n);return o<s-1&&e.push(t[s-1]),e}function At(t,i,e,n,o){var s,r,a,h=n?Se:Bt(t,e),u=Bt(i,e);for(Se=u;;){if(!(h|u))return[t,i];if(h&u)return!1;a=Bt(r=It(t,i,s=h||u,e,o),e),s===h?(t=r,h=a):(i=r,u=a)}}function It(t,i,e,n,o){var s,r,a=i.x-t.x,h=i.y-t.y,u=n.min,l=n.max;return 8&e?(s=t.x+a*(l.y-t.y)/h,r=l.y):4&e?(s=t.x+a*(u.y-t.y)/h,r=u.y):2&e?(s=l.x,r=t.y+h*(l.x-t.x)/a):1&e&&(s=u.x,r=t.y+h*(u.x-t.x)/a),new x(s,r,o)}function Bt(t,i){var e=0;return t.x<i.min.x?e|=1:t.x>i.max.x&&(e|=2),t.y<i.min.y?e|=4:t.y>i.max.y&&(e|=8),e}function Ot(t,i){var e=i.x-t.x,n=i.y-t.y;return e*e+n*n}function Rt(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return u>0&&((o=((t.x-s)*a+(t.y-r)*h)/u)>1?(s=e.x,r=e.y):o>0&&(s+=a*o,r+=h*o)),a=t.x-s,h=t.y-r,n?a*a+h*h:new x(s,r)}function Dt(t){return!ei(t[0])||"object"!=typeof t[0][0]&&void 0!==t[0][0]}function Nt(t){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),Dt(t)}function jt(t,i,e){var n,o,s,r,a,h,u,l,c,_=[1,4,2,8];for(o=0,u=t.length;o<u;o++)t[o]._code=Bt(t[o],i);for(r=0;r<4;r++){for(l=_[r],n=[],o=0,s=(u=t.length)-1;o<u;s=o++)a=t[o],h=t[s],a._code&l?h._code&l||((c=It(h,a,l,i,e))._code=Bt(c,i),n.push(c)):(h._code&l&&((c=It(h,a,l,i,e))._code=Bt(c,i),n.push(c)),n.push(a));t=n}return t}function Wt(t,i){var e,n,o,s,r="Feature"===t.type?t.geometry:t,a=r?r.coordinates:null,h=[],u=i&&i.pointToLayer,l=i&&i.coordsToLatLng||Ht;if(!a&&!r)return null;switch(r.type){case"Point":return e=l(a),u?u(t,e):new Xe(e);case"MultiPoint":for(o=0,s=a.length;o<s;o++)e=l(a[o]),h.push(u?u(t,e):new Xe(e));return new qe(h);case"LineString":case"MultiLineString":return n=Ft(a,"LineString"===r.type?0:1,l),new tn(n,i);case"Polygon":case"MultiPolygon":return n=Ft(a,"Polygon"===r.type?1:2,l),new en(n,i);case"GeometryCollection":for(o=0,s=r.geometries.length;o<s;o++){var c=Wt({geometry:r.geometries[o],type:"Feature",properties:t.properties},i);c&&h.push(c)}return new qe(h);default:throw new Error("Invalid GeoJSON object.")}}function Ht(t){return new M(t[1],t[0],t[2])}function Ft(t,i,e){for(var n,o=[],s=0,r=t.length;s<r;s++)n=i?Ft(t[s],i-1,e):(e||Ht)(t[s]),o.push(n);return o}function Ut(t,i){return i="number"==typeof i?i:6,void 0!==t.alt?[a(t.lng,i),a(t.lat,i),a(t.alt,i)]:[a(t.lng,i),a(t.lat,i)]}function Vt(t,i,e,n){for(var o=[],s=0,r=t.length;s<r;s++)o.push(i?Vt(t[s],i-1,e,n):Ut(t[s],n));return!i&&e&&o.push(o[0]),o}function qt(t,e){return t.feature?i({},t.feature,{geometry:e}):Gt(e)}function Gt(t){return"Feature"===t.type||"FeatureCollection"===t.type?t:{type:"Feature",properties:{},geometry:t}}function Kt(t,i){return new nn(t,i)}function Yt(t,i){return new dn(t,i)}function Xt(t){return Yi?new fn(t):null}function Jt(t){return Xi||Ji?new xn(t):null}var $t=Object.freeze;Object.freeze=function(t){return t};var Qt=Object.create||function(){function t(){}return function(i){return t.prototype=i,new t}}(),ti=0,ii=/\{ *([\w_-]+) *\}/g,ei=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},ni="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",oi=0,si=window.requestAnimationFrame||p("RequestAnimationFrame")||m,ri=window.cancelAnimationFrame||p("CancelAnimationFrame")||p("CancelRequestAnimationFrame")||function(t){window.clearTimeout(t)},ai=(Object.freeze||Object)({freeze:$t,extend:i,create:Qt,bind:e,lastId:ti,stamp:n,throttle:o,wrapNum:s,falseFn:r,formatNum:a,trim:h,splitWords:u,setOptions:l,getParamString:c,template:_,isArray:ei,indexOf:d,emptyImageUrl:ni,requestFn:si,cancelFn:ri,requestAnimFrame:f,cancelAnimFrame:g});v.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},n=e.__super__=this.prototype,o=Qt(n);o.constructor=e,e.prototype=o;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&"__super__"!==s&&(e[s]=this[s]);return t.statics&&(i(e,t.statics),delete t.statics),t.includes&&(y(t.includes),i.apply(null,[o].concat(t.includes)),delete t.includes),o.options&&(t.options=i(Qt(o.options),t.options)),i(o,t),o._initHooks=[],o.callInitHooks=function(){if(!this._initHooksCalled){n.callInitHooks&&n.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,i=o._initHooks.length;t<i;t++)o._initHooks[t].call(this)}},e},v.include=function(t){return i(this.prototype,t),this},v.mergeOptions=function(t){return i(this.prototype.options,t),this},v.addInitHook=function(t){var i=Array.prototype.slice.call(arguments,1),e="function"==typeof t?t:function(){this[t].apply(this,i)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(e),this};var hi={on:function(t,i,e){if("object"==typeof t)for(var n in t)this._on(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._on(t[o],i,e);return this},off:function(t,i,e){if(t)if("object"==typeof t)for(var n in t)this._off(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._off(t[o],i,e);else delete this._events;return this},_on:function(t,i,e){this._events=this._events||{};var n=this._events[t];n||(n=[],this._events[t]=n),e===this&&(e=void 0);for(var o={fn:i,ctx:e},s=n,r=0,a=s.length;r<a;r++)if(s[r].fn===i&&s[r].ctx===e)return;s.push(o)},_off:function(t,i,e){var n,o,s;if(this._events&&(n=this._events[t]))if(i){if(e===this&&(e=void 0),n)for(o=0,s=n.length;o<s;o++){var a=n[o];if(a.ctx===e&&a.fn===i)return a.fn=r,this._firingCount&&(this._events[t]=n=n.slice()),void n.splice(o,1)}}else{for(o=0,s=n.length;o<s;o++)n[o].fn=r;delete this._events[t]}},fire:function(t,e,n){if(!this.listens(t,n))return this;var o=i({},e,{type:t,target:this,sourceTarget:e&&e.sourceTarget||this});if(this._events){var s=this._events[t];if(s){this._firingCount=this._firingCount+1||1;for(var r=0,a=s.length;r<a;r++){var h=s[r];h.fn.call(h.ctx||this,o)}this._firingCount--}}return n&&this._propagateEvent(o),this},listens:function(t,i){var e=this._events&&this._events[t];if(e&&e.length)return!0;if(i)for(var n in this._eventParents)if(this._eventParents[n].listens(t,i))return!0;return!1},once:function(t,i,n){if("object"==typeof t){for(var o in t)this.once(o,t[o],i);return this}var s=e(function(){this.off(t,i,n).off(t,s,n)},this);return this.on(t,i,n).on(t,s,n)},addEventParent:function(t){return this._eventParents=this._eventParents||{},this._eventParents[n(t)]=t,this},removeEventParent:function(t){return this._eventParents&&delete this._eventParents[n(t)],this},_propagateEvent:function(t){for(var e in this._eventParents)this._eventParents[e].fire(t.type,i({layer:t.target,propagatedFrom:t.target},t),!0)}};hi.addEventListener=hi.on,hi.removeEventListener=hi.clearAllEventListeners=hi.off,hi.addOneTimeEventListener=hi.once,hi.fireEvent=hi.fire,hi.hasEventListeners=hi.listens;var ui=v.extend(hi),li=Math.trunc||function(t){return t>0?Math.floor(t):Math.ceil(t)};x.prototype={clone:function(){return new x(this.x,this.y)},add:function(t){return this.clone()._add(w(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(w(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new x(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new x(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=li(this.x),this.y=li(this.y),this},distanceTo:function(t){var i=(t=w(t)).x-this.x,e=t.y-this.y;return Math.sqrt(i*i+e*e)},equals:function(t){return(t=w(t)).x===this.x&&t.y===this.y},contains:function(t){return t=w(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+a(this.x)+", "+a(this.y)+")"}},P.prototype={extend:function(t){return t=w(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new x((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new x(this.min.x,this.max.y)},getTopRight:function(){return new x(this.max.x,this.min.y)},getTopLeft:function(){return this.min},getBottomRight:function(){return this.max},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var i,e;return(t="number"==typeof t[0]||t instanceof x?w(t):b(t))instanceof P?(i=t.min,e=t.max):i=e=t,i.x>=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.x<e.x,r=o.y>i.y&&n.y<e.y;return s&&r},isValid:function(){return!(!this.min||!this.max)}},T.prototype={extend:function(t){var i,e,n=this._southWest,o=this._northEast;if(t instanceof M)i=t,e=t;else{if(!(t instanceof T))return t?this.extend(C(t)||z(t)):this;if(i=t._southWest,e=t._northEast,!i||!e)return this}return n||o?(n.lat=Math.min(i.lat,n.lat),n.lng=Math.min(i.lng,n.lng),o.lat=Math.max(e.lat,o.lat),o.lng=Math.max(e.lng,o.lng)):(this._southWest=new M(i.lat,i.lng),this._northEast=new M(e.lat,e.lng)),this},pad:function(t){var i=this._southWest,e=this._northEast,n=Math.abs(i.lat-e.lat)*t,o=Math.abs(i.lng-e.lng)*t;return new T(new M(i.lat-n,i.lng-o),new M(e.lat+n,e.lng+o))},getCenter:function(){return new M((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new M(this.getNorth(),this.getWest())},getSouthEast:function(){return new M(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof M||"lat"in t?C(t):z(t);var i,e,n=this._southWest,o=this._northEast;return t instanceof T?(i=t.getSouthWest(),e=t.getNorthEast()):i=e=t,i.lat>=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lat<e.lat,r=o.lng>i.lng&&n.lng<e.lng;return s&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t,i){return!!t&&(t=z(t),this._southWest.equals(t.getSouthWest(),i)&&this._northEast.equals(t.getNorthEast(),i))},isValid:function(){return!(!this._southWest||!this._northEast)}},M.prototype={equals:function(t,i){return!!t&&(t=C(t),Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng))<=(void 0===i?1e-9:i))},toString:function(t){return"LatLng("+a(this.lat,t)+", "+a(this.lng,t)+")"},distanceTo:function(t){return _i.distance(this,C(t))},wrap:function(){return _i.wrapLatLng(this)},toBounds:function(t){var i=180*t/40075017,e=i/Math.cos(Math.PI/180*this.lat);return z([this.lat-i,this.lng-e],[this.lat+i,this.lng+e])},clone:function(){return new M(this.lat,this.lng,this.alt)}};var ci={latLngToPoint:function(t,i){var e=this.projection.project(t),n=this.scale(i);return this.transformation._transform(e,n)},pointToLatLng:function(t,i){var e=this.scale(i),n=this.transformation.untransform(t,e);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},unproject:function(t){return this.projection.unproject(t)},scale:function(t){return 256*Math.pow(2,t)},zoom:function(t){return Math.log(t/256)/Math.LN2},getProjectedBounds:function(t){if(this.infinite)return null;var i=this.projection.bounds,e=this.scale(t);return new P(this.transformation.transform(i.min,e),this.transformation.transform(i.max,e))},infinite:!1,wrapLatLng:function(t){var i=this.wrapLng?s(t.lng,this.wrapLng,!0):t.lng;return new M(this.wrapLat?s(t.lat,this.wrapLat,!0):t.lat,i,t.alt)},wrapLatLngBounds:function(t){var i=t.getCenter(),e=this.wrapLatLng(i),n=i.lat-e.lat,o=i.lng-e.lng;if(0===n&&0===o)return t;var s=t.getSouthWest(),r=t.getNorthEast();return new T(new M(s.lat-n,s.lng-o),new M(r.lat-n,r.lng-o))}},_i=i({},ci,{wrapLng:[-180,180],R:6371e3,distance:function(t,i){var e=Math.PI/180,n=t.lat*e,o=i.lat*e,s=Math.sin((i.lat-t.lat)*e/2),r=Math.sin((i.lng-t.lng)*e/2),a=s*s+Math.cos(n)*Math.cos(o)*r*r,h=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));return this.R*h}}),di={R:6378137,MAX_LATITUDE:85.0511287798,project:function(t){var i=Math.PI/180,e=this.MAX_LATITUDE,n=Math.max(Math.min(e,t.lat),-e),o=Math.sin(n*i);return new x(this.R*t.lng*i,this.R*Math.log((1+o)/(1-o))/2)},unproject:function(t){var i=180/Math.PI;return new M((2*Math.atan(Math.exp(t.y/this.R))-Math.PI/2)*i,t.x*i/this.R)},bounds:function(){var t=6378137*Math.PI;return new P([-t,-t],[t,t])}()};Z.prototype={transform:function(t,i){return this._transform(t.clone(),i)},_transform:function(t,i){return i=i||1,t.x=i*(this._a*t.x+this._b),t.y=i*(this._c*t.y+this._d),t},untransform:function(t,i){return i=i||1,new x((t.x/i-this._b)/this._a,(t.y/i-this._d)/this._c)}};var pi,mi,fi,gi,vi=i({},_i,{code:"EPSG:3857",projection:di,transformation:function(){var t=.5/(Math.PI*di.R);return S(t,.5,-t,.5)}()}),yi=i({},vi,{code:"EPSG:900913"}),xi=document.documentElement.style,wi="ActiveXObject"in window,Li=wi&&!document.addEventListener,Pi="msLaunchUri"in navigator&&!("documentMode"in document),bi=A("webkit"),Ti=A("android"),zi=A("android 2")||A("android 3"),Mi=parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1],10),Ci=Ti&&A("Google")&&Mi<537&&!("AudioNode"in window),Zi=!!window.opera,Si=A("chrome"),Ei=A("gecko")&&!bi&&!Zi&&!wi,ki=!Si&&A("safari"),Ai=A("phantom"),Ii="OTransition"in xi,Bi=0===navigator.platform.indexOf("Win"),Oi=wi&&"transition"in xi,Ri="WebKitCSSMatrix"in window&&"m11"in new window.WebKitCSSMatrix&&!zi,Di="MozPerspective"in xi,Ni=!window.L_DISABLE_3D&&(Oi||Ri||Di)&&!Ii&&!Ai,ji="undefined"!=typeof orientation||A("mobile"),Wi=ji&&bi,Hi=ji&&Ri,Fi=!window.PointerEvent&&window.MSPointerEvent,Ui=!(!window.PointerEvent&&!Fi),Vi=!window.L_NO_TOUCH&&(Ui||"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch),qi=ji&&Zi,Gi=ji&&Ei,Ki=(window.devicePixelRatio||window.screen.deviceXDPI/window.screen.logicalXDPI)>1,Yi=!!document.createElement("canvas").getContext,Xi=!(!document.createElementNS||!E("svg").createSVGRect),Ji=!Xi&&function(){try{var t=document.createElement("div");t.innerHTML='<v:shape adj="1"/>';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),$i=(Object.freeze||Object)({ie:wi,ielt9:Li,edge:Pi,webkit:bi,android:Ti,android23:zi,androidStock:Ci,opera:Zi,chrome:Si,gecko:Ei,safari:ki,phantom:Ai,opera12:Ii,win:Bi,ie3d:Oi,webkit3d:Ri,gecko3d:Di,any3d:Ni,mobile:ji,mobileWebkit:Wi,mobileWebkit3d:Hi,msPointer:Fi,pointer:Ui,touch:Vi,mobileOpera:qi,mobileGecko:Gi,retina:Ki,canvas:Yi,svg:Xi,vml:Ji}),Qi=Fi?"MSPointerDown":"pointerdown",te=Fi?"MSPointerMove":"pointermove",ie=Fi?"MSPointerUp":"pointerup",ee=Fi?"MSPointerCancel":"pointercancel",ne=["INPUT","SELECT","OPTION"],oe={},se=!1,re=0,ae=Fi?"MSPointerDown":Ui?"pointerdown":"touchstart",he=Fi?"MSPointerUp":Ui?"pointerup":"touchend",ue="_leaflet_",le="_leaflet_events",ce=Bi&&Si?2*window.devicePixelRatio:Ei?window.devicePixelRatio:1,_e={},de=(Object.freeze||Object)({on:V,off:q,stopPropagation:Y,disableScrollPropagation:X,disableClickPropagation:J,preventDefault:$,stop:Q,getMousePosition:tt,getWheelDelta:it,fakeStop:et,skipped:nt,isExternalTarget:ot,addListener:V,removeListener:q}),pe=xt(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),me=xt(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),fe="webkitTransition"===me||"OTransition"===me?me+"End":"transitionend";if("onselectstart"in document)mi=function(){V(window,"selectstart",$)},fi=function(){q(window,"selectstart",$)};else{var ge=xt(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);mi=function(){if(ge){var t=document.documentElement.style;gi=t[ge],t[ge]="none"}},fi=function(){ge&&(document.documentElement.style[ge]=gi,gi=void 0)}}var ve,ye,xe=(Object.freeze||Object)({TRANSFORM:pe,TRANSITION:me,TRANSITION_END:fe,get:rt,getStyle:at,create:ht,remove:ut,empty:lt,toFront:ct,toBack:_t,hasClass:dt,addClass:pt,removeClass:mt,setClass:ft,getClass:gt,setOpacity:vt,testProp:xt,setTransform:wt,setPosition:Lt,getPosition:Pt,disableTextSelection:mi,enableTextSelection:fi,disableImageDrag:bt,enableImageDrag:Tt,preventOutline:zt,restoreOutline:Mt}),we=ui.extend({run:function(t,i,e,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=e||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=Pt(t),this._offset=i.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=f(this._animate,this),this._step()},_step:function(t){var i=+new Date-this._startTime,e=1e3*this._duration;i<e?this._runFrame(this._easeOut(i/e),t):(this._runFrame(1),this._complete())},_runFrame:function(t,i){var e=this._startPos.add(this._offset.multiplyBy(t));i&&e._round(),Lt(this._el,e),this.fire("step")},_complete:function(){g(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),Le=ui.extend({options:{crs:vi,center:void 0,zoom:void 0,minZoom:void 0,maxZoom:void 0,layers:[],maxBounds:void 0,renderer:void 0,zoomAnimation:!0,zoomAnimationThreshold:4,fadeAnimation:!0,markerZoomAnimation:!0,transform3DLimit:8388608,zoomSnap:1,zoomDelta:1,trackResize:!0},initialize:function(t,i){i=l(this,i),this._initContainer(t),this._initLayout(),this._onResize=e(this._onResize,this),this._initEvents(),i.maxBounds&&this.setMaxBounds(i.maxBounds),void 0!==i.zoom&&(this._zoom=this._limitZoom(i.zoom)),i.center&&void 0!==i.zoom&&this.setView(C(i.center),i.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._sizeChanged=!0,this.callInitHooks(),this._zoomAnimated=me&&Ni&&!qi&&this.options.zoomAnimation,this._zoomAnimated&&(this._createAnimProxy(),V(this._proxy,fe,this._catchTransitionEnd,this)),this._addLayers(this.options.layers)},setView:function(t,e,n){return e=void 0===e?this._zoom:this._limitZoom(e),t=this._limitCenter(C(t),e,this.options.maxBounds),n=n||{},this._stop(),this._loaded&&!n.reset&&!0!==n&&(void 0!==n.animate&&(n.zoom=i({animate:n.animate},n.zoom),n.pan=i({animate:n.animate,duration:n.duration},n.pan)),this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan))?(clearTimeout(this._sizeTimer),this):(this._resetView(t,e),this)},setZoom:function(t,i){return this._loaded?this.setView(this.getCenter(),t,{zoom:i}):(this._zoom=t,this)},zoomIn:function(t,i){return t=t||(Ni?this.options.zoomDelta:1),this.setZoom(this._zoom+t,i)},zoomOut:function(t,i){return t=t||(Ni?this.options.zoomDelta:1),this.setZoom(this._zoom-t,i)},setZoomAround:function(t,i,e){var n=this.getZoomScale(i),o=this.getSize().divideBy(2),s=(t instanceof x?t:this.latLngToContainerPoint(t)).subtract(o).multiplyBy(1-1/n),r=this.containerPointToLatLng(o.add(s));return this.setView(r,i,{zoom:e})},_getBoundsCenterZoom:function(t,i){i=i||{},t=t.getBounds?t.getBounds():z(t);var e=w(i.paddingTopLeft||i.padding||[0,0]),n=w(i.paddingBottomRight||i.padding||[0,0]),o=this.getBoundsZoom(t,!1,e.add(n));if((o="number"==typeof i.maxZoom?Math.min(i.maxZoom,o):o)===1/0)return{center:t.getCenter(),zoom:o};var s=n.subtract(e).divideBy(2),r=this.project(t.getSouthWest(),o),a=this.project(t.getNorthEast(),o);return{center:this.unproject(r.add(a).divideBy(2).add(s),o),zoom:o}},fitBounds:function(t,i){if(!(t=z(t)).isValid())throw new Error("Bounds are not valid.");var e=this._getBoundsCenterZoom(t,i);return this.setView(e.center,e.zoom,i)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,i){return this.setView(t,this._zoom,{pan:i})},panBy:function(t,i){if(t=w(t).round(),i=i||{},!t.x&&!t.y)return this.fire("moveend");if(!0!==i.animate&&!this.getSize().contains(t))return this._resetView(this.unproject(this.project(this.getCenter()).add(t)),this.getZoom()),this;if(this._panAnim||(this._panAnim=new we,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),i.noMoveStart||this.fire("movestart"),!1!==i.animate){pt(this._mapPane,"leaflet-pan-anim");var e=this._getMapPanePos().subtract(t).round();this._panAnim.run(this._mapPane,e,i.duration||.25,i.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},flyTo:function(t,i,e){function n(t){var i=(g*g-m*m+(t?-1:1)*x*x*v*v)/(2*(t?g:m)*x*v),e=Math.sqrt(i*i+1)-i;return e<1e-9?-18:Math.log(e)}function o(t){return(Math.exp(t)-Math.exp(-t))/2}function s(t){return(Math.exp(t)+Math.exp(-t))/2}function r(t){return o(t)/s(t)}function a(t){return m*(s(w)/s(w+y*t))}function h(t){return m*(s(w)*r(w+y*t)-o(w))/x}function u(t){return 1-Math.pow(1-t,1.5)}function l(){var e=(Date.now()-L)/b,n=u(e)*P;e<=1?(this._flyToFrame=f(l,this),this._move(this.unproject(c.add(_.subtract(c).multiplyBy(h(n)/v)),p),this.getScaleZoom(m/a(n),p),{flyTo:!0})):this._move(t,i)._moveEnd(!0)}if(!1===(e=e||{}).animate||!Ni)return this.setView(t,i,e);this._stop();var c=this.project(this.getCenter()),_=this.project(t),d=this.getSize(),p=this._zoom;t=C(t),i=void 0===i?p:i;var m=Math.max(d.x,d.y),g=m*this.getZoomScale(p,i),v=_.distanceTo(c)||1,y=1.42,x=y*y,w=n(0),L=Date.now(),P=(n(1)-w)/y,b=e.duration?1e3*e.duration:1e3*P*.8;return this._moveStart(!0,e.noMoveStart),l.call(this),this},flyToBounds:function(t,i){var e=this._getBoundsCenterZoom(t,i);return this.flyTo(e.center,e.zoom,i)},setMaxBounds:function(t){return(t=z(t)).isValid()?(this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this.options.maxBounds=t,this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds)):(this.options.maxBounds=null,this.off("moveend",this._panInsideMaxBounds))},setMinZoom:function(t){var i=this.options.minZoom;return this.options.minZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()<this.options.minZoom)?this.setZoom(t):this},setMaxZoom:function(t){var i=this.options.maxZoom;return this.options.maxZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()>this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,z(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},invalidateSize:function(t){if(!this._loaded)return this;t=i({animate:!1,pan:!0},!0===t?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),s=n.divideBy(2).round(),r=o.divideBy(2).round(),a=s.subtract(r);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(e(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=i({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=e(this._handleGeolocationResponse,this),o=e(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i=t.code,e=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+e+"."})},_handleGeolocationResponse:function(t){var i=new M(t.coords.latitude,t.coords.longitude),e=i.toBounds(t.coords.accuracy),n=this._locateOptions;if(n.setView){var o=this.getBoundsZoom(e);this.setView(i,n.maxZoom?Math.min(o,n.maxZoom):o)}var s={latlng:i,bounds:e,timestamp:t.timestamp};for(var r in t.coords)"number"==typeof t.coords[r]&&(s[r]=t.coords[r]);this.fire("locationfound",s)},addHandler:function(t,i){if(!i)return this;var e=this[t]=new i(this);return this._handlers.push(e),this.options[t]&&e.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),ut(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this._loaded&&this.fire("unload");var t;for(t in this._layers)this._layers[t].remove();for(t in this._panes)ut(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){var e=ht("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),i||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new T(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=z(t),e=w(e||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),a=t.getSouthEast(),h=this.getSize().subtract(e),u=b(this.project(a,n),this.project(r,n)).getSize(),l=Ni?this.options.zoomSnap:1,c=h.x/u.x,_=h.y/u.y,d=i?Math.max(c,_):Math.min(c,_);return n=this.getScaleZoom(d,n),l&&(n=Math.round(n/(l/100))*(l/100),n=i?Math.ceil(n/l)*l:Math.floor(n/l)*l),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new x(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){var e=this._getTopLeftPoint(t,i);return new P(e,e.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;var n=e.zoom(t*e.scale(i));return isNaN(n)?1/0:n},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(C(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(w(t),i)},layerPointToLatLng:function(t){var i=w(t).add(this.getPixelOrigin());return this.unproject(i)},latLngToLayerPoint:function(t){return this.project(C(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(C(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(z(t))},distance:function(t,i){return this.options.crs.distance(C(t),C(i))},containerPointToLayerPoint:function(t){return w(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return w(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var i=this.containerPointToLayerPoint(w(t));return this.layerPointToLatLng(i)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(C(t)))},mouseEventToContainerPoint:function(t){return tt(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var i=this._container=rt(t);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");V(i,"scroll",this._onScroll,this),this._containerId=n(i)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&Ni,pt(t,"leaflet-container"+(Vi?" leaflet-touch":"")+(Ki?" leaflet-retina":"")+(Li?" leaflet-oldie":"")+(ki?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=at(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),Lt(this._mapPane,new x(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(pt(t.markerPane,"leaflet-zoom-hide"),pt(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i){Lt(this._mapPane,new x(0,0));var e=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n,!1)._move(t,i)._moveEnd(n),this.fire("viewreset"),e&&this.fire("load")},_moveStart:function(t,i){return t&&this.fire("zoomstart"),i||this.fire("movestart"),this},_move:function(t,i,e){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return g(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){Lt(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[n(this._container)]=this;var i=t?q:V;i(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),Ni&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){g(this._resizeRequest),this._resizeRequest=f(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,o=[],s="mouseout"===i||"mouseover"===i,r=t.target||t.srcElement,a=!1;r;){if((e=this._targets[n(r)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(s&&!ot(r,t))break;if(o.push(e),s)break}if(r===this._container)break;r=r.parentNode}return o.length||a||s||!ot(r,t)||(o=[this]),o},_handleDOMEvent:function(t){if(this._loaded&&!nt(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i||zt(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,n){if("click"===t.type){var o=i({},t);o.type="preclick",this._fireDOMEvent(o,o.type,n)}if(!t._stopped&&(n=(n||[]).concat(this._findEventTargets(t,e))).length){var s=n[0];"contextmenu"===e&&s.listens(e,!0)&&$(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s.getLatLng&&(!s._radius||s._radius<=10);r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h<n.length;h++)if(n[h].fire(e,r,!0),r.originalEvent._stopped||!1===n[h].options.bubblingMouseEvents&&-1!==d(this._mouseEvents,e))return}},_draggableMoved:function(t){return(t=t.dragging&&t.dragging.enabled()?t:this).dragging&&t.dragging.moved()||this.boxZoom&&this.boxZoom.moved()},_clearHandlers:function(){for(var t=0,i=this._handlers.length;t<i;t++)this._handlers[t].disable()},whenReady:function(t,i){return this._loaded?t.call(i||this,{target:this}):this.on("load",t,i),this},_getMapPanePos:function(){return Pt(this._mapPane)||new x(0,0)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(t,i){return(t&&void 0!==i?this._getNewPixelOrigin(t,i):this.getPixelOrigin()).subtract(this._getMapPanePos())},_getNewPixelOrigin:function(t,i){var e=this.getSize()._divideBy(2);return this.project(t,i)._subtract(e)._add(this._getMapPanePos())._round()},_latLngToNewLayerPoint:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return this.project(t,i)._subtract(n)},_latLngBoundsToNewLayerBounds:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return b([this.project(t.getSouthWest(),i)._subtract(n),this.project(t.getNorthWest(),i)._subtract(n),this.project(t.getSouthEast(),i)._subtract(n),this.project(t.getNorthEast(),i)._subtract(n)])},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,i,e){if(!e)return t;var n=this.project(t,i),o=this.getSize().divideBy(2),s=new P(n.subtract(o),n.add(o)),r=this._getBoundsOffset(s,e,i);return r.round().equals([0,0])?t:this.unproject(n.add(r),i)},_limitOffset:function(t,i){if(!i)return t;var e=this.getPixelBounds(),n=new P(e.min.add(t),e.max.add(t));return t.add(this._getBoundsOffset(n,i))},_getBoundsOffset:function(t,i,e){var n=b(this.project(i.getNorthEast(),e),this.project(i.getSouthWest(),e)),o=n.min.subtract(t.min),s=n.max.subtract(t.max);return new x(this._rebound(o.x,-s.x),this._rebound(o.y,-s.y))},_rebound:function(t,i){return t+i>0?Math.round(t-i)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(i))},_limitZoom:function(t){var i=this.getMinZoom(),e=this.getMaxZoom(),n=Ni?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(i,Math.min(e,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){mt(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,i){var e=this._getCenterOffset(t)._trunc();return!(!0!==(i&&i.animate)&&!this.getSize().contains(e))&&(this.panBy(e,i),!0)},_createAnimProxy:function(){var t=this._proxy=ht("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(t){var i=pe,e=this._proxy.style[i];wt(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),e===this._proxy.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var t=this.getCenter(),i=this.getZoom();wt(this._proxy,this.project(t,i),this.getZoomScale(i,1))},this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){ut(this._proxy),delete this._proxy},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,i,e){if(this._animatingZoom)return!0;if(e=e||{},!this._zoomAnimated||!1===e.animate||this._nothingToAnimate()||Math.abs(i-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(f(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,n,o){this._mapPane&&(n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,pt(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:o}),setTimeout(e(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&mt(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),f(function(){this._moveEnd(!0)},this))}}),Pe=v.extend({options:{position:"topright"},initialize:function(t){l(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return pt(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this},remove:function(){return this._map?(ut(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),be=function(t){return new Pe(t)};Le.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,o){var s=e+t+" "+e+o;i[t+o]=ht("div",s,n)}var i=this._controlCorners={},e="leaflet-",n=this._controlContainer=ht("div",e+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)ut(this._controlCorners[t]);ut(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Te=Pe.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,i,e,n){return e<n?-1:n<e?1:0}},initialize:function(t,i,e){l(this,e),this._layerControlInputs=[],this._layers=[],this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in i)this._addLayer(i[n],n,!0)},onAdd:function(t){this._initLayout(),this._update(),this._map=t,t.on("zoomend",this._checkDisabledLayers,this);for(var i=0;i<this._layers.length;i++)this._layers[i].layer.on("add remove",this._onLayerChange,this);return this._container},addTo:function(t){return Pe.prototype.addTo.call(this,t),this._expandIfNotCollapsed()},onRemove:function(){this._map.off("zoomend",this._checkDisabledLayers,this);for(var t=0;t<this._layers.length;t++)this._layers[t].layer.off("add remove",this._onLayerChange,this)},addBaseLayer:function(t,i){return this._addLayer(t,i),this._map?this._update():this},addOverlay:function(t,i){return this._addLayer(t,i,!0),this._map?this._update():this},removeLayer:function(t){t.off("add remove",this._onLayerChange,this);var i=this._getLayer(n(t));return i&&this._layers.splice(this._layers.indexOf(i),1),this._map?this._update():this},expand:function(){pt(this._container,"leaflet-control-layers-expanded"),this._form.style.height=null;var t=this._map.getSize().y-(this._container.offsetTop+50);return t<this._form.clientHeight?(pt(this._form,"leaflet-control-layers-scrollbar"),this._form.style.height=t+"px"):mt(this._form,"leaflet-control-layers-scrollbar"),this._checkDisabledLayers(),this},collapse:function(){return mt(this._container,"leaflet-control-layers-expanded"),this},_initLayout:function(){var t="leaflet-control-layers",i=this._container=ht("div",t),e=this.options.collapsed;i.setAttribute("aria-haspopup",!0),J(i),X(i);var n=this._form=ht("form",t+"-list");e&&(this._map.on("click",this.collapse,this),Ti||V(i,{mouseenter:this.expand,mouseleave:this.collapse},this));var o=this._layersLink=ht("a",t+"-toggle",i);o.href="#",o.title="Layers",Vi?(V(o,"click",Q),V(o,"click",this.expand,this)):V(o,"focus",this.expand,this),e||this.expand(),this._baseLayersList=ht("div",t+"-base",n),this._separator=ht("div",t+"-separator",n),this._overlaysList=ht("div",t+"-overlays",n),i.appendChild(n)},_getLayer:function(t){for(var i=0;i<this._layers.length;i++)if(this._layers[i]&&n(this._layers[i].layer)===t)return this._layers[i]},_addLayer:function(t,i,n){this._map&&t.on("add remove",this._onLayerChange,this),this._layers.push({layer:t,name:i,overlay:n}),this.options.sortLayers&&this._layers.sort(e(function(t,i){return this.options.sortFunction(t.layer,i.layer,t.name,i.name)},this)),this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex)),this._expandIfNotCollapsed()},_update:function(){if(!this._container)return this;lt(this._baseLayersList),lt(this._overlaysList),this._layerControlInputs=[];var t,i,e,n,o=0;for(e=0;e<this._layers.length;e++)n=this._layers[e],this._addItem(n),i=i||n.overlay,t=t||!n.overlay,o+=n.overlay?0:1;return this.options.hideSingleBase&&(t=t&&o>1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=i&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var i=this._getLayer(n(t.target)),e=i.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;e&&this._map.fire(e,i)},_createRadioElement:function(t,i){var e='<input type="radio" class="leaflet-control-layers-selector" name="'+t+'"'+(i?' checked="checked"':"")+"/>",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),o=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=o):i=this._createRadioElement("leaflet-base-layers",o),this._layerControlInputs.push(i),i.layerId=n(t.layer),V(i,"click",this._onInputClick,this);var s=document.createElement("span");s.innerHTML=" "+t.name;var r=document.createElement("div");return e.appendChild(r),r.appendChild(i),r.appendChild(s),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;s>=0;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;s<o.length;s++)this._map.hasLayer(o[s])&&this._map.removeLayer(o[s]);for(s=0;s<n.length;s++)this._map.hasLayer(n[s])||this._map.addLayer(n[s]);this._handlingClick=!1,this._refocusOnMap()},_checkDisabledLayers:function(){for(var t,i,e=this._layerControlInputs,n=this._map.getZoom(),o=e.length-1;o>=0;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.disabled=void 0!==i.options.minZoom&&n<i.options.minZoom||void 0!==i.options.maxZoom&&n>i.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),ze=Pe.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"&#x2212;",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=ht("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoom<this._map.getMaxZoom()&&this._map.zoomIn(this._map.options.zoomDelta*(t.shiftKey?3:1))},_zoomOut:function(t){!this._disabled&&this._map._zoom>this._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=ht("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),J(s),V(s,"click",Q),V(s,"click",o,this),V(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";mt(this._zoomInButton,i),mt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMinZoom())&&pt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMaxZoom())&&pt(this._zoomInButton,i)}});Le.mergeOptions({zoomControl:!0}),Le.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new ze,this.addControl(this.zoomControl))});var Me=Pe.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i=ht("div","leaflet-control-scale"),e=this.options;return this._addScales(e,"leaflet-control-scale-line",i),t.on(e.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=ht("div",i,e)),t.imperial&&(this._iScale=ht("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;o>5280?(i=o/5280,e=this._getRoundNum(i),this._updateScale(this._iScale,e+" mi",e/i)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,i,e){t.style.width=Math.round(this.options.maxWidth*e)+"px",t.innerHTML=i},_getRoundNum:function(t){var i=Math.pow(10,(Math.floor(t)+"").length-1),e=t/i;return e=e>=10?10:e>=5?5:e>=3?3:e>=2?2:1,i*e}}),Ce=Pe.extend({options:{position:"bottomright",prefix:'<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'},initialize:function(t){l(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=ht("div","leaflet-control-attribution"),J(this._container);for(var i in t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});Le.mergeOptions({attributionControl:!0}),Le.addInitHook(function(){this.options.attributionControl&&(new Ce).addTo(this)});Pe.Layers=Te,Pe.Zoom=ze,Pe.Scale=Me,Pe.Attribution=Ce,be.layers=function(t,i,e){return new Te(t,i,e)},be.zoom=function(t){return new ze(t)},be.scale=function(t){return new Me(t)},be.attribution=function(t){return new Ce(t)};var Ze=v.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});Ze.addTo=function(t,i){return t.addHandler(i,this),this};var Se,Ee={Events:hi},ke=Vi?"touchstart mousedown":"mousedown",Ae={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},Ie={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},Be=ui.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){l(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(V(this._dragStartTarget,ke,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Be._dragging===this&&this.finishDrag(),q(this._dragStartTarget,ke,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!dt(this._element,"leaflet-zoom-anim")&&!(Be._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(Be._dragging=this,this._preventOutline&&zt(this._element),bt(),mi(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t;this._startPoint=new x(i.clientX,i.clientY),V(document,Ie[t.type],this._onMove,this),V(document,Ae[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&t.touches.length>1)this._moved=!0;else{var i=t.touches&&1===t.touches.length?t.touches[0]:t,e=new x(i.clientX,i.clientY).subtract(this._startPoint);(e.x||e.y)&&(Math.abs(e.x)+Math.abs(e.y)<this.options.clickTolerance||($(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=Pt(this._element).subtract(e),pt(document.body,"leaflet-dragging"),this._lastTarget=t.target||t.srcElement,window.SVGElementInstance&&this._lastTarget instanceof SVGElementInstance&&(this._lastTarget=this._lastTarget.correspondingUseElement),pt(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(e),this._moving=!0,g(this._animRequest),this._lastEvent=t,this._animRequest=f(this._updatePosition,this,!0)))}},_updatePosition:function(){var t={originalEvent:this._lastEvent};this.fire("predrag",t),Lt(this._element,this._newPos),this.fire("drag",t)},_onUp:function(t){!t._simulated&&this._enabled&&this.finishDrag()},finishDrag:function(){mt(document.body,"leaflet-dragging"),this._lastTarget&&(mt(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in Ie)q(document,Ie[t],this._onMove,this),q(document,Ae[t],this._onUp,this);Tt(),fi(),this._moved&&this._moving&&(g(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1,Be._dragging=!1}}),Oe=(Object.freeze||Object)({simplify:Ct,pointToSegmentDistance:Zt,closestPointOnSegment:function(t,i,e){return Rt(t,i,e)},clipSegment:At,_getEdgeIntersection:It,_getBitCode:Bt,_sqClosestPointOnSegment:Rt,isFlat:Dt,_flat:Nt}),Re=(Object.freeze||Object)({clipPolygon:jt}),De={project:function(t){return new x(t.lng,t.lat)},unproject:function(t){return new M(t.y,t.x)},bounds:new P([-180,-90],[180,90])},Ne={R:6378137,R_MINOR:6356752.314245179,bounds:new P([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(t){var i=Math.PI/180,e=this.R,n=t.lat*i,o=this.R_MINOR/e,s=Math.sqrt(1-o*o),r=s*Math.sin(n),a=Math.tan(Math.PI/4-n/2)/Math.pow((1-r)/(1+r),s/2);return n=-e*Math.log(Math.max(a,1e-10)),new x(t.lng*i*e,n)},unproject:function(t){for(var i,e=180/Math.PI,n=this.R,o=this.R_MINOR/n,s=Math.sqrt(1-o*o),r=Math.exp(-t.y/n),a=Math.PI/2-2*Math.atan(r),h=0,u=.1;h<15&&Math.abs(u)>1e-7;h++)i=s*Math.sin(a),i=Math.pow((1-i)/(1+i),s/2),a+=u=Math.PI/2-2*Math.atan(r*i)-a;return new M(a*e,t.x*e/n)}},je=(Object.freeze||Object)({LonLat:De,Mercator:Ne,SphericalMercator:di}),We=i({},_i,{code:"EPSG:3395",projection:Ne,transformation:function(){var t=.5/(Math.PI*Ne.R);return S(t,.5,-t,.5)}()}),He=i({},_i,{code:"EPSG:4326",projection:De,transformation:S(1/180,1,-1/180,.5)}),Fe=i({},ci,{projection:De,transformation:S(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,i){var e=i.lng-t.lng,n=i.lat-t.lat;return Math.sqrt(e*e+n*n)},infinite:!0});ci.Earth=_i,ci.EPSG3395=We,ci.EPSG3857=vi,ci.EPSG900913=yi,ci.EPSG4326=He,ci.Simple=Fe;var Ue=ui.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[n(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[n(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var i=t.target;if(i.hasLayer(this)){if(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents){var e=this.getEvents();i.on(e,this),this.once("remove",function(){i.off(e,this)},this)}this.onAdd(i),this.getAttribution&&i.attributionControl&&i.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),i.fire("layeradd",{layer:this})}}});Le.include({addLayer:function(t){if(!t._layerAdd)throw new Error("The provided object is not a Layer.");var i=n(t);return this._layers[i]?this:(this._layers[i]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var i=n(t);return this._layers[i]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[i],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&n(t)in this._layers},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},_addLayers:function(t){for(var i=0,e=(t=t?ei(t)?t:[t]:[]).length;i<e;i++)this.addLayer(t[i])},_addZoomLimit:function(t){!isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[n(t)]=t,this._updateZoomLevels())},_removeZoomLimit:function(t){var i=n(t);this._zoomBoundLayers[i]&&(delete this._zoomBoundLayers[i],this._updateZoomLevels())},_updateZoomLevels:function(){var t=1/0,i=-1/0,e=this._getZoomSpan();for(var n in this._zoomBoundLayers){var o=this._zoomBoundLayers[n].options;t=void 0===o.minZoom?t:Math.min(t,o.minZoom),i=void 0===o.maxZoom?i:Math.max(i,o.maxZoom)}this._layersMaxZoom=i===-1/0?void 0:i,this._layersMinZoom=t===1/0?void 0:t,e!==this._getZoomSpan()&&this.fire("zoomlevelschange"),void 0===this.options.maxZoom&&this._layersMaxZoom&&this.getZoom()>this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()<this._layersMinZoom&&this.setZoom(this._layersMinZoom)}});var Ve=Ue.extend({initialize:function(t,i){l(this,i),this._layers={};var e,n;if(t)for(e=0,n=t.length;e<n;e++)this.addLayer(t[e])},addLayer:function(t){var i=this.getLayerId(t);return this._layers[i]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var i=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[i]&&this._map.removeLayer(this._layers[i]),delete this._layers[i],this},hasLayer:function(t){return!!t&&(t in this._layers||this.getLayerId(t)in this._layers)},clearLayers:function(){return this.eachLayer(this.removeLayer,this)},invoke:function(t){var i,e,n=Array.prototype.slice.call(arguments,1);for(i in this._layers)(e=this._layers[i])[t]&&e[t].apply(e,n);return this},onAdd:function(t){this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t)},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];return this.eachLayer(t.push,t),t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return n(t)}}),qe=Ve.extend({addLayer:function(t){return this.hasLayer(t)?this:(t.addEventParent(this),Ve.prototype.addLayer.call(this,t),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.removeEventParent(this),Ve.prototype.removeLayer.call(this,t),this.fire("layerremove",{layer:t})):this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new T;for(var i in this._layers){var e=this._layers[i];t.extend(e.getBounds?e.getBounds():e.getLatLng())}return t}}),Ge=v.extend({options:{popupAnchor:[0,0],tooltipAnchor:[0,0]},initialize:function(t){l(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,i){var e=this._getIconUrl(t);if(!e){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(e,i&&"IMG"===i.tagName?i:null);return this._setIconStyles(n,t),n},_setIconStyles:function(t,i){var e=this.options,n=e[i+"Size"];"number"==typeof n&&(n=[n,n]);var o=w(n),s=w("shadow"===i&&e.shadowAnchor||e.iconAnchor||o&&o.divideBy(2,!0));t.className="leaflet-marker-"+i+" "+(e.className||""),s&&(t.style.marginLeft=-s.x+"px",t.style.marginTop=-s.y+"px"),o&&(t.style.width=o.x+"px",t.style.height=o.y+"px")},_createImg:function(t,i){return i=i||document.createElement("img"),i.src=t,i},_getIconUrl:function(t){return Ki&&this.options[t+"RetinaUrl"]||this.options[t+"Url"]}}),Ke=Ge.extend({options:{iconUrl:"marker-icon.png",iconRetinaUrl:"marker-icon-2x.png",shadowUrl:"marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28],shadowSize:[41,41]},_getIconUrl:function(t){return Ke.imagePath||(Ke.imagePath=this._detectIconPath()),(this.options.imagePath||Ke.imagePath)+Ge.prototype._getIconUrl.call(this,t)},_detectIconPath:function(){var t=ht("div","leaflet-default-icon-path",document.body),i=at(t,"background-image")||at(t,"backgroundImage");return document.body.removeChild(t),i=null===i||0!==i.indexOf("url")?"":i.replace(/^url\(["']?/,"").replace(/marker-icon\.png["']?\)$/,"")}}),Ye=Ze.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new Be(t,t,!0)),this._draggable.on({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).enable(),pt(t,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).disable(),this._marker._icon&&mt(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_adjustPan:function(t){var i=this._marker,e=i._map,n=this._marker.options.autoPanSpeed,o=this._marker.options.autoPanPadding,s=L.DomUtil.getPosition(i._icon),r=e.getPixelBounds(),a=e.getPixelOrigin(),h=b(r.min._subtract(a).add(o),r.max._subtract(a).subtract(o));if(!h.contains(s)){var u=w((Math.max(h.max.x,s.x)-h.max.x)/(r.max.x-h.max.x)-(Math.min(h.min.x,s.x)-h.min.x)/(r.min.x-h.min.x),(Math.max(h.max.y,s.y)-h.max.y)/(r.max.y-h.max.y)-(Math.min(h.min.y,s.y)-h.min.y)/(r.min.y-h.min.y)).multiplyBy(n);e.panBy(u,{animate:!1}),this._draggable._newPos._add(u),this._draggable._startPos._add(u),L.DomUtil.setPosition(i._icon,this._draggable._newPos),this._onDrag(t),this._panRequest=f(this._adjustPan.bind(this,t))}},_onDragStart:function(){this._oldLatLng=this._marker.getLatLng(),this._marker.closePopup().fire("movestart").fire("dragstart")},_onPreDrag:function(t){this._marker.options.autoPan&&(g(this._panRequest),this._panRequest=f(this._adjustPan.bind(this,t)))},_onDrag:function(t){var i=this._marker,e=i._shadow,n=Pt(i._icon),o=i._map.layerPointToLatLng(n);e&&Lt(e,n),i._latlng=o,t.latlng=o,t.oldLatLng=this._oldLatLng,i.fire("move",t).fire("drag",t)},_onDragEnd:function(t){g(this._panRequest),delete this._oldLatLng,this._marker.fire("moveend").fire("dragend",t)}}),Xe=Ue.extend({options:{icon:new Ke,interactive:!0,draggable:!1,autoPan:!1,autoPanPadding:[50,50],autoPanSpeed:10,keyboard:!0,title:"",alt:"",zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250,pane:"markerPane",bubblingMouseEvents:!1},initialize:function(t,i){l(this,i),this._latlng=C(t)},onAdd:function(t){this._zoomAnimated=this._zoomAnimated&&t.options.markerZoomAnimation,this._zoomAnimated&&t.on("zoomanim",this._animateZoom,this),this._initIcon(),this.update()},onRemove:function(t){this.dragging&&this.dragging.enabled()&&(this.options.draggable=!0,this.dragging.removeHooks()),delete this.dragging,this._zoomAnimated&&t.off("zoomanim",this._animateZoom,this),this._removeIcon(),this._removeShadow()},getEvents:function(){return{zoom:this.update,viewreset:this.update}},getLatLng:function(){return this._latlng},setLatLng:function(t){var i=this._latlng;return this._latlng=C(t),this.update(),this.fire("move",{oldLatLng:i,latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update()},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup,this._popup.options),this},getElement:function(){return this._icon},update:function(){if(this._icon&&this._map){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,i="leaflet-zoom-"+(this._zoomAnimated?"animated":"hide"),e=t.icon.createIcon(this._icon),n=!1;e!==this._icon&&(this._icon&&this._removeIcon(),n=!0,t.title&&(e.title=t.title),"IMG"===e.tagName&&(e.alt=t.alt||"")),pt(e,i),t.keyboard&&(e.tabIndex="0"),this._icon=e,t.riseOnHover&&this.on({mouseover:this._bringToFront,mouseout:this._resetZIndex});var o=t.icon.createShadow(this._shadow),s=!1;o!==this._shadow&&(this._removeShadow(),s=!0),o&&(pt(o,i),o.alt=""),this._shadow=o,t.opacity<1&&this._updateOpacity(),n&&this.getPane().appendChild(this._icon),this._initInteraction(),o&&s&&this.getPane("shadowPane").appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&this.off({mouseover:this._bringToFront,mouseout:this._resetZIndex}),ut(this._icon),this.removeInteractiveTarget(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&ut(this._shadow),this._shadow=null},_setPos:function(t){Lt(this._icon,t),this._shadow&&Lt(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(i)},_initInteraction:function(){if(this.options.interactive&&(pt(this._icon,"leaflet-interactive"),this.addInteractiveTarget(this._icon),Ye)){var t=this.options.draggable;this.dragging&&(t=this.dragging.enabled(),this.dragging.disable()),this.dragging=new Ye(this),t&&this.dragging.enable()}},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},_updateOpacity:function(){var t=this.options.opacity;vt(this._icon,t),this._shadow&&vt(this._shadow,t)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)},_getPopupAnchor:function(){return this.options.icon.options.popupAnchor},_getTooltipAnchor:function(){return this.options.icon.options.tooltipAnchor}}),Je=Ue.extend({options:{stroke:!0,color:"#3388ff",weight:3,opacity:1,lineCap:"round",lineJoin:"round",dashArray:null,dashOffset:null,fill:!1,fillColor:null,fillOpacity:.2,fillRule:"evenodd",interactive:!0,bubblingMouseEvents:!0},beforeAdd:function(t){this._renderer=t.getRenderer(this)},onAdd:function(){this._renderer._initPath(this),this._reset(),this._renderer._addPath(this)},onRemove:function(){this._renderer._removePath(this)},redraw:function(){return this._map&&this._renderer._updatePath(this),this},setStyle:function(t){return l(this,t),this._renderer&&this._renderer._updateStyle(this),this},bringToFront:function(){return this._renderer&&this._renderer._bringToFront(this),this},bringToBack:function(){return this._renderer&&this._renderer._bringToBack(this),this},getElement:function(){return this._path},_reset:function(){this._project(),this._update()},_clickTolerance:function(){return(this.options.stroke?this.options.weight/2:0)+this._renderer.options.tolerance}}),$e=Je.extend({options:{fill:!0,radius:10},initialize:function(t,i){l(this,i),this._latlng=C(t),this._radius=this.options.radius},setLatLng:function(t){return this._latlng=C(t),this.redraw(),this.fire("move",{latlng:this._latlng})},getLatLng:function(){return this._latlng},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius},setStyle:function(t){var i=t&&t.radius||this._radius;return Je.prototype.setStyle.call(this,t),this.setRadius(i),this},_project:function(){this._point=this._map.latLngToLayerPoint(this._latlng),this._updateBounds()},_updateBounds:function(){var t=this._radius,i=this._radiusY||t,e=this._clickTolerance(),n=[t+e,i+e];this._pxBounds=new P(this._point.subtract(n),this._point.add(n))},_update:function(){this._map&&this._updatePath()},_updatePath:function(){this._renderer._updateCircle(this)},_empty:function(){return this._radius&&!this._renderer._bounds.intersects(this._pxBounds)},_containsPoint:function(t){return t.distanceTo(this._point)<=this._radius+this._clickTolerance()}}),Qe=$e.extend({initialize:function(t,e,n){if("number"==typeof e&&(e=i({},n,{radius:e})),l(this,e),this._latlng=C(t),isNaN(this.options.radius))throw new Error("Circle radius cannot be NaN");this._mRadius=this.options.radius},setRadius:function(t){return this._mRadius=t,this.redraw()},getRadius:function(){return this._mRadius},getBounds:function(){var t=[this._radius,this._radiusY||this._radius];return new T(this._map.layerPointToLatLng(this._point.subtract(t)),this._map.layerPointToLatLng(this._point.add(t)))},setStyle:Je.prototype.setStyle,_project:function(){var t=this._latlng.lng,i=this._latlng.lat,e=this._map,n=e.options.crs;if(n.distance===_i.distance){var o=Math.PI/180,s=this._mRadius/_i.R/o,r=e.project([i+s,t]),a=e.project([i-s,t]),h=r.add(a).divideBy(2),u=e.unproject(h).lat,l=Math.acos((Math.cos(s*o)-Math.sin(i*o)*Math.sin(u*o))/(Math.cos(i*o)*Math.cos(u*o)))/o;(isNaN(l)||0===l)&&(l=s/Math.cos(Math.PI/180*i)),this._point=h.subtract(e.getPixelOrigin()),this._radius=isNaN(l)?0:h.x-e.project([u,t-l]).x,this._radiusY=h.y-r.y}else{var c=n.unproject(n.project(this._latlng).subtract([this._mRadius,0]));this._point=e.latLngToLayerPoint(this._latlng),this._radius=this._point.x-e.latLngToLayerPoint(c).x}this._updateBounds()}}),tn=Je.extend({options:{smoothFactor:1,noClip:!1},initialize:function(t,i){l(this,i),this._setLatLngs(t)},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._setLatLngs(t),this.redraw()},isEmpty:function(){return!this._latlngs.length},closestLayerPoint:function(t){for(var i,e,n=1/0,o=null,s=Rt,r=0,a=this._parts.length;r<a;r++)for(var h=this._parts[r],u=1,l=h.length;u<l;u++){var c=s(t,i=h[u-1],e=h[u],!0);c<n&&(n=c,o=s(t,i,e))}return o&&(o.distance=Math.sqrt(n)),o},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a=this._rings[0],h=a.length;if(!h)return null;for(t=0,i=0;t<h-1;t++)i+=a[t].distanceTo(a[t+1])/2;if(0===i)return this._map.layerPointToLatLng(a[0]);for(t=0,n=0;t<h-1;t++)if(o=a[t],s=a[t+1],e=o.distanceTo(s),(n+=e)>i)return r=(n-i)/e,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,i){return i=i||this._defaultShape(),t=C(t),i.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new T,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return Dt(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var i=[],e=Dt(t),n=0,o=t.length;n<o;n++)e?(i[n]=C(t[n]),this._bounds.extend(i[n])):i[n]=this._convertLatLngs(t[n]);return i},_project:function(){var t=new P;this._rings=[],this._projectLatlngs(this._latlngs,this._rings,t);var i=this._clickTolerance(),e=new x(i,i);this._bounds.isValid()&&t.isValid()&&(t.min._subtract(e),t.max._add(e),this._pxBounds=t)},_projectLatlngs:function(t,i,e){var n,o,s=t[0]instanceof M,r=t.length;if(s){for(o=[],n=0;n<r;n++)o[n]=this._map.latLngToLayerPoint(t[n]),e.extend(o[n]);i.push(o)}else for(n=0;n<r;n++)this._projectLatlngs(t[n],i,e)},_clipPoints:function(){var t=this._renderer._bounds;if(this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else{var i,e,n,o,s,r,a,h=this._parts;for(i=0,n=0,o=this._rings.length;i<o;i++)for(e=0,s=(a=this._rings[i]).length;e<s-1;e++)(r=At(a[e],a[e+1],t,e,!0))&&(h[n]=h[n]||[],h[n].push(r[0]),r[1]===a[e+1]&&e!==s-2||(h[n].push(r[1]),n++))}},_simplifyPoints:function(){for(var t=this._parts,i=this.options.smoothFactor,e=0,n=t.length;e<n;e++)t[e]=Ct(t[e],i)},_update:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),this._updatePath())},_updatePath:function(){this._renderer._updatePoly(this)},_containsPoint:function(t,i){var e,n,o,s,r,a,h=this._clickTolerance();if(!this._pxBounds||!this._pxBounds.contains(t))return!1;for(e=0,s=this._parts.length;e<s;e++)for(n=0,o=(r=(a=this._parts[e]).length)-1;n<r;o=n++)if((i||0!==n)&&Zt(t,a[o],a[n])<=h)return!0;return!1}});tn._flat=Nt;var en=tn.extend({options:{fill:!0},isEmpty:function(){return!this._latlngs.length||!this._latlngs[0].length},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a,h,u=this._rings[0],l=u.length;if(!l)return null;for(s=r=a=0,t=0,i=l-1;t<l;i=t++)e=u[t],n=u[i],o=e.y*n.x-n.y*e.x,r+=(e.x+n.x)*o,a+=(e.y+n.y)*o,s+=3*o;return h=0===s?u[0]:[r/s,a/s],this._map.layerPointToLatLng(h)},_convertLatLngs:function(t){var i=tn.prototype._convertLatLngs.call(this,t),e=i.length;return e>=2&&i[0]instanceof M&&i[0].equals(i[e-1])&&i.pop(),i},_setLatLngs:function(t){tn.prototype._setLatLngs.call(this,t),Dt(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return Dt(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,i=this.options.weight,e=new x(i,i);if(t=new P(t.min.subtract(e),t.max.add(e)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var n,o=0,s=this._rings.length;o<s;o++)(n=jt(this._rings[o],t,!0)).length&&this._parts.push(n)},_updatePath:function(){this._renderer._updatePoly(this,!0)},_containsPoint:function(t){var i,e,n,o,s,r,a,h,u=!1;if(!this._pxBounds.contains(t))return!1;for(o=0,a=this._parts.length;o<a;o++)for(s=0,r=(h=(i=this._parts[o]).length)-1;s<h;r=s++)e=i[s],n=i[r],e.y>t.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||tn.prototype._containsPoint.call(this,t,!0)}}),nn=qe.extend({initialize:function(t,i){l(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=ei(t)?t:t.features;if(o){for(i=0,e=o.length;i<e;i++)((n=o[i]).geometries||n.geometry||n.features||n.coordinates)&&this.addData(n);return this}var s=this.options;if(s.filter&&!s.filter(t))return this;var r=Wt(t,s);return r?(r.feature=Gt(t),r.defaultOptions=r.options,this.resetStyle(r),s.onEachFeature&&s.onEachFeature(t,r),this.addLayer(r)):this},resetStyle:function(t){return t.options=i({},t.defaultOptions),this._setLayerStyle(t,this.options.style),this},setStyle:function(t){return this.eachLayer(function(i){this._setLayerStyle(i,t)},this)},_setLayerStyle:function(t,i){"function"==typeof i&&(i=i(t.feature)),t.setStyle&&t.setStyle(i)}}),on={toGeoJSON:function(t){return qt(this,{type:"Point",coordinates:Ut(this.getLatLng(),t)})}};Xe.include(on),Qe.include(on),$e.include(on),tn.include({toGeoJSON:function(t){var i=!Dt(this._latlngs),e=Vt(this._latlngs,i?1:0,!1,t);return qt(this,{type:(i?"Multi":"")+"LineString",coordinates:e})}}),en.include({toGeoJSON:function(t){var i=!Dt(this._latlngs),e=i&&!Dt(this._latlngs[0]),n=Vt(this._latlngs,e?2:i?1:0,!0,t);return i||(n=[n]),qt(this,{type:(e?"Multi":"")+"Polygon",coordinates:n})}}),Ve.include({toMultiPoint:function(t){var i=[];return this.eachLayer(function(e){i.push(e.toGeoJSON(t).geometry.coordinates)}),qt(this,{type:"MultiPoint",coordinates:i})},toGeoJSON:function(t){var i=this.feature&&this.feature.geometry&&this.feature.geometry.type;if("MultiPoint"===i)return this.toMultiPoint(t);var e="GeometryCollection"===i,n=[];return this.eachLayer(function(i){if(i.toGeoJSON){var o=i.toGeoJSON(t);if(e)n.push(o.geometry);else{var s=Gt(o);"FeatureCollection"===s.type?n.push.apply(n,s.features):n.push(s)}}}),e?qt(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}});var sn=Kt,rn=Ue.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(t,i,e){this._url=t,this._bounds=z(i),l(this,e)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(pt(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){ut(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(t){return this.options.opacity=t,this._image&&this._updateOpacity(),this},setStyle:function(t){return t.opacity&&this.setOpacity(t.opacity),this},bringToFront:function(){return this._map&&ct(this._image),this},bringToBack:function(){return this._map&&_t(this._image),this},setUrl:function(t){return this._url=t,this._image&&(this._image.src=t),this},setBounds:function(t){return this._bounds=z(t),this._map&&this._reset(),this},getEvents:function(){var t={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var t="IMG"===this._url.tagName,i=this._image=t?this._url:ht("img");pt(i,"leaflet-image-layer"),this._zoomAnimated&&pt(i,"leaflet-zoom-animated"),this.options.className&&pt(i,this.options.className),i.onselectstart=r,i.onmousemove=r,i.onload=e(this.fire,this,"load"),i.onerror=e(this._overlayOnError,this,"error"),this.options.crossOrigin&&(i.crossOrigin=""),this.options.zIndex&&this._updateZIndex(),t?this._url=i.src:(i.src=this._url,i.alt=this.options.alt)},_animateZoom:function(t){var i=this._map.getZoomScale(t.zoom),e=this._map._latLngBoundsToNewLayerBounds(this._bounds,t.zoom,t.center).min;wt(this._image,e,i)},_reset:function(){var t=this._image,i=new P(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),e=i.getSize();Lt(t,i.min),t.style.width=e.x+"px",t.style.height=e.y+"px"},_updateOpacity:function(){vt(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire("error");var t=this.options.errorOverlayUrl;t&&this._url!==t&&(this._url=t,this._image.src=t)}}),an=rn.extend({options:{autoplay:!0,loop:!0},_initImage:function(){var t="VIDEO"===this._url.tagName,i=this._image=t?this._url:ht("video");if(pt(i,"leaflet-image-layer"),this._zoomAnimated&&pt(i,"leaflet-zoom-animated"),i.onselectstart=r,i.onmousemove=r,i.onloadeddata=e(this.fire,this,"load"),t){for(var n=i.getElementsByTagName("source"),o=[],s=0;s<n.length;s++)o.push(n[s].src);this._url=n.length>0?o:[i.src]}else{ei(this._url)||(this._url=[this._url]),i.autoplay=!!this.options.autoplay,i.loop=!!this.options.loop;for(var a=0;a<this._url.length;a++){var h=ht("source");h.src=this._url[a],i.appendChild(h)}}}}),hn=Ue.extend({options:{offset:[0,7],className:"",pane:"popupPane"},initialize:function(t,i){l(this,t),this._source=i},onAdd:function(t){this._zoomAnimated=t._zoomAnimated,this._container||this._initLayout(),t._fadeAnimated&&vt(this._container,0),clearTimeout(this._removeTimeout),this.getPane().appendChild(this._container),this.update(),t._fadeAnimated&&vt(this._container,1),this.bringToFront()},onRemove:function(t){t._fadeAnimated?(vt(this._container,0),this._removeTimeout=setTimeout(e(ut,void 0,this._container),200)):ut(this._container)},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=C(t),this._map&&(this._updatePosition(),this._adjustPan()),this},getContent:function(){return this._content},setContent:function(t){return this._content=t,this.update(),this},getElement:function(){return this._container},update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},getEvents:function(){var t={zoom:this._updatePosition,viewreset:this._updatePosition};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},isOpen:function(){return!!this._map&&this._map.hasLayer(this)},bringToFront:function(){return this._map&&ct(this._container),this},bringToBack:function(){return this._map&&_t(this._container),this},_updateContent:function(){if(this._content){var t=this._contentNode,i="function"==typeof this._content?this._content(this._source||this):this._content;if("string"==typeof i)t.innerHTML=i;else{for(;t.hasChildNodes();)t.removeChild(t.firstChild);t.appendChild(i)}this.fire("contentupdate")}},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),i=w(this.options.offset),e=this._getAnchor();this._zoomAnimated?Lt(this._container,t.add(e)):i=i.add(t).add(e);var n=this._containerBottom=-i.y,o=this._containerLeft=-Math.round(this._containerWidth/2)+i.x;this._container.style.bottom=n+"px",this._container.style.left=o+"px"}},_getAnchor:function(){return[0,0]}}),un=hn.extend({options:{maxWidth:300,minWidth:50,maxHeight:null,autoPan:!0,autoPanPaddingTopLeft:null,autoPanPaddingBottomRight:null,autoPanPadding:[5,5],keepInView:!1,closeButton:!0,autoClose:!0,closeOnEscapeKey:!0,className:""},openOn:function(t){return t.openPopup(this),this},onAdd:function(t){hn.prototype.onAdd.call(this,t),t.fire("popupopen",{popup:this}),this._source&&(this._source.fire("popupopen",{popup:this},!0),this._source instanceof Je||this._source.on("preclick",Y))},onRemove:function(t){hn.prototype.onRemove.call(this,t),t.fire("popupclose",{popup:this}),this._source&&(this._source.fire("popupclose",{popup:this},!0),this._source instanceof Je||this._source.off("preclick",Y))},getEvents:function(){var t=hn.prototype.getEvents.call(this);return(void 0!==this.options.closeOnClick?this.options.closeOnClick:this._map.options.closePopupOnClick)&&(t.preclick=this._close),this.options.keepInView&&(t.moveend=this._adjustPan),t},_close:function(){this._map&&this._map.closePopup(this)},_initLayout:function(){var t="leaflet-popup",i=this._container=ht("div",t+" "+(this.options.className||"")+" leaflet-zoom-animated"),e=this._wrapper=ht("div",t+"-content-wrapper",i);if(this._contentNode=ht("div",t+"-content",e),J(e),X(this._contentNode),V(e,"contextmenu",Y),this._tipContainer=ht("div",t+"-tip-container",i),this._tip=ht("div",t+"-tip",this._tipContainer),this.options.closeButton){var n=this._closeButton=ht("a",t+"-close-button",i);n.href="#close",n.innerHTML="&#215;",V(n,"click",this._onCloseButtonClick,this)}},_updateLayout:function(){var t=this._contentNode,i=t.style;i.width="",i.whiteSpace="nowrap";var e=t.offsetWidth;e=Math.min(e,this.options.maxWidth),e=Math.max(e,this.options.minWidth),i.width=e+1+"px",i.whiteSpace="",i.height="";var n=t.offsetHeight,o=this.options.maxHeight;o&&n>o?(i.height=o+"px",pt(t,"leaflet-popup-scrolled")):mt(t,"leaflet-popup-scrolled"),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();Lt(this._container,i.add(e))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,i=parseInt(at(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+i,n=this._containerWidth,o=new x(this._containerLeft,-e-this._containerBottom);o._add(Pt(this._container));var s=t.layerPointToContainerPoint(o),r=w(this.options.autoPanPadding),a=w(this.options.autoPanPaddingTopLeft||r),h=w(this.options.autoPanPaddingBottomRight||r),u=t.getSize(),l=0,c=0;s.x+n+h.x>u.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Q(t)},_getAnchor:function(){return w(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});Le.mergeOptions({closePopupOnClick:!0}),Le.include({openPopup:function(t,i,e){return t instanceof un||(t=new un(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),Ue.include({bindPopup:function(t,i){return t instanceof un?(l(t,i),this._popup=t,t._source=this):(this._popup&&!i||(this._popup=new un(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){if(t instanceof Ue||(i=t,t=this),t instanceof qe)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(Q(t),i instanceof Je?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var ln=hn.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){hn.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){hn.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=hn.prototype.getEvents.call(this);return Vi&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=ht("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=w(this.options.offset),u=this._getAnchor();"top"===s?t=t.add(w(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t=t.subtract(w(r/2-h.x,-h.y,!0)):"center"===s?t=t.subtract(w(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.x<n.x?(s="right",t=t.add(w(h.x+u.x,u.y-a/2+h.y,!0))):(s="left",t=t.subtract(w(r+u.x-h.x,a/2-u.y-h.y,!0))),mt(e,"leaflet-tooltip-right"),mt(e,"leaflet-tooltip-left"),mt(e,"leaflet-tooltip-top"),mt(e,"leaflet-tooltip-bottom"),pt(e,"leaflet-tooltip-"+s),Lt(e,t)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},setOpacity:function(t){this.options.opacity=t,this._container&&vt(this._container,t)},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPosition(i)},_getAnchor:function(){return w(this._source&&this._source._getTooltipAnchor&&!this.options.sticky?this._source._getTooltipAnchor():[0,0])}});Le.include({openTooltip:function(t,i,e){return t instanceof ln||(t=new ln(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:this.addLayer(t)},closeTooltip:function(t){return t&&this.removeLayer(t),this}}),Ue.include({bindTooltip:function(t,i){return t instanceof ln?(l(t,i),this._tooltip=t,t._source=this):(this._tooltip&&!i||(this._tooltip=new ln(i,this)),this._tooltip.setContent(t)),this._initTooltipInteractions(),this._tooltip.options.permanent&&this._map&&this._map.hasLayer(this)&&this.openTooltip(),this},unbindTooltip:function(){return this._tooltip&&(this._initTooltipInteractions(!0),this.closeTooltip(),this._tooltip=null),this},_initTooltipInteractions:function(t){if(t||!this._tooltipHandlersAdded){var i=t?"off":"on",e={remove:this.closeTooltip,move:this._moveTooltip};this._tooltip.options.permanent?e.add=this._openTooltip:(e.mouseover=this._openTooltip,e.mouseout=this.closeTooltip,this._tooltip.options.sticky&&(e.mousemove=this._moveTooltip),Vi&&(e.click=this._openTooltip)),this[i](e),this._tooltipHandlersAdded=!t}},openTooltip:function(t,i){if(t instanceof Ue||(i=t,t=this),t instanceof qe)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._tooltip&&this._map&&(this._tooltip._source=t,this._tooltip.update(),this._map.openTooltip(this._tooltip,i),this._tooltip.options.interactive&&this._tooltip._container&&(pt(this._tooltip._container,"leaflet-clickable"),this.addInteractiveTarget(this._tooltip._container))),this},closeTooltip:function(){return this._tooltip&&(this._tooltip._close(),this._tooltip.options.interactive&&this._tooltip._container&&(mt(this._tooltip._container,"leaflet-clickable"),this.removeInteractiveTarget(this._tooltip._container))),this},toggleTooltip:function(t){return this._tooltip&&(this._tooltip._map?this.closeTooltip():this.openTooltip(t)),this},isTooltipOpen:function(){return this._tooltip.isOpen()},setTooltipContent:function(t){return this._tooltip&&this._tooltip.setContent(t),this},getTooltip:function(){return this._tooltip},_openTooltip:function(t){var i=t.layer||t.target;this._tooltip&&this._map&&this.openTooltip(i,this._tooltip.options.sticky?t.latlng:void 0)},_moveTooltip:function(t){var i,e,n=t.latlng;this._tooltip.options.sticky&&t.originalEvent&&(i=this._map.mouseEventToContainerPoint(t.originalEvent),e=this._map.containerPointToLayerPoint(i),n=this._map.layerPointToLatLng(e)),this._tooltip.setLatLng(n)}});var cn=Ge.extend({options:{iconSize:[12,12],html:!1,bgPos:null,className:"leaflet-div-icon"},createIcon:function(t){var i=t&&"DIV"===t.tagName?t:document.createElement("div"),e=this.options;if(i.innerHTML=!1!==e.html?e.html:"",e.bgPos){var n=w(e.bgPos);i.style.backgroundPosition=-n.x+"px "+-n.y+"px"}return this._setIconStyles(i,"icon"),i},createShadow:function(){return null}});Ge.Default=Ke;var _n=Ue.extend({options:{tileSize:256,opacity:1,updateWhenIdle:ji,updateWhenZooming:!0,updateInterval:200,zIndex:1,bounds:null,minZoom:0,maxZoom:void 0,maxNativeZoom:void 0,minNativeZoom:void 0,noWrap:!1,pane:"tilePane",className:"",keepBuffer:2},initialize:function(t){l(this,t)},onAdd:function(){this._initContainer(),this._levels={},this._tiles={},this._resetView(),this._update()},beforeAdd:function(t){t._addZoomLimit(this)},onRemove:function(t){this._removeAllTiles(),ut(this._container),t._removeZoomLimit(this),this._container=null,this._tileZoom=void 0},bringToFront:function(){return this._map&&(ct(this._container),this._setAutoZIndex(Math.max)),this},bringToBack:function(){return this._map&&(_t(this._container),this._setAutoZIndex(Math.min)),this},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},isLoading:function(){return this._loading},redraw:function(){return this._map&&(this._removeAllTiles(),this._update()),this},getEvents:function(){var t={viewprereset:this._invalidateAll,viewreset:this._resetView,zoom:this._resetView,moveend:this._onMoveEnd};return this.options.updateWhenIdle||(this._onMove||(this._onMove=o(this._onMoveEnd,this.options.updateInterval,this)),t.move=this._onMove),this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},createTile:function(){return document.createElement("div")},getTileSize:function(){var t=this.options.tileSize;return t instanceof x?t:new x(t,t)},_updateZIndex:function(){this._container&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t){for(var i,e=this.getPane().children,n=-t(-1/0,1/0),o=0,s=e.length;o<s;o++)i=e[o].style.zIndex,e[o]!==this._container&&i&&(n=t(n,+i));isFinite(n)&&(this.options.zIndex=n+t(-1,1),this._updateZIndex())},_updateOpacity:function(){if(this._map&&!Li){vt(this._container,this.options.opacity);var t=+new Date,i=!1,e=!1;for(var n in this._tiles){var o=this._tiles[n];if(o.current&&o.loaded){var s=Math.min(1,(t-o.loaded)/200);vt(o.el,s),s<1?i=!0:(o.active?e=!0:this._onOpaqueTile(o),o.active=!0)}}e&&!this._noPrune&&this._pruneTiles(),i&&(g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this))}},_onOpaqueTile:r,_initContainer:function(){this._container||(this._container=ht("div","leaflet-layer "+(this.options.className||"")),this._updateZIndex(),this.options.opacity<1&&this._updateOpacity(),this.getPane().appendChild(this._container))},_updateLevels:function(){var t=this._tileZoom,i=this.options.maxZoom;if(void 0!==t){for(var e in this._levels)this._levels[e].el.children.length||e===t?(this._levels[e].el.style.zIndex=i-Math.abs(t-e),this._onUpdateLevel(e)):(ut(this._levels[e].el),this._removeTilesAtZoom(e),this._onRemoveLevel(e),delete this._levels[e]);var n=this._levels[t],o=this._map;return n||((n=this._levels[t]={}).el=ht("div","leaflet-tile-container leaflet-zoom-animated",this._container),n.el.style.zIndex=i,n.origin=o.project(o.unproject(o.getPixelOrigin()),t).round(),n.zoom=t,this._setZoomTransform(n,o.getCenter(),o.getZoom()),n.el.offsetWidth,this._onCreateLevel(n)),this._level=n,n}},_onUpdateLevel:r,_onRemoveLevel:r,_onCreateLevel:r,_pruneTiles:function(){if(this._map){var t,i,e=this._map.getZoom();if(e>this.options.maxZoom||e<this.options.minZoom)this._removeAllTiles();else{for(t in this._tiles)(i=this._tiles[t]).retain=i.current;for(t in this._tiles)if((i=this._tiles[t]).current&&!i.active){var n=i.coords;this._retainParent(n.x,n.y,n.z,n.z-5)||this._retainChildren(n.x,n.y,n.z,n.z+2)}for(t in this._tiles)this._tiles[t].retain||this._removeTile(t)}}},_removeTilesAtZoom:function(t){for(var i in this._tiles)this._tiles[i].coords.z===t&&this._removeTile(i)},_removeAllTiles:function(){for(var t in this._tiles)this._removeTile(t)},_invalidateAll:function(){for(var t in this._levels)ut(this._levels[t].el),this._onRemoveLevel(t),delete this._levels[t];this._removeAllTiles(),this._tileZoom=void 0},_retainParent:function(t,i,e,n){var o=Math.floor(t/2),s=Math.floor(i/2),r=e-1,a=new x(+o,+s);a.z=+r;var h=this._tileCoordsToKey(a),u=this._tiles[h];return u&&u.active?(u.retain=!0,!0):(u&&u.loaded&&(u.retain=!0),r>n&&this._retainParent(o,s,r,n))},_retainChildren:function(t,i,e,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*i;s<2*i+2;s++){var r=new x(o,s);r.z=e+1;var a=this._tileCoordsToKey(r),h=this._tiles[a];h&&h.active?h.retain=!0:(h&&h.loaded&&(h.retain=!0),e+1<n&&this._retainChildren(o,s,e+1,n))}},_resetView:function(t){var i=t&&(t.pinch||t.flyTo);this._setView(this._map.getCenter(),this._map.getZoom(),i,i)},_animateZoom:function(t){this._setView(t.center,t.zoom,!0,t.noUpdate)},_clampZoom:function(t){var i=this.options;return void 0!==i.minNativeZoom&&t<i.minNativeZoom?i.minNativeZoom:void 0!==i.maxNativeZoom&&i.maxNativeZoom<t?i.maxNativeZoom:t},_setView:function(t,i,e,n){var o=this._clampZoom(Math.round(i));(void 0!==this.options.maxZoom&&o>this.options.maxZoom||void 0!==this.options.minZoom&&o<this.options.minZoom)&&(o=void 0);var s=this.options.updateWhenZooming&&o!==this._tileZoom;n&&!s||(this._tileZoom=o,this._abortLoading&&this._abortLoading(),this._updateLevels(),this._resetGrid(),void 0!==o&&this._update(t),e||this._pruneTiles(),this._noPrune=!!e),this._setZoomTransforms(t,i)},_setZoomTransforms:function(t,i){for(var e in this._levels)this._setZoomTransform(this._levels[e],t,i)},_setZoomTransform:function(t,i,e){var n=this._map.getZoomScale(e,t.zoom),o=t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(i,e)).round();Ni?wt(t.el,o,n):Lt(t.el,o)},_resetGrid:function(){var t=this._map,i=t.options.crs,e=this._tileSize=this.getTileSize(),n=this._tileZoom,o=this._map.getPixelWorldBounds(this._tileZoom);o&&(this._globalTileRange=this._pxBoundsToTileRange(o)),this._wrapX=i.wrapLng&&!this.options.noWrap&&[Math.floor(t.project([0,i.wrapLng[0]],n).x/e.x),Math.ceil(t.project([0,i.wrapLng[1]],n).x/e.y)],this._wrapY=i.wrapLat&&!this.options.noWrap&&[Math.floor(t.project([i.wrapLat[0],0],n).y/e.x),Math.ceil(t.project([i.wrapLat[1],0],n).y/e.y)]},_onMoveEnd:function(){this._map&&!this._map._animatingZoom&&this._update()},_getTiledPixelBounds:function(t){var i=this._map,e=i._animatingZoom?Math.max(i._animateToZoom,i.getZoom()):i.getZoom(),n=i.getZoomScale(e,this._tileZoom),o=i.project(t,this._tileZoom).floor(),s=i.getSize().divideBy(2*n);return new P(o.subtract(s),o.add(s))},_update:function(t){var i=this._map;if(i){var e=this._clampZoom(i.getZoom());if(void 0===t&&(t=i.getCenter()),void 0!==this._tileZoom){var n=this._getTiledPixelBounds(t),o=this._pxBoundsToTileRange(n),s=o.getCenter(),r=[],a=this.options.keepBuffer,h=new P(o.getBottomLeft().subtract([a,-a]),o.getTopRight().add([a,-a]));if(!(isFinite(o.min.x)&&isFinite(o.min.y)&&isFinite(o.max.x)&&isFinite(o.max.y)))throw new Error("Attempted to load an infinite number of tiles");for(var u in this._tiles){var l=this._tiles[u].coords;l.z===this._tileZoom&&h.contains(new x(l.x,l.y))||(this._tiles[u].current=!1)}if(Math.abs(e-this._tileZoom)>1)this._setView(t,e);else{for(var c=o.min.y;c<=o.max.y;c++)for(var _=o.min.x;_<=o.max.x;_++){var d=new x(_,c);if(d.z=this._tileZoom,this._isValidTile(d)){var p=this._tiles[this._tileCoordsToKey(d)];p?p.current=!0:r.push(d)}}if(r.sort(function(t,i){return t.distanceTo(s)-i.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire("loading"));var m=document.createDocumentFragment();for(_=0;_<r.length;_++)this._addTile(r[_],m);this._level.el.appendChild(m)}}}}},_isValidTile:function(t){var i=this._map.options.crs;if(!i.infinite){var e=this._globalTileRange;if(!i.wrapLng&&(t.x<e.min.x||t.x>e.max.x)||!i.wrapLat&&(t.y<e.min.y||t.y>e.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return z(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new T(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new x(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(Ci||i.el.setAttribute("src",ni),ut(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){pt(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=r,t.onmousemove=r,Li&&this.options.opacity<1&&vt(t,this.options.opacity),Ti&&!zi&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),e(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&f(e(this._tileReady,this,t,null,s)),Lt(s,n),this._tiles[o]={el:s,coords:t,current:!0},i.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,i,n){if(this._map){i&&this.fire("tileerror",{error:i,tile:n,coords:t});var o=this._tileCoordsToKey(t);(n=this._tiles[o])&&(n.loaded=+new Date,this._map._fadeAnimated?(vt(n.el,0),g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),i||(pt(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Li||!this._map._fadeAnimated?f(this._pruneTiles,this):setTimeout(e(this._pruneTiles,this),250)))}},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new x(this._wrapX?s(t.x,this._wrapX):t.x,this._wrapY?s(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new P(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),dn=_n.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=l(this,i)).detectRetina&&Ki&&i.maxZoom>0&&(i.tileSize=Math.floor(i.tileSize/2),i.zoomReverse?(i.zoomOffset--,i.minZoom++):(i.zoomOffset++,i.maxZoom--),i.minZoom=Math.max(0,i.minZoom)),"string"==typeof i.subdomains&&(i.subdomains=i.subdomains.split("")),Ti||this.on("tileunload",this._onTileRemove)},setUrl:function(t,i){return this._url=t,i||this.redraw(),this},createTile:function(t,i){var n=document.createElement("img");return V(n,"load",e(this._tileOnLoad,this,i,n)),V(n,"error",e(this._tileOnError,this,i,n)),this.options.crossOrigin&&(n.crossOrigin=""),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:Ki?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var n=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=n),e["-y"]=n}return _(this._url,i(e,this.options))},_tileOnLoad:function(t,i){Li?setTimeout(e(t,this,null,i),0):t(null,i)},_tileOnError:function(t,i,e){var n=this.options.errorTileUrl;n&&i.getAttribute("src")!==n&&(i.src=n),t(e,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,i=this.options.maxZoom,e=this.options.zoomReverse,n=this.options.zoomOffset;return e&&(t=i-t),t+n},_getSubdomain:function(t){var i=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[i]},_abortLoading:function(){var t,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&((i=this._tiles[t].el).onload=r,i.onerror=r,i.complete||(i.src=ni,ut(i),delete this._tiles[t]))}}),pn=dn.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var n=i({},this.defaultWmsParams);for(var o in e)o in this.options||(n[o]=e[o]);var s=(e=l(this,e)).detectRetina&&Ki?2:1,r=this.getTileSize();n.width=r.x*s,n.height=r.y*s,this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var i=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[i]=this._crs.code,dn.prototype.onAdd.call(this,t)},getTileUrl:function(t){var i=this._tileCoordsToNwSe(t),e=this._crs,n=b(e.project(i[0]),e.project(i[1])),o=n.min,s=n.max,r=(this._wmsVersion>=1.3&&this._crs===He?[o.y,o.x,s.y,s.x]:[o.x,o.y,s.x,s.y]).join(","),a=L.TileLayer.prototype.getTileUrl.call(this,t);return a+c(this.wmsParams,a,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+r},setParams:function(t,e){return i(this.wmsParams,t),e||this.redraw(),this}});dn.WMS=pn,Yt.wms=function(t,i){return new pn(t,i)};var mn=Ue.extend({options:{padding:.1,tolerance:0},initialize:function(t){l(this,t),n(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&pt(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,i){var e=this._map.getZoomScale(i,this._zoom),n=Pt(this._container),o=this._map.getSize().multiplyBy(.5+this.options.padding),s=this._map.project(this._center,i),r=this._map.project(t,i).subtract(s),a=o.multiplyBy(-e).add(n).add(o).subtract(r);Ni?wt(this._container,a,e):Lt(this._container,a)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,i=this._map.getSize(),e=this._map.containerPointToLayerPoint(i.multiplyBy(-t)).round();this._bounds=new P(e,e.add(i.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),fn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){mn.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");V(t,"mousemove",o(this._onMouseMove,32,this),this),V(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),V(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_destroyContainer:function(){delete this._ctx,ut(this._container),q(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){this._redrawBounds=null;for(var t in this._layers)this._layers[t]._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){this._drawnLayers={},mn.prototype._update.call(this);var t=this._bounds,i=this._container,e=t.getSize(),n=Ki?2:1;Lt(i,t.min),i.width=n*e.x,i.height=n*e.y,i.style.width=e.x+"px",i.style.height=e.y+"px",Ki&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){mn.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[n(t)]=t;var i=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=i),this._drawLast=i,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var i=t._order,e=i.next,n=i.prev;e?e.prev=n:this._drawLast=n,n?n.next=e:this._drawFirst=e,delete t._order,delete this._layers[L.stamp(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if(t.options.dashArray){var i,e=t.options.dashArray.split(","),n=[];for(i=0;i<e.length;i++)n.push(Number(e[i]));t.options._dashArray=n}},_requestRedraw:function(t){this._map&&(this._extendRedrawBounds(t),this._redrawRequest=this._redrawRequest||f(this._redraw,this))},_extendRedrawBounds:function(t){if(t._pxBounds){var i=(t.options.weight||0)+1;this._redrawBounds=this._redrawBounds||new P,this._redrawBounds.extend(t._pxBounds.min.subtract([i,i])),this._redrawBounds.extend(t._pxBounds.max.add([i,i]))}},_redraw:function(){this._redrawRequest=null,this._redrawBounds&&(this._redrawBounds.min._floor(),this._redrawBounds.max._ceil()),this._clear(),this._draw(),this._redrawBounds=null},_clear:function(){var t=this._redrawBounds;if(t){var i=t.getSize();this._ctx.clearRect(t.min.x,t.min.y,i.x,i.y)}else this._ctx.clearRect(0,0,this._container.width,this._container.height)},_draw:function(){var t,i=this._redrawBounds;if(this._ctx.save(),i){var e=i.getSize();this._ctx.beginPath(),this._ctx.rect(i.min.x,i.min.y,e.x,e.y),this._ctx.clip()}this._drawing=!0;for(var n=this._drawFirst;n;n=n.next)t=n.layer,(!i||t._pxBounds&&t._pxBounds.intersects(i))&&t._updatePath();this._drawing=!1,this._ctx.restore()},_updatePoly:function(t,i){if(this._drawing){var e,n,o,s,r=t._parts,a=r.length,h=this._ctx;if(a){for(this._drawnLayers[t._leaflet_id]=t,h.beginPath(),e=0;e<a;e++){for(n=0,o=r[e].length;n<o;n++)s=r[e][n],h[n?"lineTo":"moveTo"](s.x,s.y);i&&h.closePath()}this._fillStroke(h,t)}}},_updateCircle:function(t){if(this._drawing&&!t._empty()){var i=t._point,e=this._ctx,n=Math.max(Math.round(t._radius),1),o=(Math.max(Math.round(t._radiusY),1)||n)/n;this._drawnLayers[t._leaflet_id]=t,1!==o&&(e.save(),e.scale(1,o)),e.beginPath(),e.arc(i.x,i.y/o,n,0,2*Math.PI,!1),1!==o&&e.restore(),this._fillStroke(e,t)}},_fillStroke:function(t,i){var e=i.options;e.fill&&(t.globalAlpha=e.fillOpacity,t.fillStyle=e.fillColor||e.color,t.fill(e.fillRule||"evenodd")),e.stroke&&0!==e.weight&&(t.setLineDash&&t.setLineDash(i.options&&i.options._dashArray||[]),t.globalAlpha=e.opacity,t.lineWidth=e.weight,t.strokeStyle=e.color,t.lineCap=e.lineCap,t.lineJoin=e.lineJoin,t.stroke())},_onClick:function(t){for(var i,e,n=this._map.mouseEventToLayerPoint(t),o=this._drawFirst;o;o=o.next)(i=o.layer).options.interactive&&i._containsPoint(n)&&!this._map._draggableMoved(i)&&(e=i);e&&(et(t),this._fireEvent([e],t))},_onMouseMove:function(t){if(this._map&&!this._map.dragging.moving()&&!this._map._animatingZoom){var i=this._map.mouseEventToLayerPoint(t);this._handleMouseHover(t,i)}},_handleMouseOut:function(t){var i=this._hoveredLayer;i&&(mt(this._container,"leaflet-interactive"),this._fireEvent([i],t,"mouseout"),this._hoveredLayer=null)},_handleMouseHover:function(t,i){for(var e,n,o=this._drawFirst;o;o=o.next)(e=o.layer).options.interactive&&e._containsPoint(i)&&(n=e);n!==this._hoveredLayer&&(this._handleMouseOut(t),n&&(pt(this._container,"leaflet-interactive"),this._fireEvent([n],t,"mouseover"),this._hoveredLayer=n)),this._hoveredLayer&&this._fireEvent([this._hoveredLayer],t)},_fireEvent:function(t,i,e){this._map._fireDOMEvent(i,e||i.type,t)},_bringToFront:function(t){var i=t._order,e=i.next,n=i.prev;e&&(e.prev=n,n?n.next=e:e&&(this._drawFirst=e),i.prev=this._drawLast,this._drawLast.next=i,i.next=null,this._drawLast=i,this._requestRedraw(t))},_bringToBack:function(t){var i=t._order,e=i.next,n=i.prev;n&&(n.next=e,e?e.prev=n:n&&(this._drawLast=n),i.prev=null,i.next=this._drawFirst,this._drawFirst.prev=i,this._drawFirst=i,this._requestRedraw(t))}}),gn=function(){try{return document.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return document.createElement("<lvml:"+t+' class="lvml">')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),vn={_initContainer:function(){this._container=ht("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(mn.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=gn("shape");pt(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=gn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;ut(i),t.removeInteractiveTarget(i),delete this._layers[n(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=gn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=ei(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=gn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){ct(t._container)},_bringToBack:function(t){_t(t._container)}},yn=Ji?gn:E,xn=mn.extend({getEvents:function(){var t=mn.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=yn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=yn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){ut(this._container),q(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){mn.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),Lt(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=yn("path");t.options.className&&pt(i,t.options.className),t.options.interactive&&pt(i,"leaflet-interactive"),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){ut(t._path),t.removeInteractiveTarget(t._path),delete this._layers[n(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,k(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){ct(t._path)},_bringToBack:function(t){_t(t._path)}});Ji&&xn.include(vn),Le.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this.options.preferCanvas&&Xt()||Jt()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=xn&&Jt({pane:t})||fn&&Xt({pane:t}),this._paneRenderers[t]=i),i}});var wn=en.extend({initialize:function(t,i){en.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=z(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});xn.create=yn,xn.pointsToPath=k,nn.geometryToLayer=Wt,nn.coordsToLatLng=Ht,nn.coordsToLatLngs=Ft,nn.latLngToCoords=Ut,nn.latLngsToCoords=Vt,nn.getFeature=qt,nn.asFeature=Gt,Le.mergeOptions({boxZoom:!0});var Ln=Ze.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){V(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){q(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){ut(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),mi(),bt(),this._startPoint=this._map.mouseEventToContainerPoint(t),V(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=ht("div","leaflet-zoom-box",this._container),pt(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new P(this._point,this._startPoint),e=i.getSize();Lt(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(ut(this._box),mt(this._container,"leaflet-crosshair")),fi(),Tt(),q(document,{contextmenu:Q,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(e(this._resetState,this),0);var i=new T(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});Le.addInitHook("addHandler","boxZoom",Ln),Le.mergeOptions({doubleClickZoom:!0});var Pn=Ze.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});Le.addInitHook("addHandler","doubleClickZoom",Pn),Le.mergeOptions({dragging:!0,inertia:!zi,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var bn=Ze.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new Be(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}pt(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){mt(this._map._container,"leaflet-grab"),mt(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=z(this._map.options.maxBounds);this._offsetLimit=b(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;this._positions.length>1&&t-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),i=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=i.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,i){return t-(t-i)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),i=this._offsetLimit;t.x<i.min.x&&(t.x=this._viscousLimit(t.x,i.min.x)),t.y<i.min.y&&(t.y=this._viscousLimit(t.y,i.min.y)),t.x>i.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)<Math.abs(s+e)?o:s;this._draggable._absPos=this._draggable._newPos.clone(),this._draggable._newPos.x=r},_onDragEnd:function(t){var i=this._map,e=i.options,n=!e.inertia||this._times.length<2;if(i.fire("dragend",t),n)i.fire("moveend");else{this._prunePositions(+new Date);var o=this._lastPos.subtract(this._positions[0]),s=(this._lastTime-this._times[0])/1e3,r=e.easeLinearity,a=o.multiplyBy(r/s),h=a.distanceTo([0,0]),u=Math.min(e.inertiaMaxSpeed,h),l=a.multiplyBy(u/h),c=u/(e.inertiaDeceleration*r),_=l.multiplyBy(-c/2).round();_.x||_.y?(_=i._limitOffset(_,i.options.maxBounds),f(function(){i.panBy(_,{duration:c,easeLinearity:r,noMoveStart:!0,animate:!0})})):i.fire("moveend")}}});Le.addInitHook("addHandler","dragging",bn),Le.mergeOptions({keyboard:!0,keyboardPanDelta:80});var Tn=Ze.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,54,173]},initialize:function(t){this._map=t,this._setPanDelta(t.options.keyboardPanDelta),this._setZoomDelta(t.options.zoomDelta)},addHooks:function(){var t=this._map._container;t.tabIndex<=0&&(t.tabIndex="0"),V(t,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.on({focus:this._addHooks,blur:this._removeHooks},this)},removeHooks:function(){this._removeHooks(),q(this._map._container,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.off({focus:this._addHooks,blur:this._removeHooks},this)},_onMouseDown:function(){if(!this._focused){var t=document.body,i=document.documentElement,e=t.scrollTop||i.scrollTop,n=t.scrollLeft||i.scrollLeft;this._map._container.focus(),window.scrollTo(n,e)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanDelta:function(t){var i,e,n=this._panKeys={},o=this.keyCodes;for(i=0,e=o.left.length;i<e;i++)n[o.left[i]]=[-1*t,0];for(i=0,e=o.right.length;i<e;i++)n[o.right[i]]=[t,0];for(i=0,e=o.down.length;i<e;i++)n[o.down[i]]=[0,t];for(i=0,e=o.up.length;i<e;i++)n[o.up[i]]=[0,-1*t]},_setZoomDelta:function(t){var i,e,n=this._zoomKeys={},o=this.keyCodes;for(i=0,e=o.zoomIn.length;i<e;i++)n[o.zoomIn[i]]=t;for(i=0,e=o.zoomOut.length;i<e;i++)n[o.zoomOut[i]]=-t},_addHooks:function(){V(document,"keydown",this._onKeyDown,this)},_removeHooks:function(){q(document,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){if(!(t.altKey||t.ctrlKey||t.metaKey)){var i,e=t.keyCode,n=this._map;if(e in this._panKeys){if(n._panAnim&&n._panAnim._inProgress)return;i=this._panKeys[e],t.shiftKey&&(i=w(i).multiplyBy(3)),n.panBy(i),n.options.maxBounds&&n.panInsideBounds(n.options.maxBounds)}else if(e in this._zoomKeys)n.setZoom(n.getZoom()+(t.shiftKey?3:1)*this._zoomKeys[e]);else{if(27!==e||!n._popup||!n._popup.options.closeOnEscapeKey)return;n.closePopup()}Q(t)}}});Le.addInitHook("addHandler","keyboard",Tn),Le.mergeOptions({scrollWheelZoom:!0,wheelDebounceTime:40,wheelPxPerZoomLevel:60});var zn=Ze.extend({addHooks:function(){V(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){q(this._map._container,"mousewheel",this._onWheelScroll,this)},_onWheelScroll:function(t){var i=it(t),n=this._map.options.wheelDebounceTime;this._delta+=i,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var o=Math.max(n-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(e(this._performZoom,this),o),Q(t)},_performZoom:function(){var t=this._map,i=t.getZoom(),e=this._map.options.zoomSnap||0;t._stop();var n=this._delta/(4*this._map.options.wheelPxPerZoomLevel),o=4*Math.log(2/(1+Math.exp(-Math.abs(n))))/Math.LN2,s=e?Math.ceil(o/e)*e:o,r=t._limitZoom(i+(this._delta>0?s:-s))-i;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(i+r):t.setZoomAround(this._lastMousePos,i+r))}});Le.addInitHook("addHandler","scrollWheelZoom",zn),Le.mergeOptions({tap:!0,tapTolerance:15});var Mn=Ze.extend({addHooks:function(){V(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){q(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if($(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new x(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&pt(n,"leaflet-active"),this._holdTimeout=setTimeout(e(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),V(document,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),q(document,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],e=i.target;e&&e.tagName&&"a"===e.tagName.toLowerCase()&&mt(e,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var i=t.touches[0];this._newPos=new x(i.clientX,i.clientY),this._simulateEvent("mousemove",i)},_simulateEvent:function(t,i){var e=document.createEvent("MouseEvents");e._simulated=!0,i.target._simulatedClick=!0,e.initMouseEvent(t,!0,!0,window,1,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),i.target.dispatchEvent(e)}});Vi&&!Ui&&Le.addInitHook("addHandler","tap",Mn),Le.mergeOptions({touchZoom:Vi&&!zi,bounceAtZoomLimits:!0});var Cn=Ze.extend({addHooks:function(){pt(this._map._container,"leaflet-touch-zoom"),V(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){mt(this._map._container,"leaflet-touch-zoom"),q(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var e=i.mouseEventToContainerPoint(t.touches[0]),n=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(e.add(n)._divideBy(2))),this._startDist=e.distanceTo(n),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),V(document,"touchmove",this._onTouchMove,this),V(document,"touchend",this._onTouchEnd,this),$(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var i=this._map,n=i.mouseEventToContainerPoint(t.touches[0]),o=i.mouseEventToContainerPoint(t.touches[1]),s=n.distanceTo(o)/this._startDist;if(this._zoom=i.getScaleZoom(s,this._startZoom),!i.options.bounceAtZoomLimits&&(this._zoom<i.getMinZoom()&&s<1||this._zoom>i.getMaxZoom()&&s>1)&&(this._zoom=i._limitZoom(this._zoom)),"center"===i.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=i.unproject(i.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(i._moveStart(!0,!1),this._moved=!0),g(this._animRequest);var a=e(i._move,i,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=f(a,this,!0),$(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,g(this._animRequest),q(document,"touchmove",this._onTouchMove),q(document,"touchend",this._onTouchEnd),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}});Le.addInitHook("addHandler","touchZoom",Cn),Le.BoxZoom=Ln,Le.DoubleClickZoom=Pn,Le.Drag=bn,Le.Keyboard=Tn,Le.ScrollWheelZoom=zn,Le.Tap=Mn,Le.TouchZoom=Cn;var Zn=window.L;window.L=t,Object.freeze=$t,t.version="1.3.1+HEAD.ba6f97f",t.noConflict=function(){return window.L=Zn,this},t.Control=Pe,t.control=be,t.Browser=$i,t.Evented=ui,t.Mixin=Ee,t.Util=ai,t.Class=v,t.Handler=Ze,t.extend=i,t.bind=e,t.stamp=n,t.setOptions=l,t.DomEvent=de,t.DomUtil=xe,t.PosAnimation=we,t.Draggable=Be,t.LineUtil=Oe,t.PolyUtil=Re,t.Point=x,t.point=w,t.Bounds=P,t.bounds=b,t.Transformation=Z,t.transformation=S,t.Projection=je,t.LatLng=M,t.latLng=C,t.LatLngBounds=T,t.latLngBounds=z,t.CRS=ci,t.GeoJSON=nn,t.geoJSON=Kt,t.geoJson=sn,t.Layer=Ue,t.LayerGroup=Ve,t.layerGroup=function(t,i){return new Ve(t,i)},t.FeatureGroup=qe,t.featureGroup=function(t){return new qe(t)},t.ImageOverlay=rn,t.imageOverlay=function(t,i,e){return new rn(t,i,e)},t.VideoOverlay=an,t.videoOverlay=function(t,i,e){return new an(t,i,e)},t.DivOverlay=hn,t.Popup=un,t.popup=function(t,i){return new un(t,i)},t.Tooltip=ln,t.tooltip=function(t,i){return new ln(t,i)},t.Icon=Ge,t.icon=function(t){return new Ge(t)},t.DivIcon=cn,t.divIcon=function(t){return new cn(t)},t.Marker=Xe,t.marker=function(t,i){return new Xe(t,i)},t.TileLayer=dn,t.tileLayer=Yt,t.GridLayer=_n,t.gridLayer=function(t){return new _n(t)},t.SVG=xn,t.svg=Jt,t.Renderer=mn,t.Canvas=fn,t.canvas=Xt,t.Path=Je,t.CircleMarker=$e,t.circleMarker=function(t,i){return new $e(t,i)},t.Circle=Qe,t.circle=function(t,i,e){return new Qe(t,i,e)},t.Polyline=tn,t.polyline=function(t,i){return new tn(t,i)},t.Polygon=en,t.polygon=function(t,i){return new en(t,i)},t.Rectangle=wn,t.rectangle=function(t,i){return new wn(t,i)},t.Map=Le,t.map=function(t,i){return new Le(t,i)}});
4 !function(t,i){"object"==typeof exports&&"undefined"!=typeof module?i(exports):"function"==typeof define&&define.amd?define(["exports"],i):i(t.L={})}(this,function(t){"use strict";function i(t){var i,e,n,o;for(e=1,n=arguments.length;e<n;e++){o=arguments[e];for(i in o)t[i]=o[i]}return t}function e(t,i){var e=Array.prototype.slice;if(t.bind)return t.bind.apply(t,e.call(arguments,1));var n=e.call(arguments,2);return function(){return t.apply(i,n.length?n.concat(e.call(arguments)):arguments)}}function n(t){return t._leaflet_id=t._leaflet_id||++ei,t._leaflet_id}function o(t,i,e){var n,o,s,r;return r=function(){n=!1,o&&(s.apply(e,o),o=!1)},s=function(){n?o=arguments:(t.apply(e,arguments),setTimeout(r,i),n=!0)}}function s(t,i,e){var n=i[1],o=i[0],s=n-o;return t===n&&e?t:((t-o)%s+s)%s+o}function r(){return!1}function a(t,i){var e=Math.pow(10,void 0===i?6:i);return Math.round(t*e)/e}function h(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")}function u(t){return h(t).split(/\s+/)}function l(t,i){t.hasOwnProperty("options")||(t.options=t.options?ii(t.options):{});for(var e in i)t.options[e]=i[e];return t.options}function c(t,i,e){var n=[];for(var o in t)n.push(encodeURIComponent(e?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(i&&-1!==i.indexOf("?")?"&":"?")+n.join("&")}function _(t,i){return t.replace(ni,function(t,e){var n=i[e];if(void 0===n)throw new Error("No value provided for variable "+t);return"function"==typeof n&&(n=n(i)),n})}function d(t,i){for(var e=0;e<t.length;e++)if(t[e]===i)return e;return-1}function p(t){return window["webkit"+t]||window["moz"+t]||window["ms"+t]}function m(t){var i=+new Date,e=Math.max(0,16-(i-ri));return ri=i+e,window.setTimeout(t,e)}function f(t,i,n){if(!n||ai!==m)return ai.call(window,e(t,i));t.call(i)}function g(t){t&&hi.call(window,t)}function v(){}function y(t){if("undefined"!=typeof L&&L&&L.Mixin){t=oi(t)?t:[t];for(var i=0;i<t.length;i++)t[i]===L.Mixin.Events&&console.warn("Deprecated include of L.Mixin.Events: this property will be removed in future releases, please inherit from L.Evented instead.",(new Error).stack)}}function x(t,i,e){this.x=e?Math.round(t):t,this.y=e?Math.round(i):i}function w(t,i,e){return t instanceof x?t:oi(t)?new x(t[0],t[1]):void 0===t||null===t?t:"object"==typeof t&&"x"in t&&"y"in t?new x(t.x,t.y):new x(t,i,e)}function P(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function b(t,i){return!t||t instanceof P?t:new P(t,i)}function T(t,i){if(t)for(var e=i?[t,i]:t,n=0,o=e.length;n<o;n++)this.extend(e[n])}function z(t,i){return t instanceof T?t:new T(t,i)}function M(t,i,e){if(isNaN(t)||isNaN(i))throw new Error("Invalid LatLng object: ("+t+", "+i+")");this.lat=+t,this.lng=+i,void 0!==e&&(this.alt=+e)}function C(t,i,e){return t instanceof M?t:oi(t)&&"object"!=typeof t[0]?3===t.length?new M(t[0],t[1],t[2]):2===t.length?new M(t[0],t[1]):null:void 0===t||null===t?t:"object"==typeof t&&"lat"in t?new M(t.lat,"lng"in t?t.lng:t.lon,t.alt):void 0===i?null:new M(t,i,e)}function S(t,i,e,n){if(oi(t))return this._a=t[0],this._b=t[1],this._c=t[2],void(this._d=t[3]);this._a=t,this._b=i,this._c=e,this._d=n}function Z(t,i,e,n){return new S(t,i,e,n)}function E(t){return document.createElementNS("http://www.w3.org/2000/svg",t)}function k(t,i){var e,n,o,s,r,a,h="";for(e=0,o=t.length;e<o;e++){for(n=0,s=(r=t[e]).length;n<s;n++)a=r[n],h+=(n?"L":"M")+a.x+" "+a.y;h+=i?Ji?"z":"x":""}return h||"M0 0"}function B(t){return navigator.userAgent.toLowerCase().indexOf(t)>=0}function A(t,i,e,n){return"touchstart"===i?O(t,e,n):"touchmove"===i?W(t,e,n):"touchend"===i&&H(t,e,n),this}function I(t,i,e){var n=t["_leaflet_"+i+e];return"touchstart"===i?t.removeEventListener(te,n,!1):"touchmove"===i?t.removeEventListener(ie,n,!1):"touchend"===i&&(t.removeEventListener(ee,n,!1),t.removeEventListener(ne,n,!1)),this}function O(t,i,n){var o=e(function(t){if("mouse"!==t.pointerType&&t.MSPOINTER_TYPE_MOUSE&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE){if(!(oe.indexOf(t.target.tagName)<0))return;Pt(t)}j(t,i)});t["_leaflet_touchstart"+n]=o,t.addEventListener(te,o,!1),re||(document.documentElement.addEventListener(te,R,!0),document.documentElement.addEventListener(ie,N,!0),document.documentElement.addEventListener(ee,D,!0),document.documentElement.addEventListener(ne,D,!0),re=!0)}function R(t){se[t.pointerId]=t,ae++}function N(t){se[t.pointerId]&&(se[t.pointerId]=t)}function D(t){delete se[t.pointerId],ae--}function j(t,i){t.touches=[];for(var e in se)t.touches.push(se[e]);t.changedTouches=[t],i(t)}function W(t,i,e){var n=function(t){(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons)&&j(t,i)};t["_leaflet_touchmove"+e]=n,t.addEventListener(ie,n,!1)}function H(t,i,e){var n=function(t){j(t,i)};t["_leaflet_touchend"+e]=n,t.addEventListener(ee,n,!1),t.addEventListener(ne,n,!1)}function F(t,i,e){function n(t){var i;if(Vi){if(!bi||"mouse"===t.pointerType)return;i=ae}else i=t.touches.length;if(!(i>1)){var e=Date.now(),n=e-(s||e);r=t.touches?t.touches[0]:t,a=n>0&&n<=h,s=e}}function o(t){if(a&&!r.cancelBubble){if(Vi){if(!bi||"mouse"===t.pointerType)return;var e,n,o={};for(n in r)e=r[n],o[n]=e&&e.bind?e.bind(r):e;r=o}r.type="dblclick",i(r),s=null}}var s,r,a=!1,h=250;return t[le+he+e]=n,t[le+ue+e]=o,t[le+"dblclick"+e]=i,t.addEventListener(he,n,!1),t.addEventListener(ue,o,!1),t.addEventListener("dblclick",i,!1),this}function U(t,i){var e=t[le+he+i],n=t[le+ue+i],o=t[le+"dblclick"+i];return t.removeEventListener(he,e,!1),t.removeEventListener(ue,n,!1),bi||t.removeEventListener("dblclick",o,!1),this}function V(t){return"string"==typeof t?document.getElementById(t):t}function q(t,i){var e=t.style[i]||t.currentStyle&&t.currentStyle[i];if((!e||"auto"===e)&&document.defaultView){var n=document.defaultView.getComputedStyle(t,null);e=n?n[i]:null}return"auto"===e?null:e}function G(t,i,e){var n=document.createElement(t);return n.className=i||"",e&&e.appendChild(n),n}function K(t){var i=t.parentNode;i&&i.removeChild(t)}function Y(t){for(;t.firstChild;)t.removeChild(t.firstChild)}function X(t){var i=t.parentNode;i&&i.lastChild!==t&&i.appendChild(t)}function J(t){var i=t.parentNode;i&&i.firstChild!==t&&i.insertBefore(t,i.firstChild)}function $(t,i){if(void 0!==t.classList)return t.classList.contains(i);var e=et(t);return e.length>0&&new RegExp("(^|\\s)"+i+"(\\s|$)").test(e)}function Q(t,i){if(void 0!==t.classList)for(var e=u(i),n=0,o=e.length;n<o;n++)t.classList.add(e[n]);else if(!$(t,i)){var s=et(t);it(t,(s?s+" ":"")+i)}}function tt(t,i){void 0!==t.classList?t.classList.remove(i):it(t,h((" "+et(t)+" ").replace(" "+i+" "," ")))}function it(t,i){void 0===t.className.baseVal?t.className=i:t.className.baseVal=i}function et(t){return t.correspondingElement&&(t=t.correspondingElement),void 0===t.className.baseVal?t.className:t.className.baseVal}function nt(t,i){"opacity"in t.style?t.style.opacity=i:"filter"in t.style&&ot(t,i)}function ot(t,i){var e=!1,n="DXImageTransform.Microsoft.Alpha";try{e=t.filters.item(n)}catch(t){if(1===i)return}i=Math.round(100*i),e?(e.Enabled=100!==i,e.Opacity=i):t.style.filter+=" progid:"+n+"(opacity="+i+")"}function st(t){for(var i=document.documentElement.style,e=0;e<t.length;e++)if(t[e]in i)return t[e];return!1}function rt(t,i,e){var n=i||new x(0,0);t.style[ce]=(Ri?"translate("+n.x+"px,"+n.y+"px)":"translate3d("+n.x+"px,"+n.y+"px,0)")+(e?" scale("+e+")":"")}function at(t,i){t._leaflet_pos=i,ji?rt(t,i):(t.style.left=i.x+"px",t.style.top=i.y+"px")}function ht(t){return t._leaflet_pos||new x(0,0)}function ut(){mt(window,"dragstart",Pt)}function lt(){ft(window,"dragstart",Pt)}function ct(t){for(;-1===t.tabIndex;)t=t.parentNode;t.style&&(_t(),me=t,fe=t.style.outline,t.style.outline="none",mt(window,"keydown",_t))}function _t(){me&&(me.style.outline=fe,me=void 0,fe=void 0,ft(window,"keydown",_t))}function dt(t){do{t=t.parentNode}while(!(t.offsetWidth&&t.offsetHeight||t===document.body));return t}function pt(t){var i=t.getBoundingClientRect();return{x:i.width/t.offsetWidth||1,y:i.height/t.offsetHeight||1,boundingClientRect:i}}function mt(t,i,e,n){if("object"==typeof i)for(var o in i)gt(t,o,i[o],e);else for(var s=0,r=(i=u(i)).length;s<r;s++)gt(t,i[s],e,n);return this}function ft(t,i,e,n){if("object"==typeof i)for(var o in i)vt(t,o,i[o],e);else if(i)for(var s=0,r=(i=u(i)).length;s<r;s++)vt(t,i[s],e,n);else{for(var a in t[ye])vt(t,a,t[ye][a]);delete t[ye]}return this}function gt(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):"");if(t[ye]&&t[ye][s])return this;var r=function(i){return e.call(o||t,i||window.event)},a=r;Vi&&0===i.indexOf("touch")?A(t,i,r,s):!qi||"dblclick"!==i||!F||Vi&&Ei?"addEventListener"in t?"mousewheel"===i?t.addEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):"mouseenter"===i||"mouseleave"===i?(r=function(i){i=i||window.event,Ct(t,i)&&a(i)},t.addEventListener("mouseenter"===i?"mouseover":"mouseout",r,!1)):("click"===i&&zi&&(r=function(t){St(t,a)}),t.addEventListener(i,r,!1)):"attachEvent"in t&&t.attachEvent("on"+i,r):F(t,r,s),t[ye]=t[ye]||{},t[ye][s]=r}function vt(t,i,e,o){var s=i+n(e)+(o?"_"+n(o):""),r=t[ye]&&t[ye][s];if(!r)return this;Vi&&0===i.indexOf("touch")?I(t,i,s):!qi||"dblclick"!==i||!U||Vi&&Ei?"removeEventListener"in t?"mousewheel"===i?t.removeEventListener("onwheel"in t?"wheel":"mousewheel",r,!1):t.removeEventListener("mouseenter"===i?"mouseover":"mouseleave"===i?"mouseout":i,r,!1):"detachEvent"in t&&t.detachEvent("on"+i,r):U(t,s),t[ye][s]=null}function yt(t){return t.stopPropagation?t.stopPropagation():t.originalEvent?t.originalEvent._stopped=!0:t.cancelBubble=!0,Mt(t),this}function xt(t){return gt(t,"mousewheel",yt),this}function wt(t){return mt(t,"mousedown touchstart dblclick",yt),gt(t,"click",zt),this}function Pt(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this}function Lt(t){return Pt(t),yt(t),this}function bt(t,i){if(!i)return new x(t.clientX,t.clientY);var e=pt(i),n=e.boundingClientRect;return new x((t.clientX-n.left)/e.x-i.clientLeft,(t.clientY-n.top)/e.y-i.clientTop)}function Tt(t){return bi?t.wheelDeltaY/2:t.deltaY&&0===t.deltaMode?-t.deltaY/xe:t.deltaY&&1===t.deltaMode?20*-t.deltaY:t.deltaY&&2===t.deltaMode?60*-t.deltaY:t.deltaX||t.deltaZ?0:t.wheelDelta?(t.wheelDeltaY||t.wheelDelta)/2:t.detail&&Math.abs(t.detail)<32765?20*-t.detail:t.detail?t.detail/-32765*60:0}function zt(t){we[t.type]=!0}function Mt(t){var i=we[t.type];return we[t.type]=!1,i}function Ct(t,i){var e=i.relatedTarget;if(!e)return!0;try{for(;e&&e!==t;)e=e.parentNode}catch(t){return!1}return e!==t}function St(t,i){var e=t.timeStamp||t.originalEvent&&t.originalEvent.timeStamp,n=ge&&e-ge;n&&n>100&&n<500||t.target._simulatedClick&&!t._simulated?Lt(t):(ge=e,i(t))}function Zt(t,i){if(!i||!t.length)return t.slice();var e=i*i;return t=At(t,e),t=kt(t,e)}function Et(t,i,e){return Math.sqrt(Dt(t,i,e,!0))}function kt(t,i){var e=t.length,n=new(typeof Uint8Array!=void 0+""?Uint8Array:Array)(e);n[0]=n[e-1]=1,Bt(t,n,i,0,e-1);var o,s=[];for(o=0;o<e;o++)n[o]&&s.push(t[o]);return s}function Bt(t,i,e,n,o){var s,r,a,h=0;for(r=n+1;r<=o-1;r++)(a=Dt(t[r],t[n],t[o],!0))>h&&(s=r,h=a);h>e&&(i[s]=1,Bt(t,i,e,n,s),Bt(t,i,e,s,o))}function At(t,i){for(var e=[t[0]],n=1,o=0,s=t.length;n<s;n++)Nt(t[n],t[o])>i&&(e.push(t[n]),o=n);return o<s-1&&e.push(t[s-1]),e}function It(t,i,e,n,o){var s,r,a,h=n?ke:Rt(t,e),u=Rt(i,e);for(ke=u;;){if(!(h|u))return[t,i];if(h&u)return!1;a=Rt(r=Ot(t,i,s=h||u,e,o),e),s===h?(t=r,h=a):(i=r,u=a)}}function Ot(t,i,e,n,o){var s,r,a=i.x-t.x,h=i.y-t.y,u=n.min,l=n.max;return 8&e?(s=t.x+a*(l.y-t.y)/h,r=l.y):4&e?(s=t.x+a*(u.y-t.y)/h,r=u.y):2&e?(s=l.x,r=t.y+h*(l.x-t.x)/a):1&e&&(s=u.x,r=t.y+h*(u.x-t.x)/a),new x(s,r,o)}function Rt(t,i){var e=0;return t.x<i.min.x?e|=1:t.x>i.max.x&&(e|=2),t.y<i.min.y?e|=4:t.y>i.max.y&&(e|=8),e}function Nt(t,i){var e=i.x-t.x,n=i.y-t.y;return e*e+n*n}function Dt(t,i,e,n){var o,s=i.x,r=i.y,a=e.x-s,h=e.y-r,u=a*a+h*h;return u>0&&((o=((t.x-s)*a+(t.y-r)*h)/u)>1?(s=e.x,r=e.y):o>0&&(s+=a*o,r+=h*o)),a=t.x-s,h=t.y-r,n?a*a+h*h:new x(s,r)}function jt(t){return!oi(t[0])||"object"!=typeof t[0][0]&&void 0!==t[0][0]}function Wt(t){return console.warn("Deprecated use of _flat, please use L.LineUtil.isFlat instead."),jt(t)}function Ht(t,i,e){var n,o,s,r,a,h,u,l,c,_=[1,4,2,8];for(o=0,u=t.length;o<u;o++)t[o]._code=Rt(t[o],i);for(r=0;r<4;r++){for(l=_[r],n=[],o=0,s=(u=t.length)-1;o<u;s=o++)a=t[o],h=t[s],a._code&l?h._code&l||((c=Ot(h,a,l,i,e))._code=Rt(c,i),n.push(c)):(h._code&l&&((c=Ot(h,a,l,i,e))._code=Rt(c,i),n.push(c)),n.push(a));t=n}return t}function Ft(t,i){var e,n,o,s,r="Feature"===t.type?t.geometry:t,a=r?r.coordinates:null,h=[],u=i&&i.pointToLayer,l=i&&i.coordsToLatLng||Ut;if(!a&&!r)return null;switch(r.type){case"Point":return e=l(a),u?u(t,e):new $e(e);case"MultiPoint":for(o=0,s=a.length;o<s;o++)e=l(a[o]),h.push(u?u(t,e):new $e(e));return new Ke(h);case"LineString":case"MultiLineString":return n=Vt(a,"LineString"===r.type?0:1,l),new nn(n,i);case"Polygon":case"MultiPolygon":return n=Vt(a,"Polygon"===r.type?1:2,l),new on(n,i);case"GeometryCollection":for(o=0,s=r.geometries.length;o<s;o++){var c=Ft({geometry:r.geometries[o],type:"Feature",properties:t.properties},i);c&&h.push(c)}return new Ke(h);default:throw new Error("Invalid GeoJSON object.")}}function Ut(t){return new M(t[1],t[0],t[2])}function Vt(t,i,e){for(var n,o=[],s=0,r=t.length;s<r;s++)n=i?Vt(t[s],i-1,e):(e||Ut)(t[s]),o.push(n);return o}function qt(t,i){return i="number"==typeof i?i:6,void 0!==t.alt?[a(t.lng,i),a(t.lat,i),a(t.alt,i)]:[a(t.lng,i),a(t.lat,i)]}function Gt(t,i,e,n){for(var o=[],s=0,r=t.length;s<r;s++)o.push(i?Gt(t[s],i-1,e,n):qt(t[s],n));return!i&&e&&o.push(o[0]),o}function Kt(t,e){return t.feature?i({},t.feature,{geometry:e}):Yt(e)}function Yt(t){return"Feature"===t.type||"FeatureCollection"===t.type?t:{type:"Feature",properties:{},geometry:t}}function Xt(t,i){return new sn(t,i)}function Jt(t,i){return new mn(t,i)}function $t(t){return Xi?new vn(t):null}function Qt(t){return Ji||$i?new Pn(t):null}var ti=Object.freeze;Object.freeze=function(t){return t};var ii=Object.create||function(){function t(){}return function(i){return t.prototype=i,new t}}(),ei=0,ni=/\{ *([\w_-]+) *\}/g,oi=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},si="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=",ri=0,ai=window.requestAnimationFrame||p("RequestAnimationFrame")||m,hi=window.cancelAnimationFrame||p("CancelAnimationFrame")||p("CancelRequestAnimationFrame")||function(t){window.clearTimeout(t)},ui=(Object.freeze||Object)({freeze:ti,extend:i,create:ii,bind:e,lastId:ei,stamp:n,throttle:o,wrapNum:s,falseFn:r,formatNum:a,trim:h,splitWords:u,setOptions:l,getParamString:c,template:_,isArray:oi,indexOf:d,emptyImageUrl:si,requestFn:ai,cancelFn:hi,requestAnimFrame:f,cancelAnimFrame:g});v.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this.callInitHooks()},n=e.__super__=this.prototype,o=ii(n);o.constructor=e,e.prototype=o;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&"__super__"!==s&&(e[s]=this[s]);return t.statics&&(i(e,t.statics),delete t.statics),t.includes&&(y(t.includes),i.apply(null,[o].concat(t.includes)),delete t.includes),o.options&&(t.options=i(ii(o.options),t.options)),i(o,t),o._initHooks=[],o.callInitHooks=function(){if(!this._initHooksCalled){n.callInitHooks&&n.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,i=o._initHooks.length;t<i;t++)o._initHooks[t].call(this)}},e},v.include=function(t){return i(this.prototype,t),this},v.mergeOptions=function(t){return i(this.prototype.options,t),this},v.addInitHook=function(t){var i=Array.prototype.slice.call(arguments,1),e="function"==typeof t?t:function(){this[t].apply(this,i)};return this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(e),this};var li={on:function(t,i,e){if("object"==typeof t)for(var n in t)this._on(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._on(t[o],i,e);return this},off:function(t,i,e){if(t)if("object"==typeof t)for(var n in t)this._off(n,t[n],i);else for(var o=0,s=(t=u(t)).length;o<s;o++)this._off(t[o],i,e);else delete this._events;return this},_on:function(t,i,e){this._events=this._events||{};var n=this._events[t];n||(n=[],this._events[t]=n),e===this&&(e=void 0);for(var o={fn:i,ctx:e},s=n,r=0,a=s.length;r<a;r++)if(s[r].fn===i&&s[r].ctx===e)return;s.push(o)},_off:function(t,i,e){var n,o,s;if(this._events&&(n=this._events[t]))if(i){if(e===this&&(e=void 0),n)for(o=0,s=n.length;o<s;o++){var a=n[o];if(a.ctx===e&&a.fn===i)return a.fn=r,this._firingCount&&(this._events[t]=n=n.slice()),void n.splice(o,1)}}else{for(o=0,s=n.length;o<s;o++)n[o].fn=r;delete this._events[t]}},fire:function(t,e,n){if(!this.listens(t,n))return this;var o=i({},e,{type:t,target:this,sourceTarget:e&&e.sourceTarget||this});if(this._events){var s=this._events[t];if(s){this._firingCount=this._firingCount+1||1;for(var r=0,a=s.length;r<a;r++){var h=s[r];h.fn.call(h.ctx||this,o)}this._firingCount--}}return n&&this._propagateEvent(o),this},listens:function(t,i){var e=this._events&&this._events[t];if(e&&e.length)return!0;if(i)for(var n in this._eventParents)if(this._eventParents[n].listens(t,i))return!0;return!1},once:function(t,i,n){if("object"==typeof t){for(var o in t)this.once(o,t[o],i);return this}var s=e(function(){this.off(t,i,n).off(t,s,n)},this);return this.on(t,i,n).on(t,s,n)},addEventParent:function(t){return this._eventParents=this._eventParents||{},this._eventParents[n(t)]=t,this},removeEventParent:function(t){return this._eventParents&&delete this._eventParents[n(t)],this},_propagateEvent:function(t){for(var e in this._eventParents)this._eventParents[e].fire(t.type,i({layer:t.target,propagatedFrom:t.target},t),!0)}};li.addEventListener=li.on,li.removeEventListener=li.clearAllEventListeners=li.off,li.addOneTimeEventListener=li.once,li.fireEvent=li.fire,li.hasEventListeners=li.listens;var ci=v.extend(li),_i=Math.trunc||function(t){return t>0?Math.floor(t):Math.ceil(t)};x.prototype={clone:function(){return new x(this.x,this.y)},add:function(t){return this.clone()._add(w(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(w(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},scaleBy:function(t){return new x(this.x*t.x,this.y*t.y)},unscaleBy:function(t){return new x(this.x/t.x,this.y/t.y)},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},ceil:function(){return this.clone()._ceil()},_ceil:function(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this},trunc:function(){return this.clone()._trunc()},_trunc:function(){return this.x=_i(this.x),this.y=_i(this.y),this},distanceTo:function(t){var i=(t=w(t)).x-this.x,e=t.y-this.y;return Math.sqrt(i*i+e*e)},equals:function(t){return(t=w(t)).x===this.x&&t.y===this.y},contains:function(t){return t=w(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+a(this.x)+", "+a(this.y)+")"}},P.prototype={extend:function(t){return t=w(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new x((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new x(this.min.x,this.max.y)},getTopRight:function(){return new x(this.max.x,this.min.y)},getTopLeft:function(){return this.min},getBottomRight:function(){return this.max},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var i,e;return(t="number"==typeof t[0]||t instanceof x?w(t):b(t))instanceof P?(i=t.min,e=t.max):i=e=t,i.x>=this.min.x&&e.x<=this.max.x&&i.y>=this.min.y&&e.y<=this.max.y},intersects:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>=i.x&&n.x<=e.x,r=o.y>=i.y&&n.y<=e.y;return s&&r},overlaps:function(t){t=b(t);var i=this.min,e=this.max,n=t.min,o=t.max,s=o.x>i.x&&n.x<e.x,r=o.y>i.y&&n.y<e.y;return s&&r},isValid:function(){return!(!this.min||!this.max)}},T.prototype={extend:function(t){var i,e,n=this._southWest,o=this._northEast;if(t instanceof M)i=t,e=t;else{if(!(t instanceof T))return t?this.extend(C(t)||z(t)):this;if(i=t._southWest,e=t._northEast,!i||!e)return this}return n||o?(n.lat=Math.min(i.lat,n.lat),n.lng=Math.min(i.lng,n.lng),o.lat=Math.max(e.lat,o.lat),o.lng=Math.max(e.lng,o.lng)):(this._southWest=new M(i.lat,i.lng),this._northEast=new M(e.lat,e.lng)),this},pad:function(t){var i=this._southWest,e=this._northEast,n=Math.abs(i.lat-e.lat)*t,o=Math.abs(i.lng-e.lng)*t;return new T(new M(i.lat-n,i.lng-o),new M(e.lat+n,e.lng+o))},getCenter:function(){return new M((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new M(this.getNorth(),this.getWest())},getSouthEast:function(){return new M(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof M||"lat"in t?C(t):z(t);var i,e,n=this._southWest,o=this._northEast;return t instanceof T?(i=t.getSouthWest(),e=t.getNorthEast()):i=e=t,i.lat>=n.lat&&e.lat<=o.lat&&i.lng>=n.lng&&e.lng<=o.lng},intersects:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>=i.lat&&n.lat<=e.lat,r=o.lng>=i.lng&&n.lng<=e.lng;return s&&r},overlaps:function(t){t=z(t);var i=this._southWest,e=this._northEast,n=t.getSouthWest(),o=t.getNorthEast(),s=o.lat>i.lat&&n.lat<e.lat,r=o.lng>i.lng&&n.lng<e.lng;return s&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t,i){return!!t&&(t=z(t),this._southWest.equals(t.getSouthWest(),i)&&this._northEast.equals(t.getNorthEast(),i))},isValid:function(){return!(!this._southWest||!this._northEast)}},M.prototype={equals:function(t,i){return!!t&&(t=C(t),Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng))<=(void 0===i?1e-9:i))},toString:function(t){return"LatLng("+a(this.lat,t)+", "+a(this.lng,t)+")"},distanceTo:function(t){return pi.distance(this,C(t))},wrap:function(){return pi.wrapLatLng(this)},toBounds:function(t){var i=180*t/40075017,e=i/Math.cos(Math.PI/180*this.lat);return z([this.lat-i,this.lng-e],[this.lat+i,this.lng+e])},clone:function(){return new M(this.lat,this.lng,this.alt)}};var di={latLngToPoint:function(t,i){var e=this.projection.project(t),n=this.scale(i);return this.transformation._transform(e,n)},pointToLatLng:function(t,i){var e=this.scale(i),n=this.transformation.untransform(t,e);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},unproject:function(t){return this.projection.unproject(t)},scale:function(t){return 256*Math.pow(2,t)},zoom:function(t){return Math.log(t/256)/Math.LN2},getProjectedBounds:function(t){if(this.infinite)return null;var i=this.projection.bounds,e=this.scale(t);return new P(this.transformation.transform(i.min,e),this.transformation.transform(i.max,e))},infinite:!1,wrapLatLng:function(t){var i=this.wrapLng?s(t.lng,this.wrapLng,!0):t.lng;return new M(this.wrapLat?s(t.lat,this.wrapLat,!0):t.lat,i,t.alt)},wrapLatLngBounds:function(t){var i=t.getCenter(),e=this.wrapLatLng(i),n=i.lat-e.lat,o=i.lng-e.lng;if(0===n&&0===o)return t;var s=t.getSouthWest(),r=t.getNorthEast();return new T(new M(s.lat-n,s.lng-o),new M(r.lat-n,r.lng-o))}},pi=i({},di,{wrapLng:[-180,180],R:6371e3,distance:function(t,i){var e=Math.PI/180,n=t.lat*e,o=i.lat*e,s=Math.sin((i.lat-t.lat)*e/2),r=Math.sin((i.lng-t.lng)*e/2),a=s*s+Math.cos(n)*Math.cos(o)*r*r,h=2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a));return this.R*h}}),mi={R:6378137,MAX_LATITUDE:85.0511287798,project:function(t){var i=Math.PI/180,e=this.MAX_LATITUDE,n=Math.max(Math.min(e,t.lat),-e),o=Math.sin(n*i);return new x(this.R*t.lng*i,this.R*Math.log((1+o)/(1-o))/2)},unproject:function(t){var i=180/Math.PI;return new M((2*Math.atan(Math.exp(t.y/this.R))-Math.PI/2)*i,t.x*i/this.R)},bounds:function(){var t=6378137*Math.PI;return new P([-t,-t],[t,t])}()};S.prototype={transform:function(t,i){return this._transform(t.clone(),i)},_transform:function(t,i){return i=i||1,t.x=i*(this._a*t.x+this._b),t.y=i*(this._c*t.y+this._d),t},untransform:function(t,i){return i=i||1,new x((t.x/i-this._b)/this._a,(t.y/i-this._d)/this._c)}};var fi,gi,vi,yi=i({},pi,{code:"EPSG:3857",projection:mi,transformation:function(){var t=.5/(Math.PI*mi.R);return Z(t,.5,-t,.5)}()}),xi=i({},yi,{code:"EPSG:900913"}),wi=document.documentElement.style,Pi="ActiveXObject"in window,Li=Pi&&!document.addEventListener,bi="msLaunchUri"in navigator&&!("documentMode"in document),Ti=B("webkit"),zi=B("android"),Mi=B("android 2")||B("android 3"),Ci=parseInt(/WebKit\/([0-9]+)|$/.exec(navigator.userAgent)[1],10),Si=zi&&B("Google")&&Ci<537&&!("AudioNode"in window),Zi=!!window.opera,Ei=B("chrome"),ki=B("gecko")&&!Ti&&!Zi&&!Pi,Bi=!Ei&&B("safari"),Ai=B("phantom"),Ii="OTransition"in wi,Oi=0===navigator.platform.indexOf("Win"),Ri=Pi&&"transition"in wi,Ni="WebKitCSSMatrix"in window&&"m11"in new window.WebKitCSSMatrix&&!Mi,Di="MozPerspective"in wi,ji=!window.L_DISABLE_3D&&(Ri||Ni||Di)&&!Ii&&!Ai,Wi="undefined"!=typeof orientation||B("mobile"),Hi=Wi&&Ti,Fi=Wi&&Ni,Ui=!window.PointerEvent&&window.MSPointerEvent,Vi=!(!window.PointerEvent&&!Ui),qi=!window.L_NO_TOUCH&&(Vi||"ontouchstart"in window||window.DocumentTouch&&document instanceof window.DocumentTouch),Gi=Wi&&Zi,Ki=Wi&&ki,Yi=(window.devicePixelRatio||window.screen.deviceXDPI/window.screen.logicalXDPI)>1,Xi=!!document.createElement("canvas").getContext,Ji=!(!document.createElementNS||!E("svg").createSVGRect),$i=!Ji&&function(){try{var t=document.createElement("div");t.innerHTML='<v:shape adj="1"/>';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(t){return!1}}(),Qi=(Object.freeze||Object)({ie:Pi,ielt9:Li,edge:bi,webkit:Ti,android:zi,android23:Mi,androidStock:Si,opera:Zi,chrome:Ei,gecko:ki,safari:Bi,phantom:Ai,opera12:Ii,win:Oi,ie3d:Ri,webkit3d:Ni,gecko3d:Di,any3d:ji,mobile:Wi,mobileWebkit:Hi,mobileWebkit3d:Fi,msPointer:Ui,pointer:Vi,touch:qi,mobileOpera:Gi,mobileGecko:Ki,retina:Yi,canvas:Xi,svg:Ji,vml:$i}),te=Ui?"MSPointerDown":"pointerdown",ie=Ui?"MSPointerMove":"pointermove",ee=Ui?"MSPointerUp":"pointerup",ne=Ui?"MSPointerCancel":"pointercancel",oe=["INPUT","SELECT","OPTION"],se={},re=!1,ae=0,he=Ui?"MSPointerDown":Vi?"pointerdown":"touchstart",ue=Ui?"MSPointerUp":Vi?"pointerup":"touchend",le="_leaflet_",ce=st(["transform","webkitTransform","OTransform","MozTransform","msTransform"]),_e=st(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),de="webkitTransition"===_e||"OTransition"===_e?_e+"End":"transitionend";if("onselectstart"in document)fi=function(){mt(window,"selectstart",Pt)},gi=function(){ft(window,"selectstart",Pt)};else{var pe=st(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);fi=function(){if(pe){var t=document.documentElement.style;vi=t[pe],t[pe]="none"}},gi=function(){pe&&(document.documentElement.style[pe]=vi,vi=void 0)}}var me,fe,ge,ve=(Object.freeze||Object)({TRANSFORM:ce,TRANSITION:_e,TRANSITION_END:de,get:V,getStyle:q,create:G,remove:K,empty:Y,toFront:X,toBack:J,hasClass:$,addClass:Q,removeClass:tt,setClass:it,getClass:et,setOpacity:nt,testProp:st,setTransform:rt,setPosition:at,getPosition:ht,disableTextSelection:fi,enableTextSelection:gi,disableImageDrag:ut,enableImageDrag:lt,preventOutline:ct,restoreOutline:_t,getSizedParentNode:dt,getScale:pt}),ye="_leaflet_events",xe=Oi&&Ei?2*window.devicePixelRatio:ki?window.devicePixelRatio:1,we={},Pe=(Object.freeze||Object)({on:mt,off:ft,stopPropagation:yt,disableScrollPropagation:xt,disableClickPropagation:wt,preventDefault:Pt,stop:Lt,getMousePosition:bt,getWheelDelta:Tt,fakeStop:zt,skipped:Mt,isExternalTarget:Ct,addListener:mt,removeListener:ft}),Le=ci.extend({run:function(t,i,e,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=e||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=ht(t),this._offset=i.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(!0),this._complete())},_animate:function(){this._animId=f(this._animate,this),this._step()},_step:function(t){var i=+new Date-this._startTime,e=1e3*this._duration;i<e?this._runFrame(this._easeOut(i/e),t):(this._runFrame(1),this._complete())},_runFrame:function(t,i){var e=this._startPos.add(this._offset.multiplyBy(t));i&&e._round(),at(this._el,e),this.fire("step")},_complete:function(){g(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),be=ci.extend({options:{crs:yi,center:void 0,zoom:void 0,minZoom:void 0,maxZoom:void 0,layers:[],maxBounds:void 0,renderer:void 0,zoomAnimation:!0,zoomAnimationThreshold:4,fadeAnimation:!0,markerZoomAnimation:!0,transform3DLimit:8388608,zoomSnap:1,zoomDelta:1,trackResize:!0},initialize:function(t,i){i=l(this,i),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._sizeChanged=!0,this._initContainer(t),this._initLayout(),this._onResize=e(this._onResize,this),this._initEvents(),i.maxBounds&&this.setMaxBounds(i.maxBounds),void 0!==i.zoom&&(this._zoom=this._limitZoom(i.zoom)),i.center&&void 0!==i.zoom&&this.setView(C(i.center),i.zoom,{reset:!0}),this.callInitHooks(),this._zoomAnimated=_e&&ji&&!Gi&&this.options.zoomAnimation,this._zoomAnimated&&(this._createAnimProxy(),mt(this._proxy,de,this._catchTransitionEnd,this)),this._addLayers(this.options.layers)},setView:function(t,e,n){return e=void 0===e?this._zoom:this._limitZoom(e),t=this._limitCenter(C(t),e,this.options.maxBounds),n=n||{},this._stop(),this._loaded&&!n.reset&&!0!==n&&(void 0!==n.animate&&(n.zoom=i({animate:n.animate},n.zoom),n.pan=i({animate:n.animate,duration:n.duration},n.pan)),this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan))?(clearTimeout(this._sizeTimer),this):(this._resetView(t,e),this)},setZoom:function(t,i){return this._loaded?this.setView(this.getCenter(),t,{zoom:i}):(this._zoom=t,this)},zoomIn:function(t,i){return t=t||(ji?this.options.zoomDelta:1),this.setZoom(this._zoom+t,i)},zoomOut:function(t,i){return t=t||(ji?this.options.zoomDelta:1),this.setZoom(this._zoom-t,i)},setZoomAround:function(t,i,e){var n=this.getZoomScale(i),o=this.getSize().divideBy(2),s=(t instanceof x?t:this.latLngToContainerPoint(t)).subtract(o).multiplyBy(1-1/n),r=this.containerPointToLatLng(o.add(s));return this.setView(r,i,{zoom:e})},_getBoundsCenterZoom:function(t,i){i=i||{},t=t.getBounds?t.getBounds():z(t);var e=w(i.paddingTopLeft||i.padding||[0,0]),n=w(i.paddingBottomRight||i.padding||[0,0]),o=this.getBoundsZoom(t,!1,e.add(n));if((o="number"==typeof i.maxZoom?Math.min(i.maxZoom,o):o)===1/0)return{center:t.getCenter(),zoom:o};var s=n.subtract(e).divideBy(2),r=this.project(t.getSouthWest(),o),a=this.project(t.getNorthEast(),o);return{center:this.unproject(r.add(a).divideBy(2).add(s),o),zoom:o}},fitBounds:function(t,i){if(!(t=z(t)).isValid())throw new Error("Bounds are not valid.");var e=this._getBoundsCenterZoom(t,i);return this.setView(e.center,e.zoom,i)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,i){return this.setView(t,this._zoom,{pan:i})},panBy:function(t,i){if(t=w(t).round(),i=i||{},!t.x&&!t.y)return this.fire("moveend");if(!0!==i.animate&&!this.getSize().contains(t))return this._resetView(this.unproject(this.project(this.getCenter()).add(t)),this.getZoom()),this;if(this._panAnim||(this._panAnim=new Le,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),i.noMoveStart||this.fire("movestart"),!1!==i.animate){Q(this._mapPane,"leaflet-pan-anim");var e=this._getMapPanePos().subtract(t).round();this._panAnim.run(this._mapPane,e,i.duration||.25,i.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},flyTo:function(t,i,e){function n(t){var i=(g*g-m*m+(t?-1:1)*x*x*v*v)/(2*(t?g:m)*x*v),e=Math.sqrt(i*i+1)-i;return e<1e-9?-18:Math.log(e)}function o(t){return(Math.exp(t)-Math.exp(-t))/2}function s(t){return(Math.exp(t)+Math.exp(-t))/2}function r(t){return o(t)/s(t)}function a(t){return m*(s(w)/s(w+y*t))}function h(t){return m*(s(w)*r(w+y*t)-o(w))/x}function u(t){return 1-Math.pow(1-t,1.5)}function l(){var e=(Date.now()-P)/b,n=u(e)*L;e<=1?(this._flyToFrame=f(l,this),this._move(this.unproject(c.add(_.subtract(c).multiplyBy(h(n)/v)),p),this.getScaleZoom(m/a(n),p),{flyTo:!0})):this._move(t,i)._moveEnd(!0)}if(!1===(e=e||{}).animate||!ji)return this.setView(t,i,e);this._stop();var c=this.project(this.getCenter()),_=this.project(t),d=this.getSize(),p=this._zoom;t=C(t),i=void 0===i?p:i;var m=Math.max(d.x,d.y),g=m*this.getZoomScale(p,i),v=_.distanceTo(c)||1,y=1.42,x=y*y,w=n(0),P=Date.now(),L=(n(1)-w)/y,b=e.duration?1e3*e.duration:1e3*L*.8;return this._moveStart(!0,e.noMoveStart),l.call(this),this},flyToBounds:function(t,i){var e=this._getBoundsCenterZoom(t,i);return this.flyTo(e.center,e.zoom,i)},setMaxBounds:function(t){return(t=z(t)).isValid()?(this.options.maxBounds&&this.off("moveend",this._panInsideMaxBounds),this.options.maxBounds=t,this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds)):(this.options.maxBounds=null,this.off("moveend",this._panInsideMaxBounds))},setMinZoom:function(t){var i=this.options.minZoom;return this.options.minZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()<this.options.minZoom)?this.setZoom(t):this},setMaxZoom:function(t){var i=this.options.maxZoom;return this.options.maxZoom=t,this._loaded&&i!==t&&(this.fire("zoomlevelschange"),this.getZoom()>this.options.maxZoom)?this.setZoom(t):this},panInsideBounds:function(t,i){this._enforcingBounds=!0;var e=this.getCenter(),n=this._limitCenter(e,this._zoom,z(t));return e.equals(n)||this.panTo(n,i),this._enforcingBounds=!1,this},panInside:function(t,i){var e=w((i=i||{}).paddingTopLeft||i.padding||[0,0]),n=w(i.paddingBottomRight||i.padding||[0,0]),o=this.getCenter(),s=this.project(o),r=this.project(t),a=this.getPixelBounds(),h=a.getSize().divideBy(2),u=b([a.min.add(e),a.max.subtract(n)]);if(!u.contains(r)){this._enforcingBounds=!0;var l=s.subtract(r),c=w(r.x+l.x,r.y+l.y);(r.x<u.min.x||r.x>u.max.x)&&(c.x=s.x-l.x,l.x>0?c.x+=h.x-e.x:c.x-=h.x-n.x),(r.y<u.min.y||r.y>u.max.y)&&(c.y=s.y-l.y,l.y>0?c.y+=h.y-e.y:c.y-=h.y-n.y),this.panTo(this.unproject(c),i),this._enforcingBounds=!1}return this},invalidateSize:function(t){if(!this._loaded)return this;t=i({animate:!1,pan:!0},!0===t?{animate:!0}:t);var n=this.getSize();this._sizeChanged=!0,this._lastCenter=null;var o=this.getSize(),s=n.divideBy(2).round(),r=o.divideBy(2).round(),a=s.subtract(r);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(e(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:n,newSize:o})):this},stop:function(){return this.setZoom(this._limitZoom(this._zoom)),this.options.zoomSnap||this.fire("viewreset"),this._stop()},locate:function(t){if(t=this._locateOptions=i({timeout:1e4,watch:!1},t),!("geolocation"in navigator))return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var n=e(this._handleGeolocationResponse,this),o=e(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(n,o,t):navigator.geolocation.getCurrentPosition(n,o,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var i=t.code,e=t.message||(1===i?"permission denied":2===i?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:i,message:"Geolocation error: "+e+"."})},_handleGeolocationResponse:function(t){var i=new M(t.coords.latitude,t.coords.longitude),e=i.toBounds(2*t.coords.accuracy),n=this._locateOptions;if(n.setView){var o=this.getBoundsZoom(e);this.setView(i,n.maxZoom?Math.min(o,n.maxZoom):o)}var s={latlng:i,bounds:e,timestamp:t.timestamp};for(var r in t.coords)"number"==typeof t.coords[r]&&(s[r]=t.coords[r]);this.fire("locationfound",s)},addHandler:function(t,i){if(!i)return this;var e=this[t]=new i(this);return this._handlers.push(e),this.options[t]&&e.enable(),this},remove:function(){if(this._initEvents(!0),this._containerId!==this._container._leaflet_id)throw new Error("Map container is being reused by another instance");try{delete this._container._leaflet_id,delete this._containerId}catch(t){this._container._leaflet_id=void 0,this._containerId=void 0}void 0!==this._locationWatchId&&this.stopLocate(),this._stop(),K(this._mapPane),this._clearControlPos&&this._clearControlPos(),this._resizeRequest&&(g(this._resizeRequest),this._resizeRequest=null),this._clearHandlers(),this._loaded&&this.fire("unload");var t;for(t in this._layers)this._layers[t].remove();for(t in this._panes)K(this._panes[t]);return this._layers=[],this._panes=[],delete this._mapPane,delete this._renderer,this},createPane:function(t,i){var e=G("div","leaflet-pane"+(t?" leaflet-"+t.replace("Pane","")+"-pane":""),i||this._mapPane);return t&&(this._panes[t]=e),e},getCenter:function(){return this._checkIfLoaded(),this._lastCenter&&!this._moved()?this._lastCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds();return new T(this.unproject(t.getBottomLeft()),this.unproject(t.getTopRight()))},getMinZoom:function(){return void 0===this.options.minZoom?this._layersMinZoom||0:this.options.minZoom},getMaxZoom:function(){return void 0===this.options.maxZoom?void 0===this._layersMaxZoom?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,i,e){t=z(t),e=w(e||[0,0]);var n=this.getZoom()||0,o=this.getMinZoom(),s=this.getMaxZoom(),r=t.getNorthWest(),a=t.getSouthEast(),h=this.getSize().subtract(e),u=b(this.project(a,n),this.project(r,n)).getSize(),l=ji?this.options.zoomSnap:1,c=h.x/u.x,_=h.y/u.y,d=i?Math.max(c,_):Math.min(c,_);return n=this.getScaleZoom(d,n),l&&(n=Math.round(n/(l/100))*(l/100),n=i?Math.ceil(n/l)*l:Math.floor(n/l)*l),Math.max(o,Math.min(s,n))},getSize:function(){return this._size&&!this._sizeChanged||(this._size=new x(this._container.clientWidth||0,this._container.clientHeight||0),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(t,i){var e=this._getTopLeftPoint(t,i);return new P(e,e.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._pixelOrigin},getPixelWorldBounds:function(t){return this.options.crs.getProjectedBounds(void 0===t?this.getZoom():t)},getPane:function(t){return"string"==typeof t?this._panes[t]:t},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t,i){var e=this.options.crs;return i=void 0===i?this._zoom:i,e.scale(t)/e.scale(i)},getScaleZoom:function(t,i){var e=this.options.crs;i=void 0===i?this._zoom:i;var n=e.zoom(t*e.scale(i));return isNaN(n)?1/0:n},project:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.latLngToPoint(C(t),i)},unproject:function(t,i){return i=void 0===i?this._zoom:i,this.options.crs.pointToLatLng(w(t),i)},layerPointToLatLng:function(t){var i=w(t).add(this.getPixelOrigin());return this.unproject(i)},latLngToLayerPoint:function(t){return this.project(C(t))._round()._subtract(this.getPixelOrigin())},wrapLatLng:function(t){return this.options.crs.wrapLatLng(C(t))},wrapLatLngBounds:function(t){return this.options.crs.wrapLatLngBounds(z(t))},distance:function(t,i){return this.options.crs.distance(C(t),C(i))},containerPointToLayerPoint:function(t){return w(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return w(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var i=this.containerPointToLayerPoint(w(t));return this.layerPointToLatLng(i)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(C(t)))},mouseEventToContainerPoint:function(t){return bt(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var i=this._container=V(t);if(!i)throw new Error("Map container not found.");if(i._leaflet_id)throw new Error("Map container is already initialized.");mt(i,"scroll",this._onScroll,this),this._containerId=n(i)},_initLayout:function(){var t=this._container;this._fadeAnimated=this.options.fadeAnimation&&ji,Q(t,"leaflet-container"+(qi?" leaflet-touch":"")+(Yi?" leaflet-retina":"")+(Li?" leaflet-oldie":"")+(Bi?" leaflet-safari":"")+(this._fadeAnimated?" leaflet-fade-anim":""));var i=q(t,"position");"absolute"!==i&&"relative"!==i&&"fixed"!==i&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),at(this._mapPane,new x(0,0)),this.createPane("tilePane"),this.createPane("shadowPane"),this.createPane("overlayPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane"),this.options.markerZoomAnimation||(Q(t.markerPane,"leaflet-zoom-hide"),Q(t.shadowPane,"leaflet-zoom-hide"))},_resetView:function(t,i){at(this._mapPane,new x(0,0));var e=!this._loaded;this._loaded=!0,i=this._limitZoom(i),this.fire("viewprereset");var n=this._zoom!==i;this._moveStart(n,!1)._move(t,i)._moveEnd(n),this.fire("viewreset"),e&&this.fire("load")},_moveStart:function(t,i){return t&&this.fire("zoomstart"),i||this.fire("movestart"),this},_move:function(t,i,e){void 0===i&&(i=this._zoom);var n=this._zoom!==i;return this._zoom=i,this._lastCenter=t,this._pixelOrigin=this._getNewPixelOrigin(t),(n||e&&e.pinch)&&this.fire("zoom",e),this.fire("move",e)},_moveEnd:function(t){return t&&this.fire("zoomend"),this.fire("moveend")},_stop:function(){return g(this._flyToFrame),this._panAnim&&this._panAnim.stop(),this},_rawPanBy:function(t){at(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_panInsideMaxBounds:function(){this._enforcingBounds||this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(t){this._targets={},this._targets[n(this._container)]=this;var i=t?ft:mt;i(this._container,"click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress",this._handleDOMEvent,this),this.options.trackResize&&i(window,"resize",this._onResize,this),ji&&this.options.transform3DLimit&&(t?this.off:this.on).call(this,"moveend",this._onMoveEnd)},_onResize:function(){g(this._resizeRequest),this._resizeRequest=f(function(){this.invalidateSize({debounceMoveend:!0})},this)},_onScroll:function(){this._container.scrollTop=0,this._container.scrollLeft=0},_onMoveEnd:function(){var t=this._getMapPanePos();Math.max(Math.abs(t.x),Math.abs(t.y))>=this.options.transform3DLimit&&this._resetView(this.getCenter(),this.getZoom())},_findEventTargets:function(t,i){for(var e,o=[],s="mouseout"===i||"mouseover"===i,r=t.target||t.srcElement,a=!1;r;){if((e=this._targets[n(r)])&&("click"===i||"preclick"===i)&&!t._simulated&&this._draggableMoved(e)){a=!0;break}if(e&&e.listens(i,!0)){if(s&&!Ct(r,t))break;if(o.push(e),s)break}if(r===this._container)break;r=r.parentNode}return o.length||a||s||!Ct(r,t)||(o=[this]),o},_handleDOMEvent:function(t){if(this._loaded&&!Mt(t)){var i=t.type;"mousedown"!==i&&"keypress"!==i||ct(t.target||t.srcElement),this._fireDOMEvent(t,i)}},_mouseEvents:["click","dblclick","mouseover","mouseout","contextmenu"],_fireDOMEvent:function(t,e,n){if("click"===t.type){var o=i({},t);o.type="preclick",this._fireDOMEvent(o,o.type,n)}if(!t._stopped&&(n=(n||[]).concat(this._findEventTargets(t,e))).length){var s=n[0];"contextmenu"===e&&s.listens(e,!0)&&Pt(t);var r={originalEvent:t};if("keypress"!==t.type){var a=s.getLatLng&&(!s._radius||s._radius<=10);r.containerPoint=a?this.latLngToContainerPoint(s.getLatLng()):this.mouseEventToContainerPoint(t),r.layerPoint=this.containerPointToLayerPoint(r.containerPoint),r.latlng=a?s.getLatLng():this.layerPointToLatLng(r.layerPoint)}for(var h=0;h<n.length;h++)if(n[h].fire(e,r,!0),r.originalEvent._stopped||!1===n[h].options.bubblingMouseEvents&&-1!==d(this._mouseEvents,e))return}},_draggableMoved:function(t){return(t=t.dragging&&t.dragging.enabled()?t:this).dragging&&t.dragging.moved()||this.boxZoom&&this.boxZoom.moved()},_clearHandlers:function(){for(var t=0,i=this._handlers.length;t<i;t++)this._handlers[t].disable()},whenReady:function(t,i){return this._loaded?t.call(i||this,{target:this}):this.on("load",t,i),this},_getMapPanePos:function(){return ht(this._mapPane)||new x(0,0)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(t,i){return(t&&void 0!==i?this._getNewPixelOrigin(t,i):this.getPixelOrigin()).subtract(this._getMapPanePos())},_getNewPixelOrigin:function(t,i){var e=this.getSize()._divideBy(2);return this.project(t,i)._subtract(e)._add(this._getMapPanePos())._round()},_latLngToNewLayerPoint:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return this.project(t,i)._subtract(n)},_latLngBoundsToNewLayerBounds:function(t,i,e){var n=this._getNewPixelOrigin(e,i);return b([this.project(t.getSouthWest(),i)._subtract(n),this.project(t.getNorthWest(),i)._subtract(n),this.project(t.getSouthEast(),i)._subtract(n),this.project(t.getNorthEast(),i)._subtract(n)])},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,i,e){if(!e)return t;var n=this.project(t,i),o=this.getSize().divideBy(2),s=new P(n.subtract(o),n.add(o)),r=this._getBoundsOffset(s,e,i);return r.round().equals([0,0])?t:this.unproject(n.add(r),i)},_limitOffset:function(t,i){if(!i)return t;var e=this.getPixelBounds(),n=new P(e.min.add(t),e.max.add(t));return t.add(this._getBoundsOffset(n,i))},_getBoundsOffset:function(t,i,e){var n=b(this.project(i.getNorthEast(),e),this.project(i.getSouthWest(),e)),o=n.min.subtract(t.min),s=n.max.subtract(t.max);return new x(this._rebound(o.x,-s.x),this._rebound(o.y,-s.y))},_rebound:function(t,i){return t+i>0?Math.round(t-i)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(i))},_limitZoom:function(t){var i=this.getMinZoom(),e=this.getMaxZoom(),n=ji?this.options.zoomSnap:1;return n&&(t=Math.round(t/n)*n),Math.max(i,Math.min(e,t))},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){tt(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,i){var e=this._getCenterOffset(t)._trunc();return!(!0!==(i&&i.animate)&&!this.getSize().contains(e))&&(this.panBy(e,i),!0)},_createAnimProxy:function(){var t=this._proxy=G("div","leaflet-proxy leaflet-zoom-animated");this._panes.mapPane.appendChild(t),this.on("zoomanim",function(t){var i=ce,e=this._proxy.style[i];rt(this._proxy,this.project(t.center,t.zoom),this.getZoomScale(t.zoom,1)),e===this._proxy.style[i]&&this._animatingZoom&&this._onZoomTransitionEnd()},this),this.on("load moveend",function(){var t=this.getCenter(),i=this.getZoom();rt(this._proxy,this.project(t,i),this.getZoomScale(i,1))},this),this._on("unload",this._destroyAnimProxy,this)},_destroyAnimProxy:function(){K(this._proxy),delete this._proxy},_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,i,e){if(this._animatingZoom)return!0;if(e=e||{},!this._zoomAnimated||!1===e.animate||this._nothingToAnimate()||Math.abs(i-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(i),o=this._getCenterOffset(t)._divideBy(1-1/n);return!(!0!==e.animate&&!this.getSize().contains(o))&&(f(function(){this._moveStart(!0,!1)._animateZoom(t,i,!0)},this),!0)},_animateZoom:function(t,i,n,o){this._mapPane&&(n&&(this._animatingZoom=!0,this._animateToCenter=t,this._animateToZoom=i,Q(this._mapPane,"leaflet-zoom-anim")),this.fire("zoomanim",{center:t,zoom:i,noUpdate:o}),setTimeout(e(this._onZoomTransitionEnd,this),250))},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._mapPane&&tt(this._mapPane,"leaflet-zoom-anim"),this._animatingZoom=!1,this._move(this._animateToCenter,this._animateToZoom),f(function(){this._moveEnd(!0)},this))}}),Te=v.extend({options:{position:"topright"},initialize:function(t){l(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var i=this._map;return i&&i.removeControl(this),this.options.position=t,i&&i.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this.remove(),this._map=t;var i=this._container=this.onAdd(t),e=this.getPosition(),n=t._controlCorners[e];return Q(i,"leaflet-control"),-1!==e.indexOf("bottom")?n.insertBefore(i,n.firstChild):n.appendChild(i),this},remove:function(){return this._map?(K(this._container),this.onRemove&&this.onRemove(this._map),this._map=null,this):this},_refocusOnMap:function(t){this._map&&t&&t.screenX>0&&t.screenY>0&&this._map.getContainer().focus()}}),ze=function(t){return new Te(t)};be.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.remove(),this},_initControlPos:function(){function t(t,o){var s=e+t+" "+e+o;i[t+o]=G("div",s,n)}var i=this._controlCorners={},e="leaflet-",n=this._controlContainer=G("div",e+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){for(var t in this._controlCorners)K(this._controlCorners[t]);K(this._controlContainer),delete this._controlCorners,delete this._controlContainer}});var Me=Te.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0,hideSingleBase:!1,sortLayers:!1,sortFunction:function(t,i,e,n){return e<n?-1:n<e?1:0}},initialize:function(t,i,e){l(this,e),this._layerControlInputs=[],this._layers=[],this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in i)this._addLayer(i[n],n,!0)},onAdd:function(t){this._initLayout(),this._update(),this._map=t,t.on("zoomend",this._checkDisabledLayers,this);for(var i=0;i<this._layers.length;i++)this._layers[i].layer.on("add remove",this._onLayerChange,this);return this._container},addTo:function(t){return Te.prototype.addTo.call(this,t),this._expandIfNotCollapsed()},onRemove:function(){this._map.off("zoomend",this._checkDisabledLayers,this);for(var t=0;t<this._layers.length;t++)this._layers[t].layer.off("add remove",this._onLayerChange,this)},addBaseLayer:function(t,i){return this._addLayer(t,i),this._map?this._update():this},addOverlay:function(t,i){return this._addLayer(t,i,!0),this._map?this._update():this},removeLayer:function(t){t.off("add remove",this._onLayerChange,this);var i=this._getLayer(n(t));return i&&this._layers.splice(this._layers.indexOf(i),1),this._map?this._update():this},expand:function(){Q(this._container,"leaflet-control-layers-expanded"),this._section.style.height=null;var t=this._map.getSize().y-(this._container.offsetTop+50);return t<this._section.clientHeight?(Q(this._section,"leaflet-control-layers-scrollbar"),this._section.style.height=t+"px"):tt(this._section,"leaflet-control-layers-scrollbar"),this._checkDisabledLayers(),this},collapse:function(){return tt(this._container,"leaflet-control-layers-expanded"),this},_initLayout:function(){var t="leaflet-control-layers",i=this._container=G("div",t),e=this.options.collapsed;i.setAttribute("aria-haspopup",!0),wt(i),xt(i);var n=this._section=G("section",t+"-list");e&&(this._map.on("click",this.collapse,this),zi||mt(i,{mouseenter:this.expand,mouseleave:this.collapse},this));var o=this._layersLink=G("a",t+"-toggle",i);o.href="#",o.title="Layers",qi?(mt(o,"click",Lt),mt(o,"click",this.expand,this)):mt(o,"focus",this.expand,this),e||this.expand(),this._baseLayersList=G("div",t+"-base",n),this._separator=G("div",t+"-separator",n),this._overlaysList=G("div",t+"-overlays",n),i.appendChild(n)},_getLayer:function(t){for(var i=0;i<this._layers.length;i++)if(this._layers[i]&&n(this._layers[i].layer)===t)return this._layers[i]},_addLayer:function(t,i,n){this._map&&t.on("add remove",this._onLayerChange,this),this._layers.push({layer:t,name:i,overlay:n}),this.options.sortLayers&&this._layers.sort(e(function(t,i){return this.options.sortFunction(t.layer,i.layer,t.name,i.name)},this)),this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex)),this._expandIfNotCollapsed()},_update:function(){if(!this._container)return this;Y(this._baseLayersList),Y(this._overlaysList),this._layerControlInputs=[];var t,i,e,n,o=0;for(e=0;e<this._layers.length;e++)n=this._layers[e],this._addItem(n),i=i||n.overlay,t=t||!n.overlay,o+=n.overlay?0:1;return this.options.hideSingleBase&&(t=t&&o>1,this._baseLayersList.style.display=t?"":"none"),this._separator.style.display=i&&t?"":"none",this},_onLayerChange:function(t){this._handlingClick||this._update();var i=this._getLayer(n(t.target)),e=i.overlay?"add"===t.type?"overlayadd":"overlayremove":"add"===t.type?"baselayerchange":null;e&&this._map.fire(e,i)},_createRadioElement:function(t,i){var e='<input type="radio" class="leaflet-control-layers-selector" name="'+t+'"'+(i?' checked="checked"':"")+"/>",n=document.createElement("div");return n.innerHTML=e,n.firstChild},_addItem:function(t){var i,e=document.createElement("label"),o=this._map.hasLayer(t.layer);t.overlay?((i=document.createElement("input")).type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=o):i=this._createRadioElement("leaflet-base-layers",o),this._layerControlInputs.push(i),i.layerId=n(t.layer),mt(i,"click",this._onInputClick,this);var s=document.createElement("span");s.innerHTML=" "+t.name;var r=document.createElement("div");return e.appendChild(r),r.appendChild(i),r.appendChild(s),(t.overlay?this._overlaysList:this._baseLayersList).appendChild(e),this._checkDisabledLayers(),e},_onInputClick:function(){var t,i,e=this._layerControlInputs,n=[],o=[];this._handlingClick=!0;for(var s=e.length-1;s>=0;s--)t=e[s],i=this._getLayer(t.layerId).layer,t.checked?n.push(i):t.checked||o.push(i);for(s=0;s<o.length;s++)this._map.hasLayer(o[s])&&this._map.removeLayer(o[s]);for(s=0;s<n.length;s++)this._map.hasLayer(n[s])||this._map.addLayer(n[s]);this._handlingClick=!1,this._refocusOnMap()},_checkDisabledLayers:function(){for(var t,i,e=this._layerControlInputs,n=this._map.getZoom(),o=e.length-1;o>=0;o--)t=e[o],i=this._getLayer(t.layerId).layer,t.disabled=void 0!==i.options.minZoom&&n<i.options.minZoom||void 0!==i.options.maxZoom&&n>i.options.maxZoom},_expandIfNotCollapsed:function(){return this._map&&!this.options.collapsed&&this.expand(),this},_expand:function(){return this.expand()},_collapse:function(){return this.collapse()}}),Ce=Te.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"&#x2212;",zoomOutTitle:"Zoom out"},onAdd:function(t){var i="leaflet-control-zoom",e=G("div",i+" leaflet-bar"),n=this.options;return this._zoomInButton=this._createButton(n.zoomInText,n.zoomInTitle,i+"-in",e,this._zoomIn),this._zoomOutButton=this._createButton(n.zoomOutText,n.zoomOutTitle,i+"-out",e,this._zoomOut),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),e},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},disable:function(){return this._disabled=!0,this._updateDisabled(),this},enable:function(){return this._disabled=!1,this._updateDisabled(),this},_zoomIn:function(t){!this._disabled&&this._map._zoom<this._map.getMaxZoom()&&this._map.zoomIn(this._map.options.zoomDelta*(t.shiftKey?3:1))},_zoomOut:function(t){!this._disabled&&this._map._zoom>this._map.getMinZoom()&&this._map.zoomOut(this._map.options.zoomDelta*(t.shiftKey?3:1))},_createButton:function(t,i,e,n,o){var s=G("a",e,n);return s.innerHTML=t,s.href="#",s.title=i,s.setAttribute("role","button"),s.setAttribute("aria-label",i),wt(s),mt(s,"click",Lt),mt(s,"click",o,this),mt(s,"click",this._refocusOnMap,this),s},_updateDisabled:function(){var t=this._map,i="leaflet-disabled";tt(this._zoomInButton,i),tt(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMinZoom())&&Q(this._zoomOutButton,i),(this._disabled||t._zoom===t.getMaxZoom())&&Q(this._zoomInButton,i)}});be.mergeOptions({zoomControl:!0}),be.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new Ce,this.addControl(this.zoomControl))});var Se=Te.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0},onAdd:function(t){var i=G("div","leaflet-control-scale"),e=this.options;return this._addScales(e,"leaflet-control-scale-line",i),t.on(e.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,i,e){t.metric&&(this._mScale=G("div",i,e)),t.imperial&&(this._iScale=G("div",i,e))},_update:function(){var t=this._map,i=t.getSize().y/2,e=t.distance(t.containerPointToLatLng([0,i]),t.containerPointToLatLng([this.options.maxWidth,i]));this._updateScales(e)},_updateScales:function(t){this.options.metric&&t&&this._updateMetric(t),this.options.imperial&&t&&this._updateImperial(t)},_updateMetric:function(t){var i=this._getRoundNum(t),e=i<1e3?i+" m":i/1e3+" km";this._updateScale(this._mScale,e,i/t)},_updateImperial:function(t){var i,e,n,o=3.2808399*t;o>5280?(i=o/5280,e=this._getRoundNum(i),this._updateScale(this._iScale,e+" mi",e/i)):(n=this._getRoundNum(o),this._updateScale(this._iScale,n+" ft",n/o))},_updateScale:function(t,i,e){t.style.width=Math.round(this.options.maxWidth*e)+"px",t.innerHTML=i},_getRoundNum:function(t){var i=Math.pow(10,(Math.floor(t)+"").length-1),e=t/i;return e=e>=10?10:e>=5?5:e>=3?3:e>=2?2:1,i*e}}),Ze=Te.extend({options:{position:"bottomright",prefix:'<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'},initialize:function(t){l(this,t),this._attributions={}},onAdd:function(t){t.attributionControl=this,this._container=G("div","leaflet-control-attribution"),wt(this._container);for(var i in t._layers)t._layers[i].getAttribution&&this.addAttribution(t._layers[i].getAttribution());return this._update(),this._container},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):this},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):this},_update:function(){if(this._map){var t=[];for(var i in this._attributions)this._attributions[i]&&t.push(i);var e=[];this.options.prefix&&e.push(this.options.prefix),t.length&&e.push(t.join(", ")),this._container.innerHTML=e.join(" | ")}}});be.mergeOptions({attributionControl:!0}),be.addInitHook(function(){this.options.attributionControl&&(new Ze).addTo(this)});Te.Layers=Me,Te.Zoom=Ce,Te.Scale=Se,Te.Attribution=Ze,ze.layers=function(t,i,e){return new Me(t,i,e)},ze.zoom=function(t){return new Ce(t)},ze.scale=function(t){return new Se(t)},ze.attribution=function(t){return new Ze(t)};var Ee=v.extend({initialize:function(t){this._map=t},enable:function(){return this._enabled?this:(this._enabled=!0,this.addHooks(),this)},disable:function(){return this._enabled?(this._enabled=!1,this.removeHooks(),this):this},enabled:function(){return!!this._enabled}});Ee.addTo=function(t,i){return t.addHandler(i,this),this};var ke,Be={Events:li},Ae=qi?"touchstart mousedown":"mousedown",Ie={mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},Oe={mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"},Re=ci.extend({options:{clickTolerance:3},initialize:function(t,i,e,n){l(this,n),this._element=t,this._dragStartTarget=i||t,this._preventOutline=e},enable:function(){this._enabled||(mt(this._dragStartTarget,Ae,this._onDown,this),this._enabled=!0)},disable:function(){this._enabled&&(Re._dragging===this&&this.finishDrag(),ft(this._dragStartTarget,Ae,this._onDown,this),this._enabled=!1,this._moved=!1)},_onDown:function(t){if(!t._simulated&&this._enabled&&(this._moved=!1,!$(this._element,"leaflet-zoom-anim")&&!(Re._dragging||t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(Re._dragging=this,this._preventOutline&&ct(this._element),ut(),fi(),this._moving)))){this.fire("down");var i=t.touches?t.touches[0]:t,e=dt(this._element);this._startPoint=new x(i.clientX,i.clientY),this._parentScale=pt(e),mt(document,Oe[t.type],this._onMove,this),mt(document,Ie[t.type],this._onUp,this)}},_onMove:function(t){if(!t._simulated&&this._enabled)if(t.touches&&t.touches.length>1)this._moved=!0;else{var i=t.touches&&1===t.touches.length?t.touches[0]:t,e=new x(i.clientX,i.clientY)._subtract(this._startPoint);(e.x||e.y)&&(Math.abs(e.x)+Math.abs(e.y)<this.options.clickTolerance||(e.x/=this._parentScale.x,e.y/=this._parentScale.y,Pt(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=ht(this._element).subtract(e),Q(document.body,"leaflet-dragging"),this._lastTarget=t.target||t.srcElement,window.SVGElementInstance&&this._lastTarget instanceof SVGElementInstance&&(this._lastTarget=this._lastTarget.correspondingUseElement),Q(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(e),this._moving=!0,g(this._animRequest),this._lastEvent=t,this._animRequest=f(this._updatePosition,this,!0)))}},_updatePosition:function(){var t={originalEvent:this._lastEvent};this.fire("predrag",t),at(this._element,this._newPos),this.fire("drag",t)},_onUp:function(t){!t._simulated&&this._enabled&&this.finishDrag()},finishDrag:function(){tt(document.body,"leaflet-dragging"),this._lastTarget&&(tt(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in Oe)ft(document,Oe[t],this._onMove,this),ft(document,Ie[t],this._onUp,this);lt(),gi(),this._moved&&this._moving&&(g(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1,Re._dragging=!1}}),Ne=(Object.freeze||Object)({simplify:Zt,pointToSegmentDistance:Et,closestPointOnSegment:function(t,i,e){return Dt(t,i,e)},clipSegment:It,_getEdgeIntersection:Ot,_getBitCode:Rt,_sqClosestPointOnSegment:Dt,isFlat:jt,_flat:Wt}),De=(Object.freeze||Object)({clipPolygon:Ht}),je={project:function(t){return new x(t.lng,t.lat)},unproject:function(t){return new M(t.y,t.x)},bounds:new P([-180,-90],[180,90])},We={R:6378137,R_MINOR:6356752.314245179,bounds:new P([-20037508.34279,-15496570.73972],[20037508.34279,18764656.23138]),project:function(t){var i=Math.PI/180,e=this.R,n=t.lat*i,o=this.R_MINOR/e,s=Math.sqrt(1-o*o),r=s*Math.sin(n),a=Math.tan(Math.PI/4-n/2)/Math.pow((1-r)/(1+r),s/2);return n=-e*Math.log(Math.max(a,1e-10)),new x(t.lng*i*e,n)},unproject:function(t){for(var i,e=180/Math.PI,n=this.R,o=this.R_MINOR/n,s=Math.sqrt(1-o*o),r=Math.exp(-t.y/n),a=Math.PI/2-2*Math.atan(r),h=0,u=.1;h<15&&Math.abs(u)>1e-7;h++)i=s*Math.sin(a),i=Math.pow((1-i)/(1+i),s/2),a+=u=Math.PI/2-2*Math.atan(r*i)-a;return new M(a*e,t.x*e/n)}},He=(Object.freeze||Object)({LonLat:je,Mercator:We,SphericalMercator:mi}),Fe=i({},pi,{code:"EPSG:3395",projection:We,transformation:function(){var t=.5/(Math.PI*We.R);return Z(t,.5,-t,.5)}()}),Ue=i({},pi,{code:"EPSG:4326",projection:je,transformation:Z(1/180,1,-1/180,.5)}),Ve=i({},di,{projection:je,transformation:Z(1,0,-1,0),scale:function(t){return Math.pow(2,t)},zoom:function(t){return Math.log(t)/Math.LN2},distance:function(t,i){var e=i.lng-t.lng,n=i.lat-t.lat;return Math.sqrt(e*e+n*n)},infinite:!0});di.Earth=pi,di.EPSG3395=Fe,di.EPSG3857=yi,di.EPSG900913=xi,di.EPSG4326=Ue,di.Simple=Ve;var qe=ci.extend({options:{pane:"overlayPane",attribution:null,bubblingMouseEvents:!0},addTo:function(t){return t.addLayer(this),this},remove:function(){return this.removeFrom(this._map||this._mapToAdd)},removeFrom:function(t){return t&&t.removeLayer(this),this},getPane:function(t){return this._map.getPane(t?this.options[t]||t:this.options.pane)},addInteractiveTarget:function(t){return this._map._targets[n(t)]=this,this},removeInteractiveTarget:function(t){return delete this._map._targets[n(t)],this},getAttribution:function(){return this.options.attribution},_layerAdd:function(t){var i=t.target;if(i.hasLayer(this)){if(this._map=i,this._zoomAnimated=i._zoomAnimated,this.getEvents){var e=this.getEvents();i.on(e,this),this.once("remove",function(){i.off(e,this)},this)}this.onAdd(i),this.getAttribution&&i.attributionControl&&i.attributionControl.addAttribution(this.getAttribution()),this.fire("add"),i.fire("layeradd",{layer:this})}}});be.include({addLayer:function(t){if(!t._layerAdd)throw new Error("The provided object is not a Layer.");var i=n(t);return this._layers[i]?this:(this._layers[i]=t,t._mapToAdd=this,t.beforeAdd&&t.beforeAdd(this),this.whenReady(t._layerAdd,t),this)},removeLayer:function(t){var i=n(t);return this._layers[i]?(this._loaded&&t.onRemove(this),t.getAttribution&&this.attributionControl&&this.attributionControl.removeAttribution(t.getAttribution()),delete this._layers[i],this._loaded&&(this.fire("layerremove",{layer:t}),t.fire("remove")),t._map=t._mapToAdd=null,this):this},hasLayer:function(t){return!!t&&n(t)in this._layers},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},_addLayers:function(t){for(var i=0,e=(t=t?oi(t)?t:[t]:[]).length;i<e;i++)this.addLayer(t[i])},_addZoomLimit:function(t){!isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[n(t)]=t,this._updateZoomLevels())},_removeZoomLimit:function(t){var i=n(t);this._zoomBoundLayers[i]&&(delete this._zoomBoundLayers[i],this._updateZoomLevels())},_updateZoomLevels:function(){var t=1/0,i=-1/0,e=this._getZoomSpan();for(var n in this._zoomBoundLayers){var o=this._zoomBoundLayers[n].options;t=void 0===o.minZoom?t:Math.min(t,o.minZoom),i=void 0===o.maxZoom?i:Math.max(i,o.maxZoom)}this._layersMaxZoom=i===-1/0?void 0:i,this._layersMinZoom=t===1/0?void 0:t,e!==this._getZoomSpan()&&this.fire("zoomlevelschange"),void 0===this.options.maxZoom&&this._layersMaxZoom&&this.getZoom()>this._layersMaxZoom&&this.setZoom(this._layersMaxZoom),void 0===this.options.minZoom&&this._layersMinZoom&&this.getZoom()<this._layersMinZoom&&this.setZoom(this._layersMinZoom)}});var Ge=qe.extend({initialize:function(t,i){l(this,i),this._layers={};var e,n;if(t)for(e=0,n=t.length;e<n;e++)this.addLayer(t[e])},addLayer:function(t){var i=this.getLayerId(t);return this._layers[i]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var i=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[i]&&this._map.removeLayer(this._layers[i]),delete this._layers[i],this},hasLayer:function(t){return!!t&&(t in this._layers||this.getLayerId(t)in this._layers)},clearLayers:function(){return this.eachLayer(this.removeLayer,this)},invoke:function(t){var i,e,n=Array.prototype.slice.call(arguments,1);for(i in this._layers)(e=this._layers[i])[t]&&e[t].apply(e,n);return this},onAdd:function(t){this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t)},eachLayer:function(t,i){for(var e in this._layers)t.call(i,this._layers[e]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];return this.eachLayer(t.push,t),t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return n(t)}}),Ke=Ge.extend({addLayer:function(t){return this.hasLayer(t)?this:(t.addEventParent(this),Ge.prototype.addLayer.call(this,t),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.removeEventParent(this),Ge.prototype.removeLayer.call(this,t),this.fire("layerremove",{layer:t})):this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new T;for(var i in this._layers){var e=this._layers[i];t.extend(e.getBounds?e.getBounds():e.getLatLng())}return t}}),Ye=v.extend({options:{popupAnchor:[0,0],tooltipAnchor:[0,0]},initialize:function(t){l(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,i){var e=this._getIconUrl(t);if(!e){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n=this._createImg(e,i&&"IMG"===i.tagName?i:null);return this._setIconStyles(n,t),n},_setIconStyles:function(t,i){var e=this.options,n=e[i+"Size"];"number"==typeof n&&(n=[n,n]);var o=w(n),s=w("shadow"===i&&e.shadowAnchor||e.iconAnchor||o&&o.divideBy(2,!0));t.className="leaflet-marker-"+i+" "+(e.className||""),s&&(t.style.marginLeft=-s.x+"px",t.style.marginTop=-s.y+"px"),o&&(t.style.width=o.x+"px",t.style.height=o.y+"px")},_createImg:function(t,i){return i=i||document.createElement("img"),i.src=t,i},_getIconUrl:function(t){return Yi&&this.options[t+"RetinaUrl"]||this.options[t+"Url"]}}),Xe=Ye.extend({options:{iconUrl:"marker-icon.png",iconRetinaUrl:"marker-icon-2x.png",shadowUrl:"marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],tooltipAnchor:[16,-28],shadowSize:[41,41]},_getIconUrl:function(t){return Xe.imagePath||(Xe.imagePath=this._detectIconPath()),(this.options.imagePath||Xe.imagePath)+Ye.prototype._getIconUrl.call(this,t)},_detectIconPath:function(){var t=G("div","leaflet-default-icon-path",document.body),i=q(t,"background-image")||q(t,"backgroundImage");return document.body.removeChild(t),i=null===i||0!==i.indexOf("url")?"":i.replace(/^url\(["']?/,"").replace(/marker-icon\.png["']?\)$/,"")}}),Je=Ee.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new Re(t,t,!0)),this._draggable.on({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).enable(),Q(t,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off({dragstart:this._onDragStart,predrag:this._onPreDrag,drag:this._onDrag,dragend:this._onDragEnd},this).disable(),this._marker._icon&&tt(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_adjustPan:function(t){var i=this._marker,e=i._map,n=this._marker.options.autoPanSpeed,o=this._marker.options.autoPanPadding,s=ht(i._icon),r=e.getPixelBounds(),a=e.getPixelOrigin(),h=b(r.min._subtract(a).add(o),r.max._subtract(a).subtract(o));if(!h.contains(s)){var u=w((Math.max(h.max.x,s.x)-h.max.x)/(r.max.x-h.max.x)-(Math.min(h.min.x,s.x)-h.min.x)/(r.min.x-h.min.x),(Math.max(h.max.y,s.y)-h.max.y)/(r.max.y-h.max.y)-(Math.min(h.min.y,s.y)-h.min.y)/(r.min.y-h.min.y)).multiplyBy(n);e.panBy(u,{animate:!1}),this._draggable._newPos._add(u),this._draggable._startPos._add(u),at(i._icon,this._draggable._newPos),this._onDrag(t),this._panRequest=f(this._adjustPan.bind(this,t))}},_onDragStart:function(){this._oldLatLng=this._marker.getLatLng(),this._marker.closePopup().fire("movestart").fire("dragstart")},_onPreDrag:function(t){this._marker.options.autoPan&&(g(this._panRequest),this._panRequest=f(this._adjustPan.bind(this,t)))},_onDrag:function(t){var i=this._marker,e=i._shadow,n=ht(i._icon),o=i._map.layerPointToLatLng(n);e&&at(e,n),i._latlng=o,t.latlng=o,t.oldLatLng=this._oldLatLng,i.fire("move",t).fire("drag",t)},_onDragEnd:function(t){g(this._panRequest),delete this._oldLatLng,this._marker.fire("moveend").fire("dragend",t)}}),$e=qe.extend({options:{icon:new Xe,interactive:!0,keyboard:!0,title:"",alt:"",zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250,pane:"markerPane",bubblingMouseEvents:!1,draggable:!1,autoPan:!1,autoPanPadding:[50,50],autoPanSpeed:10},initialize:function(t,i){l(this,i),this._latlng=C(t)},onAdd:function(t){this._zoomAnimated=this._zoomAnimated&&t.options.markerZoomAnimation,this._zoomAnimated&&t.on("zoomanim",this._animateZoom,this),this._initIcon(),this.update()},onRemove:function(t){this.dragging&&this.dragging.enabled()&&(this.options.draggable=!0,this.dragging.removeHooks()),delete this.dragging,this._zoomAnimated&&t.off("zoomanim",this._animateZoom,this),this._removeIcon(),this._removeShadow()},getEvents:function(){return{zoom:this.update,viewreset:this.update}},getLatLng:function(){return this._latlng},setLatLng:function(t){var i=this._latlng;return this._latlng=C(t),this.update(),this.fire("move",{oldLatLng:i,latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update()},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup,this._popup.options),this},getElement:function(){return this._icon},update:function(){if(this._icon&&this._map){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,i="leaflet-zoom-"+(this._zoomAnimated?"animated":"hide"),e=t.icon.createIcon(this._icon),n=!1;e!==this._icon&&(this._icon&&this._removeIcon(),n=!0,t.title&&(e.title=t.title),"IMG"===e.tagName&&(e.alt=t.alt||"")),Q(e,i),t.keyboard&&(e.tabIndex="0"),this._icon=e,t.riseOnHover&&this.on({mouseover:this._bringToFront,mouseout:this._resetZIndex});var o=t.icon.createShadow(this._shadow),s=!1;o!==this._shadow&&(this._removeShadow(),s=!0),o&&(Q(o,i),o.alt=""),this._shadow=o,t.opacity<1&&this._updateOpacity(),n&&this.getPane().appendChild(this._icon),this._initInteraction(),o&&s&&this.getPane("shadowPane").appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&this.off({mouseover:this._bringToFront,mouseout:this._resetZIndex}),K(this._icon),this.removeInteractiveTarget(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&K(this._shadow),this._shadow=null},_setPos:function(t){at(this._icon,t),this._shadow&&at(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(i)},_initInteraction:function(){if(this.options.interactive&&(Q(this._icon,"leaflet-interactive"),this.addInteractiveTarget(this._icon),Je)){var t=this.options.draggable;this.dragging&&(t=this.dragging.enabled(),this.dragging.disable()),this.dragging=new Je(this),t&&this.dragging.enable()}},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},_updateOpacity:function(){var t=this.options.opacity;nt(this._icon,t),this._shadow&&nt(this._shadow,t)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)},_getPopupAnchor:function(){return this.options.icon.options.popupAnchor},_getTooltipAnchor:function(){return this.options.icon.options.tooltipAnchor}}),Qe=qe.extend({options:{stroke:!0,color:"#3388ff",weight:3,opacity:1,lineCap:"round",lineJoin:"round",dashArray:null,dashOffset:null,fill:!1,fillColor:null,fillOpacity:.2,fillRule:"evenodd",interactive:!0,bubblingMouseEvents:!0},beforeAdd:function(t){this._renderer=t.getRenderer(this)},onAdd:function(){this._renderer._initPath(this),this._reset(),this._renderer._addPath(this)},onRemove:function(){this._renderer._removePath(this)},redraw:function(){return this._map&&this._renderer._updatePath(this),this},setStyle:function(t){return l(this,t),this._renderer&&this._renderer._updateStyle(this),this},bringToFront:function(){return this._renderer&&this._renderer._bringToFront(this),this},bringToBack:function(){return this._renderer&&this._renderer._bringToBack(this),this},getElement:function(){return this._path},_reset:function(){this._project(),this._update()},_clickTolerance:function(){return(this.options.stroke?this.options.weight/2:0)+this._renderer.options.tolerance}}),tn=Qe.extend({options:{fill:!0,radius:10},initialize:function(t,i){l(this,i),this._latlng=C(t),this._radius=this.options.radius},setLatLng:function(t){return this._latlng=C(t),this.redraw(),this.fire("move",{latlng:this._latlng})},getLatLng:function(){return this._latlng},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius},setStyle:function(t){var i=t&&t.radius||this._radius;return Qe.prototype.setStyle.call(this,t),this.setRadius(i),this},_project:function(){this._point=this._map.latLngToLayerPoint(this._latlng),this._updateBounds()},_updateBounds:function(){var t=this._radius,i=this._radiusY||t,e=this._clickTolerance(),n=[t+e,i+e];this._pxBounds=new P(this._point.subtract(n),this._point.add(n))},_update:function(){this._map&&this._updatePath()},_updatePath:function(){this._renderer._updateCircle(this)},_empty:function(){return this._radius&&!this._renderer._bounds.intersects(this._pxBounds)},_containsPoint:function(t){return t.distanceTo(this._point)<=this._radius+this._clickTolerance()}}),en=tn.extend({initialize:function(t,e,n){if("number"==typeof e&&(e=i({},n,{radius:e})),l(this,e),this._latlng=C(t),isNaN(this.options.radius))throw new Error("Circle radius cannot be NaN");this._mRadius=this.options.radius},setRadius:function(t){return this._mRadius=t,this.redraw()},getRadius:function(){return this._mRadius},getBounds:function(){var t=[this._radius,this._radiusY||this._radius];return new T(this._map.layerPointToLatLng(this._point.subtract(t)),this._map.layerPointToLatLng(this._point.add(t)))},setStyle:Qe.prototype.setStyle,_project:function(){var t=this._latlng.lng,i=this._latlng.lat,e=this._map,n=e.options.crs;if(n.distance===pi.distance){var o=Math.PI/180,s=this._mRadius/pi.R/o,r=e.project([i+s,t]),a=e.project([i-s,t]),h=r.add(a).divideBy(2),u=e.unproject(h).lat,l=Math.acos((Math.cos(s*o)-Math.sin(i*o)*Math.sin(u*o))/(Math.cos(i*o)*Math.cos(u*o)))/o;(isNaN(l)||0===l)&&(l=s/Math.cos(Math.PI/180*i)),this._point=h.subtract(e.getPixelOrigin()),this._radius=isNaN(l)?0:h.x-e.project([u,t-l]).x,this._radiusY=h.y-r.y}else{var c=n.unproject(n.project(this._latlng).subtract([this._mRadius,0]));this._point=e.latLngToLayerPoint(this._latlng),this._radius=this._point.x-e.latLngToLayerPoint(c).x}this._updateBounds()}}),nn=Qe.extend({options:{smoothFactor:1,noClip:!1},initialize:function(t,i){l(this,i),this._setLatLngs(t)},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._setLatLngs(t),this.redraw()},isEmpty:function(){return!this._latlngs.length},closestLayerPoint:function(t){for(var i,e,n=1/0,o=null,s=Dt,r=0,a=this._parts.length;r<a;r++)for(var h=this._parts[r],u=1,l=h.length;u<l;u++){var c=s(t,i=h[u-1],e=h[u],!0);c<n&&(n=c,o=s(t,i,e))}return o&&(o.distance=Math.sqrt(n)),o},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a=this._rings[0],h=a.length;if(!h)return null;for(t=0,i=0;t<h-1;t++)i+=a[t].distanceTo(a[t+1])/2;if(0===i)return this._map.layerPointToLatLng(a[0]);for(t=0,n=0;t<h-1;t++)if(o=a[t],s=a[t+1],e=o.distanceTo(s),(n+=e)>i)return r=(n-i)/e,this._map.layerPointToLatLng([s.x-r*(s.x-o.x),s.y-r*(s.y-o.y)])},getBounds:function(){return this._bounds},addLatLng:function(t,i){return i=i||this._defaultShape(),t=C(t),i.push(t),this._bounds.extend(t),this.redraw()},_setLatLngs:function(t){this._bounds=new T,this._latlngs=this._convertLatLngs(t)},_defaultShape:function(){return jt(this._latlngs)?this._latlngs:this._latlngs[0]},_convertLatLngs:function(t){for(var i=[],e=jt(t),n=0,o=t.length;n<o;n++)e?(i[n]=C(t[n]),this._bounds.extend(i[n])):i[n]=this._convertLatLngs(t[n]);return i},_project:function(){var t=new P;this._rings=[],this._projectLatlngs(this._latlngs,this._rings,t);var i=this._clickTolerance(),e=new x(i,i);this._bounds.isValid()&&t.isValid()&&(t.min._subtract(e),t.max._add(e),this._pxBounds=t)},_projectLatlngs:function(t,i,e){var n,o,s=t[0]instanceof M,r=t.length;if(s){for(o=[],n=0;n<r;n++)o[n]=this._map.latLngToLayerPoint(t[n]),e.extend(o[n]);i.push(o)}else for(n=0;n<r;n++)this._projectLatlngs(t[n],i,e)},_clipPoints:function(){var t=this._renderer._bounds;if(this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else{var i,e,n,o,s,r,a,h=this._parts;for(i=0,n=0,o=this._rings.length;i<o;i++)for(e=0,s=(a=this._rings[i]).length;e<s-1;e++)(r=It(a[e],a[e+1],t,e,!0))&&(h[n]=h[n]||[],h[n].push(r[0]),r[1]===a[e+1]&&e!==s-2||(h[n].push(r[1]),n++))}},_simplifyPoints:function(){for(var t=this._parts,i=this.options.smoothFactor,e=0,n=t.length;e<n;e++)t[e]=Zt(t[e],i)},_update:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),this._updatePath())},_updatePath:function(){this._renderer._updatePoly(this)},_containsPoint:function(t,i){var e,n,o,s,r,a,h=this._clickTolerance();if(!this._pxBounds||!this._pxBounds.contains(t))return!1;for(e=0,s=this._parts.length;e<s;e++)for(n=0,o=(r=(a=this._parts[e]).length)-1;n<r;o=n++)if((i||0!==n)&&Et(t,a[o],a[n])<=h)return!0;return!1}});nn._flat=Wt;var on=nn.extend({options:{fill:!0},isEmpty:function(){return!this._latlngs.length||!this._latlngs[0].length},getCenter:function(){if(!this._map)throw new Error("Must add layer to map before using getCenter()");var t,i,e,n,o,s,r,a,h,u=this._rings[0],l=u.length;if(!l)return null;for(s=r=a=0,t=0,i=l-1;t<l;i=t++)e=u[t],n=u[i],o=e.y*n.x-n.y*e.x,r+=(e.x+n.x)*o,a+=(e.y+n.y)*o,s+=3*o;return h=0===s?u[0]:[r/s,a/s],this._map.layerPointToLatLng(h)},_convertLatLngs:function(t){var i=nn.prototype._convertLatLngs.call(this,t),e=i.length;return e>=2&&i[0]instanceof M&&i[0].equals(i[e-1])&&i.pop(),i},_setLatLngs:function(t){nn.prototype._setLatLngs.call(this,t),jt(this._latlngs)&&(this._latlngs=[this._latlngs])},_defaultShape:function(){return jt(this._latlngs[0])?this._latlngs[0]:this._latlngs[0][0]},_clipPoints:function(){var t=this._renderer._bounds,i=this.options.weight,e=new x(i,i);if(t=new P(t.min.subtract(e),t.max.add(e)),this._parts=[],this._pxBounds&&this._pxBounds.intersects(t))if(this.options.noClip)this._parts=this._rings;else for(var n,o=0,s=this._rings.length;o<s;o++)(n=Ht(this._rings[o],t,!0)).length&&this._parts.push(n)},_updatePath:function(){this._renderer._updatePoly(this,!0)},_containsPoint:function(t){var i,e,n,o,s,r,a,h,u=!1;if(!this._pxBounds||!this._pxBounds.contains(t))return!1;for(o=0,a=this._parts.length;o<a;o++)for(s=0,r=(h=(i=this._parts[o]).length)-1;s<h;r=s++)e=i[s],n=i[r],e.y>t.y!=n.y>t.y&&t.x<(n.x-e.x)*(t.y-e.y)/(n.y-e.y)+e.x&&(u=!u);return u||nn.prototype._containsPoint.call(this,t,!0)}}),sn=Ke.extend({initialize:function(t,i){l(this,i),this._layers={},t&&this.addData(t)},addData:function(t){var i,e,n,o=oi(t)?t:t.features;if(o){for(i=0,e=o.length;i<e;i++)((n=o[i]).geometries||n.geometry||n.features||n.coordinates)&&this.addData(n);return this}var s=this.options;if(s.filter&&!s.filter(t))return this;var r=Ft(t,s);return r?(r.feature=Yt(t),r.defaultOptions=r.options,this.resetStyle(r),s.onEachFeature&&s.onEachFeature(t,r),this.addLayer(r)):this},resetStyle:function(t){return t.options=i({},t.defaultOptions),this._setLayerStyle(t,this.options.style),this},setStyle:function(t){return this.eachLayer(function(i){this._setLayerStyle(i,t)},this)},_setLayerStyle:function(t,i){"function"==typeof i&&(i=i(t.feature)),t.setStyle&&t.setStyle(i)}}),rn={toGeoJSON:function(t){return Kt(this,{type:"Point",coordinates:qt(this.getLatLng(),t)})}};$e.include(rn),en.include(rn),tn.include(rn),nn.include({toGeoJSON:function(t){var i=!jt(this._latlngs),e=Gt(this._latlngs,i?1:0,!1,t);return Kt(this,{type:(i?"Multi":"")+"LineString",coordinates:e})}}),on.include({toGeoJSON:function(t){var i=!jt(this._latlngs),e=i&&!jt(this._latlngs[0]),n=Gt(this._latlngs,e?2:i?1:0,!0,t);return i||(n=[n]),Kt(this,{type:(e?"Multi":"")+"Polygon",coordinates:n})}}),Ge.include({toMultiPoint:function(t){var i=[];return this.eachLayer(function(e){i.push(e.toGeoJSON(t).geometry.coordinates)}),Kt(this,{type:"MultiPoint",coordinates:i})},toGeoJSON:function(t){var i=this.feature&&this.feature.geometry&&this.feature.geometry.type;if("MultiPoint"===i)return this.toMultiPoint(t);var e="GeometryCollection"===i,n=[];return this.eachLayer(function(i){if(i.toGeoJSON){var o=i.toGeoJSON(t);if(e)n.push(o.geometry);else{var s=Yt(o);"FeatureCollection"===s.type?n.push.apply(n,s.features):n.push(s)}}}),e?Kt(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}});var an=Xt,hn=qe.extend({options:{opacity:1,alt:"",interactive:!1,crossOrigin:!1,errorOverlayUrl:"",zIndex:1,className:""},initialize:function(t,i,e){this._url=t,this._bounds=z(i),l(this,e)},onAdd:function(){this._image||(this._initImage(),this.options.opacity<1&&this._updateOpacity()),this.options.interactive&&(Q(this._image,"leaflet-interactive"),this.addInteractiveTarget(this._image)),this.getPane().appendChild(this._image),this._reset()},onRemove:function(){K(this._image),this.options.interactive&&this.removeInteractiveTarget(this._image)},setOpacity:function(t){return this.options.opacity=t,this._image&&this._updateOpacity(),this},setStyle:function(t){return t.opacity&&this.setOpacity(t.opacity),this},bringToFront:function(){return this._map&&X(this._image),this},bringToBack:function(){return this._map&&J(this._image),this},setUrl:function(t){return this._url=t,this._image&&(this._image.src=t),this},setBounds:function(t){return this._bounds=z(t),this._map&&this._reset(),this},getEvents:function(){var t={zoom:this._reset,viewreset:this._reset};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},getBounds:function(){return this._bounds},getElement:function(){return this._image},_initImage:function(){var t="IMG"===this._url.tagName,i=this._image=t?this._url:G("img");Q(i,"leaflet-image-layer"),this._zoomAnimated&&Q(i,"leaflet-zoom-animated"),this.options.className&&Q(i,this.options.className),i.onselectstart=r,i.onmousemove=r,i.onload=e(this.fire,this,"load"),i.onerror=e(this._overlayOnError,this,"error"),(this.options.crossOrigin||""===this.options.crossOrigin)&&(i.crossOrigin=!0===this.options.crossOrigin?"":this.options.crossOrigin),this.options.zIndex&&this._updateZIndex(),t?this._url=i.src:(i.src=this._url,i.alt=this.options.alt)},_animateZoom:function(t){var i=this._map.getZoomScale(t.zoom),e=this._map._latLngBoundsToNewLayerBounds(this._bounds,t.zoom,t.center).min;rt(this._image,e,i)},_reset:function(){var t=this._image,i=new P(this._map.latLngToLayerPoint(this._bounds.getNorthWest()),this._map.latLngToLayerPoint(this._bounds.getSouthEast())),e=i.getSize();at(t,i.min),t.style.width=e.x+"px",t.style.height=e.y+"px"},_updateOpacity:function(){nt(this._image,this.options.opacity)},_updateZIndex:function(){this._image&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._image.style.zIndex=this.options.zIndex)},_overlayOnError:function(){this.fire("error");var t=this.options.errorOverlayUrl;t&&this._url!==t&&(this._url=t,this._image.src=t)}}),un=hn.extend({options:{autoplay:!0,loop:!0},_initImage:function(){var t="VIDEO"===this._url.tagName,i=this._image=t?this._url:G("video");if(Q(i,"leaflet-image-layer"),this._zoomAnimated&&Q(i,"leaflet-zoom-animated"),i.onselectstart=r,i.onmousemove=r,i.onloadeddata=e(this.fire,this,"load"),t){for(var n=i.getElementsByTagName("source"),o=[],s=0;s<n.length;s++)o.push(n[s].src);this._url=n.length>0?o:[i.src]}else{oi(this._url)||(this._url=[this._url]),i.autoplay=!!this.options.autoplay,i.loop=!!this.options.loop;for(var a=0;a<this._url.length;a++){var h=G("source");h.src=this._url[a],i.appendChild(h)}}}}),ln=qe.extend({options:{offset:[0,7],className:"",pane:"popupPane"},initialize:function(t,i){l(this,t),this._source=i},onAdd:function(t){this._zoomAnimated=t._zoomAnimated,this._container||this._initLayout(),t._fadeAnimated&&nt(this._container,0),clearTimeout(this._removeTimeout),this.getPane().appendChild(this._container),this.update(),t._fadeAnimated&&nt(this._container,1),this.bringToFront()},onRemove:function(t){t._fadeAnimated?(nt(this._container,0),this._removeTimeout=setTimeout(e(K,void 0,this._container),200)):K(this._container)},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=C(t),this._map&&(this._updatePosition(),this._adjustPan()),this},getContent:function(){return this._content},setContent:function(t){return this._content=t,this.update(),this},getElement:function(){return this._container},update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},getEvents:function(){var t={zoom:this._updatePosition,viewreset:this._updatePosition};return this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},isOpen:function(){return!!this._map&&this._map.hasLayer(this)},bringToFront:function(){return this._map&&X(this._container),this},bringToBack:function(){return this._map&&J(this._container),this},_updateContent:function(){if(this._content){var t=this._contentNode,i="function"==typeof this._content?this._content(this._source||this):this._content;if("string"==typeof i)t.innerHTML=i;else{for(;t.hasChildNodes();)t.removeChild(t.firstChild);t.appendChild(i)}this.fire("contentupdate")}},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),i=w(this.options.offset),e=this._getAnchor();this._zoomAnimated?at(this._container,t.add(e)):i=i.add(t).add(e);var n=this._containerBottom=-i.y,o=this._containerLeft=-Math.round(this._containerWidth/2)+i.x;this._container.style.bottom=n+"px",this._container.style.left=o+"px"}},_getAnchor:function(){return[0,0]}}),cn=ln.extend({options:{maxWidth:300,minWidth:50,maxHeight:null,autoPan:!0,autoPanPaddingTopLeft:null,autoPanPaddingBottomRight:null,autoPanPadding:[5,5],keepInView:!1,closeButton:!0,autoClose:!0,closeOnEscapeKey:!0,className:""},openOn:function(t){return t.openPopup(this),this},onAdd:function(t){ln.prototype.onAdd.call(this,t),t.fire("popupopen",{popup:this}),this._source&&(this._source.fire("popupopen",{popup:this},!0),this._source instanceof Qe||this._source.on("preclick",yt))},onRemove:function(t){ln.prototype.onRemove.call(this,t),t.fire("popupclose",{popup:this}),this._source&&(this._source.fire("popupclose",{popup:this},!0),this._source instanceof Qe||this._source.off("preclick",yt))},getEvents:function(){var t=ln.prototype.getEvents.call(this);return(void 0!==this.options.closeOnClick?this.options.closeOnClick:this._map.options.closePopupOnClick)&&(t.preclick=this._close),this.options.keepInView&&(t.moveend=this._adjustPan),t},_close:function(){this._map&&this._map.closePopup(this)},_initLayout:function(){var t="leaflet-popup",i=this._container=G("div",t+" "+(this.options.className||"")+" leaflet-zoom-animated"),e=this._wrapper=G("div",t+"-content-wrapper",i);if(this._contentNode=G("div",t+"-content",e),wt(e),xt(this._contentNode),mt(e,"contextmenu",yt),this._tipContainer=G("div",t+"-tip-container",i),this._tip=G("div",t+"-tip",this._tipContainer),this.options.closeButton){var n=this._closeButton=G("a",t+"-close-button",i);n.href="#close",n.innerHTML="&#215;",mt(n,"click",this._onCloseButtonClick,this)}},_updateLayout:function(){var t=this._contentNode,i=t.style;i.width="",i.whiteSpace="nowrap";var e=t.offsetWidth;e=Math.min(e,this.options.maxWidth),e=Math.max(e,this.options.minWidth),i.width=e+1+"px",i.whiteSpace="",i.height="";var n=t.offsetHeight,o=this.options.maxHeight;o&&n>o?(i.height=o+"px",Q(t,"leaflet-popup-scrolled")):tt(t,"leaflet-popup-scrolled"),this._containerWidth=this._container.offsetWidth},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),e=this._getAnchor();at(this._container,i.add(e))},_adjustPan:function(){if(this.options.autoPan){this._map._panAnim&&this._map._panAnim.stop();var t=this._map,i=parseInt(q(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+i,n=this._containerWidth,o=new x(this._containerLeft,-e-this._containerBottom);o._add(ht(this._container));var s=t.layerPointToContainerPoint(o),r=w(this.options.autoPanPadding),a=w(this.options.autoPanPaddingTopLeft||r),h=w(this.options.autoPanPaddingBottomRight||r),u=t.getSize(),l=0,c=0;s.x+n+h.x>u.x&&(l=s.x+n-u.x+h.x),s.x-l-a.x<0&&(l=s.x-a.x),s.y+e+h.y>u.y&&(c=s.y+e-u.y+h.y),s.y-c-a.y<0&&(c=s.y-a.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}},_onCloseButtonClick:function(t){this._close(),Lt(t)},_getAnchor:function(){return w(this._source&&this._source._getPopupAnchor?this._source._getPopupAnchor():[0,0])}});be.mergeOptions({closePopupOnClick:!0}),be.include({openPopup:function(t,i,e){return t instanceof cn||(t=new cn(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:(this._popup&&this._popup.options.autoClose&&this.closePopup(),this._popup=t,this.addLayer(t))},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&this.removeLayer(t),this}}),qe.include({bindPopup:function(t,i){return t instanceof cn?(l(t,i),this._popup=t,t._source=this):(this._popup&&!i||(this._popup=new cn(i,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this.off({click:this._openPopup,keypress:this._onKeyPress,remove:this.closePopup,move:this._movePopup}),this._popupHandlersAdded=!1,this._popup=null),this},openPopup:function(t,i){if(t instanceof qe||(i=t,t=this),t instanceof Ke)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._popup&&this._map&&(this._popup._source=t,this._popup.update(),this._map.openPopup(this._popup,i)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(t){return this._popup&&(this._popup._map?this.closePopup():this.openPopup(t)),this},isPopupOpen:function(){return!!this._popup&&this._popup.isOpen()},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},getPopup:function(){return this._popup},_openPopup:function(t){var i=t.layer||t.target;this._popup&&this._map&&(Lt(t),i instanceof Qe?this.openPopup(t.layer||t.target,t.latlng):this._map.hasLayer(this._popup)&&this._popup._source===i?this.closePopup():this.openPopup(i,t.latlng))},_movePopup:function(t){this._popup.setLatLng(t.latlng)},_onKeyPress:function(t){13===t.originalEvent.keyCode&&this._openPopup(t)}});var _n=ln.extend({options:{pane:"tooltipPane",offset:[0,0],direction:"auto",permanent:!1,sticky:!1,interactive:!1,opacity:.9},onAdd:function(t){ln.prototype.onAdd.call(this,t),this.setOpacity(this.options.opacity),t.fire("tooltipopen",{tooltip:this}),this._source&&this._source.fire("tooltipopen",{tooltip:this},!0)},onRemove:function(t){ln.prototype.onRemove.call(this,t),t.fire("tooltipclose",{tooltip:this}),this._source&&this._source.fire("tooltipclose",{tooltip:this},!0)},getEvents:function(){var t=ln.prototype.getEvents.call(this);return qi&&!this.options.permanent&&(t.preclick=this._close),t},_close:function(){this._map&&this._map.closeTooltip(this)},_initLayout:function(){var t="leaflet-tooltip "+(this.options.className||"")+" leaflet-zoom-"+(this._zoomAnimated?"animated":"hide");this._contentNode=this._container=G("div",t)},_updateLayout:function(){},_adjustPan:function(){},_setPosition:function(t){var i=this._map,e=this._container,n=i.latLngToContainerPoint(i.getCenter()),o=i.layerPointToContainerPoint(t),s=this.options.direction,r=e.offsetWidth,a=e.offsetHeight,h=w(this.options.offset),u=this._getAnchor();"top"===s?t=t.add(w(-r/2+h.x,-a+h.y+u.y,!0)):"bottom"===s?t=t.subtract(w(r/2-h.x,-h.y,!0)):"center"===s?t=t.subtract(w(r/2+h.x,a/2-u.y+h.y,!0)):"right"===s||"auto"===s&&o.x<n.x?(s="right",t=t.add(w(h.x+u.x,u.y-a/2+h.y,!0))):(s="left",t=t.subtract(w(r+u.x-h.x,a/2-u.y-h.y,!0))),tt(e,"leaflet-tooltip-right"),tt(e,"leaflet-tooltip-left"),tt(e,"leaflet-tooltip-top"),tt(e,"leaflet-tooltip-bottom"),Q(e,"leaflet-tooltip-"+s),at(e,t)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},setOpacity:function(t){this.options.opacity=t,this._container&&nt(this._container,t)},_animateZoom:function(t){var i=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);this._setPosition(i)},_getAnchor:function(){return w(this._source&&this._source._getTooltipAnchor&&!this.options.sticky?this._source._getTooltipAnchor():[0,0])}});be.include({openTooltip:function(t,i,e){return t instanceof _n||(t=new _n(e).setContent(t)),i&&t.setLatLng(i),this.hasLayer(t)?this:this.addLayer(t)},closeTooltip:function(t){return t&&this.removeLayer(t),this}}),qe.include({bindTooltip:function(t,i){return t instanceof _n?(l(t,i),this._tooltip=t,t._source=this):(this._tooltip&&!i||(this._tooltip=new _n(i,this)),this._tooltip.setContent(t)),this._initTooltipInteractions(),this._tooltip.options.permanent&&this._map&&this._map.hasLayer(this)&&this.openTooltip(),this},unbindTooltip:function(){return this._tooltip&&(this._initTooltipInteractions(!0),this.closeTooltip(),this._tooltip=null),this},_initTooltipInteractions:function(t){if(t||!this._tooltipHandlersAdded){var i=t?"off":"on",e={remove:this.closeTooltip,move:this._moveTooltip};this._tooltip.options.permanent?e.add=this._openTooltip:(e.mouseover=this._openTooltip,e.mouseout=this.closeTooltip,this._tooltip.options.sticky&&(e.mousemove=this._moveTooltip),qi&&(e.click=this._openTooltip)),this[i](e),this._tooltipHandlersAdded=!t}},openTooltip:function(t,i){if(t instanceof qe||(i=t,t=this),t instanceof Ke)for(var e in this._layers){t=this._layers[e];break}return i||(i=t.getCenter?t.getCenter():t.getLatLng()),this._tooltip&&this._map&&(this._tooltip._source=t,this._tooltip.update(),this._map.openTooltip(this._tooltip,i),this._tooltip.options.interactive&&this._tooltip._container&&(Q(this._tooltip._container,"leaflet-clickable"),this.addInteractiveTarget(this._tooltip._container))),this},closeTooltip:function(){return this._tooltip&&(this._tooltip._close(),this._tooltip.options.interactive&&this._tooltip._container&&(tt(this._tooltip._container,"leaflet-clickable"),this.removeInteractiveTarget(this._tooltip._container))),this},toggleTooltip:function(t){return this._tooltip&&(this._tooltip._map?this.closeTooltip():this.openTooltip(t)),this},isTooltipOpen:function(){return this._tooltip.isOpen()},setTooltipContent:function(t){return this._tooltip&&this._tooltip.setContent(t),this},getTooltip:function(){return this._tooltip},_openTooltip:function(t){var i=t.layer||t.target;this._tooltip&&this._map&&this.openTooltip(i,this._tooltip.options.sticky?t.latlng:void 0)},_moveTooltip:function(t){var i,e,n=t.latlng;this._tooltip.options.sticky&&t.originalEvent&&(i=this._map.mouseEventToContainerPoint(t.originalEvent),e=this._map.containerPointToLayerPoint(i),n=this._map.layerPointToLatLng(e)),this._tooltip.setLatLng(n)}});var dn=Ye.extend({options:{iconSize:[12,12],html:!1,bgPos:null,className:"leaflet-div-icon"},createIcon:function(t){var i=t&&"DIV"===t.tagName?t:document.createElement("div"),e=this.options;if(i.innerHTML=!1!==e.html?e.html:"",e.bgPos){var n=w(e.bgPos);i.style.backgroundPosition=-n.x+"px "+-n.y+"px"}return this._setIconStyles(i,"icon"),i},createShadow:function(){return null}});Ye.Default=Xe;var pn=qe.extend({options:{tileSize:256,opacity:1,updateWhenIdle:Wi,updateWhenZooming:!0,updateInterval:200,zIndex:1,bounds:null,minZoom:0,maxZoom:void 0,maxNativeZoom:void 0,minNativeZoom:void 0,noWrap:!1,pane:"tilePane",className:"",keepBuffer:2},initialize:function(t){l(this,t)},onAdd:function(){this._initContainer(),this._levels={},this._tiles={},this._resetView(),this._update()},beforeAdd:function(t){t._addZoomLimit(this)},onRemove:function(t){this._removeAllTiles(),K(this._container),t._removeZoomLimit(this),this._container=null,this._tileZoom=void 0},bringToFront:function(){return this._map&&(X(this._container),this._setAutoZIndex(Math.max)),this},bringToBack:function(){return this._map&&(J(this._container),this._setAutoZIndex(Math.min)),this},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},isLoading:function(){return this._loading},redraw:function(){return this._map&&(this._removeAllTiles(),this._update()),this},getEvents:function(){var t={viewprereset:this._invalidateAll,viewreset:this._resetView,zoom:this._resetView,moveend:this._onMoveEnd};return this.options.updateWhenIdle||(this._onMove||(this._onMove=o(this._onMoveEnd,this.options.updateInterval,this)),t.move=this._onMove),this._zoomAnimated&&(t.zoomanim=this._animateZoom),t},createTile:function(){return document.createElement("div")},getTileSize:function(){var t=this.options.tileSize;return t instanceof x?t:new x(t,t)},_updateZIndex:function(){this._container&&void 0!==this.options.zIndex&&null!==this.options.zIndex&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t){for(var i,e=this.getPane().children,n=-t(-1/0,1/0),o=0,s=e.length;o<s;o++)i=e[o].style.zIndex,e[o]!==this._container&&i&&(n=t(n,+i));isFinite(n)&&(this.options.zIndex=n+t(-1,1),this._updateZIndex())},_updateOpacity:function(){if(this._map&&!Li){nt(this._container,this.options.opacity);var t=+new Date,i=!1,e=!1;for(var n in this._tiles){var o=this._tiles[n];if(o.current&&o.loaded){var s=Math.min(1,(t-o.loaded)/200);nt(o.el,s),s<1?i=!0:(o.active?e=!0:this._onOpaqueTile(o),o.active=!0)}}e&&!this._noPrune&&this._pruneTiles(),i&&(g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this))}},_onOpaqueTile:r,_initContainer:function(){this._container||(this._container=G("div","leaflet-layer "+(this.options.className||"")),this._updateZIndex(),this.options.opacity<1&&this._updateOpacity(),this.getPane().appendChild(this._container))},_updateLevels:function(){var t=this._tileZoom,i=this.options.maxZoom;if(void 0!==t){for(var e in this._levels)this._levels[e].el.children.length||e===t?(this._levels[e].el.style.zIndex=i-Math.abs(t-e),this._onUpdateLevel(e)):(K(this._levels[e].el),this._removeTilesAtZoom(e),this._onRemoveLevel(e),delete this._levels[e]);var n=this._levels[t],o=this._map;return n||((n=this._levels[t]={}).el=G("div","leaflet-tile-container leaflet-zoom-animated",this._container),n.el.style.zIndex=i,n.origin=o.project(o.unproject(o.getPixelOrigin()),t).round(),n.zoom=t,this._setZoomTransform(n,o.getCenter(),o.getZoom()),n.el.offsetWidth,this._onCreateLevel(n)),this._level=n,n}},_onUpdateLevel:r,_onRemoveLevel:r,_onCreateLevel:r,_pruneTiles:function(){if(this._map){var t,i,e=this._map.getZoom();if(e>this.options.maxZoom||e<this.options.minZoom)this._removeAllTiles();else{for(t in this._tiles)(i=this._tiles[t]).retain=i.current;for(t in this._tiles)if((i=this._tiles[t]).current&&!i.active){var n=i.coords;this._retainParent(n.x,n.y,n.z,n.z-5)||this._retainChildren(n.x,n.y,n.z,n.z+2)}for(t in this._tiles)this._tiles[t].retain||this._removeTile(t)}}},_removeTilesAtZoom:function(t){for(var i in this._tiles)this._tiles[i].coords.z===t&&this._removeTile(i)},_removeAllTiles:function(){for(var t in this._tiles)this._removeTile(t)},_invalidateAll:function(){for(var t in this._levels)K(this._levels[t].el),this._onRemoveLevel(t),delete this._levels[t];this._removeAllTiles(),this._tileZoom=void 0},_retainParent:function(t,i,e,n){var o=Math.floor(t/2),s=Math.floor(i/2),r=e-1,a=new x(+o,+s);a.z=+r;var h=this._tileCoordsToKey(a),u=this._tiles[h];return u&&u.active?(u.retain=!0,!0):(u&&u.loaded&&(u.retain=!0),r>n&&this._retainParent(o,s,r,n))},_retainChildren:function(t,i,e,n){for(var o=2*t;o<2*t+2;o++)for(var s=2*i;s<2*i+2;s++){var r=new x(o,s);r.z=e+1;var a=this._tileCoordsToKey(r),h=this._tiles[a];h&&h.active?h.retain=!0:(h&&h.loaded&&(h.retain=!0),e+1<n&&this._retainChildren(o,s,e+1,n))}},_resetView:function(t){var i=t&&(t.pinch||t.flyTo);this._setView(this._map.getCenter(),this._map.getZoom(),i,i)},_animateZoom:function(t){this._setView(t.center,t.zoom,!0,t.noUpdate)},_clampZoom:function(t){var i=this.options;return void 0!==i.minNativeZoom&&t<i.minNativeZoom?i.minNativeZoom:void 0!==i.maxNativeZoom&&i.maxNativeZoom<t?i.maxNativeZoom:t},_setView:function(t,i,e,n){var o=this._clampZoom(Math.round(i));(void 0!==this.options.maxZoom&&o>this.options.maxZoom||void 0!==this.options.minZoom&&o<this.options.minZoom)&&(o=void 0);var s=this.options.updateWhenZooming&&o!==this._tileZoom;n&&!s||(this._tileZoom=o,this._abortLoading&&this._abortLoading(),this._updateLevels(),this._resetGrid(),void 0!==o&&this._update(t),e||this._pruneTiles(),this._noPrune=!!e),this._setZoomTransforms(t,i)},_setZoomTransforms:function(t,i){for(var e in this._levels)this._setZoomTransform(this._levels[e],t,i)},_setZoomTransform:function(t,i,e){var n=this._map.getZoomScale(e,t.zoom),o=t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(i,e)).round();ji?rt(t.el,o,n):at(t.el,o)},_resetGrid:function(){var t=this._map,i=t.options.crs,e=this._tileSize=this.getTileSize(),n=this._tileZoom,o=this._map.getPixelWorldBounds(this._tileZoom);o&&(this._globalTileRange=this._pxBoundsToTileRange(o)),this._wrapX=i.wrapLng&&!this.options.noWrap&&[Math.floor(t.project([0,i.wrapLng[0]],n).x/e.x),Math.ceil(t.project([0,i.wrapLng[1]],n).x/e.y)],this._wrapY=i.wrapLat&&!this.options.noWrap&&[Math.floor(t.project([i.wrapLat[0],0],n).y/e.x),Math.ceil(t.project([i.wrapLat[1],0],n).y/e.y)]},_onMoveEnd:function(){this._map&&!this._map._animatingZoom&&this._update()},_getTiledPixelBounds:function(t){var i=this._map,e=i._animatingZoom?Math.max(i._animateToZoom,i.getZoom()):i.getZoom(),n=i.getZoomScale(e,this._tileZoom),o=i.project(t,this._tileZoom).floor(),s=i.getSize().divideBy(2*n);return new P(o.subtract(s),o.add(s))},_update:function(t){var i=this._map;if(i){var e=this._clampZoom(i.getZoom());if(void 0===t&&(t=i.getCenter()),void 0!==this._tileZoom){var n=this._getTiledPixelBounds(t),o=this._pxBoundsToTileRange(n),s=o.getCenter(),r=[],a=this.options.keepBuffer,h=new P(o.getBottomLeft().subtract([a,-a]),o.getTopRight().add([a,-a]));if(!(isFinite(o.min.x)&&isFinite(o.min.y)&&isFinite(o.max.x)&&isFinite(o.max.y)))throw new Error("Attempted to load an infinite number of tiles");for(var u in this._tiles){var l=this._tiles[u].coords;l.z===this._tileZoom&&h.contains(new x(l.x,l.y))||(this._tiles[u].current=!1)}if(Math.abs(e-this._tileZoom)>1)this._setView(t,e);else{for(var c=o.min.y;c<=o.max.y;c++)for(var _=o.min.x;_<=o.max.x;_++){var d=new x(_,c);if(d.z=this._tileZoom,this._isValidTile(d)){var p=this._tiles[this._tileCoordsToKey(d)];p?p.current=!0:r.push(d)}}if(r.sort(function(t,i){return t.distanceTo(s)-i.distanceTo(s)}),0!==r.length){this._loading||(this._loading=!0,this.fire("loading"));var m=document.createDocumentFragment();for(_=0;_<r.length;_++)this._addTile(r[_],m);this._level.el.appendChild(m)}}}}},_isValidTile:function(t){var i=this._map.options.crs;if(!i.infinite){var e=this._globalTileRange;if(!i.wrapLng&&(t.x<e.min.x||t.x>e.max.x)||!i.wrapLat&&(t.y<e.min.y||t.y>e.max.y))return!1}if(!this.options.bounds)return!0;var n=this._tileCoordsToBounds(t);return z(this.options.bounds).overlaps(n)},_keyToBounds:function(t){return this._tileCoordsToBounds(this._keyToTileCoords(t))},_tileCoordsToNwSe:function(t){var i=this._map,e=this.getTileSize(),n=t.scaleBy(e),o=n.add(e);return[i.unproject(n,t.z),i.unproject(o,t.z)]},_tileCoordsToBounds:function(t){var i=this._tileCoordsToNwSe(t),e=new T(i[0],i[1]);return this.options.noWrap||(e=this._map.wrapLatLngBounds(e)),e},_tileCoordsToKey:function(t){return t.x+":"+t.y+":"+t.z},_keyToTileCoords:function(t){var i=t.split(":"),e=new x(+i[0],+i[1]);return e.z=+i[2],e},_removeTile:function(t){var i=this._tiles[t];i&&(K(i.el),delete this._tiles[t],this.fire("tileunload",{tile:i.el,coords:this._keyToTileCoords(t)}))},_initTile:function(t){Q(t,"leaflet-tile");var i=this.getTileSize();t.style.width=i.x+"px",t.style.height=i.y+"px",t.onselectstart=r,t.onmousemove=r,Li&&this.options.opacity<1&&nt(t,this.options.opacity),zi&&!Mi&&(t.style.WebkitBackfaceVisibility="hidden")},_addTile:function(t,i){var n=this._getTilePos(t),o=this._tileCoordsToKey(t),s=this.createTile(this._wrapCoords(t),e(this._tileReady,this,t));this._initTile(s),this.createTile.length<2&&f(e(this._tileReady,this,t,null,s)),at(s,n),this._tiles[o]={el:s,coords:t,current:!0},i.appendChild(s),this.fire("tileloadstart",{tile:s,coords:t})},_tileReady:function(t,i,n){i&&this.fire("tileerror",{error:i,tile:n,coords:t});var o=this._tileCoordsToKey(t);(n=this._tiles[o])&&(n.loaded=+new Date,this._map._fadeAnimated?(nt(n.el,0),g(this._fadeFrame),this._fadeFrame=f(this._updateOpacity,this)):(n.active=!0,this._pruneTiles()),i||(Q(n.el,"leaflet-tile-loaded"),this.fire("tileload",{tile:n.el,coords:t})),this._noTilesToLoad()&&(this._loading=!1,this.fire("load"),Li||!this._map._fadeAnimated?f(this._pruneTiles,this):setTimeout(e(this._pruneTiles,this),250)))},_getTilePos:function(t){return t.scaleBy(this.getTileSize()).subtract(this._level.origin)},_wrapCoords:function(t){var i=new x(this._wrapX?s(t.x,this._wrapX):t.x,this._wrapY?s(t.y,this._wrapY):t.y);return i.z=t.z,i},_pxBoundsToTileRange:function(t){var i=this.getTileSize();return new P(t.min.unscaleBy(i).floor(),t.max.unscaleBy(i).ceil().subtract([1,1]))},_noTilesToLoad:function(){for(var t in this._tiles)if(!this._tiles[t].loaded)return!1;return!0}}),mn=pn.extend({options:{minZoom:0,maxZoom:18,subdomains:"abc",errorTileUrl:"",zoomOffset:0,tms:!1,zoomReverse:!1,detectRetina:!1,crossOrigin:!1},initialize:function(t,i){this._url=t,(i=l(this,i)).detectRetina&&Yi&&i.maxZoom>0&&(i.tileSize=Math.floor(i.tileSize/2),i.zoomReverse?(i.zoomOffset--,i.minZoom++):(i.zoomOffset++,i.maxZoom--),i.minZoom=Math.max(0,i.minZoom)),"string"==typeof i.subdomains&&(i.subdomains=i.subdomains.split("")),zi||this.on("tileunload",this._onTileRemove)},setUrl:function(t,i){return this._url===t&&void 0===i&&(i=!0),this._url=t,i||this.redraw(),this},createTile:function(t,i){var n=document.createElement("img");return mt(n,"load",e(this._tileOnLoad,this,i,n)),mt(n,"error",e(this._tileOnError,this,i,n)),(this.options.crossOrigin||""===this.options.crossOrigin)&&(n.crossOrigin=!0===this.options.crossOrigin?"":this.options.crossOrigin),n.alt="",n.setAttribute("role","presentation"),n.src=this.getTileUrl(t),n},getTileUrl:function(t){var e={r:Yi?"@2x":"",s:this._getSubdomain(t),x:t.x,y:t.y,z:this._getZoomForUrl()};if(this._map&&!this._map.options.crs.infinite){var n=this._globalTileRange.max.y-t.y;this.options.tms&&(e.y=n),e["-y"]=n}return _(this._url,i(e,this.options))},_tileOnLoad:function(t,i){Li?setTimeout(e(t,this,null,i),0):t(null,i)},_tileOnError:function(t,i,e){var n=this.options.errorTileUrl;n&&i.getAttribute("src")!==n&&(i.src=n),t(e,i)},_onTileRemove:function(t){t.tile.onload=null},_getZoomForUrl:function(){var t=this._tileZoom,i=this.options.maxZoom,e=this.options.zoomReverse,n=this.options.zoomOffset;return e&&(t=i-t),t+n},_getSubdomain:function(t){var i=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[i]},_abortLoading:function(){var t,i;for(t in this._tiles)this._tiles[t].coords.z!==this._tileZoom&&((i=this._tiles[t].el).onload=r,i.onerror=r,i.complete||(i.src=si,K(i),delete this._tiles[t]))},_removeTile:function(t){var i=this._tiles[t];if(i)return Si||i.el.setAttribute("src",si),pn.prototype._removeTile.call(this,t)},_tileReady:function(t,i,e){if(this._map&&(!e||e.getAttribute("src")!==si))return pn.prototype._tileReady.call(this,t,i,e)}}),fn=mn.extend({defaultWmsParams:{service:"WMS",request:"GetMap",layers:"",styles:"",format:"image/jpeg",transparent:!1,version:"1.1.1"},options:{crs:null,uppercase:!1},initialize:function(t,e){this._url=t;var n=i({},this.defaultWmsParams);for(var o in e)o in this.options||(n[o]=e[o]);var s=(e=l(this,e)).detectRetina&&Yi?2:1,r=this.getTileSize();n.width=r.x*s,n.height=r.y*s,this.wmsParams=n},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var i=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[i]=this._crs.code,mn.prototype.onAdd.call(this,t)},getTileUrl:function(t){var i=this._tileCoordsToNwSe(t),e=this._crs,n=b(e.project(i[0]),e.project(i[1])),o=n.min,s=n.max,r=(this._wmsVersion>=1.3&&this._crs===Ue?[o.y,o.x,s.y,s.x]:[o.x,o.y,s.x,s.y]).join(","),a=mn.prototype.getTileUrl.call(this,t);return a+c(this.wmsParams,a,this.options.uppercase)+(this.options.uppercase?"&BBOX=":"&bbox=")+r},setParams:function(t,e){return i(this.wmsParams,t),e||this.redraw(),this}});mn.WMS=fn,Jt.wms=function(t,i){return new fn(t,i)};var gn=qe.extend({options:{padding:.1,tolerance:0},initialize:function(t){l(this,t),n(this),this._layers=this._layers||{}},onAdd:function(){this._container||(this._initContainer(),this._zoomAnimated&&Q(this._container,"leaflet-zoom-animated")),this.getPane().appendChild(this._container),this._update(),this.on("update",this._updatePaths,this)},onRemove:function(){this.off("update",this._updatePaths,this),this._destroyContainer()},getEvents:function(){var t={viewreset:this._reset,zoom:this._onZoom,moveend:this._update,zoomend:this._onZoomEnd};return this._zoomAnimated&&(t.zoomanim=this._onAnimZoom),t},_onAnimZoom:function(t){this._updateTransform(t.center,t.zoom)},_onZoom:function(){this._updateTransform(this._map.getCenter(),this._map.getZoom())},_updateTransform:function(t,i){var e=this._map.getZoomScale(i,this._zoom),n=ht(this._container),o=this._map.getSize().multiplyBy(.5+this.options.padding),s=this._map.project(this._center,i),r=this._map.project(t,i).subtract(s),a=o.multiplyBy(-e).add(n).add(o).subtract(r);ji?rt(this._container,a,e):at(this._container,a)},_reset:function(){this._update(),this._updateTransform(this._center,this._zoom);for(var t in this._layers)this._layers[t]._reset()},_onZoomEnd:function(){for(var t in this._layers)this._layers[t]._project()},_updatePaths:function(){for(var t in this._layers)this._layers[t]._update()},_update:function(){var t=this.options.padding,i=this._map.getSize(),e=this._map.containerPointToLayerPoint(i.multiplyBy(-t)).round();this._bounds=new P(e,e.add(i.multiplyBy(1+2*t)).round()),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}}),vn=gn.extend({getEvents:function(){var t=gn.prototype.getEvents.call(this);return t.viewprereset=this._onViewPreReset,t},_onViewPreReset:function(){this._postponeUpdatePaths=!0},onAdd:function(){gn.prototype.onAdd.call(this),this._draw()},_initContainer:function(){var t=this._container=document.createElement("canvas");mt(t,"mousemove",o(this._onMouseMove,32,this),this),mt(t,"click dblclick mousedown mouseup contextmenu",this._onClick,this),mt(t,"mouseout",this._handleMouseOut,this),this._ctx=t.getContext("2d")},_destroyContainer:function(){g(this._redrawRequest),delete this._ctx,K(this._container),ft(this._container),delete this._container},_updatePaths:function(){if(!this._postponeUpdatePaths){this._redrawBounds=null;for(var t in this._layers)this._layers[t]._update();this._redraw()}},_update:function(){if(!this._map._animatingZoom||!this._bounds){gn.prototype._update.call(this);var t=this._bounds,i=this._container,e=t.getSize(),n=Yi?2:1;at(i,t.min),i.width=n*e.x,i.height=n*e.y,i.style.width=e.x+"px",i.style.height=e.y+"px",Yi&&this._ctx.scale(2,2),this._ctx.translate(-t.min.x,-t.min.y),this.fire("update")}},_reset:function(){gn.prototype._reset.call(this),this._postponeUpdatePaths&&(this._postponeUpdatePaths=!1,this._updatePaths())},_initPath:function(t){this._updateDashArray(t),this._layers[n(t)]=t;var i=t._order={layer:t,prev:this._drawLast,next:null};this._drawLast&&(this._drawLast.next=i),this._drawLast=i,this._drawFirst=this._drawFirst||this._drawLast},_addPath:function(t){this._requestRedraw(t)},_removePath:function(t){var i=t._order,e=i.next,o=i.prev;e?e.prev=o:this._drawLast=o,o?o.next=e:this._drawFirst=e,delete t._order,delete this._layers[n(t)],this._requestRedraw(t)},_updatePath:function(t){this._extendRedrawBounds(t),t._project(),t._update(),this._requestRedraw(t)},_updateStyle:function(t){this._updateDashArray(t),this._requestRedraw(t)},_updateDashArray:function(t){if("string"==typeof t.options.dashArray){var i,e,n=t.options.dashArray.split(/[, ]+/),o=[];for(e=0;e<n.length;e++){if(i=Number(n[e]),isNaN(i))return;o.push(i)}t.options._dashArray=o}else t.options._dashArray=t.options.dashArray},_requestRedraw:function(t){this._map&&(this._extendRedrawBounds(t),this._redrawRequest=this._redrawRequest||f(this._redraw,this))},_extendRedrawBounds:function(t){if(t._pxBounds){var i=(t.options.weight||0)+1;this._redrawBounds=this._redrawBounds||new P,this._redrawBounds.extend(t._pxBounds.min.subtract([i,i])),this._redrawBounds.extend(t._pxBounds.max.add([i,i]))}},_redraw:function(){this._redrawRequest=null,this._redrawBounds&&(this._redrawBounds.min._floor(),this._redrawBounds.max._ceil()),this._clear(),this._draw(),this._redrawBounds=null},_clear:function(){var t=this._redrawBounds;if(t){var i=t.getSize();this._ctx.clearRect(t.min.x,t.min.y,i.x,i.y)}else this._ctx.clearRect(0,0,this._container.width,this._container.height)},_draw:function(){var t,i=this._redrawBounds;if(this._ctx.save(),i){var e=i.getSize();this._ctx.beginPath(),this._ctx.rect(i.min.x,i.min.y,e.x,e.y),this._ctx.clip()}this._drawing=!0;for(var n=this._drawFirst;n;n=n.next)t=n.layer,(!i||t._pxBounds&&t._pxBounds.intersects(i))&&t._updatePath();this._drawing=!1,this._ctx.restore()},_updatePoly:function(t,i){if(this._drawing){var e,n,o,s,r=t._parts,a=r.length,h=this._ctx;if(a){for(h.beginPath(),e=0;e<a;e++){for(n=0,o=r[e].length;n<o;n++)s=r[e][n],h[n?"lineTo":"moveTo"](s.x,s.y);i&&h.closePath()}this._fillStroke(h,t)}}},_updateCircle:function(t){if(this._drawing&&!t._empty()){var i=t._point,e=this._ctx,n=Math.max(Math.round(t._radius),1),o=(Math.max(Math.round(t._radiusY),1)||n)/n;1!==o&&(e.save(),e.scale(1,o)),e.beginPath(),e.arc(i.x,i.y/o,n,0,2*Math.PI,!1),1!==o&&e.restore(),this._fillStroke(e,t)}},_fillStroke:function(t,i){var e=i.options;e.fill&&(t.globalAlpha=e.fillOpacity,t.fillStyle=e.fillColor||e.color,t.fill(e.fillRule||"evenodd")),e.stroke&&0!==e.weight&&(t.setLineDash&&t.setLineDash(i.options&&i.options._dashArray||[]),t.globalAlpha=e.opacity,t.lineWidth=e.weight,t.strokeStyle=e.color,t.lineCap=e.lineCap,t.lineJoin=e.lineJoin,t.stroke())},_onClick:function(t){for(var i,e,n=this._map.mouseEventToLayerPoint(t),o=this._drawFirst;o;o=o.next)(i=o.layer).options.interactive&&i._containsPoint(n)&&!this._map._draggableMoved(i)&&(e=i);e&&(zt(t),this._fireEvent([e],t))},_onMouseMove:function(t){if(this._map&&!this._map.dragging.moving()&&!this._map._animatingZoom){var i=this._map.mouseEventToLayerPoint(t);this._handleMouseHover(t,i)}},_handleMouseOut:function(t){var i=this._hoveredLayer;i&&(tt(this._container,"leaflet-interactive"),this._fireEvent([i],t,"mouseout"),this._hoveredLayer=null)},_handleMouseHover:function(t,i){for(var e,n,o=this._drawFirst;o;o=o.next)(e=o.layer).options.interactive&&e._containsPoint(i)&&(n=e);n!==this._hoveredLayer&&(this._handleMouseOut(t),n&&(Q(this._container,"leaflet-interactive"),this._fireEvent([n],t,"mouseover"),this._hoveredLayer=n)),this._hoveredLayer&&this._fireEvent([this._hoveredLayer],t)},_fireEvent:function(t,i,e){this._map._fireDOMEvent(i,e||i.type,t)},_bringToFront:function(t){var i=t._order;if(i){var e=i.next,n=i.prev;e&&(e.prev=n,n?n.next=e:e&&(this._drawFirst=e),i.prev=this._drawLast,this._drawLast.next=i,i.next=null,this._drawLast=i,this._requestRedraw(t))}},_bringToBack:function(t){var i=t._order;if(i){var e=i.next,n=i.prev;n&&(n.next=e,e?e.prev=n:n&&(this._drawLast=n),i.prev=null,i.next=this._drawFirst,this._drawFirst.prev=i,this._drawFirst=i,this._requestRedraw(t))}}}),yn=function(){try{return document.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return document.createElement("<lvml:"+t+' class="lvml">')}}catch(t){return function(t){return document.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),xn={_initContainer:function(){this._container=G("div","leaflet-vml-container")},_update:function(){this._map._animatingZoom||(gn.prototype._update.call(this),this.fire("update"))},_initPath:function(t){var i=t._container=yn("shape");Q(i,"leaflet-vml-shape "+(this.options.className||"")),i.coordsize="1 1",t._path=yn("path"),i.appendChild(t._path),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){var i=t._container;this._container.appendChild(i),t.options.interactive&&t.addInteractiveTarget(i)},_removePath:function(t){var i=t._container;K(i),t.removeInteractiveTarget(i),delete this._layers[n(t)]},_updateStyle:function(t){var i=t._stroke,e=t._fill,n=t.options,o=t._container;o.stroked=!!n.stroke,o.filled=!!n.fill,n.stroke?(i||(i=t._stroke=yn("stroke")),o.appendChild(i),i.weight=n.weight+"px",i.color=n.color,i.opacity=n.opacity,n.dashArray?i.dashStyle=oi(n.dashArray)?n.dashArray.join(" "):n.dashArray.replace(/( *, *)/g," "):i.dashStyle="",i.endcap=n.lineCap.replace("butt","flat"),i.joinstyle=n.lineJoin):i&&(o.removeChild(i),t._stroke=null),n.fill?(e||(e=t._fill=yn("fill")),o.appendChild(e),e.color=n.fillColor||n.color,e.opacity=n.fillOpacity):e&&(o.removeChild(e),t._fill=null)},_updateCircle:function(t){var i=t._point.round(),e=Math.round(t._radius),n=Math.round(t._radiusY||e);this._setPath(t,t._empty()?"M0 0":"AL "+i.x+","+i.y+" "+e+","+n+" 0,23592600")},_setPath:function(t,i){t._path.v=i},_bringToFront:function(t){X(t._container)},_bringToBack:function(t){J(t._container)}},wn=$i?yn:E,Pn=gn.extend({getEvents:function(){var t=gn.prototype.getEvents.call(this);return t.zoomstart=this._onZoomStart,t},_initContainer:function(){this._container=wn("svg"),this._container.setAttribute("pointer-events","none"),this._rootGroup=wn("g"),this._container.appendChild(this._rootGroup)},_destroyContainer:function(){K(this._container),ft(this._container),delete this._container,delete this._rootGroup,delete this._svgSize},_onZoomStart:function(){this._update()},_update:function(){if(!this._map._animatingZoom||!this._bounds){gn.prototype._update.call(this);var t=this._bounds,i=t.getSize(),e=this._container;this._svgSize&&this._svgSize.equals(i)||(this._svgSize=i,e.setAttribute("width",i.x),e.setAttribute("height",i.y)),at(e,t.min),e.setAttribute("viewBox",[t.min.x,t.min.y,i.x,i.y].join(" ")),this.fire("update")}},_initPath:function(t){var i=t._path=wn("path");t.options.className&&Q(i,t.options.className),t.options.interactive&&Q(i,"leaflet-interactive"),this._updateStyle(t),this._layers[n(t)]=t},_addPath:function(t){this._rootGroup||this._initContainer(),this._rootGroup.appendChild(t._path),t.addInteractiveTarget(t._path)},_removePath:function(t){K(t._path),t.removeInteractiveTarget(t._path),delete this._layers[n(t)]},_updatePath:function(t){t._project(),t._update()},_updateStyle:function(t){var i=t._path,e=t.options;i&&(e.stroke?(i.setAttribute("stroke",e.color),i.setAttribute("stroke-opacity",e.opacity),i.setAttribute("stroke-width",e.weight),i.setAttribute("stroke-linecap",e.lineCap),i.setAttribute("stroke-linejoin",e.lineJoin),e.dashArray?i.setAttribute("stroke-dasharray",e.dashArray):i.removeAttribute("stroke-dasharray"),e.dashOffset?i.setAttribute("stroke-dashoffset",e.dashOffset):i.removeAttribute("stroke-dashoffset")):i.setAttribute("stroke","none"),e.fill?(i.setAttribute("fill",e.fillColor||e.color),i.setAttribute("fill-opacity",e.fillOpacity),i.setAttribute("fill-rule",e.fillRule||"evenodd")):i.setAttribute("fill","none"))},_updatePoly:function(t,i){this._setPath(t,k(t._parts,i))},_updateCircle:function(t){var i=t._point,e=Math.max(Math.round(t._radius),1),n="a"+e+","+(Math.max(Math.round(t._radiusY),1)||e)+" 0 1,0 ",o=t._empty()?"M0 0":"M"+(i.x-e)+","+i.y+n+2*e+",0 "+n+2*-e+",0 ";this._setPath(t,o)},_setPath:function(t,i){t._path.setAttribute("d",i)},_bringToFront:function(t){X(t._path)},_bringToBack:function(t){J(t._path)}});$i&&Pn.include(xn),be.include({getRenderer:function(t){var i=t.options.renderer||this._getPaneRenderer(t.options.pane)||this.options.renderer||this._renderer;return i||(i=this._renderer=this._createRenderer()),this.hasLayer(i)||this.addLayer(i),i},_getPaneRenderer:function(t){if("overlayPane"===t||void 0===t)return!1;var i=this._paneRenderers[t];return void 0===i&&(i=this._createRenderer({pane:t}),this._paneRenderers[t]=i),i},_createRenderer:function(t){return this.options.preferCanvas&&$t(t)||Qt(t)}});var Ln=on.extend({initialize:function(t,i){on.prototype.initialize.call(this,this._boundsToLatLngs(t),i)},setBounds:function(t){return this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=z(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}});Pn.create=wn,Pn.pointsToPath=k,sn.geometryToLayer=Ft,sn.coordsToLatLng=Ut,sn.coordsToLatLngs=Vt,sn.latLngToCoords=qt,sn.latLngsToCoords=Gt,sn.getFeature=Kt,sn.asFeature=Yt,be.mergeOptions({boxZoom:!0});var bn=Ee.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._resetStateTimeout=0,t.on("unload",this._destroy,this)},addHooks:function(){mt(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){ft(this._container,"mousedown",this._onMouseDown,this)},moved:function(){return this._moved},_destroy:function(){K(this._pane),delete this._pane},_resetState:function(){this._resetStateTimeout=0,this._moved=!1},_clearDeferredResetState:function(){0!==this._resetStateTimeout&&(clearTimeout(this._resetStateTimeout),this._resetStateTimeout=0)},_onMouseDown:function(t){if(!t.shiftKey||1!==t.which&&1!==t.button)return!1;this._clearDeferredResetState(),this._resetState(),fi(),ut(),this._startPoint=this._map.mouseEventToContainerPoint(t),mt(document,{contextmenu:Lt,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseMove:function(t){this._moved||(this._moved=!0,this._box=G("div","leaflet-zoom-box",this._container),Q(this._container,"leaflet-crosshair"),this._map.fire("boxzoomstart")),this._point=this._map.mouseEventToContainerPoint(t);var i=new P(this._point,this._startPoint),e=i.getSize();at(this._box,i.min),this._box.style.width=e.x+"px",this._box.style.height=e.y+"px"},_finish:function(){this._moved&&(K(this._box),tt(this._container,"leaflet-crosshair")),gi(),lt(),ft(document,{contextmenu:Lt,mousemove:this._onMouseMove,mouseup:this._onMouseUp,keydown:this._onKeyDown},this)},_onMouseUp:function(t){if((1===t.which||1===t.button)&&(this._finish(),this._moved)){this._clearDeferredResetState(),this._resetStateTimeout=setTimeout(e(this._resetState,this),0);var i=new T(this._map.containerPointToLatLng(this._startPoint),this._map.containerPointToLatLng(this._point));this._map.fitBounds(i).fire("boxzoomend",{boxZoomBounds:i})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}});be.addInitHook("addHandler","boxZoom",bn),be.mergeOptions({doubleClickZoom:!0});var Tn=Ee.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var i=this._map,e=i.getZoom(),n=i.options.zoomDelta,o=t.originalEvent.shiftKey?e-n:e+n;"center"===i.options.doubleClickZoom?i.setZoom(o):i.setZoomAround(t.containerPoint,o)}});be.addInitHook("addHandler","doubleClickZoom",Tn),be.mergeOptions({dragging:!0,inertia:!Mi,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,easeLinearity:.2,worldCopyJump:!1,maxBoundsViscosity:0});var zn=Ee.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new Re(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),this._draggable.on("predrag",this._onPreDragLimit,this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDragWrap,this),t.on("zoomend",this._onZoomEnd,this),t.whenReady(this._onZoomEnd,this))}Q(this._map._container,"leaflet-grab leaflet-touch-drag"),this._draggable.enable(),this._positions=[],this._times=[]},removeHooks:function(){tt(this._map._container,"leaflet-grab"),tt(this._map._container,"leaflet-touch-drag"),this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},moving:function(){return this._draggable&&this._draggable._moving},_onDragStart:function(){var t=this._map;if(t._stop(),this._map.options.maxBounds&&this._map.options.maxBoundsViscosity){var i=z(this._map.options.maxBounds);this._offsetLimit=b(this._map.latLngToContainerPoint(i.getNorthWest()).multiplyBy(-1),this._map.latLngToContainerPoint(i.getSouthEast()).multiplyBy(-1).add(this._map.getSize())),this._viscosity=Math.min(1,Math.max(0,this._map.options.maxBoundsViscosity))}else this._offsetLimit=null;t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(t){if(this._map.options.inertia){var i=this._lastTime=+new Date,e=this._lastPos=this._draggable._absPos||this._draggable._newPos;this._positions.push(e),this._times.push(i),this._prunePositions(i)}this._map.fire("move",t).fire("drag",t)},_prunePositions:function(t){for(;this._positions.length>1&&t-this._times[0]>50;)this._positions.shift(),this._times.shift()},_onZoomEnd:function(){var t=this._map.getSize().divideBy(2),i=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=i.subtract(t).x,this._worldWidth=this._map.getPixelWorldBounds().getSize().x},_viscousLimit:function(t,i){return t-(t-i)*this._viscosity},_onPreDragLimit:function(){if(this._viscosity&&this._offsetLimit){var t=this._draggable._newPos.subtract(this._draggable._startPos),i=this._offsetLimit;t.x<i.min.x&&(t.x=this._viscousLimit(t.x,i.min.x)),t.y<i.min.y&&(t.y=this._viscousLimit(t.y,i.min.y)),t.x>i.max.x&&(t.x=this._viscousLimit(t.x,i.max.x)),t.y>i.max.y&&(t.y=this._viscousLimit(t.y,i.max.y)),this._draggable._newPos=this._draggable._startPos.add(t)}},_onPreDragWrap:function(){var t=this._worldWidth,i=Math.round(t/2),e=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-i+e)%t+i-e,s=(n+i+e)%t-i-e,r=Math.abs(o+e)<Math.abs(s+e)?o:s;this._draggable._absPos=this._draggable._newPos.clone(),this._draggable._newPos.x=r},_onDragEnd:function(t){var i=this._map,e=i.options,n=!e.inertia||this._times.length<2;if(i.fire("dragend",t),n)i.fire("moveend");else{this._prunePositions(+new Date);var o=this._lastPos.subtract(this._positions[0]),s=(this._lastTime-this._times[0])/1e3,r=e.easeLinearity,a=o.multiplyBy(r/s),h=a.distanceTo([0,0]),u=Math.min(e.inertiaMaxSpeed,h),l=a.multiplyBy(u/h),c=u/(e.inertiaDeceleration*r),_=l.multiplyBy(-c/2).round();_.x||_.y?(_=i._limitOffset(_,i.options.maxBounds),f(function(){i.panBy(_,{duration:c,easeLinearity:r,noMoveStart:!0,animate:!0})})):i.fire("moveend")}}});be.addInitHook("addHandler","dragging",zn),be.mergeOptions({keyboard:!0,keyboardPanDelta:80});var Mn=Ee.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,54,173]},initialize:function(t){this._map=t,this._setPanDelta(t.options.keyboardPanDelta),this._setZoomDelta(t.options.zoomDelta)},addHooks:function(){var t=this._map._container;t.tabIndex<=0&&(t.tabIndex="0"),mt(t,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.on({focus:this._addHooks,blur:this._removeHooks},this)},removeHooks:function(){this._removeHooks(),ft(this._map._container,{focus:this._onFocus,blur:this._onBlur,mousedown:this._onMouseDown},this),this._map.off({focus:this._addHooks,blur:this._removeHooks},this)},_onMouseDown:function(){if(!this._focused){var t=document.body,i=document.documentElement,e=t.scrollTop||i.scrollTop,n=t.scrollLeft||i.scrollLeft;this._map._container.focus(),window.scrollTo(n,e)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanDelta:function(t){var i,e,n=this._panKeys={},o=this.keyCodes;for(i=0,e=o.left.length;i<e;i++)n[o.left[i]]=[-1*t,0];for(i=0,e=o.right.length;i<e;i++)n[o.right[i]]=[t,0];for(i=0,e=o.down.length;i<e;i++)n[o.down[i]]=[0,t];for(i=0,e=o.up.length;i<e;i++)n[o.up[i]]=[0,-1*t]},_setZoomDelta:function(t){var i,e,n=this._zoomKeys={},o=this.keyCodes;for(i=0,e=o.zoomIn.length;i<e;i++)n[o.zoomIn[i]]=t;for(i=0,e=o.zoomOut.length;i<e;i++)n[o.zoomOut[i]]=-t},_addHooks:function(){mt(document,"keydown",this._onKeyDown,this)},_removeHooks:function(){ft(document,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){if(!(t.altKey||t.ctrlKey||t.metaKey)){var i,e=t.keyCode,n=this._map;if(e in this._panKeys)n._panAnim&&n._panAnim._inProgress||(i=this._panKeys[e],t.shiftKey&&(i=w(i).multiplyBy(3)),n.panBy(i),n.options.maxBounds&&n.panInsideBounds(n.options.maxBounds));else if(e in this._zoomKeys)n.setZoom(n.getZoom()+(t.shiftKey?3:1)*this._zoomKeys[e]);else{if(27!==e||!n._popup||!n._popup.options.closeOnEscapeKey)return;n.closePopup()}Lt(t)}}});be.addInitHook("addHandler","keyboard",Mn),be.mergeOptions({scrollWheelZoom:!0,wheelDebounceTime:40,wheelPxPerZoomLevel:60});var Cn=Ee.extend({addHooks:function(){mt(this._map._container,"mousewheel",this._onWheelScroll,this),this._delta=0},removeHooks:function(){ft(this._map._container,"mousewheel",this._onWheelScroll,this)},_onWheelScroll:function(t){var i=Tt(t),n=this._map.options.wheelDebounceTime;this._delta+=i,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var o=Math.max(n-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(e(this._performZoom,this),o),Lt(t)},_performZoom:function(){var t=this._map,i=t.getZoom(),e=this._map.options.zoomSnap||0;t._stop();var n=this._delta/(4*this._map.options.wheelPxPerZoomLevel),o=4*Math.log(2/(1+Math.exp(-Math.abs(n))))/Math.LN2,s=e?Math.ceil(o/e)*e:o,r=t._limitZoom(i+(this._delta>0?s:-s))-i;this._delta=0,this._startTime=null,r&&("center"===t.options.scrollWheelZoom?t.setZoom(i+r):t.setZoomAround(this._lastMousePos,i+r))}});be.addInitHook("addHandler","scrollWheelZoom",Cn),be.mergeOptions({tap:!0,tapTolerance:15});var Sn=Ee.extend({addHooks:function(){mt(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){ft(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(Pt(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new x(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&Q(n,"leaflet-active"),this._holdTimeout=setTimeout(e(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),this._simulateEvent("mousedown",i),mt(document,{touchmove:this._onMove,touchend:this._onUp},this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),ft(document,{touchmove:this._onMove,touchend:this._onUp},this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],e=i.target;e&&e.tagName&&"a"===e.tagName.toLowerCase()&&tt(e,"leaflet-active"),this._simulateEvent("mouseup",i),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var i=t.touches[0];this._newPos=new x(i.clientX,i.clientY),this._simulateEvent("mousemove",i)},_simulateEvent:function(t,i){var e=document.createEvent("MouseEvents");e._simulated=!0,i.target._simulatedClick=!0,e.initMouseEvent(t,!0,!0,window,1,i.screenX,i.screenY,i.clientX,i.clientY,!1,!1,!1,!1,0,null),i.target.dispatchEvent(e)}});qi&&!Vi&&be.addInitHook("addHandler","tap",Sn),be.mergeOptions({touchZoom:qi&&!Mi,bounceAtZoomLimits:!0});var Zn=Ee.extend({addHooks:function(){Q(this._map._container,"leaflet-touch-zoom"),mt(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){tt(this._map._container,"leaflet-touch-zoom"),ft(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var e=i.mouseEventToContainerPoint(t.touches[0]),n=i.mouseEventToContainerPoint(t.touches[1]);this._centerPoint=i.getSize()._divideBy(2),this._startLatLng=i.containerPointToLatLng(this._centerPoint),"center"!==i.options.touchZoom&&(this._pinchStartLatLng=i.containerPointToLatLng(e.add(n)._divideBy(2))),this._startDist=e.distanceTo(n),this._startZoom=i.getZoom(),this._moved=!1,this._zooming=!0,i._stop(),mt(document,"touchmove",this._onTouchMove,this),mt(document,"touchend",this._onTouchEnd,this),Pt(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&this._zooming){var i=this._map,n=i.mouseEventToContainerPoint(t.touches[0]),o=i.mouseEventToContainerPoint(t.touches[1]),s=n.distanceTo(o)/this._startDist;if(this._zoom=i.getScaleZoom(s,this._startZoom),!i.options.bounceAtZoomLimits&&(this._zoom<i.getMinZoom()&&s<1||this._zoom>i.getMaxZoom()&&s>1)&&(this._zoom=i._limitZoom(this._zoom)),"center"===i.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{var r=n._add(o)._divideBy(2)._subtract(this._centerPoint);if(1===s&&0===r.x&&0===r.y)return;this._center=i.unproject(i.project(this._pinchStartLatLng,this._zoom).subtract(r),this._zoom)}this._moved||(i._moveStart(!0,!1),this._moved=!0),g(this._animRequest);var a=e(i._move,i,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=f(a,this,!0),Pt(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,g(this._animRequest),ft(document,"touchmove",this._onTouchMove),ft(document,"touchend",this._onTouchEnd),this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.zoomSnap):this._map._resetView(this._center,this._map._limitZoom(this._zoom))):this._zooming=!1}});be.addInitHook("addHandler","touchZoom",Zn),be.BoxZoom=bn,be.DoubleClickZoom=Tn,be.Drag=zn,be.Keyboard=Mn,be.ScrollWheelZoom=Cn,be.Tap=Sn,be.TouchZoom=Zn,Object.freeze=ti,t.version="1.4.0+HEAD.3337f36",t.Control=Te,t.control=ze,t.Browser=Qi,t.Evented=ci,t.Mixin=Be,t.Util=ui,t.Class=v,t.Handler=Ee,t.extend=i,t.bind=e,t.stamp=n,t.setOptions=l,t.DomEvent=Pe,t.DomUtil=ve,t.PosAnimation=Le,t.Draggable=Re,t.LineUtil=Ne,t.PolyUtil=De,t.Point=x,t.point=w,t.Bounds=P,t.bounds=b,t.Transformation=S,t.transformation=Z,t.Projection=He,t.LatLng=M,t.latLng=C,t.LatLngBounds=T,t.latLngBounds=z,t.CRS=di,t.GeoJSON=sn,t.geoJSON=Xt,t.geoJson=an,t.Layer=qe,t.LayerGroup=Ge,t.layerGroup=function(t,i){return new Ge(t,i)},t.FeatureGroup=Ke,t.featureGroup=function(t){return new Ke(t)},t.ImageOverlay=hn,t.imageOverlay=function(t,i,e){return new hn(t,i,e)},t.VideoOverlay=un,t.videoOverlay=function(t,i,e){return new un(t,i,e)},t.DivOverlay=ln,t.Popup=cn,t.popup=function(t,i){return new cn(t,i)},t.Tooltip=_n,t.tooltip=function(t,i){return new _n(t,i)},t.Icon=Ye,t.icon=function(t){return new Ye(t)},t.DivIcon=dn,t.divIcon=function(t){return new dn(t)},t.Marker=$e,t.marker=function(t,i){return new $e(t,i)},t.TileLayer=mn,t.tileLayer=Jt,t.GridLayer=pn,t.gridLayer=function(t){return new pn(t)},t.SVG=Pn,t.svg=Qt,t.Renderer=gn,t.Canvas=vn,t.canvas=$t,t.Path=Qe,t.CircleMarker=tn,t.circleMarker=function(t,i){return new tn(t,i)},t.Circle=en,t.circle=function(t,i,e){return new en(t,i,e)},t.Polyline=nn,t.polyline=function(t,i){return new nn(t,i)},t.Polygon=on,t.polygon=function(t,i){return new on(t,i)},t.Rectangle=Ln,t.rectangle=function(t,i){return new Ln(t,i)},t.Map=be,t.map=function(t,i){return new be(t,i)};var En=window.L;t.noConflict=function(){return window.L=En,this},window.L=t});
00 /*
1 Leaflet.markercluster, Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps.
2 https://github.com/Leaflet/Leaflet.markercluster
3 (c) 2012-2013, Dave Leaver, smartrak
4 */
5 (function (window, document, undefined) {/*
1 * Leaflet.markercluster 1.4.1+master.37ab9a2,
2 * Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps.
3 * https://github.com/Leaflet/Leaflet.markercluster
4 * (c) 2012-2017, Dave Leaver, smartrak
5 */
6 (function (global, factory) {
7 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8 typeof define === 'function' && define.amd ? define(['exports'], factory) :
9 (factory((global.Leaflet = global.Leaflet || {}, global.Leaflet.markercluster = global.Leaflet.markercluster || {})));
10 }(this, (function (exports) { 'use strict';
11
12 /*
613 * L.MarkerClusterGroup extends L.FeatureGroup by clustering the markers contained within
714 */
815
9 L.MarkerClusterGroup = L.FeatureGroup.extend({
16 var MarkerClusterGroup = L.MarkerClusterGroup = L.FeatureGroup.extend({
1017
1118 options: {
1219 maxClusterRadius: 80, //A cluster will cover at most this many pixels from its center
1320 iconCreateFunction: null,
21 clusterPane: L.Marker.prototype.options.pane,
1422
1523 spiderfyOnMaxZoom: true,
1624 showCoverageOnHover: true,
5361 if (!this.options.iconCreateFunction) {
5462 this.options.iconCreateFunction = this._defaultIconCreateFunction;
5563 }
56 if (!this.options.clusterPane) {
57 this.options.clusterPane = L.Marker.prototype.options.pane;
58 }
5964
6065 this._featureGroup = L.featureGroup();
6166 this._featureGroup.addEventParent(this);
422427 //If we aren't on the map (yet), blow away the markers we know of
423428 if (!this._map) {
424429 this._needsClustering = [];
430 this._needsRemoving = [];
425431 delete this._gridClusters;
426432 delete this._gridUnclustered;
427433 }
504510 //Overrides LayerGroup.getLayer, WARNING: Really bad performance
505511 getLayer: function (id) {
506512 var result = null;
507
513
508514 id = parseInt(id, 10);
509515
510516 this.eachLayer(function (l) {
713719 },
714720
715721 _childMarkerDragEnd: function (e) {
716 if (e.target.__dragStart) {
717 this._moveChild(e.target, e.target.__dragStart, e.target._latlng);
718 }
722 var dragStart = e.target.__dragStart;
719723 delete e.target.__dragStart;
720 },
721
724 if (dragStart) {
725 this._moveChild(e.target, dragStart, e.target._latlng);
726 }
727 },
728
722729
723730 //Internal function for removing a marker from everything.
724731 //dontUpdateMap: set to true if you will handle updating the map manually (for bulk functions)
932939 minZoom = Math.floor(this._map.getMinZoom()),
933940 radius = this.options.maxClusterRadius,
934941 radiusFn = radius;
935
942
936943 //If we just set maxClusterRadius to a single number, we need to create
937944 //a simple function to return that number. Otherwise, we just have to
938945 //use the function we've passed in.
946953 this._maxZoom = maxZoom;
947954 this._gridClusters = {};
948955 this._gridUnclustered = {};
949
956
950957 //Set up DistanceGrids for each zoom
951958 for (var zoom = maxZoom; zoom >= minZoom; zoom--) {
952959 this._gridClusters[zoom] = new L.DistanceGrid(radiusFn(zoom));
13771384 return new L.MarkerClusterGroup(options);
13781385 };
13791386
1380
1381 L.MarkerCluster = L.Marker.extend({
1387 var MarkerCluster = L.MarkerCluster = L.Marker.extend({
1388 options: L.Icon.prototype.options,
1389
13821390 initialize: function (group, zoom, a, b) {
13831391
13841392 L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0),
14041412 },
14051413
14061414 //Recursively retrieve all child markers of this cluster
1407 getAllChildMarkers: function (storageArray) {
1415 getAllChildMarkers: function (storageArray, ignoreDraggedMarker) {
14081416 storageArray = storageArray || [];
14091417
14101418 for (var i = this._childClusters.length - 1; i >= 0; i--) {
14121420 }
14131421
14141422 for (var j = this._markers.length - 1; j >= 0; j--) {
1423 if (ignoreDraggedMarker && this._markers[j].__dragStart) {
1424 continue;
1425 }
14151426 storageArray.push(this._markers[j]);
14161427 }
14171428
17621773 if (zoom < zoomLevelToStart || zoom < zoomLevelToStop) {
17631774 for (i = childClusters.length - 1; i >= 0; i--) {
17641775 c = childClusters[i];
1776 if (c._boundsNeedUpdate) {
1777 c._recalculateBounds();
1778 }
17651779 if (boundsToApplyTo.intersects(c._bounds)) {
17661780 c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel);
17671781 }
17761790 }
17771791 });
17781792
1779
1780
17811793 /*
17821794 * Extends L.Marker to include two extra methods: clusterHide and clusterShow.
17831795 *
17841796 * They work as setOpacity(0) and setOpacity(1) respectively, but
1785 * they will remember the marker's opacity when hiding and showing it again.
1797 * don't overwrite the options.opacity
17861798 *
17871799 */
17881800
1789
17901801 L.Marker.include({
1791
17921802 clusterHide: function () {
1793 this.options.opacityWhenUnclustered = this.options.opacity || 1;
1794 return this.setOpacity(0);
1803 var backup = this.options.opacity;
1804 this.setOpacity(0);
1805 this.options.opacity = backup;
1806 return this;
17951807 },
17961808
17971809 clusterShow: function () {
1798 var ret = this.setOpacity(this.options.opacity || this.options.opacityWhenUnclustered);
1799 delete this.options.opacityWhenUnclustered;
1800 return ret;
1810 return this.setOpacity(this.options.opacity);
18011811 }
1802
18031812 });
1804
1805
1806
1807
18081813
18091814 L.DistanceGrid = function (cellSize) {
18101815 this._cellSize = cellSize;
18991904 for (k = 0, len = cell.length; k < len; k++) {
19001905 obj = cell[k];
19011906 dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point);
1902 if (dist < closestDistSq) {
1907 if (dist < closestDistSq ||
1908 dist <= closestDistSq && closest === null) {
19031909 closestDistSq = dist;
19041910 closest = obj;
19051911 }
19121918 },
19131919
19141920 _getCoord: function (x) {
1915 return Math.floor(x / this._cellSize);
1921 var coord = Math.floor(x / this._cellSize);
1922 return isFinite(coord) ? coord : x;
19161923 },
19171924
19181925 _sqDist: function (p, p2) {
19211928 return dx * dx + dy * dy;
19221929 }
19231930 };
1924
19251931
19261932 /* Copyright (c) 2012 the authors listed at the following URL, and/or
19271933 the authors of referenced articles or incorporated external code:
20892095 }
20902096 });
20912097
2092
20932098 //This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet
20942099 //Huge thanks to jawj for implementing it first to make my job easy :-)
20952100
20972102
20982103 _2PI: Math.PI * 2,
20992104 _circleFootSeparation: 25, //related to circumference of circle
2100 _circleStartAngle: Math.PI / 6,
2105 _circleStartAngle: 0,
21012106
21022107 _spiralFootSeparation: 28, //related to size of spiral (experiment!)
21032108 _spiralLengthStart: 11,
21112116 return;
21122117 }
21132118
2114 var childMarkers = this.getAllChildMarkers(),
2119 var childMarkers = this.getAllChildMarkers(null, true),
21152120 group = this._group,
21162121 map = group._map,
21172122 center = map.latLngToLayerPoint(this._latlng),
21492154 res = [],
21502155 i, angle;
21512156
2157 legLength = Math.max(legLength, 35); // Minimum distance to get outside the cluster icon.
2158
21522159 res.length = count;
21532160
2154 for (i = count - 1; i >= 0; i--) {
2161 for (i = 0; i < count; i++) { // Clockwise, like spiral.
21552162 angle = this._circleStartAngle + i * angleStep;
21562163 res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
21572164 }
21712178 res.length = count;
21722179
21732180 // Higher index, closer position to cluster center.
2174 for (i = count - 1; i >= 0; i--) {
2181 for (i = count; i >= 0; i--) {
2182 // Skip the first position, so that we are already farther from center and we avoid
2183 // being under the default cluster icon (especially important for Circle Markers).
2184 if (i < count) {
2185 res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
2186 }
21752187 angle += separation / legLength + i * 0.0005;
2176 res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
21772188 legLength += lengthFactor / angle;
21782189 }
21792190 return res;
21832194 var group = this._group,
21842195 map = group._map,
21852196 fg = group._featureGroup,
2186 childMarkers = this.getAllChildMarkers(),
2197 childMarkers = this.getAllChildMarkers(null, true),
21872198 m, i;
21882199
21892200 group._ignoreMove = true;
23762387 map = group._map,
23772388 fg = group._featureGroup,
23782389 thisLayerPos = zoomDetails ? map._latLngToNewLayerPoint(this._latlng, zoomDetails.zoom, zoomDetails.center) : map.latLngToLayerPoint(this._latlng),
2379 childMarkers = this.getAllChildMarkers(),
2390 childMarkers = this.getAllChildMarkers(null, true),
23802391 svg = L.Path.SVG,
23812392 m, i, leg, legPath, legLength, nonAnimatable;
23822393
25592570 }
25602571 }
25612572 });
2562
25632573
25642574 /**
25652575 * Adds 1 public method to MCG and 1 to L.Marker to facilitate changing
26722682 }
26732683 });
26742684
2675
2676 }(window, document));
2685 exports.MarkerClusterGroup = MarkerClusterGroup;
2686 exports.MarkerCluster = MarkerCluster;
2687
2688 })));
2689 //# sourceMappingURL=leaflet.markercluster-src.js.map
0 /*
1 Leaflet.markercluster, Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps.
2 https://github.com/Leaflet/Leaflet.markercluster
3 (c) 2012-2013, Dave Leaver, smartrak
4 */
5 !function(e,t,i){L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animate:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,spiderLegPolylineOptions:{weight:1.5,color:"#222",opacity:.5},chunkedLoading:!1,chunkInterval:200,chunkDelay:50,chunkProgress:null,polygonOptions:{}},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this.options.clusterPane||(this.options.clusterPane=L.Marker.prototype.options.pane),this._featureGroup=L.featureGroup(),this._featureGroup.addEventParent(this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.addEventParent(this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null,this._queue=[],this._childMarkerEventHandlers={dragstart:this._childMarkerDragStart,move:this._childMarkerMoved,dragend:this._childMarkerDragEnd};var t=L.DomUtil.TRANSITION&&this.options.animate;L.extend(this,t?this._withAnimation:this._noAnimation),this._markerCluster=t?L.MarkerCluster:L.MarkerClusterNonAnimated},addLayer:function(e){if(e instanceof L.LayerGroup)return this.addLayers([e]);if(!e.getLatLng)return this._nonPointGroup.addLayer(e),this.fire("layeradd",{layer:e}),this;if(!this._map)return this._needsClustering.push(e),this.fire("layeradd",{layer:e}),this;if(this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom),this.fire("layeradd",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons();var t=e,i=this._zoom;if(e.__parent)for(;t.__parent._zoom>=i;)t=t.__parent;return this._currentShownBounds.contains(t.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(e,t):this._animationAddLayerNonAnimated(e,t)),this},removeLayer:function(e){return e instanceof L.LayerGroup?this.removeLayers([e]):e.getLatLng?this._map?e.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),this.fire("layerremove",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),e.off(this._childMarkerEventHandlers,this),this._featureGroup.hasLayer(e)&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow()),this):this:(!this._arraySplice(this._needsClustering,e)&&this.hasLayer(e)&&this._needsRemoving.push({layer:e,latlng:e._latlng}),this.fire("layerremove",{layer:e}),this):(this._nonPointGroup.removeLayer(e),this.fire("layerremove",{layer:e}),this)},addLayers:function(e,t){if(!L.Util.isArray(e))return this.addLayer(e);var i,n=this._featureGroup,r=this._nonPointGroup,s=this.options.chunkedLoading,o=this.options.chunkInterval,a=this.options.chunkProgress,h=e.length,l=0,u=!0;if(this._map){var _=(new Date).getTime(),d=L.bind(function(){for(var c=(new Date).getTime();h>l;l++){if(s&&0===l%200){var p=(new Date).getTime()-c;if(p>o)break}if(i=e[l],i instanceof L.LayerGroup)u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length;else if(i.getLatLng){if(!this.hasLayer(i)&&(this._addLayer(i,this._maxZoom),t||this.fire("layeradd",{layer:i}),i.__parent&&2===i.__parent.getChildCount())){var f=i.__parent.getAllChildMarkers(),m=f[0]===i?f[1]:f[0];n.removeLayer(m)}}else r.addLayer(i),t||this.fire("layeradd",{layer:i})}a&&a(l,h,(new Date).getTime()-_),l===h?(this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)):setTimeout(d,this.options.chunkDelay)},this);d()}else for(var c=this._needsClustering;h>l;l++)i=e[l],i instanceof L.LayerGroup?(u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length):i.getLatLng?this.hasLayer(i)||c.push(i):r.addLayer(i);return this},removeLayers:function(e){var t,i,n=e.length,r=this._featureGroup,s=this._nonPointGroup,o=!0;if(!this._map){for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):(this._arraySplice(this._needsClustering,i),s.removeLayer(i),this.hasLayer(i)&&this._needsRemoving.push({layer:i,latlng:i._latlng}),this.fire("layerremove",{layer:i}));return this}if(this._unspiderfy){this._unspiderfy();var a=e.slice(),h=n;for(t=0;h>t;t++)i=a[t],i instanceof L.LayerGroup?(this._extractNonGroupLayers(i,a),h=a.length):this._unspiderfyLayer(i)}for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):i.__parent?(this._removeLayer(i,!0,!0),this.fire("layerremove",{layer:i}),r.hasLayer(i)&&(r.removeLayer(i),i.clusterShow&&i.clusterShow())):(s.removeLayer(i),this.fire("layerremove",{layer:i}));return this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),this},clearLayers:function(){return this._map||(this._needsClustering=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(e){e.off(this._childMarkerEventHandlers,this),delete e.__parent},this),this._map&&this._generateInitialClusters(),this},getBounds:function(){var e=new L.LatLngBounds;this._topClusterLevel&&e.extend(this._topClusterLevel._bounds);for(var t=this._needsClustering.length-1;t>=0;t--)e.extend(this._needsClustering[t].getLatLng());return e.extend(this._nonPointGroup.getBounds()),e},eachLayer:function(e,t){var i,n,r,s=this._needsClustering.slice(),o=this._needsRemoving;for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(s),n=s.length-1;n>=0;n--){for(i=!0,r=o.length-1;r>=0;r--)if(o[r].layer===s[n]){i=!1;break}i&&e.call(t,s[n])}this._nonPointGroup.eachLayer(e,t)},getLayers:function(){var e=[];return this.eachLayer(function(t){e.push(t)}),e},getLayer:function(e){var t=null;return e=parseInt(e,10),this.eachLayer(function(i){L.stamp(i)===e&&(t=i)}),t},hasLayer:function(e){if(!e)return!1;var t,i=this._needsClustering;for(t=i.length-1;t>=0;t--)if(i[t]===e)return!0;for(i=this._needsRemoving,t=i.length-1;t>=0;t--)if(i[t].layer===e)return!1;return!(!e.__parent||e.__parent._group!==this)||this._nonPointGroup.hasLayer(e)},zoomToShowLayer:function(e,t){"function"!=typeof t&&(t=function(){});var i=function(){!e._icon&&!e.__parent._icon||this._inZoomAnimation||(this._map.off("moveend",i,this),this.off("animationend",i,this),e._icon?t():e.__parent._icon&&(this.once("spiderfied",t,this),e.__parent.spiderfy()))};e._icon&&this._map.getBounds().contains(e.getLatLng())?t():e.__parent._zoom<Math.round(this._map._zoom)?(this._map.on("moveend",i,this),this._map.panTo(e.getLatLng())):(this._map.on("moveend",i,this),this.on("animationend",i,this),e.__parent.zoomToBounds())},onAdd:function(e){this._map=e;var t,i,n;if(!isFinite(this._map.getMaxZoom()))throw"Map has no maxZoom specified";for(this._featureGroup.addTo(e),this._nonPointGroup.addTo(e),this._gridClusters||this._generateInitialClusters(),this._maxLat=e.options.crs.projection.MAX_LATITUDE,t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],n.newlatlng=n.layer._latlng,n.layer._latlng=n.latlng;for(t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],this._removeLayer(n.layer,!0),n.layer._latlng=n.newlatlng;this._needsRemoving=[],this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds(),this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),i=this._needsClustering,this._needsClustering=[],this.addLayers(i,!0)},onRemove:function(e){e.off("zoomend",this._zoomEnd,this),e.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),delete this._maxLat,this._hideCoverage(),this._featureGroup.remove(),this._nonPointGroup.remove(),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(e){for(var t=e;t&&!t._icon;)t=t.__parent;return t||null},_arraySplice:function(e,t){for(var i=e.length-1;i>=0;i--)if(e[i]===t)return e.splice(i,1),!0},_removeFromGridUnclustered:function(e,t){for(var i=this._map,n=this._gridUnclustered,r=Math.floor(this._map.getMinZoom());t>=r&&n[t].removeObject(e,i.project(e.getLatLng(),t));t--);},_childMarkerDragStart:function(e){e.target.__dragStart=e.target._latlng},_childMarkerMoved:function(e){if(!this._ignoreMove&&!e.target.__dragStart){var t=e.target._popup&&e.target._popup.isOpen();this._moveChild(e.target,e.oldLatLng,e.latlng),t&&e.target.openPopup()}},_moveChild:function(e,t,i){e._latlng=t,this.removeLayer(e),e._latlng=i,this.addLayer(e)},_childMarkerDragEnd:function(e){e.target.__dragStart&&this._moveChild(e.target,e.target.__dragStart,e.target._latlng),delete e.target.__dragStart},_removeLayer:function(e,t,i){var n=this._gridClusters,r=this._gridUnclustered,s=this._featureGroup,o=this._map,a=Math.floor(this._map.getMinZoom());t&&this._removeFromGridUnclustered(e,this._maxZoom);var h,l=e.__parent,u=l._markers;for(this._arraySplice(u,e);l&&(l._childCount--,l._boundsNeedUpdate=!0,!(l._zoom<a));)t&&l._childCount<=1?(h=l._markers[0]===e?l._markers[1]:l._markers[0],n[l._zoom].removeObject(l,o.project(l._cLatLng,l._zoom)),r[l._zoom].addObject(h,o.project(h.getLatLng(),l._zoom)),this._arraySplice(l.__parent._childClusters,l),l.__parent._markers.push(h),h.__parent=l.__parent,l._icon&&(s.removeLayer(l),i||s.addLayer(h))):l._iconNeedsUpdate=!0,l=l.__parent;delete e.__parent},_isOrIsParent:function(e,t){for(;t;){if(e===t)return!0;t=t.parentNode}return!1},fire:function(e,t,i){if(t&&t.layer instanceof L.MarkerCluster){if(t.originalEvent&&this._isOrIsParent(t.layer._icon,t.originalEvent.relatedTarget))return;e="cluster"+e}L.FeatureGroup.prototype.fire.call(this,e,t,i)},listens:function(e,t){return L.FeatureGroup.prototype.listens.call(this,e,t)||L.FeatureGroup.prototype.listens.call(this,"cluster"+e,t)},_defaultIconCreateFunction:function(e){var t=e.getChildCount(),i=" marker-cluster-";return i+=10>t?"small":100>t?"medium":"large",new L.DivIcon({html:"<div><span>"+t+"</span></div>",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=this._map,t=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(t||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),e.on("zoomend",this._hideCoverage,this))},_zoomOrSpiderfy:function(e){for(var t=e.layer,i=t;1===i._childClusters.length;)i=i._childClusters[0];i._zoom===this._maxZoom&&i._childCount===t._childCount&&this.options.spiderfyOnMaxZoom?t.spiderfy():this.options.zoomToBoundsOnClick&&t.zoomToBounds(),e.originalEvent&&13===e.originalEvent.keyCode&&this._map._container.focus()},_showCoverage:function(e){var t=this._map;this._inZoomAnimation||(this._shownPolygon&&t.removeLayer(this._shownPolygon),e.layer.getChildCount()>2&&e.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(e.layer.getConvexHull(),this.options.polygonOptions),t.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_unbindEvents:function(){var e=this.options.spiderfyOnMaxZoom,t=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(e||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),t&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,Math.round(this._map._zoom),e),this._currentShownBounds=e}},_generateInitialClusters:function(){var e=Math.ceil(this._map.getMaxZoom()),t=Math.floor(this._map.getMinZoom()),i=this.options.maxClusterRadius,n=i;"function"!=typeof i&&(n=function(){return i}),null!==this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var r=e;r>=t;r--)this._gridClusters[r]=new L.DistanceGrid(n(r)),this._gridUnclustered[r]=new L.DistanceGrid(n(r));this._topClusterLevel=new this._markerCluster(this,t-1)},_addLayer:function(e,t){var i,n,r=this._gridClusters,s=this._gridUnclustered,o=Math.floor(this._map.getMinZoom());for(this.options.singleMarkerMode&&this._overrideMarkerIcon(e),e.on(this._childMarkerEventHandlers,this);t>=o;t--){i=this._map.project(e.getLatLng(),t);var a=r[t].getNearObject(i);if(a)return a._addChild(e),e.__parent=a,void 0;if(a=s[t].getNearObject(i)){var h=a.__parent;h&&this._removeLayer(a,!1);var l=new this._markerCluster(this,t,a,e);r[t].addObject(l,this._map.project(l._cLatLng,t)),a.__parent=l,e.__parent=l;var u=l;for(n=t-1;n>h._zoom;n--)u=new this._markerCluster(this,n,u),r[n].addObject(u,this._map.project(a.getLatLng(),n));return h._addChild(u),this._removeFromGridUnclustered(a,t),void 0}s[t].addObject(e,i)}this._topClusterLevel._addChild(e),e.__parent=this._topClusterLevel},_refreshClustersIcons:function(){this._featureGroup.eachLayer(function(e){e instanceof L.MarkerCluster&&e._iconNeedsUpdate&&e._updateIcon()})},_enqueue:function(e){this._queue.push(e),this._queueTimeout||(this._queueTimeout=setTimeout(L.bind(this._processQueue,this),300))},_processQueue:function(){for(var e=0;e<this._queue.length;e++)this._queue[e].call(this);this._queue.length=0,clearTimeout(this._queueTimeout),this._queueTimeout=null},_mergeSplitClusters:function(){var e=Math.round(this._map._zoom);this._processQueue(),this._zoom<e&&this._currentShownBounds.intersects(this._getExpandedVisibleBounds())?(this._animationStart(),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,this._getExpandedVisibleBounds()),this._animationZoomIn(this._zoom,e)):this._zoom>e?(this._animationStart(),this._animationZoomOut(this._zoom,e)):this._moveEnd()},_getExpandedVisibleBounds:function(){return this.options.removeOutsideVisibleBounds?L.Browser.mobile?this._checkBoundsMaxLat(this._map.getBounds()):this._checkBoundsMaxLat(this._map.getBounds().pad(1)):this._mapBoundsInfinite},_checkBoundsMaxLat:function(e){var t=this._maxLat;return t!==i&&(e.getNorth()>=t&&(e._northEast.lat=1/0),e.getSouth()<=-t&&(e._southWest.lat=-1/0)),e},_animationAddLayerNonAnimated:function(e,t){if(t===e)this._featureGroup.addLayer(e);else if(2===t._childCount){t._addToMap();var i=t.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else t._updateIcon()},_extractNonGroupLayers:function(e,t){var i,n=e.getLayers(),r=0;for(t=t||[];r<n.length;r++)i=n[r],i instanceof L.LayerGroup?this._extractNonGroupLayers(i,t):t.push(i);return t},_overrideMarkerIcon:function(e){var t=e.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[e]}});return t}}),L.MarkerClusterGroup.include({_mapBoundsInfinite:new L.LatLngBounds(new L.LatLng(-1/0,-1/0),new L.LatLng(1/0,1/0))}),L.MarkerClusterGroup.include({_noAnimation:{_animationStart:function(){},_animationZoomIn:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationZoomOut:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationAddLayer:function(e,t){this._animationAddLayerNonAnimated(e,t)}},_withAnimation:{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationZoomIn:function(e,t){var i,n=this._getExpandedVisibleBounds(),r=this._featureGroup,s=Math.floor(this._map.getMinZoom());this._ignoreMove=!0,this._topClusterLevel._recursively(n,e,s,function(s){var o,a=s._latlng,h=s._markers;for(n.contains(a)||(a=null),s._isSingleParent()&&e+1===t?(r.removeLayer(s),s._recursivelyAddChildrenToMap(null,t,n)):(s.clusterHide(),s._recursivelyAddChildrenToMap(a,t,n)),i=h.length-1;i>=0;i--)o=h[i],n.contains(o._latlng)||r.removeLayer(o)}),this._forceLayout(),this._topClusterLevel._recursivelyBecomeVisible(n,t),r.eachLayer(function(e){e instanceof L.MarkerCluster||!e._icon||e.clusterShow()}),this._topClusterLevel._recursively(n,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),this._ignoreMove=!1,this._enqueue(function(){this._topClusterLevel._recursively(n,e,s,function(e){r.removeLayer(e),e.clusterShow()}),this._animationEnd()})},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e-1,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){var i=this,n=this._featureGroup;n.addLayer(e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.clusterHide(),this._enqueue(function(){n.removeLayer(e),e.clusterShow(),i._animationEnd()})):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(t,this._map.getMaxZoom(),this._zoom)))}},_animationZoomOutSingle:function(e,t,i){var n=this._getExpandedVisibleBounds(),r=Math.floor(this._map.getMinZoom());e._recursivelyAnimateChildrenInAndAddSelfToMap(n,r,t+1,i);var s=this;this._forceLayout(),e._recursivelyBecomeVisible(n,i),this._enqueue(function(){if(1===e._childCount){var o=e._markers[0];this._ignoreMove=!0,o.setLatLng(o.getLatLng()),this._ignoreMove=!1,o.clusterShow&&o.clusterShow()}else e._recursively(n,i,r,function(e){e._recursivelyRemoveChildrenFromMap(n,r,t+1)});s._animationEnd()})},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_forceLayout:function(){L.Util.falseFn(t.body.offsetWidth)}}),L.markerClusterGroup=function(e){return new L.MarkerClusterGroup(e)},L.MarkerCluster=L.Marker.extend({initialize:function(e,t,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this,pane:e.options.clusterPane}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(e){e=e||[];for(var t=this._childClusters.length-1;t>=0;t--)this._childClusters[t].getAllChildMarkers(e);for(var i=this._markers.length-1;i>=0;i--)e.push(this._markers[i]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(e){for(var t,i=this._childClusters.slice(),n=this._group._map,r=n.getBoundsZoom(this._bounds),s=this._zoom+1,o=n.getZoom();i.length>0&&r>s;){s++;var a=[];for(t=0;t<i.length;t++)a=a.concat(i[t]._childClusters);i=a}r>s?this._group._map.setView(this._latlng,s):o>=r?this._group._map.setView(this._latlng,o+1):this._group._map.fitBounds(this._bounds,e)},getBounds:function(){var e=new L.LatLngBounds;return e.extend(this._bounds),e},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._setClusterCenter(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_setClusterCenter:function(e){this._cLatLng||(this._cLatLng=e._cLatLng||e._latlng)},_resetBounds:function(){var e=this._bounds;e._southWest&&(e._southWest.lat=1/0,e._southWest.lng=1/0),e._northEast&&(e._northEast.lat=-1/0,e._northEast.lng=-1/0)},_recalculateBounds:function(){var e,t,i,n,r=this._markers,s=this._childClusters,o=0,a=0,h=this._childCount;if(0!==h){for(this._resetBounds(),e=0;e<r.length;e++)i=r[e]._latlng,this._bounds.extend(i),o+=i.lat,a+=i.lng;for(e=0;e<s.length;e++)t=s[e],t._boundsNeedUpdate&&t._recalculateBounds(),this._bounds.extend(t._bounds),i=t._wLatLng,n=t._childCount,o+=i.lat*n,a+=i.lng*n;this._latlng=this._wLatLng=new L.LatLng(o/h,a/h),this._boundsNeedUpdate=!1}},_addToMap:function(e){e&&(this._backupLatlng=this._latlng,this.setLatLng(e)),this._group._featureGroup.addLayer(this)},_recursivelyAnimateChildrenIn:function(e,t,i){this._recursively(e,this._group._map.getMinZoom(),i-1,function(e){var i,n,r=e._markers;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())},function(e){var i,n,r=e._childClusters;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,i,n){this._recursively(e,n,t,function(r){r._recursivelyAnimateChildrenIn(e,r._group._map.latLngToLayerPoint(r.getLatLng()).round(),i),r._isSingleParent()&&i-1===n?(r.clusterShow(),r._recursivelyRemoveChildrenFromMap(e,t,i)):r.clusterHide(),r._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,this._group._map.getMinZoom(),t,null,function(e){e.clusterShow()})},_recursivelyAddChildrenToMap:function(e,t,i){this._recursively(i,this._group._map.getMinZoom()-1,t,function(n){if(t!==n._zoom)for(var r=n._markers.length-1;r>=0;r--){var s=n._markers[r];i.contains(s._latlng)&&(e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.clusterHide&&s.clusterHide()),n._group._featureGroup.addLayer(s))}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var i=this._markers[t];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(e-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,i,n){var r,s;this._recursively(e,t-1,i-1,function(e){for(s=e._markers.length-1;s>=0;s--)r=e._markers[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())},function(e){for(s=e._childClusters.length-1;s>=0;s--)r=e._childClusters[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())})},_recursively:function(e,t,i,n,r){var s,o,a=this._childClusters,h=this._zoom;if(h>=t&&(n&&n(this),r&&h===i&&r(this)),t>h||i>h)for(s=a.length-1;s>=0;s--)o=a[s],e.intersects(o._bounds)&&o._recursively(e,t,i,n,r)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}}),L.Marker.include({clusterHide:function(){return this.options.opacityWhenUnclustered=this.options.opacity||1,this.setOpacity(0)},clusterShow:function(){var e=this.setOpacity(this.options.opacity||this.options.opacityWhenUnclustered);return delete this.options.opacityWhenUnclustered,e}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var i=this._getCoord(t.x),n=this._getCoord(t.y),r=this._grid,s=r[n]=r[n]||{},o=s[i]=s[i]||[],a=L.Util.stamp(e);this._objectPoint[a]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var i,n,r=this._getCoord(t.x),s=this._getCoord(t.y),o=this._grid,a=o[s]=o[s]||{},h=a[r]=a[r]||[];for(delete this._objectPoint[L.Util.stamp(e)],i=0,n=h.length;n>i;i++)if(h[i]===e)return h.splice(i,1),1===n&&delete a[r],!0},eachObject:function(e,t){var i,n,r,s,o,a,h,l=this._grid;for(i in l){o=l[i];for(n in o)for(a=o[n],r=0,s=a.length;s>r;r++)h=e.call(t,a[r]),h&&(r--,s--)}},getNearObject:function(e){var t,i,n,r,s,o,a,h,l=this._getCoord(e.x),u=this._getCoord(e.y),_=this._objectPoint,d=this._sqCellSize,c=null;for(t=u-1;u+1>=t;t++)if(r=this._grid[t])for(i=l-1;l+1>=i;i++)if(s=r[i])for(n=0,o=s.length;o>n;n++)a=s[n],h=this._sqDist(_[L.Util.stamp(a)],e),d>h&&(d=h,c=a);return c},_getCoord:function(e){return Math.floor(e/this._cellSize)},_sqDist:function(e,t){var i=t.x-e.x,n=t.y-e.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(e,t){var i=t[1].lat-t[0].lat,n=t[0].lng-t[1].lng;return n*(e.lat-t[0].lat)+i*(e.lng-t[0].lng)},findMostDistantPointFromBaseLine:function(e,t){var i,n,r,s=0,o=null,a=[];for(i=t.length-1;i>=0;i--)n=t[i],r=this.getDistant(n,e),r>0&&(a.push(n),r>s&&(s=r,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(e,t){var i=[],n=this.findMostDistantPointFromBaseLine(e,t);return n.maxPoint?(i=i.concat(this.buildConvexHull([e[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,e[1]],n.newPoints))):[e[0]]},getConvexHull:function(e){var t,i=!1,n=!1,r=!1,s=!1,o=null,a=null,h=null,l=null,u=null,_=null;for(t=e.length-1;t>=0;t--){var d=e[t];(i===!1||d.lat>i)&&(o=d,i=d.lat),(n===!1||d.lat<n)&&(a=d,n=d.lat),(r===!1||d.lng>r)&&(h=d,r=d.lng),(s===!1||d.lng<s)&&(l=d,s=d.lng)}n!==i?(_=a,u=o):(_=l,u=h);var c=[].concat(this.buildConvexHull([_,u],e),this.buildConvexHull([u,_],e));return c}}}(),L.MarkerCluster.include({getConvexHull:function(){var e,t,i=this.getAllChildMarkers(),n=[];for(t=i.length-1;t>=0;t--)e=i[t].getLatLng(),n.push(e);return L.QuickHull.getConvexHull(n)}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:Math.PI/6,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var e,t=this.getAllChildMarkers(),i=this._group,n=i._map,r=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,t.length>=this._circleSpiralSwitchover?e=this._generatePointsSpiral(t.length,r):(r.y+=10,e=this._generatePointsCircle(t.length,r)),this._animationSpiderfy(t,e)}},unspiderfy:function(e){this._group._inZoomAnimation||(this._animationUnspiderfy(e),this._group._spiderfied=null)},_generatePointsCircle:function(e,t){var i,n,r=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+e),s=r/this._2PI,o=this._2PI/e,a=[];for(a.length=e,i=e-1;i>=0;i--)n=this._circleStartAngle+i*o,a[i]=new L.Point(t.x+s*Math.cos(n),t.y+s*Math.sin(n))._round();return a},_generatePointsSpiral:function(e,t){var i,n=this._group.options.spiderfyDistanceMultiplier,r=n*this._spiralLengthStart,s=n*this._spiralFootSeparation,o=n*this._spiralLengthFactor*this._2PI,a=0,h=[];for(h.length=e,i=e-1;i>=0;i--)a+=s/r+5e-4*i,h[i]=new L.Point(t.x+r*Math.cos(a),t.y+r*Math.sin(a))._round(),r+=o/a;return h},_noanimationUnspiderfy:function(){var e,t,i=this._group,n=i._map,r=i._featureGroup,s=this.getAllChildMarkers();for(i._ignoreMove=!0,this.setOpacity(1),t=s.length-1;t>=0;t--)e=s[t],r.removeLayer(e),e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng),e.setZIndexOffset&&e.setZIndexOffset(0),e._spiderLeg&&(n.removeLayer(e._spiderLeg),delete e._spiderLeg);i.fire("unspiderfied",{cluster:this,markers:s}),i._ignoreMove=!1,i._spiderfied=null}}),L.MarkerClusterNonAnimated=L.MarkerCluster.extend({_animationSpiderfy:function(e,t){var i,n,r,s,o=this._group,a=o._map,h=o._featureGroup,l=this._group.options.spiderLegPolylineOptions;for(o._ignoreMove=!0,i=0;i<e.length;i++)s=a.layerPointToLatLng(t[i]),n=e[i],r=new L.Polyline([this._latlng,s],l),a.addLayer(r),n._spiderLeg=r,n._preSpiderfyLatlng=n._latlng,n.setLatLng(s),n.setZIndexOffset&&n.setZIndexOffset(1e6),h.addLayer(n);this.setOpacity(.3),o._ignoreMove=!1,o.fire("spiderfied",{cluster:this,markers:e})},_animationUnspiderfy:function(){this._noanimationUnspiderfy()}}),L.MarkerCluster.include({_animationSpiderfy:function(e,t){var n,r,s,o,a,h,l=this,u=this._group,_=u._map,d=u._featureGroup,c=this._latlng,p=_.latLngToLayerPoint(c),f=L.Path.SVG,m=L.extend({},this._group.options.spiderLegPolylineOptions),g=m.opacity;for(g===i&&(g=L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity),f?(m.opacity=0,m.className=(m.className||"")+" leaflet-cluster-spider-leg"):m.opacity=g,u._ignoreMove=!0,n=0;n<e.length;n++)r=e[n],h=_.layerPointToLatLng(t[n]),s=new L.Polyline([c,h],m),_.addLayer(s),r._spiderLeg=s,f&&(o=s._path,a=o.getTotalLength()+.1,o.style.strokeDasharray=a,o.style.strokeDashoffset=a),r.setZIndexOffset&&r.setZIndexOffset(1e6),r.clusterHide&&r.clusterHide(),d.addLayer(r),r._setPos&&r._setPos(p);for(u._forceLayout(),u._animationStart(),n=e.length-1;n>=0;n--)h=_.layerPointToLatLng(t[n]),r=e[n],r._preSpiderfyLatlng=r._latlng,r.setLatLng(h),r.clusterShow&&r.clusterShow(),f&&(s=r._spiderLeg,o=s._path,o.style.strokeDashoffset=0,s.setStyle({opacity:g}));this.setOpacity(.3),u._ignoreMove=!1,setTimeout(function(){u._animationEnd(),u.fire("spiderfied",{cluster:l,markers:e})},200)},_animationUnspiderfy:function(e){var t,i,n,r,s,o,a=this,h=this._group,l=h._map,u=h._featureGroup,_=e?l._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):l.latLngToLayerPoint(this._latlng),d=this.getAllChildMarkers(),c=L.Path.SVG;for(h._ignoreMove=!0,h._animationStart(),this.setOpacity(1),i=d.length-1;i>=0;i--)t=d[i],t._preSpiderfyLatlng&&(t.closePopup(),t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng,o=!0,t._setPos&&(t._setPos(_),o=!1),t.clusterHide&&(t.clusterHide(),o=!1),o&&u.removeLayer(t),c&&(n=t._spiderLeg,r=n._path,s=r.getTotalLength()+.1,r.style.strokeDashoffset=s,n.setStyle({opacity:0})));h._ignoreMove=!1,setTimeout(function(){var e=0;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&e++;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&(t.clusterShow&&t.clusterShow(),t.setZIndexOffset&&t.setZIndexOffset(0),e>1&&u.removeLayer(t),l.removeLayer(t._spiderLeg),delete t._spiderLeg);h._animationEnd(),h.fire("unspiderfied",{cluster:a,markers:d})},200)}}),L.MarkerClusterGroup.include({_spiderfied:null,unspiderfy:function(){this._unspiderfy.apply(this,arguments)},_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation&&this._map.on("zoomstart",this._unspiderfyZoomStart,this),this._map.on("zoomend",this._noanimationUnspiderfy,this),L.Browser.touch||this._map.getRenderer(this)},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._map.off("zoomend",this._noanimationUnspiderfy,this),this._noanimationUnspiderfy()},_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e))},_unspiderfyWrapper:function(){this._unspiderfy()
6 },_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(e){e._spiderLeg&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow(),e.setZIndexOffset&&e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}}),L.MarkerClusterGroup.include({refreshClusters:function(e){return e?e instanceof L.MarkerClusterGroup?e=e._topClusterLevel.getAllChildMarkers():e instanceof L.LayerGroup?e=e._layers:e instanceof L.MarkerCluster?e=e.getAllChildMarkers():e instanceof L.Marker&&(e=[e]):e=this._topClusterLevel.getAllChildMarkers(),this._flagParentsIconsNeedUpdate(e),this._refreshClustersIcons(),this.options.singleMarkerMode&&this._refreshSingleMarkerModeMarkers(e),this},_flagParentsIconsNeedUpdate:function(e){var t,i;for(t in e)for(i=e[t].__parent;i;)i._iconNeedsUpdate=!0,i=i.__parent},_refreshSingleMarkerModeMarkers:function(e){var t,i;for(t in e)i=e[t],this.hasLayer(i)&&i.setIcon(this._overrideMarkerIcon(i))}}),L.Marker.include({refreshIconOptions:function(e,t){var i=this.options.icon;return L.setOptions(i,e),this.setIcon(i),t&&this.__parent&&this.__parent._group.refreshClusters(this),this}})}(window,document);
0 !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e.Leaflet=e.Leaflet||{},e.Leaflet.markercluster=e.Leaflet.markercluster||{}))}(this,function(e){"use strict";var t=L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,clusterPane:L.Marker.prototype.options.pane,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animate:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,spiderLegPolylineOptions:{weight:1.5,color:"#222",opacity:.5},chunkedLoading:!1,chunkInterval:200,chunkDelay:50,chunkProgress:null,polygonOptions:{}},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this._featureGroup=L.featureGroup(),this._featureGroup.addEventParent(this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.addEventParent(this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null,this._queue=[],this._childMarkerEventHandlers={dragstart:this._childMarkerDragStart,move:this._childMarkerMoved,dragend:this._childMarkerDragEnd};var t=L.DomUtil.TRANSITION&&this.options.animate;L.extend(this,t?this._withAnimation:this._noAnimation),this._markerCluster=t?L.MarkerCluster:L.MarkerClusterNonAnimated},addLayer:function(e){if(e instanceof L.LayerGroup)return this.addLayers([e]);if(!e.getLatLng)return this._nonPointGroup.addLayer(e),this.fire("layeradd",{layer:e}),this;if(!this._map)return this._needsClustering.push(e),this.fire("layeradd",{layer:e}),this;if(this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom),this.fire("layeradd",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons();var t=e,i=this._zoom;if(e.__parent)for(;t.__parent._zoom>=i;)t=t.__parent;return this._currentShownBounds.contains(t.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(e,t):this._animationAddLayerNonAnimated(e,t)),this},removeLayer:function(e){return e instanceof L.LayerGroup?this.removeLayers([e]):e.getLatLng?this._map?e.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),this.fire("layerremove",{layer:e}),this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),e.off(this._childMarkerEventHandlers,this),this._featureGroup.hasLayer(e)&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow()),this):this:(!this._arraySplice(this._needsClustering,e)&&this.hasLayer(e)&&this._needsRemoving.push({layer:e,latlng:e._latlng}),this.fire("layerremove",{layer:e}),this):(this._nonPointGroup.removeLayer(e),this.fire("layerremove",{layer:e}),this)},addLayers:function(e,t){if(!L.Util.isArray(e))return this.addLayer(e);var i,n=this._featureGroup,r=this._nonPointGroup,s=this.options.chunkedLoading,o=this.options.chunkInterval,a=this.options.chunkProgress,h=e.length,l=0,u=!0;if(this._map){var _=(new Date).getTime(),d=L.bind(function(){for(var c=(new Date).getTime();h>l;l++){if(s&&0===l%200){var p=(new Date).getTime()-c;if(p>o)break}if(i=e[l],i instanceof L.LayerGroup)u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length;else if(i.getLatLng){if(!this.hasLayer(i)&&(this._addLayer(i,this._maxZoom),t||this.fire("layeradd",{layer:i}),i.__parent&&2===i.__parent.getChildCount())){var f=i.__parent.getAllChildMarkers(),m=f[0]===i?f[1]:f[0];n.removeLayer(m)}}else r.addLayer(i),t||this.fire("layeradd",{layer:i})}a&&a(l,h,(new Date).getTime()-_),l===h?(this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)):setTimeout(d,this.options.chunkDelay)},this);d()}else for(var c=this._needsClustering;h>l;l++)i=e[l],i instanceof L.LayerGroup?(u&&(e=e.slice(),u=!1),this._extractNonGroupLayers(i,e),h=e.length):i.getLatLng?this.hasLayer(i)||c.push(i):r.addLayer(i);return this},removeLayers:function(e){var t,i,n=e.length,r=this._featureGroup,s=this._nonPointGroup,o=!0;if(!this._map){for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):(this._arraySplice(this._needsClustering,i),s.removeLayer(i),this.hasLayer(i)&&this._needsRemoving.push({layer:i,latlng:i._latlng}),this.fire("layerremove",{layer:i}));return this}if(this._unspiderfy){this._unspiderfy();var a=e.slice(),h=n;for(t=0;h>t;t++)i=a[t],i instanceof L.LayerGroup?(this._extractNonGroupLayers(i,a),h=a.length):this._unspiderfyLayer(i)}for(t=0;n>t;t++)i=e[t],i instanceof L.LayerGroup?(o&&(e=e.slice(),o=!1),this._extractNonGroupLayers(i,e),n=e.length):i.__parent?(this._removeLayer(i,!0,!0),this.fire("layerremove",{layer:i}),r.hasLayer(i)&&(r.removeLayer(i),i.clusterShow&&i.clusterShow())):(s.removeLayer(i),this.fire("layerremove",{layer:i}));return this._topClusterLevel._recalculateBounds(),this._refreshClustersIcons(),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),this},clearLayers:function(){return this._map||(this._needsClustering=[],this._needsRemoving=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(e){e.off(this._childMarkerEventHandlers,this),delete e.__parent},this),this._map&&this._generateInitialClusters(),this},getBounds:function(){var e=new L.LatLngBounds;this._topClusterLevel&&e.extend(this._topClusterLevel._bounds);for(var t=this._needsClustering.length-1;t>=0;t--)e.extend(this._needsClustering[t].getLatLng());return e.extend(this._nonPointGroup.getBounds()),e},eachLayer:function(e,t){var i,n,r,s=this._needsClustering.slice(),o=this._needsRemoving;for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(s),n=s.length-1;n>=0;n--){for(i=!0,r=o.length-1;r>=0;r--)if(o[r].layer===s[n]){i=!1;break}i&&e.call(t,s[n])}this._nonPointGroup.eachLayer(e,t)},getLayers:function(){var e=[];return this.eachLayer(function(t){e.push(t)}),e},getLayer:function(e){var t=null;return e=parseInt(e,10),this.eachLayer(function(i){L.stamp(i)===e&&(t=i)}),t},hasLayer:function(e){if(!e)return!1;var t,i=this._needsClustering;for(t=i.length-1;t>=0;t--)if(i[t]===e)return!0;for(i=this._needsRemoving,t=i.length-1;t>=0;t--)if(i[t].layer===e)return!1;return!(!e.__parent||e.__parent._group!==this)||this._nonPointGroup.hasLayer(e)},zoomToShowLayer:function(e,t){"function"!=typeof t&&(t=function(){});var i=function(){!e._icon&&!e.__parent._icon||this._inZoomAnimation||(this._map.off("moveend",i,this),this.off("animationend",i,this),e._icon?t():e.__parent._icon&&(this.once("spiderfied",t,this),e.__parent.spiderfy()))};e._icon&&this._map.getBounds().contains(e.getLatLng())?t():e.__parent._zoom<Math.round(this._map._zoom)?(this._map.on("moveend",i,this),this._map.panTo(e.getLatLng())):(this._map.on("moveend",i,this),this.on("animationend",i,this),e.__parent.zoomToBounds())},onAdd:function(e){this._map=e;var t,i,n;if(!isFinite(this._map.getMaxZoom()))throw"Map has no maxZoom specified";for(this._featureGroup.addTo(e),this._nonPointGroup.addTo(e),this._gridClusters||this._generateInitialClusters(),this._maxLat=e.options.crs.projection.MAX_LATITUDE,t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],n.newlatlng=n.layer._latlng,n.layer._latlng=n.latlng;for(t=0,i=this._needsRemoving.length;i>t;t++)n=this._needsRemoving[t],this._removeLayer(n.layer,!0),n.layer._latlng=n.newlatlng;this._needsRemoving=[],this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds(),this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),i=this._needsClustering,this._needsClustering=[],this.addLayers(i,!0)},onRemove:function(e){e.off("zoomend",this._zoomEnd,this),e.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),delete this._maxLat,this._hideCoverage(),this._featureGroup.remove(),this._nonPointGroup.remove(),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(e){for(var t=e;t&&!t._icon;)t=t.__parent;return t||null},_arraySplice:function(e,t){for(var i=e.length-1;i>=0;i--)if(e[i]===t)return e.splice(i,1),!0},_removeFromGridUnclustered:function(e,t){for(var i=this._map,n=this._gridUnclustered,r=Math.floor(this._map.getMinZoom());t>=r&&n[t].removeObject(e,i.project(e.getLatLng(),t));t--);},_childMarkerDragStart:function(e){e.target.__dragStart=e.target._latlng},_childMarkerMoved:function(e){if(!this._ignoreMove&&!e.target.__dragStart){var t=e.target._popup&&e.target._popup.isOpen();this._moveChild(e.target,e.oldLatLng,e.latlng),t&&e.target.openPopup()}},_moveChild:function(e,t,i){e._latlng=t,this.removeLayer(e),e._latlng=i,this.addLayer(e)},_childMarkerDragEnd:function(e){var t=e.target.__dragStart;delete e.target.__dragStart,t&&this._moveChild(e.target,t,e.target._latlng)},_removeLayer:function(e,t,i){var n=this._gridClusters,r=this._gridUnclustered,s=this._featureGroup,o=this._map,a=Math.floor(this._map.getMinZoom());t&&this._removeFromGridUnclustered(e,this._maxZoom);var h,l=e.__parent,u=l._markers;for(this._arraySplice(u,e);l&&(l._childCount--,l._boundsNeedUpdate=!0,!(l._zoom<a));)t&&l._childCount<=1?(h=l._markers[0]===e?l._markers[1]:l._markers[0],n[l._zoom].removeObject(l,o.project(l._cLatLng,l._zoom)),r[l._zoom].addObject(h,o.project(h.getLatLng(),l._zoom)),this._arraySplice(l.__parent._childClusters,l),l.__parent._markers.push(h),h.__parent=l.__parent,l._icon&&(s.removeLayer(l),i||s.addLayer(h))):l._iconNeedsUpdate=!0,l=l.__parent;delete e.__parent},_isOrIsParent:function(e,t){for(;t;){if(e===t)return!0;t=t.parentNode}return!1},fire:function(e,t,i){if(t&&t.layer instanceof L.MarkerCluster){if(t.originalEvent&&this._isOrIsParent(t.layer._icon,t.originalEvent.relatedTarget))return;e="cluster"+e}L.FeatureGroup.prototype.fire.call(this,e,t,i)},listens:function(e,t){return L.FeatureGroup.prototype.listens.call(this,e,t)||L.FeatureGroup.prototype.listens.call(this,"cluster"+e,t)},_defaultIconCreateFunction:function(e){var t=e.getChildCount(),i=" marker-cluster-";return i+=10>t?"small":100>t?"medium":"large",new L.DivIcon({html:"<div><span>"+t+"</span></div>",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=this._map,t=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(t||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),e.on("zoomend",this._hideCoverage,this))},_zoomOrSpiderfy:function(e){for(var t=e.layer,i=t;1===i._childClusters.length;)i=i._childClusters[0];i._zoom===this._maxZoom&&i._childCount===t._childCount&&this.options.spiderfyOnMaxZoom?t.spiderfy():this.options.zoomToBoundsOnClick&&t.zoomToBounds(),e.originalEvent&&13===e.originalEvent.keyCode&&this._map._container.focus()},_showCoverage:function(e){var t=this._map;this._inZoomAnimation||(this._shownPolygon&&t.removeLayer(this._shownPolygon),e.layer.getChildCount()>2&&e.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(e.layer.getConvexHull(),this.options.polygonOptions),t.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_unbindEvents:function(){var e=this.options.spiderfyOnMaxZoom,t=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(e||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),t&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=Math.round(this._map._zoom),this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,Math.round(this._map._zoom),e),this._currentShownBounds=e}},_generateInitialClusters:function(){var e=Math.ceil(this._map.getMaxZoom()),t=Math.floor(this._map.getMinZoom()),i=this.options.maxClusterRadius,n=i;"function"!=typeof i&&(n=function(){return i}),null!==this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var r=e;r>=t;r--)this._gridClusters[r]=new L.DistanceGrid(n(r)),this._gridUnclustered[r]=new L.DistanceGrid(n(r));this._topClusterLevel=new this._markerCluster(this,t-1)},_addLayer:function(e,t){var i,n,r=this._gridClusters,s=this._gridUnclustered,o=Math.floor(this._map.getMinZoom());for(this.options.singleMarkerMode&&this._overrideMarkerIcon(e),e.on(this._childMarkerEventHandlers,this);t>=o;t--){i=this._map.project(e.getLatLng(),t);var a=r[t].getNearObject(i);if(a)return a._addChild(e),e.__parent=a,void 0;if(a=s[t].getNearObject(i)){var h=a.__parent;h&&this._removeLayer(a,!1);var l=new this._markerCluster(this,t,a,e);r[t].addObject(l,this._map.project(l._cLatLng,t)),a.__parent=l,e.__parent=l;var u=l;for(n=t-1;n>h._zoom;n--)u=new this._markerCluster(this,n,u),r[n].addObject(u,this._map.project(a.getLatLng(),n));return h._addChild(u),this._removeFromGridUnclustered(a,t),void 0}s[t].addObject(e,i)}this._topClusterLevel._addChild(e),e.__parent=this._topClusterLevel},_refreshClustersIcons:function(){this._featureGroup.eachLayer(function(e){e instanceof L.MarkerCluster&&e._iconNeedsUpdate&&e._updateIcon()})},_enqueue:function(e){this._queue.push(e),this._queueTimeout||(this._queueTimeout=setTimeout(L.bind(this._processQueue,this),300))},_processQueue:function(){for(var e=0;e<this._queue.length;e++)this._queue[e].call(this);this._queue.length=0,clearTimeout(this._queueTimeout),this._queueTimeout=null},_mergeSplitClusters:function(){var e=Math.round(this._map._zoom);this._processQueue(),this._zoom<e&&this._currentShownBounds.intersects(this._getExpandedVisibleBounds())?(this._animationStart(),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),this._zoom,this._getExpandedVisibleBounds()),this._animationZoomIn(this._zoom,e)):this._zoom>e?(this._animationStart(),this._animationZoomOut(this._zoom,e)):this._moveEnd()},_getExpandedVisibleBounds:function(){return this.options.removeOutsideVisibleBounds?L.Browser.mobile?this._checkBoundsMaxLat(this._map.getBounds()):this._checkBoundsMaxLat(this._map.getBounds().pad(1)):this._mapBoundsInfinite},_checkBoundsMaxLat:function(e){var t=this._maxLat;return void 0!==t&&(e.getNorth()>=t&&(e._northEast.lat=1/0),e.getSouth()<=-t&&(e._southWest.lat=-1/0)),e},_animationAddLayerNonAnimated:function(e,t){if(t===e)this._featureGroup.addLayer(e);else if(2===t._childCount){t._addToMap();var i=t.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else t._updateIcon()},_extractNonGroupLayers:function(e,t){var i,n=e.getLayers(),r=0;for(t=t||[];r<n.length;r++)i=n[r],i instanceof L.LayerGroup?this._extractNonGroupLayers(i,t):t.push(i);return t},_overrideMarkerIcon:function(e){var t=e.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[e]}});return t}});L.MarkerClusterGroup.include({_mapBoundsInfinite:new L.LatLngBounds(new L.LatLng(-1/0,-1/0),new L.LatLng(1/0,1/0))}),L.MarkerClusterGroup.include({_noAnimation:{_animationStart:function(){},_animationZoomIn:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationZoomOut:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationAddLayer:function(e,t){this._animationAddLayerNonAnimated(e,t)}},_withAnimation:{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationZoomIn:function(e,t){var i,n=this._getExpandedVisibleBounds(),r=this._featureGroup,s=Math.floor(this._map.getMinZoom());this._ignoreMove=!0,this._topClusterLevel._recursively(n,e,s,function(s){var o,a=s._latlng,h=s._markers;for(n.contains(a)||(a=null),s._isSingleParent()&&e+1===t?(r.removeLayer(s),s._recursivelyAddChildrenToMap(null,t,n)):(s.clusterHide(),s._recursivelyAddChildrenToMap(a,t,n)),i=h.length-1;i>=0;i--)o=h[i],n.contains(o._latlng)||r.removeLayer(o)}),this._forceLayout(),this._topClusterLevel._recursivelyBecomeVisible(n,t),r.eachLayer(function(e){e instanceof L.MarkerCluster||!e._icon||e.clusterShow()}),this._topClusterLevel._recursively(n,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),this._ignoreMove=!1,this._enqueue(function(){this._topClusterLevel._recursively(n,e,s,function(e){r.removeLayer(e),e.clusterShow()}),this._animationEnd()})},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e-1,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,Math.floor(this._map.getMinZoom()),e,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){var i=this,n=this._featureGroup;n.addLayer(e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.clusterHide(),this._enqueue(function(){n.removeLayer(e),e.clusterShow(),i._animationEnd()})):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(t,this._map.getMaxZoom(),this._zoom)))}},_animationZoomOutSingle:function(e,t,i){var n=this._getExpandedVisibleBounds(),r=Math.floor(this._map.getMinZoom());e._recursivelyAnimateChildrenInAndAddSelfToMap(n,r,t+1,i);var s=this;this._forceLayout(),e._recursivelyBecomeVisible(n,i),this._enqueue(function(){if(1===e._childCount){var o=e._markers[0];this._ignoreMove=!0,o.setLatLng(o.getLatLng()),this._ignoreMove=!1,o.clusterShow&&o.clusterShow()}else e._recursively(n,i,r,function(e){e._recursivelyRemoveChildrenFromMap(n,r,t+1)});s._animationEnd()})},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_forceLayout:function(){L.Util.falseFn(document.body.offsetWidth)}}),L.markerClusterGroup=function(e){return new L.MarkerClusterGroup(e)};var i=L.MarkerCluster=L.Marker.extend({options:L.Icon.prototype.options,initialize:function(e,t,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this,pane:e.options.clusterPane}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(e,t){e=e||[];for(var i=this._childClusters.length-1;i>=0;i--)this._childClusters[i].getAllChildMarkers(e);for(var n=this._markers.length-1;n>=0;n--)t&&this._markers[n].__dragStart||e.push(this._markers[n]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(e){for(var t,i=this._childClusters.slice(),n=this._group._map,r=n.getBoundsZoom(this._bounds),s=this._zoom+1,o=n.getZoom();i.length>0&&r>s;){s++;var a=[];for(t=0;t<i.length;t++)a=a.concat(i[t]._childClusters);i=a}r>s?this._group._map.setView(this._latlng,s):o>=r?this._group._map.setView(this._latlng,o+1):this._group._map.fitBounds(this._bounds,e)},getBounds:function(){var e=new L.LatLngBounds;return e.extend(this._bounds),e},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._boundsNeedUpdate=!0,this._setClusterCenter(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_setClusterCenter:function(e){this._cLatLng||(this._cLatLng=e._cLatLng||e._latlng)},_resetBounds:function(){var e=this._bounds;e._southWest&&(e._southWest.lat=1/0,e._southWest.lng=1/0),e._northEast&&(e._northEast.lat=-1/0,e._northEast.lng=-1/0)},_recalculateBounds:function(){var e,t,i,n,r=this._markers,s=this._childClusters,o=0,a=0,h=this._childCount;if(0!==h){for(this._resetBounds(),e=0;e<r.length;e++)i=r[e]._latlng,this._bounds.extend(i),o+=i.lat,a+=i.lng;for(e=0;e<s.length;e++)t=s[e],t._boundsNeedUpdate&&t._recalculateBounds(),this._bounds.extend(t._bounds),i=t._wLatLng,n=t._childCount,o+=i.lat*n,a+=i.lng*n;this._latlng=this._wLatLng=new L.LatLng(o/h,a/h),this._boundsNeedUpdate=!1}},_addToMap:function(e){e&&(this._backupLatlng=this._latlng,this.setLatLng(e)),this._group._featureGroup.addLayer(this)},_recursivelyAnimateChildrenIn:function(e,t,i){this._recursively(e,this._group._map.getMinZoom(),i-1,function(e){var i,n,r=e._markers;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())},function(e){var i,n,r=e._childClusters;for(i=r.length-1;i>=0;i--)n=r[i],n._icon&&(n._setPos(t),n.clusterHide())})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,i,n){this._recursively(e,n,t,function(r){r._recursivelyAnimateChildrenIn(e,r._group._map.latLngToLayerPoint(r.getLatLng()).round(),i),r._isSingleParent()&&i-1===n?(r.clusterShow(),r._recursivelyRemoveChildrenFromMap(e,t,i)):r.clusterHide(),r._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,this._group._map.getMinZoom(),t,null,function(e){e.clusterShow()})},_recursivelyAddChildrenToMap:function(e,t,i){this._recursively(i,this._group._map.getMinZoom()-1,t,function(n){if(t!==n._zoom)for(var r=n._markers.length-1;r>=0;r--){var s=n._markers[r];i.contains(s._latlng)&&(e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.clusterHide&&s.clusterHide()),n._group._featureGroup.addLayer(s))}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var i=this._markers[t];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(e-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,i,n){var r,s;this._recursively(e,t-1,i-1,function(e){for(s=e._markers.length-1;s>=0;s--)r=e._markers[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())},function(e){for(s=e._childClusters.length-1;s>=0;s--)r=e._childClusters[s],n&&n.contains(r._latlng)||(e._group._featureGroup.removeLayer(r),r.clusterShow&&r.clusterShow())})},_recursively:function(e,t,i,n,r){var s,o,a=this._childClusters,h=this._zoom;if(h>=t&&(n&&n(this),r&&h===i&&r(this)),t>h||i>h)for(s=a.length-1;s>=0;s--)o=a[s],o._boundsNeedUpdate&&o._recalculateBounds(),e.intersects(o._bounds)&&o._recursively(e,t,i,n,r)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}});L.Marker.include({clusterHide:function(){var e=this.options.opacity;return this.setOpacity(0),this.options.opacity=e,this},clusterShow:function(){return this.setOpacity(this.options.opacity)}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var i=this._getCoord(t.x),n=this._getCoord(t.y),r=this._grid,s=r[n]=r[n]||{},o=s[i]=s[i]||[],a=L.Util.stamp(e);this._objectPoint[a]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var i,n,r=this._getCoord(t.x),s=this._getCoord(t.y),o=this._grid,a=o[s]=o[s]||{},h=a[r]=a[r]||[];for(delete this._objectPoint[L.Util.stamp(e)],i=0,n=h.length;n>i;i++)if(h[i]===e)return h.splice(i,1),1===n&&delete a[r],!0},eachObject:function(e,t){var i,n,r,s,o,a,h,l=this._grid;for(i in l){o=l[i];for(n in o)for(a=o[n],r=0,s=a.length;s>r;r++)h=e.call(t,a[r]),h&&(r--,s--)}},getNearObject:function(e){var t,i,n,r,s,o,a,h,l=this._getCoord(e.x),u=this._getCoord(e.y),_=this._objectPoint,d=this._sqCellSize,c=null;for(t=u-1;u+1>=t;t++)if(r=this._grid[t])for(i=l-1;l+1>=i;i++)if(s=r[i])for(n=0,o=s.length;o>n;n++)a=s[n],h=this._sqDist(_[L.Util.stamp(a)],e),(d>h||d>=h&&null===c)&&(d=h,c=a);return c},_getCoord:function(e){var t=Math.floor(e/this._cellSize);return isFinite(t)?t:e},_sqDist:function(e,t){var i=t.x-e.x,n=t.y-e.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(e,t){var i=t[1].lat-t[0].lat,n=t[0].lng-t[1].lng;return n*(e.lat-t[0].lat)+i*(e.lng-t[0].lng)},findMostDistantPointFromBaseLine:function(e,t){var i,n,r,s=0,o=null,a=[];for(i=t.length-1;i>=0;i--)n=t[i],r=this.getDistant(n,e),r>0&&(a.push(n),r>s&&(s=r,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(e,t){var i=[],n=this.findMostDistantPointFromBaseLine(e,t);return n.maxPoint?(i=i.concat(this.buildConvexHull([e[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,e[1]],n.newPoints))):[e[0]]},getConvexHull:function(e){var t,i=!1,n=!1,r=!1,s=!1,o=null,a=null,h=null,l=null,u=null,_=null;for(t=e.length-1;t>=0;t--){var d=e[t];(i===!1||d.lat>i)&&(o=d,i=d.lat),(n===!1||d.lat<n)&&(a=d,n=d.lat),(r===!1||d.lng>r)&&(h=d,r=d.lng),(s===!1||d.lng<s)&&(l=d,s=d.lng)}n!==i?(_=a,u=o):(_=l,u=h);var c=[].concat(this.buildConvexHull([_,u],e),this.buildConvexHull([u,_],e));return c}}}(),L.MarkerCluster.include({getConvexHull:function(){var e,t,i=this.getAllChildMarkers(),n=[];for(t=i.length-1;t>=0;t--)e=i[t].getLatLng(),n.push(e);return L.QuickHull.getConvexHull(n)}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:0,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var e,t=this.getAllChildMarkers(null,!0),i=this._group,n=i._map,r=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,t.length>=this._circleSpiralSwitchover?e=this._generatePointsSpiral(t.length,r):(r.y+=10,e=this._generatePointsCircle(t.length,r)),this._animationSpiderfy(t,e)}},unspiderfy:function(e){this._group._inZoomAnimation||(this._animationUnspiderfy(e),this._group._spiderfied=null)},_generatePointsCircle:function(e,t){var i,n,r=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+e),s=r/this._2PI,o=this._2PI/e,a=[];for(s=Math.max(s,35),a.length=e,i=0;e>i;i++)n=this._circleStartAngle+i*o,a[i]=new L.Point(t.x+s*Math.cos(n),t.y+s*Math.sin(n))._round();return a},_generatePointsSpiral:function(e,t){var i,n=this._group.options.spiderfyDistanceMultiplier,r=n*this._spiralLengthStart,s=n*this._spiralFootSeparation,o=n*this._spiralLengthFactor*this._2PI,a=0,h=[];for(h.length=e,i=e;i>=0;i--)e>i&&(h[i]=new L.Point(t.x+r*Math.cos(a),t.y+r*Math.sin(a))._round()),a+=s/r+5e-4*i,r+=o/a;return h},_noanimationUnspiderfy:function(){var e,t,i=this._group,n=i._map,r=i._featureGroup,s=this.getAllChildMarkers(null,!0);for(i._ignoreMove=!0,this.setOpacity(1),t=s.length-1;t>=0;t--)e=s[t],r.removeLayer(e),e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng),e.setZIndexOffset&&e.setZIndexOffset(0),e._spiderLeg&&(n.removeLayer(e._spiderLeg),delete e._spiderLeg);i.fire("unspiderfied",{cluster:this,markers:s}),i._ignoreMove=!1,i._spiderfied=null}}),L.MarkerClusterNonAnimated=L.MarkerCluster.extend({_animationSpiderfy:function(e,t){var i,n,r,s,o=this._group,a=o._map,h=o._featureGroup,l=this._group.options.spiderLegPolylineOptions;for(o._ignoreMove=!0,i=0;i<e.length;i++)s=a.layerPointToLatLng(t[i]),n=e[i],r=new L.Polyline([this._latlng,s],l),a.addLayer(r),n._spiderLeg=r,n._preSpiderfyLatlng=n._latlng,n.setLatLng(s),n.setZIndexOffset&&n.setZIndexOffset(1e6),h.addLayer(n);this.setOpacity(.3),o._ignoreMove=!1,o.fire("spiderfied",{cluster:this,markers:e})},_animationUnspiderfy:function(){this._noanimationUnspiderfy()}}),L.MarkerCluster.include({_animationSpiderfy:function(e,t){var i,n,r,s,o,a,h=this,l=this._group,u=l._map,_=l._featureGroup,d=this._latlng,c=u.latLngToLayerPoint(d),p=L.Path.SVG,f=L.extend({},this._group.options.spiderLegPolylineOptions),m=f.opacity;for(void 0===m&&(m=L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity),p?(f.opacity=0,f.className=(f.className||"")+" leaflet-cluster-spider-leg"):f.opacity=m,l._ignoreMove=!0,i=0;i<e.length;i++)n=e[i],a=u.layerPointToLatLng(t[i]),r=new L.Polyline([d,a],f),u.addLayer(r),n._spiderLeg=r,p&&(s=r._path,o=s.getTotalLength()+.1,s.style.strokeDasharray=o,s.style.strokeDashoffset=o),n.setZIndexOffset&&n.setZIndexOffset(1e6),n.clusterHide&&n.clusterHide(),_.addLayer(n),n._setPos&&n._setPos(c);for(l._forceLayout(),l._animationStart(),i=e.length-1;i>=0;i--)a=u.layerPointToLatLng(t[i]),n=e[i],n._preSpiderfyLatlng=n._latlng,n.setLatLng(a),n.clusterShow&&n.clusterShow(),p&&(r=n._spiderLeg,s=r._path,s.style.strokeDashoffset=0,r.setStyle({opacity:m}));this.setOpacity(.3),l._ignoreMove=!1,setTimeout(function(){l._animationEnd(),l.fire("spiderfied",{cluster:h,markers:e})},200)},_animationUnspiderfy:function(e){var t,i,n,r,s,o,a=this,h=this._group,l=h._map,u=h._featureGroup,_=e?l._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):l.latLngToLayerPoint(this._latlng),d=this.getAllChildMarkers(null,!0),c=L.Path.SVG;for(h._ignoreMove=!0,h._animationStart(),this.setOpacity(1),i=d.length-1;i>=0;i--)t=d[i],t._preSpiderfyLatlng&&(t.closePopup(),t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng,o=!0,t._setPos&&(t._setPos(_),o=!1),t.clusterHide&&(t.clusterHide(),o=!1),o&&u.removeLayer(t),c&&(n=t._spiderLeg,r=n._path,s=r.getTotalLength()+.1,r.style.strokeDashoffset=s,n.setStyle({opacity:0})));h._ignoreMove=!1,setTimeout(function(){var e=0;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&e++;for(i=d.length-1;i>=0;i--)t=d[i],t._spiderLeg&&(t.clusterShow&&t.clusterShow(),t.setZIndexOffset&&t.setZIndexOffset(0),e>1&&u.removeLayer(t),l.removeLayer(t._spiderLeg),delete t._spiderLeg);h._animationEnd(),h.fire("unspiderfied",{cluster:a,markers:d})},200)}}),L.MarkerClusterGroup.include({_spiderfied:null,unspiderfy:function(){this._unspiderfy.apply(this,arguments)},_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation&&this._map.on("zoomstart",this._unspiderfyZoomStart,this),this._map.on("zoomend",this._noanimationUnspiderfy,this),L.Browser.touch||this._map.getRenderer(this)},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._map.off("zoomend",this._noanimationUnspiderfy,this),this._noanimationUnspiderfy()
1 },_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e))},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(e){e._spiderLeg&&(this._featureGroup.removeLayer(e),e.clusterShow&&e.clusterShow(),e.setZIndexOffset&&e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}}),L.MarkerClusterGroup.include({refreshClusters:function(e){return e?e instanceof L.MarkerClusterGroup?e=e._topClusterLevel.getAllChildMarkers():e instanceof L.LayerGroup?e=e._layers:e instanceof L.MarkerCluster?e=e.getAllChildMarkers():e instanceof L.Marker&&(e=[e]):e=this._topClusterLevel.getAllChildMarkers(),this._flagParentsIconsNeedUpdate(e),this._refreshClustersIcons(),this.options.singleMarkerMode&&this._refreshSingleMarkerModeMarkers(e),this},_flagParentsIconsNeedUpdate:function(e){var t,i;for(t in e)for(i=e[t].__parent;i;)i._iconNeedsUpdate=!0,i=i.__parent},_refreshSingleMarkerModeMarkers:function(e){var t,i;for(t in e)i=e[t],this.hasLayer(i)&&i.setIcon(this._overrideMarkerIcon(i))}}),L.Marker.include({refreshIconOptions:function(e,t){var i=this.options.icon;return L.setOptions(i,e),this.setIcon(i),t&&this.__parent&&this.__parent._group.refreshClusters(this),this}}),e.MarkerClusterGroup=t,e.MarkerCluster=i});
2 //# sourceMappingURL=leaflet.markercluster.js.map
00 <?php
1
2 use OpenCage\Loader\CompatLoader;
13
24 $this->provideHook('monitoring/HostActions');
35 $this->provideHook('monitoring/ServiceActions');
46 $this->provideHook('cube/Actions', 'CubeLinks');
7
8 require_once __DIR__ . '/library/vendor/OpenCage/Loader/CompatLoader.php';
9 CompatLoader::delegateLoadingToIcingaWeb($this->app);