diff --git a/geotrek/common/templates/common/leaflet_test.html b/geotrek/common/templates/common/leaflet_test.html new file mode 100644 index 0000000000..7f3bdec502 --- /dev/null +++ b/geotrek/common/templates/common/leaflet_test.html @@ -0,0 +1,89 @@ +{% load static %} + + + + + Leaflet tiling test + + + + +
+
+ + + + + diff --git a/geotrek/common/urls.py b/geotrek/common/urls.py index 3b3bb2bd48..e6bef90dc6 100644 --- a/geotrek/common/urls.py +++ b/geotrek/common/urls.py @@ -2,7 +2,7 @@ from mapentity.registry import MapEntityOptions from .views import (JSSettings, admin_check_extents, DocumentPublic, DocumentBookletPublic, import_view, import_update_json, - ThemeViewSet, MarkupPublic, sync_view, sync_update_json, SyncRandoRedirect) + ThemeViewSet, MarkupPublic, sync_view, sync_update_json, SyncRandoRedirect, LeafletTestView) class LangConverter(converters.StringConverter): @@ -21,6 +21,7 @@ class LangConverter(converters.StringConverter): path('commands/syncview', sync_view, name='sync_randos_view'), path('commands/statesync/', sync_update_json, name='sync_randos_state'), path('api//themes.json', ThemeViewSet.as_view({'get': 'list'}), name="themes_json"), + path('leaflet-tiles/', LeafletTestView.as_view(), name="leaflet-tiles"), ] diff --git a/geotrek/common/views.py b/geotrek/common/views.py index f1c593c197..f409fb496d 100644 --- a/geotrek/common/views.py +++ b/geotrek/common/views.py @@ -12,8 +12,7 @@ from django.utils import timezone from django.views import static from django.utils.translation import gettext as _ -from django.views.generic import TemplateView -from django.views.generic import RedirectView, View +from django.views.generic import TemplateView, RedirectView, View from mapentity.helpers import api_bbox from mapentity.registry import registry @@ -479,3 +478,7 @@ def post(self, request, *args, **kwargs): home = last_list + + +class LeafletTestView(TemplateView): + template_name = "common/leaflet_test.html" diff --git a/geotrek/core/static/core/geotrek.forms.snap.js b/geotrek/core/static/core/geotrek.forms.snap.js index 37a8207923..d4bd868098 100644 --- a/geotrek/core/static/core/geotrek.forms.snap.js +++ b/geotrek/core/static/core/geotrek.forms.snap.js @@ -40,11 +40,10 @@ MapEntity.GeometryField.GeometryFieldPathMixin = { * (At least for the fix to propagate events) */ buildPathsLayer: function (objectsLayer) { - var url_path = window.SETTINGS.urls.path_layer - var pathsLayer = MapEntity.pathsLayer({style: {clickable: true}, no_draft: objectsLayer.modelname != 'path'}); + var url_path = window.SETTINGS.urls.tile.replace(new RegExp('modelname', 'g'), 'path'); if (objectsLayer.modelname != 'path') url_path += '?no_draft=true'; - pathsLayer.load(url_path, true); + var pathsLayer = MapEntity.pathsLayer(url_path, {style: {clickable: true}, no_draft: objectsLayer.modelname != 'path'}); this._map.addLayer(pathsLayer); @@ -94,8 +93,8 @@ MapEntity.GeometryField.GeometryFieldSnap = MapEntity.GeometryField.extend({ this._objectsLayer = null; }, - buildObjectsLayer: function () { - this._objectsLayer = MapEntity.GeometryField.prototype.buildObjectsLayer(arguments); + buildObjectsLayer: function (url) { + this._objectsLayer = MapEntity.GeometryField.prototype.buildObjectsLayer(url); this._guidesLayers.push(this._objectsLayer); if (this.getModelName() != 'path') { @@ -147,19 +146,20 @@ MapEntity.GeometryField.GeometryFieldSnap = MapEntity.GeometryField.extend({ }, this); // On edition, show start and end markers as snapped - this._map.on('draw:editstart', function (e) { - setTimeout(function () { - if (!layer.editing) { - console.warn('Layer has no snap editing'); - return; // should never happen ;) - } - var markers = layer.editing._markers; - var first = markers[0], - last = markers[markers.length - 1]; - first.fire('move'); - last.fire('move'); - }, 0); - }); + // FIXME: disabled temporarily + // this._map.on('draw:editstart', function (e) { + // setTimeout(function () { + // if (!layer.editing) { + // console.warn('Layer has no snap editing'); + // return; // should never happen ;) + // } + // var markers = layer.editing._markers; + // var first = markers[0], + // last = markers[markers.length - 1]; + // first.fire('move'); + // last.fire('move'); + // }, 0); + // }); }, diff --git a/geotrek/core/static/core/geotrek.forms.topology.js b/geotrek/core/static/core/geotrek.forms.topology.js index 2224101b4f..83ebcc659e 100644 --- a/geotrek/core/static/core/geotrek.forms.topology.js +++ b/geotrek/core/static/core/geotrek.forms.topology.js @@ -97,8 +97,8 @@ MapEntity.GeometryField.TopologyField = MapEntity.GeometryField.extend({ } }, - buildObjectsLayer: function () { - this._objectsLayer = MapEntity.GeometryField.prototype.buildObjectsLayer.call(this); + buildObjectsLayer: function (url) { + this._objectsLayer = MapEntity.GeometryField.prototype.buildObjectsLayer.call(this, url); this._pathsLayer = this.buildPathsLayer(this._objectsLayer); this._pathsLayer.on('loaded', this._loadTopologyGraph, this); return this._objectsLayer; diff --git a/geotrek/core/static/core/images/marker-source-2x.png b/geotrek/core/static/core/images/marker-source-2x.png new file mode 100644 index 0000000000..f838bd937c Binary files /dev/null and b/geotrek/core/static/core/images/marker-source-2x.png differ diff --git a/geotrek/core/static/core/images/marker-target-2x.png b/geotrek/core/static/core/images/marker-target-2x.png new file mode 100644 index 0000000000..ddce49ed20 Binary files /dev/null and b/geotrek/core/static/core/images/marker-target-2x.png differ diff --git a/geotrek/core/static/core/leaflet.textpath.js b/geotrek/core/static/core/leaflet.textpath.js index f0b783334f..cf027b3382 100644 --- a/geotrek/core/static/core/leaflet.textpath.js +++ b/geotrek/core/static/core/leaflet.textpath.js @@ -1,34 +1,38 @@ /* + * Leaflet.TextPath - Shows text along a polyline * Inspired by Tom Mac Wright article : * http://mapbox.com/osmdev/2012/11/20/getting-serious-about-svg/ */ -var PolylineTextPath = { +(function () { + +var __onAdd = L.Polyline.prototype.onAdd, + __onRemove = L.Polyline.prototype.onRemove, + __updatePath = L.Polyline.prototype._updatePath, + __bringToFront = L.Polyline.prototype.bringToFront; - __updatePath: L.Polyline.prototype._updatePath, - __bringToFront: L.Polyline.prototype.bringToFront, - __onAdd: L.Polyline.prototype.onAdd, - __onRemove: L.Polyline.prototype.onRemove, + +var PolylineTextPath = { onAdd: function (map) { - this.__onAdd.call(this, map); + __onAdd.call(this, map); this._textRedraw(); }, onRemove: function (map) { map = map || this._map; - if (map && this._textNode) - map._pathRoot.removeChild(this._textNode); - this.__onRemove.call(this, map); + if (map && this._textNode && map._renderer._container) + map._renderer._container.removeChild(this._textNode); + __onRemove.call(this, map); }, bringToFront: function () { - this.__bringToFront.call(this); + __bringToFront.call(this); this._textRedraw(); }, _updatePath: function () { - this.__updatePath.call(this); + __updatePath.call(this); this._textRedraw(); }, @@ -44,24 +48,39 @@ var PolylineTextPath = { this._text = text; this._textOptions = options; - var defaults = {repeat: false, fillColor: 'black', attributes: {}}; + /* If not in SVG mode or Polyline not added to map yet return */ + /* setText will be called by onAdd, using value stored in this._text */ + if (!L.Browser.svg || typeof this._map === 'undefined') { + return this; + } + + var defaults = { + repeat: false, + fillColor: 'black', + attributes: {}, + below: false, + }; options = L.Util.extend(defaults, options); /* If empty text, hide */ if (!text) { - if (this._textNode) - this._map._pathRoot.removeChild(this._textNode); + if (this._textNode && this._textNode.parentNode) { + this._map._renderer._container.removeChild(this._textNode); + + /* delete the node, so it will not be removed a 2nd time if the layer is later removed from the map */ + delete this._textNode; + } return this; } text = text.replace(/ /g, '\u00A0'); // Non breakable spaces var id = 'pathdef-' + L.Util.stamp(this); - var svg = this._map._pathRoot; + var svg = this._map._renderer._container; this._path.setAttribute('id', id); if (options.repeat) { /* Compute single pattern length */ - var pattern = L.Path.prototype._createElement('text'); + var pattern = L.SVG.create('text'); for (var attr in options.attributes) pattern.setAttribute(attr, options.attributes[attr]); pattern.appendChild(document.createTextNode(text)); @@ -70,12 +89,12 @@ var PolylineTextPath = { svg.removeChild(pattern); /* Create string as long as path */ - text = new Array(Math.ceil(this._path.getTotalLength() / alength)).join(text); + text = new Array(Math.ceil(isNaN(this._path.getTotalLength() / alength) ? 0 : this._path.getTotalLength() / alength)).join(text); } /* Put it along the path using textPath */ - var textNode = L.Path.prototype._createElement('text'), - textPath = L.Path.prototype._createElement('textPath'); + var textNode = L.SVG.create('text'), + textPath = L.SVG.create('textPath'); var dy = options.offset || this._path.getAttribute('stroke-width'); @@ -85,8 +104,55 @@ var PolylineTextPath = { textNode.setAttribute(attr, options.attributes[attr]); textPath.appendChild(document.createTextNode(text)); textNode.appendChild(textPath); - svg.appendChild(textNode); this._textNode = textNode; + + if (options.below) { + svg.insertBefore(textNode, svg.firstChild); + } + else { + svg.appendChild(textNode); + } + + /* Center text according to the path's bounding box */ + if (options.center) { + var textLength = textNode.getComputedTextLength(); + var pathLength = this._path.getTotalLength(); + /* Set the position for the left side of the textNode */ + textNode.setAttribute('dx', ((pathLength / 2) - (textLength / 2))); + } + + /* Change label rotation (if required) */ + if (options.orientation) { + var rotateAngle = 0; + switch (options.orientation) { + case 'flip': + rotateAngle = 180; + break; + case 'perpendicular': + rotateAngle = 90; + break; + default: + rotateAngle = options.orientation; + } + + var rotatecenterX = (textNode.getBBox().x + textNode.getBBox().width / 2); + var rotatecenterY = (textNode.getBBox().y + textNode.getBBox().height / 2); + textNode.setAttribute('transform','rotate(' + rotateAngle + ' ' + rotatecenterX + ' ' + rotatecenterY + ')'); + } + + /* Initialize mouse events for the additional nodes */ + if (this.options.interactive) { + if (L.Browser.svg || !L.Browser.vml) { + textPath.setAttribute('class', 'leaflet-interactive'); + } + + var events = ['click', 'dblclick', 'mousedown', 'mouseover', + 'mouseout', 'mousemove', 'contextmenu']; + for (var i = 0; i < events.length; i++) { + L.DomEvent.on(textNode, events[i], this.fire, this); + } + } + return this; } }; @@ -103,3 +169,7 @@ L.LayerGroup.include({ return this; } }); + + + +})(); diff --git a/geotrek/core/static/core/main.js b/geotrek/core/static/core/main.js index ae9e675739..3338458ec0 100644 --- a/geotrek/core/static/core/main.js +++ b/geotrek/core/static/core/main.js @@ -1,8 +1,8 @@ -MapEntity.pathsLayer = function buildPathLayer(options) { +MapEntity.pathsLayer = function buildPathLayer(url, options) { var options = options || {}; options.style = L.Util.extend(options.style || {}, window.SETTINGS.map.styles.path); - var pathsLayer = new L.ObjectsLayer(null, options); + var pathsLayer = new L.ObjectsLayer(url, options); // Show paths extremities if (window.SETTINGS.showExtremities) { @@ -21,15 +21,14 @@ $(window).on('entity:map', function (e, data) { var is_form_view = /add|update/.test(data.viewname); if (!is_form_view && (data.viewname == 'detail' || data.modelname != 'path')) { - - var pathsLayer = MapEntity.pathsLayer({ + var pathsLayer = MapEntity.pathsLayer(window.SETTINGS.urls.tile.replace(new RegExp('modelname', 'g'), 'path'), { indexing: false, style: { clickable: false }, modelname: 'path', no_draft: data.modelname != 'path', }); + if (data.viewname == 'detail'){ - pathsLayer.load(window.SETTINGS.urls.path_layer); pathsLayer.addTo(map); }; pathsLayer.on('loaded', function () { @@ -37,53 +36,6 @@ $(window).on('entity:map', function (e, data) { pathsLayer.bringToBack(); }); - map.on('layeradd', function (e) { - // Start ajax loading at last - url = window.SETTINGS.urls.path_layer - - var options = e.layer.options || {'modelname': 'None'}; - if (! loaded_path){ - if (options.modelname == 'path' && data.viewname != 'detail'){ - e.layer.load(url + '?no_draft=true', true); - loaded_path = true; - }; - - } - - if (e.layer === pathsLayer) { - - if (!e.layer._map) { - return; - } - if (e.layer.loading) { - e.layer.on('loaded', function () { - if (!e.layer._map) { - return; - } - e.layer.bringToBack(); - }); - } - else { - e.layer.bringToBack(); - } - } - else { - if (e.layer instanceof L.ObjectsLayer) { - if (e.layer.loading) { - e.layer.on('loaded', function () { - if (!e.layer._map) { - return; - } - e.layer.bringToFront(); - }); - } - else { - e.layer.bringToFront(); - } - } - } - }); - var style = pathsLayer.options.style; var nameHTML = ' ' + tr('Paths'); map.layerscontrol.addOverlay(pathsLayer, nameHTML, tr('Objects')); @@ -112,22 +64,36 @@ $(window).on('detailmap:ready', function (e, data) { // Show start and end layer.eachLayer(function (layer) { - if (layer instanceof L.MultiPolyline) - return; if (layer instanceof L.Polygon) return; if (typeof layer.getLatLngs != 'function') // points return; // Show start and end markers (similar to edition) - var _iconUrl = window.SETTINGS.urls.static + "core/images/marker-"; + var imagePath = window.SETTINGS.urls.static + "core/images/"; L.marker(layer.getLatLngs()[0], { clickable: false, - icon: new L.Icon.Default({iconUrl: _iconUrl + "source.png"}) + icon: new L.Icon({ + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + iconUrl: "/static/core/images/marker-source.png", + iconRetinaUrl: "/static/core/images/marker-source-2x.png", + shadowUrl: null + }) }).addTo(map); L.marker(layer.getLatLngs().slice(-1)[0], { clickable: false, - icon: new L.Icon.Default({iconUrl: _iconUrl + "target.png"}) + icon: new L.Icon({ + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + tooltipAnchor: [16, -28], + iconUrl: "/static/core/images/marker-target.png", + iconRetinaUrl: "/static/core/images/marker-target-2x.png", + shadowUrl: null + }) }).addTo(map); // Also add line orientation diff --git a/geotrek/core/static/core/multipath.js b/geotrek/core/static/core/multipath.js index ad9990655f..5a09c17b97 100644 --- a/geotrek/core/static/core/multipath.js +++ b/geotrek/core/static/core/multipath.js @@ -234,7 +234,7 @@ L.ActivableMarker = L.Marker.extend({ L.Handler.MultiPath = L.Handler.extend({ - includes: L.Mixin.Events, + includes: L.Evented.prototype, initialize: function (map, guidesLayer, options) { this.map = map; @@ -272,7 +272,7 @@ L.Handler.MultiPath = L.Handler.extend({ var self = this; (function() { function dragstart(e) { - var next_step_idx = self.draggable_marker.group_layer.step_idx + 1; + var next_step_idx = self.draggable_marker.layer.step_idx + 1; self.addViaStep(self.draggable_marker, next_step_idx); } function dragend(e) { @@ -797,17 +797,15 @@ L.Handler.MultiPath = L.Handler.extend({ var layerPoint = a.layerPoint , min_dist = Number.MAX_VALUE , closest_point = null - , matching_group_layer = null; - - topology.layer && topology.layer.eachLayer(function(group_layer) { - group_layer.eachLayer(function(layer) { - var p = layer.closestLayerPoint(layerPoint); - if (p && p.distance < min_dist && p.distance < MIN_DIST) { - min_dist = p.distance; - closest_point = p; - matching_group_layer = group_layer; - } - }); + , matching_layer = null; + + topology.layer && topology.layer.eachLayer(function(layer) { + var p = layer.closestLayerPoint(layerPoint); + if (p && p.distance < min_dist && p.distance < MIN_DIST) { + min_dist = p.distance; + closest_point = p; + matching_layer = layer; + } }); if (closest_point) { @@ -815,7 +813,7 @@ L.Handler.MultiPath = L.Handler.extend({ self.draggable_marker.addTo(self.map); L.DomUtil.addClass(self.draggable_marker._icon, self.draggable_marker.classname); self.draggable_marker._removeShadow(); - self.draggable_marker.group_layer = matching_group_layer; + self.draggable_marker.layer = matching_layer; } else { self.draggable_marker && self.map.removeLayer(self.draggable_marker); } @@ -844,7 +842,7 @@ Geotrek.PointOnPolyline = function (marker) { this.percent_distance = null; this._activated = false; - this.events = L.Util.extend({}, L.Mixin.Events); + this.events = L.Util.extend({}, L.Evented.prototype); this.markerEvents = { 'move': function onMove (e) { diff --git a/geotrek/core/static/core/topology_helper.js b/geotrek/core/static/core/topology_helper.js index 41b935cb49..1627a12347 100644 --- a/geotrek/core/static/core/topology_helper.js +++ b/geotrek/core/static/core/topology_helper.js @@ -237,7 +237,7 @@ Geotrek.TopologyHelper = (function() { /** * @param topology {Object} with ``offset``, ``positions`` and ``paths`` as returned by buildSubTopology() * @param idToLayer {function} callback to obtain layer from id - * @returns L.multiPolyline + * @returns L.polyline */ function buildGeometryFromTopology(topology, idToLayer) { var latlngs = []; @@ -252,7 +252,7 @@ Geotrek.TopologyHelper = (function() { console.warn('Topology problem: ' + i + ' not in ' + JSON.stringify(topology.positions)); } } - return L.multiPolyline(latlngs); + return L.polyline(latlngs); } /** diff --git a/geotrek/core/templates/core/core_extrabody_fragment.html b/geotrek/core/templates/core/core_extrabody_fragment.html index 164661d713..4746d15a9b 100644 --- a/geotrek/core/templates/core/core_extrabody_fragment.html +++ b/geotrek/core/templates/core/core_extrabody_fragment.html @@ -2,7 +2,6 @@ diff --git a/geotrek/core/templates/core/path_list.html b/geotrek/core/templates/core/path_list.html index 45d4b55831..f4c4354267 100644 --- a/geotrek/core/templates/core/path_list.html +++ b/geotrek/core/templates/core/path_list.html @@ -23,8 +23,10 @@ $(function(){ $('span#nbresults').replaceWith(' ?') $('#objects-list').on( 'xhr', function(e,o) { - obj = JSON.parse(o.jqXHR.responseText); - $( "#sumPath" ).html(' ('+ obj.sumPath + ' km)') + if (o.jqXHR.responseText !== undefined) { + obj = JSON.parse(o.jqXHR.responseText); + $( "#sumPath" ).html(' ('+ obj.sumPath + ' km)') + } }); $('#btn-merge').click(function() { diff --git a/geotrek/core/tests/test_views.py b/geotrek/core/tests/test_views.py index d69f1c3718..652ea232aa 100644 --- a/geotrek/core/tests/test_views.py +++ b/geotrek/core/tests/test_views.py @@ -644,7 +644,7 @@ def test_denormalized_path_trails(self): PathFactory.create_batch(size=50) TrailFactory.create_batch(size=50) self.login() - with self.assertNumQueries(7): + with self.assertNumQueries(9): self.client.get(reverse('core:path_json_list')) diff --git a/geotrek/core/views.py b/geotrek/core/views.py index 5f138eb103..9daee2fbd7 100644 --- a/geotrek/core/views.py +++ b/geotrek/core/views.py @@ -65,6 +65,7 @@ def get_initial(self): class PathLayer(MapEntityLayer): properties = ['name', 'draft'] queryset = Path.objects.all() + geometry_field_db = 'geom' def get_queryset(self): qs = super().get_queryset() @@ -303,6 +304,7 @@ def get_graph_json(request): class TrailLayer(MapEntityLayer): queryset = Trail.objects.existing() properties = ['name'] + geometry_field_db = 'geom' class TrailList(MapEntityList): @@ -312,7 +314,8 @@ class TrailList(MapEntityList): class TrailJsonList(MapEntityJsonList, TrailList): - pass + def get_context_data(self, **kwargs): + return super(TrailJsonList, self).get_context_data(queryset=self.filterform(self.request.GET).qs, **kwargs) class TrailFormatList(MapEntityFormat, TrailList): diff --git a/geotrek/diving/tests/test_views.py b/geotrek/diving/tests/test_views.py index 06f546d6ae..64e2a23a9f 100644 --- a/geotrek/diving/tests/test_views.py +++ b/geotrek/diving/tests/test_views.py @@ -113,6 +113,13 @@ def test_pois_on_treks_not_public(self): response = self.client.get(reverse('diving:dive_poi_geojson', kwargs={'lang': translation.get_language(), 'pk': dive.pk})) self.assertEqual(response.status_code, 404) + def test_bbox_filter(self): + class DiveGoodGeomFactory(DiveFactory): + geom = 'Point(700000 6600000)' + + self.modelfactory = DiveGoodGeomFactory + super(DiveViewsTests, self).test_bbox_filter() + class DiveViewsLiveTests(CommonLiveTest): model = Dive diff --git a/geotrek/diving/views.py b/geotrek/diving/views.py index 90aa82d6a6..7e5a5ca843 100644 --- a/geotrek/diving/views.py +++ b/geotrek/diving/views.py @@ -28,12 +28,14 @@ class DiveLayer(MapEntityLayer): properties = ['name', 'published'] queryset = Dive.objects.existing() + geometry_field_db = 'geom' class DiveList(FlattenPicturesMixin, MapEntityList): filterform = DiveFilterSet columns = ['id', 'name', 'levels', 'thumbnail'] queryset = Dive.objects.existing() + template_name = 'diving/dive_list.html' class DiveJsonList(MapEntityJsonList, DiveList): diff --git a/geotrek/feedback/views.py b/geotrek/feedback/views.py index 9f01615c52..8d43d8fc72 100644 --- a/geotrek/feedback/views.py +++ b/geotrek/feedback/views.py @@ -21,6 +21,7 @@ class ReportLayer(mapentity_views.MapEntityLayer): model = feedback_models.Report filterform = ReportFilterSet properties = ['email'] + geometry_field_db = 'geom' class ReportList(mapentity_views.MapEntityList): diff --git a/geotrek/infrastructure/views.py b/geotrek/infrastructure/views.py index 3d34487efa..437889b706 100755 --- a/geotrek/infrastructure/views.py +++ b/geotrek/infrastructure/views.py @@ -20,6 +20,7 @@ class InfrastructureLayer(MapEntityLayer): queryset = Infrastructure.objects.existing() properties = ['name', 'published'] + geometry_field_db = 'geom' class InfrastructureList(MapEntityList): diff --git a/geotrek/land/views.py b/geotrek/land/views.py index 8ebe618059..b1f46d6187 100644 --- a/geotrek/land/views.py +++ b/geotrek/land/views.py @@ -12,6 +12,7 @@ class PhysicalEdgeLayer(MapEntityLayer): queryset = PhysicalEdge.objects.existing() properties = ['color_index', 'name'] + geometry_field_db = 'geom' class PhysicalEdgeList(MapEntityList): @@ -57,6 +58,7 @@ class PhysicalEdgeDelete(MapEntityDelete): class LandEdgeLayer(MapEntityLayer): queryset = LandEdge.objects.existing() properties = ['color_index', 'name'] + geometry_field_db = 'geom' class LandEdgeList(MapEntityList): @@ -102,6 +104,7 @@ class LandEdgeDelete(MapEntityDelete): class CompetenceEdgeLayer(MapEntityLayer): queryset = CompetenceEdge.objects.existing() properties = ['color_index', 'name'] + geometry_field_db = 'geom' class CompetenceEdgeList(MapEntityList): @@ -147,6 +150,7 @@ class CompetenceEdgeDelete(MapEntityDelete): class WorkManagementEdgeLayer(MapEntityLayer): queryset = WorkManagementEdge.objects.existing() properties = ['color_index', 'name'] + geometry_field_db = 'geom' class WorkManagementEdgeList(MapEntityList): @@ -192,6 +196,7 @@ class WorkManagementEdgeDelete(MapEntityDelete): class SignageManagementEdgeLayer(MapEntityLayer): queryset = SignageManagementEdge.objects.existing() properties = ['color_index', 'name'] + geometry_field_db = 'geom' class SignageManagementEdgeList(MapEntityList): diff --git a/geotrek/maintenance/filters.py b/geotrek/maintenance/filters.py index d10aba6788..307038f596 100644 --- a/geotrek/maintenance/filters.py +++ b/geotrek/maintenance/filters.py @@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _ from django_filters import ChoiceFilter, MultipleChoiceFilter -from mapentity.filters import PolygonFilter, PythonPolygonFilter +from mapentity.filters import PolygonFilter from geotrek.core.models import Topology from geotrek.authent.filters import StructureRelatedFilterSet @@ -116,7 +116,6 @@ class Meta(StructureRelatedFilterSet.Meta): class ProjectFilterSet(StructureRelatedFilterSet): - bbox = PythonPolygonFilter(field_name='geom') year = MultipleChoiceFilter( label=_("Year of activity"), method='filter_year', choices=lambda: Project.objects.year_choices() # Could change over time diff --git a/geotrek/maintenance/templates/maintenance/project_detail.html b/geotrek/maintenance/templates/maintenance/project_detail.html index f2735b3d7e..b90b0b1e8b 100644 --- a/geotrek/maintenance/templates/maintenance/project_detail.html +++ b/geotrek/maintenance/templates/maintenance/project_detail.html @@ -118,12 +118,12 @@ function showInterventionLabel (geojson, layer) { - if (geojson.properties.name) layer.bindLabel(geojson.properties.name); + if (geojson.properties.name) layer.bindTooltip(geojson.properties.name); } - function interventionUrl(properties, layer) { + function interventionUrl(id) { return window.SETTINGS.urls.detail.replace(new RegExp('modelname', 'g'), 'intervention') - .replace('0', properties.pk); + .replace('0', id); }; }); diff --git a/geotrek/maintenance/tests/test_views.py b/geotrek/maintenance/tests/test_views.py index dfb2fa198e..d44ab20fc0 100644 --- a/geotrek/maintenance/tests/test_views.py +++ b/geotrek/maintenance/tests/test_views.py @@ -505,10 +505,10 @@ def jsonlist(bbox): # Check that projects without interventions are always present self.assertEqual(len(Project.objects.all()), 3) self.assertEqual(len(jsonlist('')), 3) - self.assertEqual(len(jsonlist('?bbox=POLYGON((1%202%200%2C1%202%200%2C1%202%200%2C1%202%200%2C1%202%200))')), 2) + self.assertEqual(len(jsonlist('?tiles=5,16,11')), 2) # Give a bbox that match intervention, and check that all 3 projects are back - bbox = '?bbox=POLYGON((2.9%2046.4%2C%203.1%2046.4%2C%203.1%2046.6%2C%202.9%2046.6%2C%202.9%2046.4))' + bbox = '?tiles=5,16,11' self.assertEqual(len(jsonlist(bbox)), 3) def test_deleted_interventions(self): diff --git a/geotrek/maintenance/views.py b/geotrek/maintenance/views.py index 38b100dd83..d7104042f9 100755 --- a/geotrek/maintenance/views.py +++ b/geotrek/maintenance/views.py @@ -110,6 +110,7 @@ def get_queryset(self): class ProjectLayer(MapEntityLayer): queryset = Project.objects.existing() properties = ['name'] + geometry_field_db = 'geom' def get_queryset(self): nonemptyqs = Intervention.objects.existing().filter(project__isnull=False).values('project') diff --git a/geotrek/sensitivity/views.py b/geotrek/sensitivity/views.py index e3e1efa2ab..be6e5db5f8 100644 --- a/geotrek/sensitivity/views.py +++ b/geotrek/sensitivity/views.py @@ -30,6 +30,7 @@ class SensitiveAreaLayer(MapEntityLayer): queryset = SensitiveArea.objects.existing() properties = ['species', 'radius', 'published'] + geometry_field_db = 'geom' class SensitiveAreaList(MapEntityList): diff --git a/geotrek/settings/base.py b/geotrek/settings/base.py index 6eb742d1bf..a2a32c9f09 100644 --- a/geotrek/settings/base.py +++ b/geotrek/settings/base.py @@ -433,6 +433,7 @@ def api_bbox(bbox, buffer): # Let this be defined at instance-level LEAFLET_CONFIG = { + # 'ANIMATE': False, 'SRID': 3857, 'TILES': [ ('OpenTopoMap', 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', 'Données: © Contributeurs OpenStreetMap, SRTM | Affichage: © OpenTopoMap (CC-BY-SA)'), diff --git a/geotrek/signage/static/signage/main.js b/geotrek/signage/static/signage/main.js index 65ef1fc921..c83b607b01 100644 --- a/geotrek/signage/static/signage/main.js +++ b/geotrek/signage/static/signage/main.js @@ -10,7 +10,7 @@ $(window).on('entity:map', function (e, data) { modelname: 'signage', style: L.Util.extend(window.SETTINGS.map.styles['signage'] || {}, { clickable:false }), pointToLayer: function (feature, latlng) { - return L.marker(latlng).bindLabel(feature.properties.name, { noHide: true }); + return L.marker(latlng).bindTooltip(feature.properties.name, { permanent: true }); } }); var url = window.SETTINGS.urls['signage_layer']; diff --git a/geotrek/signage/views.py b/geotrek/signage/views.py index 0f076b1829..e2c0ea6275 100755 --- a/geotrek/signage/views.py +++ b/geotrek/signage/views.py @@ -31,6 +31,7 @@ class LineMixin(FormsetMixin): class SignageLayer(MapEntityLayer): queryset = Signage.objects.existing() properties = ['name', 'published'] + geometry_field_db = 'geom' class SignageList(MapEntityList): @@ -184,6 +185,7 @@ class BladeJsonList(MapEntityJsonList, BladeList): class BladeLayer(MapEntityLayer): queryset = Blade.objects.all() properties = ['number'] + geometry_field_db = 'signage__geom' class BladeFormatList(MapEntityFormat, BladeList): diff --git a/geotrek/static/style.css b/geotrek/static/style.css index 5ea9495e40..c8aad631f8 100644 --- a/geotrek/static/style.css +++ b/geotrek/static/style.css @@ -159,6 +159,7 @@ span.aggregation { border: 1px solid #d4d4d4; padding: 5px; width: 30%; + z-index: 1000; } #altitudegraph h4 { @@ -237,7 +238,7 @@ body.screamshot #altitudegraph { .poi-marker-icon img { - max-width: 100% !important; + width: 100%; } .leaflet-marker-icon.point-reference { diff --git a/geotrek/tourism/static/tourism/main.js b/geotrek/tourism/static/tourism/main.js index 03e3150191..6c28467054 100644 --- a/geotrek/tourism/static/tourism/main.js +++ b/geotrek/tourism/static/tourism/main.js @@ -5,32 +5,15 @@ $(window).on('entity:map', function (e, data) { var map = data.map; - var loaded_event = false; - var loaded_touristic = false; // Show tourism layer in application maps $.each(['touristiccontent', 'touristicevent'], function (i, modelname) { - var layer = new L.ObjectsLayer(null, { - modelname: modelname, - style: L.Util.extend(window.SETTINGS.map.styles[modelname] || {}, {clickable:false}), - }); if (data.modelname != modelname){ + var layer = new L.ObjectsLayer(window.SETTINGS.urls.tile.replace('modelname', modelname), { + modelname: modelname, + style: L.Util.extend(window.SETTINGS.map.styles[modelname] || {}, {clickable:false}), + }); map.layerscontrol.addOverlay(layer, tr(modelname), tr('Tourism')); }; - map.on('layeradd', function(e){ - var options = e.layer.options || {'modelname': 'None'}; - if (! loaded_event){ - if (options.modelname == 'touristicevent' && options.modelname != data.modelname){ - e.layer.load(window.SETTINGS.urls.touristicevent_layer); - loaded_event = true; - } - } - if (! loaded_touristic){ - if (options.modelname == 'touristiccontent' && options.modelname != data.modelname){ - e.layer.load(window.SETTINGS.urls.touristiccontent_layer); - loaded_touristic = true; - } - } - }); }); }); diff --git a/geotrek/tourism/tests/test_functional.py b/geotrek/tourism/tests/test_functional.py index d623f22559..f56c35020f 100644 --- a/geotrek/tourism/tests/test_functional.py +++ b/geotrek/tourism/tests/test_functional.py @@ -125,6 +125,12 @@ def test_intersection_zoning(self): self.assertEqual(response.status_code, 200) self.assertEqual(len(response.json()["map_obj_pk"]), 0) + def test_bbox_filter(self): + class TouristicContentGoodGeomFactory(TouristicContentFactory): + geom = 'Point(700000 6600000)' + self.modelfactory = TouristicContentGoodGeomFactory + super(TouristicContentViewsTests, self).test_bbox_filter() + class TouristicEventViewsTests(CommonTest): model = TouristicEvent @@ -234,3 +240,9 @@ def test_document_export_with_attachment(self, mock_requests): first_path = os.path.join(settings.MEDIA_ROOT, 'maps', element_in_dir[0]) second_path = os.path.join(settings.MEDIA_ROOT, attachment.attachment_file.name) self.assertTrue(filecmp.cmp(first_path, second_path)) + + def test_bbox_filter(self): + class TouristicEventGoodGeomFactory(TouristicEventFactory): + geom = 'Point(700000 6600000)' + self.modelfactory = TouristicEventGoodGeomFactory + super(TouristicEventViewsTests, self).test_bbox_filter() diff --git a/geotrek/tourism/views.py b/geotrek/tourism/views.py index 83b3c41700..a7ea1386d9 100644 --- a/geotrek/tourism/views.py +++ b/geotrek/tourism/views.py @@ -42,6 +42,7 @@ class TouristicContentLayer(MapEntityLayer): queryset = TouristicContent.objects.existing() properties = ['name'] + geometry_field_db = 'geom' class TouristicContentList(MapEntityList): @@ -158,6 +159,7 @@ class TouristicContentMeta(MetaMixin, DetailView): class TouristicEventLayer(MapEntityLayer): queryset = TouristicEvent.objects.existing() properties = ['name'] + geometry_field_db = 'geom' class TouristicEventList(MapEntityList): diff --git a/geotrek/trekking/static/trekking/signagelayer.js b/geotrek/trekking/static/trekking/signagelayer.js index d4b1a9be57..c5eca3d1c7 100644 --- a/geotrek/trekking/static/trekking/signagelayer.js +++ b/geotrek/trekking/static/trekking/signagelayer.js @@ -24,6 +24,6 @@ var SignagesLayer = L.GeoJSON.extend({ iconSize: [this.options.iconSize, this.options.iconSize], html: img}); - return L.marker(latlng, {icon: serviceicon}).bindLabel(featureData.properties.name, {noHide: true}); + return L.marker(latlng, {icon: serviceicon}).bindTooltip(featureData.properties.name, {permanent: true}); } }); diff --git a/geotrek/trekking/tests/test_views.py b/geotrek/trekking/tests/test_views.py index 0b1c3b0137..3746e83aa0 100755 --- a/geotrek/trekking/tests/test_views.py +++ b/geotrek/trekking/tests/test_views.py @@ -140,10 +140,10 @@ def test_listing_number_queries(self): self.modelfactory.build_batch(1000) DistrictFactory.build_batch(10) - with self.assertNumQueries(6): + with self.assertNumQueries(7): self.client.get(self.model.get_jsonlist_url()) - with self.assertNumQueries(9): + with self.assertNumQueries(10): self.client.get(self.model.get_format_list_url()) def test_pois_on_treks_do_not_exist(self): @@ -1496,8 +1496,8 @@ def test_listing_number_queries(self): self.modelfactory.build_batch(1000) DistrictFactory.build_batch(10) - # 1) session, 2) user, 3) user perms, 4) group perms, 5) last modified, 6) list - with self.assertNumQueries(6): + # 1) session, 2) user, 3) user perms, 4) group perms, 5) last modified, 6) list json init x3 + with self.assertNumQueries(8): self.client.get(self.model.get_jsonlist_url()) # 1) session, 2) user, 3) user perms, 4) group perms, 5) list diff --git a/geotrek/trekking/views.py b/geotrek/trekking/views.py index 45b42632f2..46f2c5e19f 100755 --- a/geotrek/trekking/views.py +++ b/geotrek/trekking/views.py @@ -11,7 +11,7 @@ from django.views.generic import CreateView, ListView, DetailView from django.views.generic.detail import BaseDetailView from mapentity.helpers import alphabet_enumeration -from mapentity.views import (MapEntityLayer, MapEntityList, MapEntityJsonList, +from mapentity.views import (MapEntityLayer, MapEntityTileLayer, MapEntityList, MapEntityJsonList, MapEntityFormat, MapEntityDetail, MapEntityMapImage, MapEntityDocument, MapEntityCreate, MapEntityUpdate, MapEntityDelete, LastModifiedMixin, MapEntityViewSet) @@ -57,16 +57,23 @@ def get_queryset(self): class TrekLayer(MapEntityLayer): properties = ['name', 'published'] queryset = Trek.objects.existing() + geometry_field_db = 'geom' + + +class TrekTileLayer(MapEntityTileLayer): + queryset = Trek.objects.existing() class TrekList(FlattenPicturesMixin, MapEntityList): filterform = TrekFilterSet columns = ['id', 'name', 'duration', 'difficulty', 'departure', 'thumbnail'] queryset = Trek.objects.existing() + template_name = 'trekking/trek_list.html' class TrekJsonList(MapEntityJsonList, TrekList): - pass + def get_context_data(self, **kwargs): + return super(TrekJsonList, self).get_context_data(queryset=self.filterform(self.request.GET).qs, **kwargs) class TrekFormatList(MapEntityFormat, TrekList): @@ -242,13 +249,15 @@ class TrekMeta(MetaMixin, DetailView): class POILayer(MapEntityLayer): queryset = POI.objects.existing() properties = ['name', 'published'] + geometry_field_db = 'geom' class POIList(FlattenPicturesMixin, MapEntityList): model = POI filterform = POIFilterSet columns = ['id', 'name', 'type', 'thumbnail'] - queryset = model.objects.existing() + queryset = POI.objects.existing() + template_name = 'trekking/poi_list.html' class POIJsonList(MapEntityJsonList, POIList): @@ -428,6 +437,7 @@ def get_queryset(self): class ServiceLayer(MapEntityLayer): properties = ['label', 'published'] queryset = Service.objects.existing() + geometry_field_db = 'geom' class ServiceList(MapEntityList): diff --git a/geotrek/zoning/views.py b/geotrek/zoning/views.py index e43bb9c5be..85e8ea7aad 100644 --- a/geotrek/zoning/views.py +++ b/geotrek/zoning/views.py @@ -4,6 +4,7 @@ from django.utils.decorators import method_decorator from djgeojson.views import GeoJSONLayerView +from mapentity.filters import MapEntityFilterSet from .models import City, RestrictedArea, RestrictedAreaType, District @@ -17,6 +18,12 @@ class LandLayerMixin: def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs) + def get_queryset(self): + # ensure mapentity filters are working for non Mapentity view + qs = super().get_queryset() + filtered = MapEntityFilterSet(self.request.GET, queryset=qs) + return filtered.qs + class CityGeoJSONLayer(LandLayerMixin, GeoJSONLayerView): model = City diff --git a/mapentity/decorators.py b/mapentity/decorators.py index 661ffe3aa9..a198b5e096 100644 --- a/mapentity/decorators.py +++ b/mapentity/decorators.py @@ -68,7 +68,7 @@ def decorator(view_func): def _wrapped_view(self, request, *args, **kwargs): view_model = self.get_model() - cache_latest = cache_last_modified(lambda x: view_model.latest_updated()) + cache_latest = cache_last_modified(lambda request, *args, **kwargs: view_model.latest_updated()) cbv_cache_latest = method_decorator(cache_latest) @method_decorator(cache_control(max_age=0, must_revalidate=True)) diff --git a/mapentity/models.py b/mapentity/models.py index 88504dd994..fa6342028a 100755 --- a/mapentity/models.py +++ b/mapentity/models.py @@ -24,6 +24,7 @@ # Used to create the matching url name ENTITY_LAYER = "layer" +ENTITY_TILE_LAYER = "tile_layer" ENTITY_LIST = "list" ENTITY_JSON_LIST = "json_list" ENTITY_FORMAT_LIST = "format_list" @@ -37,7 +38,7 @@ ENTITY_UPDATE_GEOM = "update_geom" ENTITY_KINDS = ( - ENTITY_LAYER, ENTITY_LIST, ENTITY_JSON_LIST, + ENTITY_LAYER, ENTITY_TILE_LAYER, ENTITY_LIST, ENTITY_JSON_LIST, ENTITY_FORMAT_LIST, ENTITY_DETAIL, ENTITY_MAPIMAGE, ENTITY_DOCUMENT, ENTITY_MARKUP, ENTITY_CREATE, ENTITY_UPDATE, ENTITY_DELETE, ENTITY_UPDATE_GEOM ) @@ -95,6 +96,7 @@ def get_entity_kind_permission(cls, entity_kind): ENTITY_DELETE: ENTITY_PERMISSION_DELETE, ENTITY_DETAIL: ENTITY_PERMISSION_READ, ENTITY_LAYER: ENTITY_PERMISSION_READ, + ENTITY_TILE_LAYER: ENTITY_PERMISSION_READ, ENTITY_LIST: ENTITY_PERMISSION_READ, ENTITY_JSON_LIST: ENTITY_PERMISSION_READ, ENTITY_MARKUP: ENTITY_PERMISSION_READ, @@ -148,6 +150,10 @@ def delete(self, *args, **kwargs): def get_layer_url(cls): return reverse(cls._entity.url_name(ENTITY_LAYER)) + @classmethod + def get_tile_layer_url(cls): + return reverse(cls._entity.url_name(ENTITY_TILE_LAYER)) + @classmethod def get_list_url(cls): return reverse(cls._entity.url_name(ENTITY_LIST)) diff --git a/mapentity/registry.py b/mapentity/registry.py index 1245bc0dcc..0c2dff24f1 100644 --- a/mapentity/registry.py +++ b/mapentity/registry.py @@ -65,12 +65,13 @@ def scan_views(self): # Filter to views inherited from MapEntity base views picked = [] rest_viewset = None + tile_view = None list_view = None for name, view in inspect.getmembers(views_module): if inspect.isclass(view) and issubclass(view, View): # Pick-up views - if hasattr(view, 'get_entity_kind') or issubclass(view, mapentity_views.MapEntityViewSet): + if hasattr(view, 'get_entity_kind') or issubclass(view, mapentity_views.MapEntityViewSet) or issubclass(view, mapentity_views.MapEntityTileLayer): try: view_model = view.model or view.queryset.model except AttributeError: @@ -79,6 +80,8 @@ def scan_views(self): if view_model is self.model: if issubclass(view, mapentity_views.MapEntityViewSet): rest_viewset = view + elif issubclass(view, mapentity_views.MapEntityTileLayer): + tile_view = view elif issubclass(view, mapentity_views.MapEntityList): picked.append(view) list_view = view @@ -123,6 +126,19 @@ class dynamic_viewset(mapentity_views.MapEntityViewSet): self.rest_router.register(self.modelname + 's', rest_viewset, basename=self.modelname) + # Dynamically define tile view + if tile_view is None: + class dynamic_tile_view(mapentity_views.MapEntityTileLayer): + pass + tile_view = dynamic_tile_view + + if tile_view.queryset is None: + tile_view.queryset = self.get_queryset() + if tile_view.serializer_class is None: + tile_view.serializer_class = self.get_tile_serializer() + + picked.append(tile_view) + # Returns Django URL patterns return self.__view_classes_to_url(*picked) @@ -151,12 +167,27 @@ class Meta: return Serializer + def get_tile_serializer(self): + _model = self.model + + class Serializer(GeoFeatureModelSerializer): + api_geom = GeometryField(read_only=True, precision=7) + + class Meta: + model = _model + geo_field = "api_geom" + id_field = 'id' + fields = ('id', ) + + return Serializer + def get_queryset(self): return self.model.objects.all() def _url_path(self, view_kind): kind_to_urlpath = { mapentity_models.ENTITY_LAYER: r'^api/{modelname}/{modelname}.geojson$', + mapentity_models.ENTITY_TILE_LAYER: r'^api/{modelname}/(?P\d+)/(?P\d+)/(?P\d+).geojson$', mapentity_models.ENTITY_LIST: r'^{modelname}/list/$', mapentity_models.ENTITY_JSON_LIST: r'^api/{modelname}/{modelname}s.json$', mapentity_models.ENTITY_FORMAT_LIST: r'^{modelname}/list/export/$', diff --git a/mapentity/serializers/datatables.py b/mapentity/serializers/datatables.py index a17d5cd2f5..fb2078c89b 100644 --- a/mapentity/serializers/datatables.py +++ b/mapentity/serializers/datatables.py @@ -6,12 +6,12 @@ class DatatablesSerializer(Serializer): - def serialize(self, queryset, **options): - model = options.pop('model', None) or queryset.model - columns = options.pop('fields') + def serialize(self, queryset, fields, model=None, total_records=0, total_display_records=0, echo=None, **options): + if model is None: + model = queryset.model getters = {} - for field in columns: + for field in fields: if hasattr(model, field + '_display'): getters[field] = lambda obj, field: getattr(obj, field + '_display') else: @@ -31,11 +31,14 @@ def fixfloat(obj, field): map_obj_pk = [] data_table_rows = [] for obj in queryset: - row = [getters[field](obj, field) for field in columns] + row = [getters[field](obj, field) for field in fields] data_table_rows.append(row) map_obj_pk.append(obj.pk) return { + 'sEcho': echo, + 'iTotalRecords': total_records, + 'iTotalDisplayRecords': total_display_records, # aaData is the key looked up by dataTables 'aaData': data_table_rows, 'map_obj_pk': map_obj_pk, diff --git a/mapentity/settings.py b/mapentity/settings.py index 594cf77471..e534cbdcf2 100644 --- a/mapentity/settings.py +++ b/mapentity/settings.py @@ -100,10 +100,6 @@ ('leaflet.overintent', { 'js': 'mapentity/Leaflet.OverIntent/leaflet.overintent.js', }), - ('leaflet.label', { - 'css': 'mapentity/Leaflet.label/dist/leaflet.label.css', - 'js': 'mapentity/Leaflet.label/dist/leaflet.label.js' - }), ('leaflet.spin', { 'js': ['paperclip/spin.min.js', 'mapentity/Leaflet.Spin/leaflet.spin.js'] @@ -135,6 +131,9 @@ 'css': 'mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.css', 'js': 'mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.js' }), + ('leaflet.geojson.grid', { + 'js': 'mapentity/leaflet.geojson.grid.js', + }), ('mapentity', { 'js': ['mapentity/mapentity.js', 'mapentity/mapentity.forms.js'], diff --git a/mapentity/static/mapentity/Leaflet.FileLayer/README.md b/mapentity/static/mapentity/Leaflet.FileLayer/README.md index ca77e3cc66..1cd285da10 100644 --- a/mapentity/static/mapentity/Leaflet.FileLayer/README.md +++ b/mapentity/static/mapentity/Leaflet.FileLayer/README.md @@ -1,7 +1,7 @@ Leaflet.FileLayer ================= -Loads local files (GeoJSON, GPX, KML) into the map using the [HTML5 FileReader API](http://caniuse.com/filereader), **without server call** ! +Loads local files (GeoJSON, JSON, GPX, KML) into the map using the [HTML5 FileReader API](http://caniuse.com/filereader), **without server call** ! * A simple map control * The user can browse a file locally @@ -12,29 +12,124 @@ Check out the [demo](http://makinacorpus.github.com/Leaflet.FileLayer/) ! For GPX and KML files, it currently depends on [Tom MacWright's togeojson.js](https://github.com/tmcw/togeojson). +[![Build Status](https://travis-ci.org/makinacorpus/Leaflet.FileLayer.png?branch=master)](https://travis-ci.org/makinacorpus/Leaflet.FileLayer) + +Install +----- +In order to use this plugin in your app you can either: +* install it via your favorite package manager: + * `npm i leaflet-filelayer` + * `bower install git://github.com:makinacorpus/Leaflet.FileLayer.git` +* download the repository and import the `leaflet.filelayer.js` file in your app. + +Dependencies and compatibilities +----- +In order to use this plugin, you need to have both `leaflet` and `togeojson` installed. +If you're using Leaflet < 1, you need to use the version `0.6.0` of this plugin. After that, Leaflet > 1 is required. + Usage ----- -``` +```javascript var map = L.map('map').fitWorld(); ... L.Control.fileLayerLoad({ - layerOptions: {style: {color:'red'}} + // Allows you to use a customized version of L.geoJson. + // For example if you are using the Proj4Leaflet leaflet plugin, + // you can pass L.Proj.geoJson and load the files into the + // L.Proj.GeoJson instead of the L.geoJson. + layer: L.geoJson, + // See http://leafletjs.com/reference.html#geojson-options + layerOptions: {style: {color:'red'}}, + // Add to map after loading (default: true) ? + addToMap: true, + // File size limit in kb (default: 1024) ? + fileSizeLimit: 1024, + // Restrict accepted file formats (default: .geojson, .json, .kml, and .gpx) ? + formats: [ + '.geojson', + '.kml' + ] }).addTo(map); ``` Events: -``` +* **data:loaded** (event) + +```javascript var control = L.Control.fileLayerLoad(); - control.loader.on('data:loaded', function (e) { + control.loader.on('data:loaded', function (event) { + // event.layer gives you access to the layers you just uploaded! + // Add to map layer switcher - layerswitcher.addOverlay(e.layer, e.filename); + layerswitcher.addOverlay(event.layer, event.filename); }); ``` +* **data:error** (error) +```javascript + var control = L.Control.fileLayerLoad(); + control.loader.on('data:error', function (error) { + // Do something usefull with the error! + console,error(error); + }); +``` + +Changelog +--------- + +### 1.2.0 ### + +* Leaflet 1.2.0 compatibility +* Accept `json` file as input (thanks kkdd) + +### 1.1.0 ### + +* Leaflet 1.1.0 compatibility (thanks @thorinii) + +### 0.6.0 ### + +* Better plugin packaging and dependencies +* Adding bower support (thanks @george-silva) +* Adding support for custom geoJson layers (thanks @MuellerMatthew) +* Treating json files as geoJson (thanks @Jmuccigr) + +### 0.5.0 ### + +* Load multiple files (thanks @jens-duttke) + +### 0.4.0 ### + +* Support whitelist for file formats (thanks CJ Cenizal) + +### 0.3.0 ### + +* Add `data:error` event (thanks @joeybaker) +* Fix multiple uploads (thanks @joeybaker) +* Add `addToMap` option (thanks @joeybaker) + +(* Did not release version 0.2 to prevent conflicts with Joey's fork. *) + +### 0.1.0 ### + +* Initial working version Authors ------- [![Makina Corpus](http://depot.makina-corpus.org/public/logo.gif)](http://makinacorpus.com) + +Contributions + +* Mathieu Leplatre +* Joey Baker http://byjoeybaker.com +* CJ Cenizal +* Jens-duttke +* Jmuccigr +* Matthew Mueller +* George Silva +* Simon Bats +* Opoto +* Lachlan Phillips +* kkdd diff --git a/mapentity/static/mapentity/Leaflet.FileLayer/leaflet.filelayer.js b/mapentity/static/mapentity/Leaflet.FileLayer/leaflet.filelayer.js index 1b3df3d50e..19b6ae7d6b 100644 --- a/mapentity/static/mapentity/Leaflet.FileLayer/leaflet.filelayer.js +++ b/mapentity/static/mapentity/Leaflet.FileLayer/leaflet.filelayer.js @@ -1,168 +1,342 @@ /* * Load files *locally* (GeoJSON, KML, GPX) into the map * using the HTML5 File API. - * - * Requires Pavel Shramov's GPX.js - * https://github.com/shramov/leaflet-plugins/blob/d74d67/layer/vector/GPX.js + * + * Requires Mapbox's togeojson.js to be in global scope + * https://github.com/mapbox/togeojson */ -var FileLoader = L.Class.extend({ - includes: L.Mixin.Events, - options: { - layerOptions: {} - }, - - initialize: function (map, options) { - this._map = map; - L.Util.setOptions(this, options); - - this._parsers = { - 'geojson': this._loadGeoJSON, - 'gpx': this._convertToGeoJSON, - 'kml': this._convertToGeoJSON + +(function (factory, window) { + // define an AMD module that relies on 'leaflet' + if (typeof define === 'function' && define.amd && window.toGeoJSON) { + define(['leaflet'], function (L) { + factory(L, window.toGeoJSON); + }); + } else if (typeof module === 'object' && module.exports) { + // require('LIBRARY') returns a factory that requires window to + // build a LIBRARY instance, we normalize how we use modules + // that require this pattern but the window provided is a noop + // if it's defined + module.exports = function (root, L, toGeoJSON) { + if (L === undefined) { + if (typeof window !== 'undefined') { + L = require('leaflet'); + } else { + L = require('leaflet')(root); + } + } + if (toGeoJSON === undefined) { + if (typeof window !== 'undefined') { + toGeoJSON = require('togeojson'); + } else { + toGeoJSON = require('togeojson')(root); + } + } + factory(L, toGeoJSON); + return L; }; - }, + } else if (typeof window !== 'undefined' && window.L && window.toGeoJSON) { + factory(window.L, window.toGeoJSON); + } +}(function fileLoaderFactory(L, toGeoJSON) { + var FileLoader = L.Layer.extend({ + options: { + layer: L.geoJson, + layerOptions: {}, + fileSizeLimit: 1024 + }, + + initialize: function (map, options) { + this._map = map; + L.Util.setOptions(this, options); + + this._parsers = { + geojson: this._loadGeoJSON, + json: this._loadGeoJSON, + gpx: this._convertToGeoJSON, + kml: this._convertToGeoJSON + }; + }, + + load: function (file, ext) { + var parser, + reader; + + // Check file is defined + if (this._isParameterMissing(file, 'file')) { + return false; + } + + // Check file size + if (!this._isFileSizeOk(file.size)) { + return false; + } + + // Get parser for this data type + parser = this._getParser(file.name, ext); + if (!parser) { + return false; + } + + // Read selected file using HTML5 File API + reader = new FileReader(); + reader.onload = L.Util.bind(function (e) { + var layer; + try { + this.fire('data:loading', { filename: file.name, format: parser.ext }); + layer = parser.processor.call(this, e.target.result, parser.ext); + this.fire('data:loaded', { + layer: layer, + filename: file.name, + format: parser.ext + }); + } catch (err) { + this.fire('data:error', { error: err }); + } + }, this); + // Testing trick: tests don't pass a real file, + // but an object with file.testing set to true. + // This object cannot be read by reader, just skip it. + if (!file.testing) { + reader.readAsText(file); + } + // We return this to ease testing + return reader; + }, - load: function (file /* File */) { - // Check file extension - var ext = file.name.split('.').pop(), + loadMultiple: function (files, ext) { + var readers = []; + if (files[0]) { + files = Array.prototype.slice.apply(files); + while (files.length > 0) { + readers.push(this.load(files.shift(), ext)); + } + } + // return first reader (or false if no file), + // which is also used for subsequent loadings + return readers; + }, + + loadData: function (data, name, ext) { + var parser; + var layer; + + // Check required parameters + if ((this._isParameterMissing(data, 'data')) + || (this._isParameterMissing(name, 'name'))) { + return; + } + + // Check file size + if (!this._isFileSizeOk(data.length)) { + return; + } + + // Get parser for this data type + parser = this._getParser(name, ext); + if (!parser) { + return; + } + + // Process data + try { + this.fire('data:loading', { filename: name, format: parser.ext }); + layer = parser.processor.call(this, data, parser.ext); + this.fire('data:loaded', { + layer: layer, + filename: name, + format: parser.ext + }); + } catch (err) { + this.fire('data:error', { error: err }); + } + }, + + _isParameterMissing: function (v, vname) { + if (typeof v === 'undefined') { + this.fire('data:error', { + error: new Error('Missing parameter: ' + vname) + }); + return true; + } + return false; + }, + + _getParser: function (name, ext) { + var parser; + ext = ext || name.split('.').pop(); parser = this._parsers[ext]; - if (!parser) { - window.alert("Unsupported file type " + file.type + '(' + ext + ')'); - return; - } - // Read selected file using HTML5 File API - var reader = new FileReader(); - reader.onload = L.Util.bind(function (e) { - this.fire('data:loading', {filename: file.name, format: ext}); - var layer = parser.call(this, e.target.result, ext); - this.fire('data:loaded', {layer: layer, filename: file.name, format: ext}); - }, this); - reader.readAsText(file); - }, - - _loadGeoJSON: function (content) { - if (typeof content == 'string') { - content = JSON.parse(content); - } - return L.geoJson(content, this.options.layerOptions).addTo(this._map); - }, + if (!parser) { + this.fire('data:error', { + error: new Error('Unsupported file type (' + ext + ')') + }); + return undefined; + } + return { + processor: parser, + ext: ext + }; + }, + + _isFileSizeOk: function (size) { + var fileSize = (size / 1024).toFixed(4); + if (fileSize > this.options.fileSizeLimit) { + this.fire('data:error', { + error: new Error( + 'File size exceeds limit (' + + fileSize + ' > ' + + this.options.fileSizeLimit + 'kb)' + ) + }); + return false; + } + return true; + }, + + _loadGeoJSON: function _loadGeoJSON(content) { + var layer; + if (typeof content === 'string') { + content = JSON.parse(content); + } + layer = this.options.layer(content, this.options.layerOptions); + + if (layer.getLayers().length === 0) { + throw new Error('GeoJSON has no valid layers.'); + } - _convertToGeoJSON: function (content, format) { - // Format is either 'gpx' or 'kml' - if (typeof content == 'string') { - content = ( new window.DOMParser() ).parseFromString(content, "text/xml"); + if (this.options.addToMap) { + layer.addTo(this._map); + } + return layer; + }, + + _convertToGeoJSON: function _convertToGeoJSON(content, format) { + var geojson; + // Format is either 'gpx' or 'kml' + if (typeof content === 'string') { + content = (new window.DOMParser()).parseFromString(content, 'text/xml'); + } + geojson = toGeoJSON[format](content); + return this._loadGeoJSON(geojson); } - var geojson = toGeoJSON[format](content); - return this._loadGeoJSON(geojson); - } -}); - - -L.Control.FileLayerLoad = L.Control.extend({ - statics: { - TITLE: 'Load local file (GPX, KML, GeoJSON)', - LABEL: '⌅' - }, - options: { - position: 'topleft', - fitBounds: true, - layerOptions: {} - }, - - initialize: function (options) { - L.Util.setOptions(this, options); - this.loader = null; - }, - - onAdd: function (map) { - this.loader = new FileLoader(map, {layerOptions: this.options.layerOptions}); - - this.loader.on('data:loaded', function (e) { - // Fit bounds after loading - if (this.options.fitBounds) { - window.setTimeout(function () { - map.fitBounds(e.layer.getBounds()).zoomOut(); - }, 500); - } - }, this); - - // Initialize Drag-and-drop - this._initDragAndDrop(map); - - // Initialize map control - return this._initContainer(); - }, - - _initDragAndDrop: function (map) { - var fileLoader = this.loader, - dropbox = map._container; - - var callbacks = { - dragenter: function () { - map.scrollWheelZoom.disable(); - }, - dragleave: function () { - map.scrollWheelZoom.enable(); - }, - dragover: function (e) { - e.stopPropagation(); - e.preventDefault(); - }, - drop: function (e) { - e.stopPropagation(); - e.preventDefault(); + }); + + var FileLayerLoad = L.Control.extend({ + statics: { + TITLE: 'Load local file (GPX, KML, GeoJSON)', + LABEL: '⌅' + }, + options: { + position: 'topleft', + fitBounds: true, + layerOptions: {}, + addToMap: true, + fileSizeLimit: 1024 + }, + + initialize: function (options) { + L.Util.setOptions(this, options); + this.loader = null; + }, - var files = Array.prototype.slice.apply(e.dataTransfer.files), - i = files.length; - setTimeout(function(){ - fileLoader.load(files.shift()); - if (files.length > 0) { - setTimeout(arguments.callee, 25); - } - }, 25); - map.scrollWheelZoom.enable(); + onAdd: function (map) { + this.loader = L.FileLayer.fileLoader(map, this.options); + + this.loader.on('data:loaded', function (e) { + // Fit bounds after loading + if (this.options.fitBounds) { + window.setTimeout(function () { + map.fitBounds(e.layer.getBounds()); + }, 500); + } + }, this); + + // Initialize Drag-and-drop + this._initDragAndDrop(map); + + // Initialize map control + return this._initContainer(); + }, + + _initDragAndDrop: function (map) { + var callbackName; + var thisLoader = this.loader; + var dropbox = map._container; + + var callbacks = { + dragenter: function () { + map.scrollWheelZoom.disable(); + }, + dragleave: function () { + map.scrollWheelZoom.enable(); + }, + dragover: function (e) { + e.stopPropagation(); + e.preventDefault(); + }, + drop: function (e) { + e.stopPropagation(); + e.preventDefault(); + + thisLoader.loadMultiple(e.dataTransfer.files); + map.scrollWheelZoom.enable(); + } + }; + for (callbackName in callbacks) { + if (callbacks.hasOwnProperty(callbackName)) { + dropbox.addEventListener(callbackName, callbacks[callbackName], false); + } } - }; - for (var name in callbacks) - dropbox.addEventListener(name, callbacks[name], false); - }, - - _initContainer: function () { - // Create an invisible file input - var fileInput = L.DomUtil.create('input', 'hidden', container); - fileInput.type = 'file'; - fileInput.accept = '.gpx,.kml,.geojson'; - fileInput.style.display = 'none'; - // Load on file change - var fileLoader = this.loader; - fileInput.addEventListener("change", function (e) { - fileLoader.load(this.files[0]); - }, false); - - // Create a button, and bind click on hidden file input - var zoomName = 'leaflet-control-filelayer leaflet-control-zoom', - barName = 'leaflet-bar', - partName = barName + '-part', - container = L.DomUtil.create('div', zoomName + ' ' + barName); - var link = L.DomUtil.create('a', zoomName + '-in ' + partName, container); - link.innerHTML = L.Control.FileLayerLoad.LABEL; - link.href = '#'; - link.title = L.Control.FileLayerLoad.TITLE; - - var stop = L.DomEvent.stopPropagation; - L.DomEvent - .on(link, 'click', stop) - .on(link, 'mousedown', stop) - .on(link, 'dblclick', stop) - .on(link, 'click', L.DomEvent.preventDefault) - .on(link, 'click', function (e) { + }, + + _initContainer: function () { + var thisLoader = this.loader; + + // Create a button, and bind click on hidden file input + var fileInput; + var zoomName = 'leaflet-control-filelayer leaflet-control-zoom'; + var barName = 'leaflet-bar'; + var partName = barName + '-part'; + var container = L.DomUtil.create('div', zoomName + ' ' + barName); + var link = L.DomUtil.create('a', zoomName + '-in ' + partName, container); + link.innerHTML = L.Control.FileLayerLoad.LABEL; + link.href = '#'; + link.title = L.Control.FileLayerLoad.TITLE; + + // Create an invisible file input + fileInput = L.DomUtil.create('input', 'hidden', container); + fileInput.type = 'file'; + fileInput.multiple = 'multiple'; + if (!this.options.formats) { + fileInput.accept = '.gpx,.kml,.json,.geojson'; + } else { + fileInput.accept = this.options.formats.join(','); + } + fileInput.style.display = 'none'; + // Load on file change + fileInput.addEventListener('change', function () { + thisLoader.loadMultiple(this.files); + // reset so that the user can upload the same file again if they want to + this.value = ''; + }, false); + + L.DomEvent.disableClickPropagation(link); + L.DomEvent.on(link, 'click', function (e) { fileInput.click(); e.preventDefault(); }); - return container; - } -}); + return container; + } + }); + + L.FileLayer = {}; + L.FileLayer.FileLoader = FileLoader; + L.FileLayer.fileLoader = function (map, options) { + return new L.FileLayer.FileLoader(map, options); + }; -L.Control.fileLayerLoad = function (options) { - return new L.Control.FileLayerLoad(options); -}; + L.Control.FileLayerLoad = FileLayerLoad; + L.Control.fileLayerLoad = function (options) { + return new L.Control.FileLayerLoad(options); + }; +}, window)); diff --git a/mapentity/static/mapentity/Leaflet.FileLayer/package.json b/mapentity/static/mapentity/Leaflet.FileLayer/package.json index 34e2950068..3ca8216cf0 100644 --- a/mapentity/static/mapentity/Leaflet.FileLayer/package.json +++ b/mapentity/static/mapentity/Leaflet.FileLayer/package.json @@ -1,21 +1,40 @@ { - "name": "leaflet-filelayer" - , "version": "0.1.0" - , "description": "Loads local files (GeoJSON, GPX, KML) into the map using the HTML5 FileReader API" - , "keywords": ["Leaflet", "GIS", "HTML5"] - , "main": "leaflet.filelayer.js" - , "bugs": { + "name": "leaflet-filelayer", + "version": "1.2.0", + "description": "Loads local files (GeoJSON, GPX, KML) into the map using the HTML5 FileReader API", + "keywords": [ + "Leaflet", + "GIS", + "HTML5" + ], + "main": "src/leaflet.filelayer.js", + "bugs": { "url": "https://github.com/makinacorpus/Leaflet.FileLayer/issues" - } - , "repository": { + }, + "repository": { "type": "git", "url": "git://github.com/makinacorpus/Leaflet.FileLayer.git" - } - , "license": "MIT" - , "scripts": { - } - , "dependencies": { - "leaflet": "*", - "togeojson": "*" + }, + "license": "MIT", + "scripts": { + "test": "mocha-phantomjs -p ./node_modules/.bin/phantomjs test/index.html", + "start": "live-server --open=./dev" + }, + "peerDependencies": { + "leaflet": ">= 0.7.7 < 2", + "togeojson": "^0.16.0" + }, + "devDependencies": { + "chai": "^3.5.0", + "eslint": "^3.2.2", + "eslint-config-airbnb-base": "^5.0.1", + "eslint-plugin-import": "^1.12.0", + "happen": "^0.3.1", + "live-server": "^1.1.0", + "mocha": "^4.0.1", + "mocha-phantomjs": "^4.1.0", + "phantomjs-prebuilt": "^2.1.16", + "requirejs": "^2.2.0", + "sinon": "^1.17.5" } } diff --git a/mapentity/static/mapentity/Leaflet.GeometryUtil/dist/leaflet.geometryutil.js b/mapentity/static/mapentity/Leaflet.GeometryUtil/dist/leaflet.geometryutil.js index 56ee763a38..145a8428ce 100644 --- a/mapentity/static/mapentity/Leaflet.GeometryUtil/dist/leaflet.geometryutil.js +++ b/mapentity/static/mapentity/Leaflet.GeometryUtil/dist/leaflet.geometryutil.js @@ -17,6 +17,11 @@ }(function (L) { "use strict"; +L.Polyline._flat = L.LineUtil.isFlat || L.Polyline._flat || function (latlngs) { + // true if it's a flat array of latlngs; false if nested + return !L.Util.isArray(latlngs[0]) || (typeof latlngs[0][0] !== 'object' && typeof latlngs[0][0] !== 'undefined'); +}; + /** * @fileOverview Leaflet Geometry utilities for distances and linear referencing. * @name L.GeometryUtil @@ -26,10 +31,13 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Shortcut function for planar distance between two {L.LatLng} at current zoom. - @param {L.Map} map - @param {L.LatLng} latlngA - @param {L.LatLng} latlngB - @returns {Number} in pixels + + @tutorial distance-length + + @param {L.Map} map Leaflet map to be used for this method + @param {L.LatLng} latlngA geographical point A + @param {L.LatLng} latlngB geographical point B + @returns {Number} planar distance */ distance: function (map, latlngA, latlngB) { return map.latLngToLayerPoint(latlngA).distanceTo(map.latLngToLayerPoint(latlngB)); @@ -37,11 +45,11 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Shortcut function for planar distance between a {L.LatLng} and a segment (A-B). - @param {L.Map} map - @param {L.LatLng} latlng - @param {L.LatLng} latlngA - @param {L.LatLng} latlngB - @returns {Number} in pixels + @param {L.Map} map Leaflet map to be used for this method + @param {L.LatLng} latlng - The position to search + @param {L.LatLng} latlngA geographical point A of the segment + @param {L.LatLng} latlngB geographical point B of the segment + @returns {Number} planar distance */ distanceSegment: function (map, latlng, latlngA, latlngB) { var p = map.latLngToLayerPoint(latlng), @@ -52,9 +60,9 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Shortcut function for converting distance to readable distance. - @param {Number} distance - @param {String} unit ('metric' or 'imperial') - @returns {Number} in yard or miles + @param {Number} distance distance to be converted + @param {String} unit 'metric' or 'imperial' + @returns {String} in yard or miles */ readableDistance: function (distance, unit) { var isMetric = (unit !== 'imperial'), @@ -81,11 +89,11 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { }, /** - Returns true if the latlng belongs to segment. - param {L.LatLng} latlng - @param {L.LatLng} latlngA - @param {L.LatLng} latlngB - @param {?Number} [tolerance=0.2] + Returns true if the latlng belongs to segment A-B + @param {L.LatLng} latlng - The position to search + @param {L.LatLng} latlngA geographical point A of the segment + @param {L.LatLng} latlngB geographical point B of the segment + @param {?Number} [tolerance=0.2] tolerance to accept if latlng belongs really @returns {boolean} */ belongsSegment: function(latlng, latlngA, latlngB, tolerance) { @@ -97,8 +105,10 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** * Returns total length of line - * @param {L.Polyline|Array|Array} - * @returns {Number} in meters + * @tutorial distance-length + * + * @param {L.Polyline|Array|Array} coords Set of coordinates + * @returns {Number} Total length (pixels for Point, meters for LatLng) */ length: function (coords) { var accumulated = L.GeometryUtil.accumulatedLengths(coords); @@ -107,8 +117,8 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** * Returns a list of accumulated length along a line. - * @param {L.Polyline|Array|Array} - * @returns {Number} in meters + * @param {L.Polyline|Array|Array} coords Set of coordinates + * @returns {Array} Array of accumulated lengths (pixels for Point, meters for LatLng) */ accumulatedLengths: function (coords) { if (typeof coords.getLatLngs == 'function') { @@ -127,11 +137,14 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns the closest point of a {L.LatLng} on the segment (A-B) - @param {L.Map} map - @param {L.LatLng} latlng - @param {L.LatLng} latlngA - @param {L.LatLng} latlngB - @returns {L.LatLng} + + @tutorial closest + + @param {L.Map} map Leaflet map to be used for this method + @param {L.LatLng} latlng - The position to search + @param {L.LatLng} latlngA geographical point A of the segment + @param {L.LatLng} latlngB geographical point B of the segment + @returns {L.LatLng} Closest geographical point */ closestOnSegment: function (map, latlng, latlngA, latlngB) { var maxzoom = map.getMaxZoom(); @@ -146,59 +159,123 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns the closest latlng on layer. - @param {L.Map} map - @param {Array|L.PolyLine} layer - Layer that contains the result. - @param {L.LatLng} latlng + + Accept nested arrays + + @tutorial closest + + @param {L.Map} map Leaflet map to be used for this method + @param {Array|Array>|L.PolyLine|L.Polygon} layer - Layer that contains the result + @param {L.LatLng} latlng - The position to search @param {?boolean} [vertices=false] - Whether to restrict to path vertices. - @returns {L.LatLng} + @returns {L.LatLng} Closest geographical point or null if layer param is incorrect */ closest: function (map, layer, latlng, vertices) { - if (typeof layer.getLatLngs != 'function') - layer = L.polyline(layer); - var latlngs = layer.getLatLngs().slice(0), + var latlngs, mindist = Infinity, result = null, - i, n, distance; - - // Lookup vertices - if (vertices) { - for(i = 0, n = latlngs.length; i < n; i++) { - var ll = latlngs[i]; - distance = L.GeometryUtil.distance(map, latlng, ll); - if (distance < mindist) { - mindist = distance; - result = ll; - result.distance = distance; + i, n, distance, subResult; + + if (layer instanceof Array) { + // if layer is Array> + if (layer[0] instanceof Array && typeof layer[0][0] !== 'number') { + // if we have nested arrays, we calc the closest for each array + // recursive + for (i = 0; i < layer.length; i++) { + subResult = L.GeometryUtil.closest(map, layer[i], latlng, vertices); + if (subResult && subResult.distance < mindist) { + mindist = subResult.distance; + result = subResult; + } } + return result; + } else if (layer[0] instanceof L.LatLng + || typeof layer[0][0] === 'number' + || typeof layer[0].lat === 'number') { // we could have a latlng as [x,y] with x & y numbers or {lat, lng} + layer = L.polyline(layer); + } else { + return result; } - return result; } + // if we don't have here a Polyline, that means layer is incorrect + // see https://github.com/makinacorpus/Leaflet.GeometryUtil/issues/23 + if (! ( layer instanceof L.Polyline ) ) + return result; + + // deep copy of latlngs + latlngs = JSON.parse(JSON.stringify(layer.getLatLngs().slice(0))); + + // add the last segment for L.Polygon if (layer instanceof L.Polygon) { - latlngs.push(latlngs[0]); + // add the last segment for each child that is a nested array + var addLastSegment = function(latlngs) { + if (L.Polyline._flat(latlngs)) { + latlngs.push(latlngs[0]); + } else { + for (var i = 0; i < latlngs.length; i++) { + addLastSegment(latlngs[i]); + } + } + }; + addLastSegment(latlngs); } - // Keep the closest point of all segments - for (i = 0, n = latlngs.length; i < n-1; i++) { - var latlngA = latlngs[i], - latlngB = latlngs[i+1]; - distance = L.GeometryUtil.distanceSegment(map, latlng, latlngA, latlngB); - if (distance <= mindist) { - mindist = distance; - result = L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB); - result.distance = distance; + // we have a multi polygon / multi polyline / polygon with holes + // use recursive to explore and return the good result + if ( ! L.Polyline._flat(latlngs) ) { + for (i = 0; i < latlngs.length; i++) { + // if we are at the lower level, and if we have a L.Polygon, we add the last segment + subResult = L.GeometryUtil.closest(map, latlngs[i], latlng, vertices); + if (subResult.distance < mindist) { + mindist = subResult.distance; + result = subResult; + } } + return result; + + } else { + + // Lookup vertices + if (vertices) { + for(i = 0, n = latlngs.length; i < n; i++) { + var ll = latlngs[i]; + distance = L.GeometryUtil.distance(map, latlng, ll); + if (distance < mindist) { + mindist = distance; + result = ll; + result.distance = distance; + } + } + return result; + } + + // Keep the closest point of all segments + for (i = 0, n = latlngs.length; i < n-1; i++) { + var latlngA = latlngs[i], + latlngB = latlngs[i+1]; + distance = L.GeometryUtil.distanceSegment(map, latlng, latlngA, latlngB); + if (distance <= mindist) { + mindist = distance; + result = L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB); + result.distance = distance; + } + } + return result; } - return result; + }, /** Returns the closest layer to latlng among a list of layers. - @param {L.Map} map - @param {Array} layers - @param {L.LatLng} latlng - @returns {object} with layer, latlng and distance or {null} if list is empty; + + @tutorial closest + + @param {L.Map} map Leaflet map to be used for this method + @param {Array} layers Set of layers + @param {L.LatLng} latlng - The position to search + @returns {object} ``{layer, latlng, distance}`` or ``null`` if list is empty; */ closestLayer: function (map, layers, latlng) { var mindist = Infinity, @@ -208,31 +285,132 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { for (var i = 0, n = layers.length; i < n; i++) { var layer = layers[i]; - // Single dimension, snap on points, else snap on closest - if (typeof layer.getLatLng == 'function') { - ll = layer.getLatLng(); - distance = L.GeometryUtil.distance(map, latlng, ll); - } - else { - ll = L.GeometryUtil.closest(map, layer, latlng); - if (ll) distance = ll.distance; // Can return null if layer has no points. - } - if (distance < mindist) { - mindist = distance; - result = {layer: layer, latlng: ll, distance: distance}; + if (layer instanceof L.LayerGroup) { + // recursive + var subResult = L.GeometryUtil.closestLayer(map, layer.getLayers(), latlng); + if (subResult.distance < mindist) { + mindist = subResult.distance; + result = subResult; + } + } else { + // Single dimension, snap on points, else snap on closest + if (typeof layer.getLatLng == 'function') { + ll = layer.getLatLng(); + distance = L.GeometryUtil.distance(map, latlng, ll); + } + else { + ll = L.GeometryUtil.closest(map, layer, latlng); + if (ll) distance = ll.distance; // Can return null if layer has no points. + } + if (distance < mindist) { + mindist = distance; + result = {layer: layer, latlng: ll, distance: distance}; + } } } return result; }, + /** + Returns the n closest layers to latlng among a list of input layers. + + @param {L.Map} map - Leaflet map to be used for this method + @param {Array} layers - Set of layers + @param {L.LatLng} latlng - The position to search + @param {?Number} [n=layers.length] - the expected number of output layers. + @returns {Array} an array of objects ``{layer, latlng, distance}`` or ``null`` if the input is invalid (empty list or negative n) + */ + nClosestLayers: function (map, layers, latlng, n) { + n = typeof n === 'number' ? n : layers.length; + + if (n < 1 || layers.length < 1) { + return null; + } + + var results = []; + var distance, ll; + + for (var i = 0, m = layers.length; i < m; i++) { + var layer = layers[i]; + if (layer instanceof L.LayerGroup) { + // recursive + var subResult = L.GeometryUtil.closestLayer(map, layer.getLayers(), latlng); + results.push(subResult); + } else { + // Single dimension, snap on points, else snap on closest + if (typeof layer.getLatLng == 'function') { + ll = layer.getLatLng(); + distance = L.GeometryUtil.distance(map, latlng, ll); + } + else { + ll = L.GeometryUtil.closest(map, layer, latlng); + if (ll) distance = ll.distance; // Can return null if layer has no points. + } + results.push({layer: layer, latlng: ll, distance: distance}); + } + } + + results.sort(function(a, b) { + return a.distance - b.distance; + }); + + if (results.length > n) { + return results.slice(0, n); + } else { + return results; + } + }, + + /** + * Returns all layers within a radius of the given position, in an ascending order of distance. + @param {L.Map} map Leaflet map to be used for this method + @param {Array} layers - A list of layers. + @param {L.LatLng} latlng - The position to search + @param {?Number} [radius=Infinity] - Search radius in pixels + @return {object[]} an array of objects including layer within the radius, closest latlng, and distance + */ + layersWithin: function(map, layers, latlng, radius) { + radius = typeof radius == 'number' ? radius : Infinity; + + var results = []; + var ll = null; + var distance = 0; + + for (var i = 0, n = layers.length; i < n; i++) { + var layer = layers[i]; + + if (typeof layer.getLatLng == 'function') { + ll = layer.getLatLng(); + distance = L.GeometryUtil.distance(map, latlng, ll); + } + else { + ll = L.GeometryUtil.closest(map, layer, latlng); + if (ll) distance = ll.distance; // Can return null if layer has no points. + } + + if (ll && distance < radius) { + results.push({layer: layer, latlng: ll, distance: distance}); + } + } + + var sortedResults = results.sort(function(a, b) { + return a.distance - b.distance; + }); + + return sortedResults; + }, + /** Returns the closest position from specified {LatLng} among specified layers, with a maximum tolerance in pixels, providing snapping behaviour. - @param {L.Map} map + + @tutorial closest + + @param {L.Map} map Leaflet map to be used for this method @param {Array} layers - A list of layers to snap on. - @param {L.LatLng} latlng - The position to snap. + @param {L.LatLng} latlng - The position to snap @param {?Number} [tolerance=Infinity] - Maximum number of pixels. - @param {?boolean} [withVertices=true] - Snap to layers vertices. + @param {?boolean} [withVertices=true] - Snap to layers vertices or segment points (not only vertex) @returns {object} with snapped {LatLng} and snapped {Layer} or null if tolerance exceeded. */ closestLayerSnap: function (map, layers, latlng, tolerance, withVertices) { @@ -256,8 +434,8 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns the Point located on a segment at the specified ratio of the segment length. - @param {L.Point} pA - @param {L.Point} pB + @param {L.Point} pA coordinates of point A + @param {L.Point} pB coordinates of point B @param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive. @returns {L.Point} the interpolated point. */ @@ -270,9 +448,9 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns the coordinate of the point located on a line at the specified ratio of the line length. - @param {L.Map} map - @param {Array|L.PolyLine} latlngs - @param {Number} the length ratio, expressed as a decimal between 0 and 1, inclusive + @param {L.Map} map Leaflet map to be used for this method + @param {Array|L.PolyLine} latlngs Set of geographical points + @param {Number} ratio the length ratio, expressed as a decimal between 0 and 1, inclusive @returns {Object} an object with latLng ({LatLng}) and predecessor ({Number}), the index of the preceding vertex in the Polyline (-1 if the interpolated point is the first vertex) */ @@ -283,6 +461,9 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { return null; } + // ensure the ratio is between 0 and 1; + ratio = Math.max(Math.min(ratio, 1), 0); + if (ratio === 0) { return { latLng: latLngs[0] instanceof L.LatLng ? latLngs[0] : L.latLng(latLngs[0]), @@ -296,9 +477,6 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { }; } - // ensure the ratio is between 0 and 1; - ratio = Math.max(Math.min(ratio, 1), 0); - // project the LatLngs as Points, // and compute total planar length of the line at max precision var maxzoom = map.getMaxZoom(); @@ -313,36 +491,38 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { } var ratioDist = lineLength * ratio; - var a = pts[0], - b = pts[1], - distA = 0, - distB = a.distanceTo(b); - // follow the line segments [ab], adding lengths, + + // follow the line segments [ab], adding lengths, // until we find the segment where the points should lie on - var index = 1; - for (; index < n && distB < ratioDist; index++) { - a = b; - distA = distB; - b = pts[index]; - distB += a.distanceTo(b); - } - // compute the ratio relative to the segment [ab] - var segmentRatio = ((distB - distA) !== 0) ? ((ratioDist - distA) / (distB - distA)) : 0; - var interpolatedPoint = L.GeometryUtil.interpolateOnPointSegment(a, b, segmentRatio); - return { - latLng: map.unproject(interpolatedPoint, maxzoom), - predecessor: index-2 - }; + var cumulativeDistanceToA = 0, cumulativeDistanceToB = 0; + for (var i = 0; cumulativeDistanceToB < ratioDist; i++) { + var pointA = pts[i], pointB = pts[i+1]; + + cumulativeDistanceToA = cumulativeDistanceToB; + cumulativeDistanceToB += pointA.distanceTo(pointB); + } + + if (pointA == undefined && pointB == undefined) { // Happens when line has no length + var pointA = pts[0], pointB = pts[1], i = 1; + } + + // compute the ratio relative to the segment [ab] + var segmentRatio = ((cumulativeDistanceToB - cumulativeDistanceToA) !== 0) ? ((ratioDist - cumulativeDistanceToA) / (cumulativeDistanceToB - cumulativeDistanceToA)) : 0; + var interpolatedPoint = L.GeometryUtil.interpolateOnPointSegment(pointA, pointB, segmentRatio); + return { + latLng: map.unproject(interpolatedPoint, maxzoom), + predecessor: i-1 + }; }, /** Returns a float between 0 and 1 representing the location of the - closest point on polyline to the given latlng, as a fraction of total 2d line length. + closest point on polyline to the given latlng, as a fraction of total line length. (opposite of L.GeometryUtil.interpolateOnLine()) - @param {L.Map} map - @param {L.PolyLine} polyline - @param {L.LatLng} latlng - @returns {Number} + @param {L.Map} map Leaflet map to be used for this method + @param {L.PolyLine} polyline Polyline on which the latlng will be search + @param {L.LatLng} latlng The position to search + @returns {Number} Float between 0 and 1 */ locateOnLine: function (map, polyline, latlng) { var latlngs = polyline.getLatLngs(); @@ -374,8 +554,8 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns a clone with reversed coordinates. - @param {L.PolyLine} polyline - @returns {L.PolyLine} + @param {L.PolyLine} polyline polyline to reverse + @returns {L.PolyLine} polyline reversed */ reverse: function (polyline) { return L.polyline(polyline.getLatLngs().slice(0).reverse()); @@ -384,11 +564,11 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns a sub-part of the polyline, from start to end. If start is superior to end, returns extraction from inverted line. - @param {L.Map} map - @param {L.PolyLine} latlngs + @param {L.Map} map Leaflet map to be used for this method + @param {L.PolyLine} polyline Polyline on which will be extracted the sub-part @param {Number} start ratio, expressed as a decimal between 0 and 1, inclusive @param {Number} end ratio, expressed as a decimal between 0 and 1, inclusive - @returns {Array} + @returns {Array} new polyline */ extract: function (map, polyline, start, end) { if (start > end) { @@ -420,8 +600,8 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns true if first polyline ends where other second starts. - @param {L.PolyLine} polyline - @param {L.PolyLine} other + @param {L.PolyLine} polyline First polyline + @param {L.PolyLine} other Second polyline @returns {bool} */ isBefore: function (polyline, other) { @@ -433,8 +613,8 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns true if first polyline starts where second ends. - @param {L.PolyLine} polyline - @param {L.PolyLine} other + @param {L.PolyLine} polyline First polyline + @param {L.PolyLine} other Second polyline @returns {bool} */ isAfter: function (polyline, other) { @@ -446,8 +626,8 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns true if first polyline starts where second ends or start. - @param {L.PolyLine} polyline - @param {L.PolyLine} other + @param {L.PolyLine} polyline First polyline + @param {L.PolyLine} other Second polyline @returns {bool} */ startsAtExtremity: function (polyline, other) { @@ -460,9 +640,9 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns horizontal angle in degres between two points. - @param {L.Point} a - @param {L.Point} b - @returns {float} + @param {L.Point} a Coordinates of point A + @param {L.Point} b Coordinates of point B + @returns {Number} horizontal angle */ computeAngle: function(a, b) { return (Math.atan2(b.y - a.y, b.x - a.x) * 180 / Math.PI); @@ -470,8 +650,8 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { /** Returns slope (Ax+B) between two points. - @param {L.Point} a - @param {L.Point} b + @param {L.Point} a Coordinates of point A + @param {L.Point} b Coordinates of point B @returns {Object} with ``a`` and ``b`` properties. */ computeSlope: function(a, b) { @@ -497,9 +677,91 @@ L.GeometryUtil = L.extend(L.GeometryUtil || {}, { x2 = Math.cos(angleRad)*(pPoint.x-pCenter.x) - Math.sin(angleRad)*(pPoint.y-pCenter.y) + pCenter.x, y2 = Math.sin(angleRad)*(pPoint.x-pCenter.x) + Math.cos(angleRad)*(pPoint.y-pCenter.y) + pCenter.y; return map.unproject(new L.Point(x2,y2), maxzoom); - } + }, + + /** + Returns the bearing in degrees clockwise from north (0 degrees) + from the first L.LatLng to the second, at the first LatLng + @param {L.LatLng} latlng1: origin point of the bearing + @param {L.LatLng} latlng2: destination point of the bearing + @returns {float} degrees clockwise from north. + */ + bearing: function(latlng1, latlng2) { + var rad = Math.PI / 180, + lat1 = latlng1.lat * rad, + lat2 = latlng2.lat * rad, + lon1 = latlng1.lng * rad, + lon2 = latlng2.lng * rad, + y = Math.sin(lon2 - lon1) * Math.cos(lat2), + x = Math.cos(lat1) * Math.sin(lat2) - + Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1); + + var bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360; + return bearing >= 180 ? bearing-360 : bearing; + }, + + /** + Returns the point that is a distance and heading away from + the given origin point. + @param {L.LatLng} latlng: origin point + @param {float} heading: heading in degrees, clockwise from 0 degrees north. + @param {float} distance: distance in meters + @returns {L.latLng} the destination point. + Many thanks to Chris Veness at http://www.movable-type.co.uk/scripts/latlong.html + for a great reference and examples. + */ + destination: function(latlng, heading, distance) { + heading = (heading + 360) % 360; + var rad = Math.PI / 180, + radInv = 180 / Math.PI, + R = 6378137, // approximation of Earth's radius + lon1 = latlng.lng * rad, + lat1 = latlng.lat * rad, + rheading = heading * rad, + sinLat1 = Math.sin(lat1), + cosLat1 = Math.cos(lat1), + cosDistR = Math.cos(distance / R), + sinDistR = Math.sin(distance / R), + lat2 = Math.asin(sinLat1 * cosDistR + cosLat1 * + sinDistR * Math.cos(rheading)), + lon2 = lon1 + Math.atan2(Math.sin(rheading) * sinDistR * + cosLat1, cosDistR - sinLat1 * Math.sin(lat2)); + lon2 = lon2 * radInv; + lon2 = lon2 > 180 ? lon2 - 360 : lon2 < -180 ? lon2 + 360 : lon2; + return L.latLng([lat2 * radInv, lon2]); + }, + + /** + Returns the the angle of the given segment and the Equator in degrees, + clockwise from 0 degrees north. + @param {L.Map} map: Leaflet map to be used for this method + @param {L.LatLng} latlngA: geographical point A of the segment + @param {L.LatLng} latlngB: geographical point B of the segment + @returns {Float} the angle in degrees. + */ + angle: function(map, latlngA, latlngB) { + var pointA = map.latLngToContainerPoint(latlngA), + pointB = map.latLngToContainerPoint(latlngB), + angleDeg = Math.atan2(pointB.y - pointA.y, pointB.x - pointA.x) * 180 / Math.PI + 90; + angleDeg += angleDeg < 0 ? 360 : 0; + return angleDeg; + }, + + /** + Returns a point snaps on the segment and heading away from the given origin point a distance. + @param {L.Map} map: Leaflet map to be used for this method + @param {L.LatLng} latlngA: geographical point A of the segment + @param {L.LatLng} latlngB: geographical point B of the segment + @param {float} distance: distance in meters + @returns {L.latLng} the destination point. + */ + destinationOnSegment: function(map, latlngA, latlngB, distance) { + var angleDeg = L.GeometryUtil.angle(map, latlngA, latlngB), + latlng = L.GeometryUtil.destination(latlngA, angleDeg, distance); + return L.GeometryUtil.closestOnSegment(map, latlng, latlngA, latlngB); + }, }); return L.GeometryUtil; -})); \ No newline at end of file +})); diff --git a/mapentity/static/mapentity/Leaflet.OverIntent/README.md b/mapentity/static/mapentity/Leaflet.OverIntent/README.md index b1f88bac3a..df70c50d2f 100644 --- a/mapentity/static/mapentity/Leaflet.OverIntent/README.md +++ b/mapentity/static/mapentity/Leaflet.OverIntent/README.md @@ -14,7 +14,7 @@ Usage ```javascript - var marker = L.marker([]).addTo(map); + var marker = L.marker([lat, lng]).addTo(map); marker.on('mouseintent', function (e) { // User meant it ! @@ -23,6 +23,11 @@ Usage ( *works with markers and vectorial layers* ) +**Options** + +* ``intentDuration`` : default is 300ms (See demo.) + + Authors ------- diff --git a/mapentity/static/mapentity/Leaflet.OverIntent/index.html b/mapentity/static/mapentity/Leaflet.OverIntent/index.html deleted file mode 100644 index c0ad0fbe74..0000000000 --- a/mapentity/static/mapentity/Leaflet.OverIntent/index.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - Leaflet.OverIntent - - - - - - - - - -

Move your mouse over, and observe the difference !

-
- - - - \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.OverIntent/leaflet.overintent.js b/mapentity/static/mapentity/Leaflet.OverIntent/leaflet.overintent.js index bbf23f299e..a4692ddeb1 100644 --- a/mapentity/static/mapentity/Leaflet.OverIntent/leaflet.overintent.js +++ b/mapentity/static/mapentity/Leaflet.OverIntent/leaflet.overintent.js @@ -1,24 +1,47 @@ -L.OverIntentInitHook = function () { - var duration = 500, - timer = null; +(function (factory, window) { + if (typeof define === 'function' && define.amd) { + define(['leaflet'], function (L) { + factory(L); + }); + } else if (typeof module === 'object' && module.exports) { + module.exports = function (L) { + if (L === undefined && typeof window !== 'undefined') { + L = require('leaflet'); + } + factory(L); + return L; + }; + } else if (typeof window !== 'undefined' && window.L) { + factory(window.L); + } +}(function overIntentFactory(L) { + L.OverIntentInitHook = function () { + var timer = null; this.on('mouseover', function (e) { - if (timer !== null) return; + var duration; + if (timer !== null) return; + + duration = this.options.intentDuration || 300; - timer = setTimeout(L.Util.bind(function () { - this.fire('mouseintent', {latlng: e.latlng, layer: e.layer}); - timer = null; - }, this), duration); + timer = setTimeout(L.Util.bind(function () { + this.fire('mouseintent', { + latlng: e.latlng, + layer: e.layer + }); + timer = null; + }, this), duration); }); this.on('mouseout', function (e) { - if (timer !== null) { - clearTimeout(timer); - timer = null; - } + if (timer !== null) { + clearTimeout(timer); + timer = null; + } }); -}; + }; -L.Marker.addInitHook(L.OverIntentInitHook); -L.Path.addInitHook(L.OverIntentInitHook); -L.FeatureGroup.addInitHook(L.OverIntentInitHook); \ No newline at end of file + L.Marker.addInitHook(L.OverIntentInitHook); + L.Path.addInitHook(L.OverIntentInitHook); + L.FeatureGroup.addInitHook(L.OverIntentInitHook); +}, window)); diff --git a/mapentity/static/mapentity/Leaflet.Snap/README.md b/mapentity/static/mapentity/Leaflet.Snap/README.md index 79d22c22c4..3a779929cb 100644 --- a/mapentity/static/mapentity/Leaflet.Snap/README.md +++ b/mapentity/static/mapentity/Leaflet.Snap/README.md @@ -1,7 +1,7 @@ Leaflet.Snap ============ -Enables snapping of draggable markers to polylines and other layers ! +Enables snapping of draggable markers to polylines and other layers ! Now updated to work with Leaflet.Draw for Leaflet 1.0! Check out the [demo](http://makinacorpus.github.com/Leaflet.Snap/) ! @@ -12,10 +12,18 @@ For snapping on shape drawing or edition snapping, it also depends on [Leaflet.d If your guide layer contains several thousands for features, adding the [LayerIndex](https://github.com/makinacorpus/Leaflet.LayerIndex) is recommended, this plugin takes advantage of the spatial index if it is available. +Install +----- +In order to use this plugin in your app you can either: +* install it via your favorite package manager: + * `npm i leaflet-snap` + * `bower install git@github.com:makinacorpus/Leaflet.Snap.git` +* download the repository and import the `leaflet.snap.js` file in your app. + Usage ----- -* Add ``leaflet.snap.js`` and ``leaflet.geometryutil.js`` (optionally ``leaflet.draw.js``) +* Add `leaflet.snap.js` and `leaflet.geometryutil.js` (optionally `leaflet.draw.js`) ### For markers : @@ -32,7 +40,6 @@ Usage ### For Leaflet.Draw : ```javascript - var guideLayers = [guides, road]; map.drawControl.setDrawingOptions({ @@ -41,7 +48,7 @@ Usage }); ``` -### For editing existing polylines : +### For editing existing polyline : ```javascript var polyline = L.polyline(...).addTo(map); @@ -50,12 +57,30 @@ Usage polyline.snapediting.enable(); ``` -Both ``L.Handler.MarkerSnap`` and ``L.Handler.PolylineSnap`` accept options as a third +Both `L.Handler.MarkerSnap` and `L.Handler.PolylineSnap` accept options as a third argument. +### For editing existing polyline with L.EditToolbar.SnapEdit : + +L.EditToolbar.SnapEdit is an extension of L.EditToolbar.Edit in Leaflet.draw. + +```javascript +var polyline = L.polyline(...).addTo(map); +var editToolbar = new L.EditToolbar.SnapEdit(map, { + featureGroup: L.featureGroup([polyline]), + snapOptions: { + guideLayers: [guideLayer] + } +}); + +editToolbar.enable(); +``` + +`L.EditToolbar.SnapEdit` accepts options and initial guide layers. + ### Options: -**snapDistance** : (default 30) distance in pixels where snapping occurs +**snapDistance** : (default 15) distance in pixels where snapping occurs **snapVertices** : (default true) whether layers vertices add additional snap attraction @@ -66,12 +91,74 @@ argument. **unsnap** ( _layer_ ) : fired when unsnapped from ``layer`` +Leaflet.Snap.Guidelines +============ + +L.Snap.Guidelines ia a class that works with Leaflet Draw to draw horizontal and vertical guidelines along all other existing rectangles and circles when in draw or edit mode, and allows the mouse to snap to those lines, to help the user to draw things that line up perfectly despite their being distance between them. Note that other shapes will always have snapping-prioirty over the guidelines. + +```javascript +var guideLines = new L.Snap.Guidelines(map, guideLayers, {}); +``` + +### Options: + +**guideStyle** : The style of the guidelines, as a hash. By default, we have weight:1, color:black, opacity:0.1, and dashArray: 15, 10, 5. + +### Methods: + +**enabled** : as expected. +**enable** : as expected. +**disable** : as expected. + + +Leaflet.Snap.Gridlines +============ + +```javascript +var gridlines = new L.Snap.Gridlines(map, guideLayers, { + 'pixelSpacingX' : 98, + 'pixelSpacingY' : 98 +}); +``` + +L.Snap.Gridlines draws a grid on the map, and enables L.Snap to snap to its lines. Intersections of NS/WE gridlines will be preferred over sticking to a single line, but other shapes will always have snapping priority over the grid itself. + +### Options: + +**maxBounds** : Specifies the NSWE bounds for the gridlines. If the maxBounds option is not set, the maxBounds on the map itself is used by default. + +**numGridlines** -or- **numGridlinesLat**/**numGridlinesLng** -or- **gridSpacingLat**/**gridSpacingLng** -or- **pixelSpacingX**/**pixelSpacingY** : Only one of these options (or pairs of options) should be set. the "num" options specify the number of gridlines to fit in the maxBounds for each direction, calculating their spacing based on the total width/height, while the "spacing" options do the opposite, calculating the number based on the spacing. Spacing also allows one to specify its values in pixel or latlng coordinates. + +**gridStyle** : The style of the gridlines themselves, as a hash. By default, we have weight:1, color:black, and opacity:0.1. + +### Methods: + +**enabled** : as expected. +**enable** : as expected. +**disable** : as expected. +**disableSnap** : disable snap without changing the show/hide status of the grid. +**disableSnap** : re-enables snap without changing the show/hide status of the grid. +**calcDimensions** : redraws the grid with new dimensions; options are the same as the constructor. +**show** : shows the grid and disables snap on the grid. +**hide** : hides the grid and disables snap on the grid. + CHANGELOG --------- -### dev +### 0.0.4 + +* Fix to use leaflet-draw ^0.3.0 +* Delete git module and use node module instead +* Can Snap for 'editing existing polyline with L.EditToolbar.SnapEdit' +* Example can be use with http / https + +### 0.0.3 + +* Set the newly drawn point/vertex to the snapped location + +### 0.0.2 -* Add snapping while drawing in Leaflet.Draw +* Disable snapping when the feature is deleted ### 0.0.1 @@ -88,5 +175,6 @@ Authors * Mathieu Leplatre * Tobias Bieniek +* Frédéric Bonifas [![Makina Corpus](http://depot.makina-corpus.org/public/logo.gif)](http://makinacorpus.com) diff --git a/mapentity/static/mapentity/Leaflet.Snap/index.html b/mapentity/static/mapentity/Leaflet.Snap/index.html deleted file mode 100644 index 9ae2427344..0000000000 --- a/mapentity/static/mapentity/Leaflet.Snap/index.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - Leaflet.Snap - - - - - - - - - - - - - -

Move stuff around and it will snap ! - Sources - -

-
- - - - diff --git a/mapentity/static/mapentity/Leaflet.Snap/package.json b/mapentity/static/mapentity/Leaflet.Snap/package.json index a78b7fce06..d549a68424 100644 --- a/mapentity/static/mapentity/Leaflet.Snap/package.json +++ b/mapentity/static/mapentity/Leaflet.Snap/package.json @@ -1,24 +1,37 @@ { - "name": "leaflet-snap" - , "version": "0.0.1" - , "description": "Enable snapping between layers" - , "keywords": ["Leaflet", "GIS"] - , "main": "leaflet.snap.js" - , "bugs": { - "url": "https://github.com/makinacorpus/Leaflet.Snap/issues" - } - , "repository": { - "type": "git", - "url": "git://github.com/makinacorpus/Leaflet.Snap.git" - } - , "license": "MIT" - , "scripts": { - } - , "dependencies": { - "leaflet": "*", - "leaflet-geometryutil": "*" - } - , "optionalDependencies": { - "leaflet-draw": "*", - } + "name": "leaflet-snap", + "version": "0.0.5", + "description": "Enable snapping between layers", + "keywords": [ + "Leaflet", + "GIS" + ], + "main": "leaflet.snap.js", + "bugs": { + "url": "https://github.com/makinacorpus/Leaflet.Snap/issues" + }, + "scripts": { + "test": "mocha-phantomjs ./test/index.html --reporter list || true" + }, + "repository": { + "type": "git", + "url": "git://github.com/makinacorpus/Leaflet.Snap.git" + }, + "license": "MIT", + "dependencies": { + "leaflet-geometryutil": "*" + }, + "peerDependencies": { + "leaflet": ">=0.7.0 <2.0.0" + }, + "optionalDependencies": { + "leaflet-draw": "^1.0.0" + }, + "devDependencies": { + "chai": "^3.5.0", + "mocha": "^3.0.2", + "happen": "^0.3.1", + "mocha-phantomjs": "^4.1.0", + "sinon": "^1.17.5" + } } diff --git a/mapentity/static/mapentity/Leaflet.Spin/README.md b/mapentity/static/mapentity/Leaflet.Spin/README.md index e36092d746..a70cdd136b 100644 --- a/mapentity/static/mapentity/Leaflet.Spin/README.md +++ b/mapentity/static/mapentity/Leaflet.Spin/README.md @@ -1,19 +1,31 @@ Leaflet.Spin ============ -Shows a nice spin cursor on the map. +Shows a nice spin cursor on the map. See [online demo](http://makinacorpus.github.io/Leaflet.Spin/). -Requires [Spin.js](http://fgnass.github.com/spin.js/). +This plugin requires [Spin.js](http://fgnass.github.com/spin.js/). -See [online demo](http://makinacorpus.github.io/Leaflet.Spin/). -Usage +Install ----- +### NPM + +``` +npm install leaflet-spin +``` ### Manually +Download the [latest release](https://github.com/makinacorpus/Leaflet.Spin/releases/tag/1.1.2) and include it in your app + + +Usage +----- + +This plugin can be loaded with AMD/CommonJS. + ```javascript map.spin(true); // on ... @@ -33,40 +45,69 @@ map.spin(true, {lines: 13, length: 40}); ### With AJAX / JQuery -``` - map.spin(true); - $.ajax({url: 'http://server/api/'}) - .done(function() { - map.spin(false); - }) - .error(function () { - map.spin(false); - }); - +```javascript +map.spin(true); +$.ajax({url: 'http://server/api/'}) +.done(function() { + map.spin(false); +}) +.error(function () { + map.spin(false); +}); ``` Using events: -``` - var layer = L.geoJson(null).addTo(map); - - layer.fire('data:loading'); - $.getJSON('http://server/path.geojson', function (data) { - layer.fire('data:loaded'); - layer.addData(data); - }); +```javascript +var layer = L.geoJson(null).addTo(map); +layer.fire('data:loading'); +$.getJSON('http://server/path.geojson', function (data) { + layer.fire('data:loaded'); + layer.addData(data); +}); ``` ### With [Leaflet.AJAX](https://github.com/calvinmetcalf/leaflet-ajax/) +```javascript +var layer = L.geoJson.ajax(); +layer.addUrl('http://server/path.geojson'); ``` - var layer = L.geoJson.ajax(); - layer.addUrl('http://server/path.geojson'); +Development +----- + +You can use example folder for testing. ``` +npm run release # minify js and copy leaflet.spin.min.js in example folder +npm run deploy # deploy to gh-pages +``` + + +Changelog +----- + +### 1.1.2 +Should work with all 0.x and 1.x versions of leaflet + +### 1.1.1 +Add the dependency to spin.js in the module definition + +### 1.1.0 +Update export and extend system + +### 1.0.0 +Update structure with official Leaflet plugin rules + +### 0.1.1 +Update bower dependencies + +### 0.1.0 +Initial version + Authors diff --git a/mapentity/static/mapentity/Leaflet.Spin/leaflet.spin.js b/mapentity/static/mapentity/Leaflet.Spin/leaflet.spin.js index cee5d6aa69..34116db6f4 100644 --- a/mapentity/static/mapentity/Leaflet.Spin/leaflet.spin.js +++ b/mapentity/static/mapentity/Leaflet.Spin/leaflet.spin.js @@ -1,41 +1,72 @@ -L.SpinMapMixin = { - spin: function (state, options) { - if (!!state) { - // start spinning ! - if (!this._spinner) { - this._spinner = new Spinner(options).spin(this._container); - this._spinning = 0; +(function (factory, window) { + // define an AMD module that relies on 'leaflet' + if (typeof define === 'function' && define.amd) { + define(['leaflet','spin.js'], function (L, Spinner) { + factory(L, Spinner); + }); + + // define a Common JS module that relies on 'leaflet' + } else if (typeof exports === 'object') { + module.exports = function (L, Spinner) { + if (L === undefined) { + L = require('leaflet'); } - this._spinning++; - } - else { - this._spinning--; - if (this._spinning <= 0) { - // end spinning ! - if (this._spinner) { - this._spinner.stop(); - this._spinner = null; + if (Spinner === undefined) { + Spinner = require('spin.js'); + } + factory(L, Spinner); + return L; + }; + // attach your plugin to the global 'L' variable + } else if (typeof window !== 'undefined' && window.L && window.Spinner) { + factory(window.L, window.Spinner); + } +}(function leafletSpinFactory(L, Spinner) { + var SpinMapMixin = { + spin: function (state, options) { + if (!!state) { + // start spinning ! + if (!this._spinner) { + this._spinner = new Spinner(options) + .spin(this._container); + this._spinning = 0; + } + this._spinning++; + } + else { + this._spinning--; + if (this._spinning <= 0) { + // end spinning ! + if (this._spinner) { + this._spinner.stop(); + this._spinner = null; + } } } } - } -}; + }; -L.Map.include(L.SpinMapMixin); + var SpinMapInitHook = function () { + this.on('layeradd', function (e) { + // If added layer is currently loading, spin ! + if (e.layer.loading) this.spin(true); + if (typeof e.layer.on !== 'function') return; + e.layer.on('data:loading', function () { + this.spin(true); + }, this); + e.layer.on('data:loaded', function () { + this.spin(false); + }, this); + }, this); + this.on('layerremove', function (e) { + // Clean-up + if (e.layer.loading) this.spin(false); + if (typeof e.layer.on !== 'function') return; + e.layer.off('data:loaded'); + e.layer.off('data:loading'); + }, this); + }; -L.Map.addInitHook(function () { - this.on('layeradd', function (e) { - // If added layer is currently loading, spin ! - if (e.layer.loading) this.spin(true); - if (typeof e.layer.on != 'function') return; - e.layer.on('data:loading', function () { this.spin(true); }, this); - e.layer.on('data:loaded', function () { this.spin(false); }, this); - }, this); - this.on('layerremove', function (e) { - // Clean-up - if (e.layer.loading) this.spin(false); - if (typeof e.layer.on != 'function') return; - e.layer.off('data:loaded'); - e.layer.off('data:loading'); - }, this); -}); + L.Map.include(SpinMapMixin); + L.Map.addInitHook(SpinMapInitHook); +}, window)); diff --git a/mapentity/static/mapentity/Leaflet.Spin/package.json b/mapentity/static/mapentity/Leaflet.Spin/package.json new file mode 100644 index 0000000000..bcdc0efe7d --- /dev/null +++ b/mapentity/static/mapentity/Leaflet.Spin/package.json @@ -0,0 +1,39 @@ +{ + "name": "leaflet-spin", + "version": "1.1.2", + "description": "Show a spinner on a Leaflet map using Spin.js", + "main": "leaflet.spin.js", + "scripts": { + "build": "minify --output leaflet.spin.min.js leaflet.spin.js", + "serve": "live-server --open=example", + "copy": "cp leaflet.spin.min.js example", + "release": "npm run build && npm run copy", + "deploy": "gh-pages -d example" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/makinacorpus/Leaflet.Spin.git" + }, + "keywords": [ + "Leaflet", + "Leaflet.js", + "Spin.js", + "spinner" + ], + "authors": "Makina Corpus (http://makina-corpus.com/)", + "license": "MIT", + "dependencies": { + "spin.js": "^2.3.1" + }, + "bugs": { + "url": "https://github.com/makinacorpus/Leaflet.Spin/issues" + }, + "homepage": "http://makinacorpus.github.io/Leaflet.Spin/", + "devDependencies": { + "live-server": "^1.1.0", + "minifier": "^0.8.0" + }, + "peerDependencies": { + "leaflet": "<=1" + } +} diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/.gitattributes b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/.gitattributes deleted file mode 100644 index ceae4debe8..0000000000 --- a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Set default behaviour, in case users don't have core.autocrlf set. -* text=auto diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/MIT-LICENSE.txt b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/LICENSE.txt similarity index 93% rename from mapentity/static/mapentity/Leaflet.groupedlayercontrol/MIT-LICENSE.txt rename to mapentity/static/mapentity/Leaflet.groupedlayercontrol/LICENSE.txt index 20f21fe36a..d2f91f60c9 100644 --- a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/MIT-LICENSE.txt +++ b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/LICENSE.txt @@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/README.md b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/README.md index 78ff628417..1de8bc5c85 100644 --- a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/README.md +++ b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/README.md @@ -1,11 +1,25 @@ -Leaflet.groupedlayercontrol +leaflet-groupedlayercontrol =========================== Leaflet layer control with support for grouping overlays together. +Also supports making groups exclusive (radio instead of checkbox). + +> This project is looking for a maintainer. Interested? Open an issue. ![preview](preview.png) -[Demo](http://ismyrnow.github.io/Leaflet.groupedlayercontrol/example/basic.html) +Demos: [Basic](http://ismyrnow.github.io/leaflet-groupedlayercontrol/example/basic.html) | +[Advanced](http://ismyrnow.github.io/leaflet-groupedlayercontrol/example/advanced.html) + +## Installation + +Include the compressed JavaScript and CSS files located in the `/dist` folder. + +This project is also available via bower and jspm: + +``` +bower install leaflet-groupedlayercontrol +``` ## Usage @@ -28,7 +42,22 @@ var groupedOverlays = { L.control.groupedLayers(baseLayers, groupedOverlays).addTo(map); ``` -The [example](example/basic.html) shows its usage with various layers. +### Advanced usage + +For added functionality, pass options when creating the layer control. + +```javascript +var options = { + // Make the "Landmarks" group exclusive (use radio inputs) + exclusiveGroups: ["Landmarks"], + // Show a checkbox next to non-exclusive group labels for toggling all + groupCheckboxes: true +}; + +L.control.groupedLayers(baseLayers, groupedOverlays, options).addTo(map); +``` + +![advanced preview](preview-advanced.png) ### Adding a layer @@ -36,17 +65,18 @@ Adding a layer individually works similarly to the default layer control, except that you can also specify a group name, along with the layer and layer name. ```javascript -layerControl.addOverlay(cities, "Cities", "Landmarks"). +layerControl.addOverlay(cities, "Cities", "Landmarks"); ``` ## Note -This plugin only affects how the layers are dislpayed in the layer control, +This plugin only affects how the layers are displayed in the layer control, and not how they are rendered or layered on the map. -Grouping base layers is not currently supported. +Grouping base layers is not currently supported, but adding exclusive layer +groups is. Layers in an exclusive layer group render as radio inputs. ## License -Leaflet.groupedlayercontrol is free software, and may be redistributed under +leaflet-groupedlayercontrol is free software, and may be redistributed under the MIT-LICENSE. diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/example/basic.html b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/example/basic.html deleted file mode 100644 index 725fda4280..0000000000 --- a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/example/basic.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - Basic example - - - - - - - - - - - -
- - - - diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/package.json b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/package.json new file mode 100644 index 0000000000..5b7f25b1fe --- /dev/null +++ b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/package.json @@ -0,0 +1,49 @@ +{ + "name": "leaflet-groupedlayercontrol", + "version": "0.6.1", + "description": "A Leaflet layer control with support for grouping overlays together", + "main": "dist/leaflet.groupedlayercontrol.min.js", + "directories": { + "example": "example" + }, + "scripts": { + "test": "eslint src", + "build": "grunt" + }, + "repository": { + "type": "git", + "url": "git@github.com:ismyrnow/leaflet-groupedlayercontrol.git" + }, + "keywords": [ + "leaflet", + "overlay", + "groups", + "grouped", + "control", + "plugin" + ], + "author": "Ishmael Smyrnow", + "license": "MIT", + "bugs": { + "url": "https://github.com/ismyrnow/leaflet-groupedlayercontrol/issues" + }, + "homepage": "https://github.com/ismyrnow/leaflet-groupedlayercontrol", + "devDependencies": { + "eslint": "^3.1.1", + "grunt": "^0.4.5", + "grunt-contrib-cssmin": "^0.12.2", + "grunt-contrib-uglify": "^0.8.0", + "load-grunt-tasks": "^3.1.0" + }, + "dependencies": { + "leaflet": "^1.0.1" + }, + "jspm": { + "registry": "jspm", + "shim": { + "dist/leaflet.groupedlayercontrol.min": [ + "leaflet" + ] + } + } +} diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/preview-advanced.png b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/preview-advanced.png new file mode 100644 index 0000000000..83a4a37bd2 Binary files /dev/null and b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/preview-advanced.png differ diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/preview.png b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/preview.png index 41806e6d11..4ce20d514e 100644 Binary files a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/preview.png and b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/preview.png differ diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.css b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.css index df179ab1ac..3be2cc4478 100644 --- a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.css +++ b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.css @@ -1,13 +1,14 @@ .leaflet-control-layers-group-name { font-weight: bold; margin-bottom: .2em; - display: block; + margin-left: 3px; } .leaflet-control-layers-group { margin-bottom: .5em; } -.leaflet-control-layers-group label { - padding-left: .5em; +.leaflet-control-layers-scrollbar { + overflow-y: scroll; + padding-right: 10px; } diff --git a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.js b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.js index 6a64584b2d..b1e80d190b 100644 --- a/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.js +++ b/mapentity/static/mapentity/Leaflet.groupedlayercontrol/src/leaflet.groupedlayercontrol.js @@ -1,22 +1,28 @@ -// A layer control which provides for layer groupings. +/* global L */ + +// A layer control which provides for layer groupings. // Author: Ishmael Smyrnow L.Control.GroupedLayers = L.Control.extend({ + options: { collapsed: true, position: 'topright', - autoZIndex: true + autoZIndex: true, + exclusiveGroups: [], + groupCheckboxes: false }, initialize: function (baseLayers, groupedOverlays, options) { + var i, j; L.Util.setOptions(this, options); - this._layers = {}; + this._layers = []; this._lastZIndex = 0; this._handlingClick = false; this._groupList = []; this._domGroups = []; - for (var i in baseLayers) { + for (i in baseLayers) { this._addLayer(baseLayers[i], i); } @@ -40,8 +46,8 @@ L.Control.GroupedLayers = L.Control.extend({ onRemove: function (map) { map - .off('layeradd', this._onLayerChange) - .off('layerremove', this._onLayerChange); + .off('layeradd', this._onLayerChange, this) + .off('layerremove', this._onLayerChange, this); }, addBaseLayer: function (layer, name) { @@ -58,23 +64,34 @@ L.Control.GroupedLayers = L.Control.extend({ removeLayer: function (layer) { var id = L.Util.stamp(layer); - delete this._layers[id]; + var _layer = this._getLayer(id); + if (_layer) { + delete this._layers[this._layers.indexOf(_layer)]; + } this._update(); return this; }, + _getLayer: function (id) { + for (var i = 0; i < this._layers.length; i++) { + if (this._layers[i] && L.stamp(this._layers[i].layer) === id) { + return this._layers[i]; + } + } + }, + _initLayout: function () { var className = 'leaflet-control-layers', - container = this._container = L.DomUtil.create('div', className); + container = this._container = L.DomUtil.create('div', className); - //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released + // Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released container.setAttribute('aria-haspopup', true); - if (!L.Browser.touch) { + if (L.Browser.touch) { + L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); + } else { L.DomEvent.disableClickPropagation(container); L.DomEvent.on(container, 'wheel', L.DomEvent.stopPropagation); - } else { - L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); } var form = this._form = L.DomUtil.create('form', className + '-list'); @@ -93,8 +110,7 @@ L.Control.GroupedLayers = L.Control.extend({ L.DomEvent .on(link, 'click', L.DomEvent.stop) .on(link, 'click', this._expand, this); - } - else { + } else { L.DomEvent.on(link, 'focus', this._expand, this); } @@ -114,22 +130,26 @@ L.Control.GroupedLayers = L.Control.extend({ _addLayer: function (layer, name, group, overlay) { var id = L.Util.stamp(layer); - this._layers[id] = { + var _layer = { layer: layer, name: name, overlay: overlay }; + this._layers.push(_layer); group = group || ''; - var groupId = this._groupList.indexOf(group); + var groupId = this._indexOf(this._groupList, group); if (groupId === -1) { groupId = this._groupList.push(group) - 1; } - this._layers[id].group = { + var exclusive = (this._indexOf(this.options.exclusiveGroups, group) !== -1); + + _layer.group = { name: group, - id: groupId + id: groupId, + exclusive: exclusive }; if (this.options.autoZIndex && layer.setZIndex) { @@ -148,10 +168,10 @@ L.Control.GroupedLayers = L.Control.extend({ this._domGroups.length = 0; var baseLayersPresent = false, - overlaysPresent = false, - i, obj; + overlaysPresent = false, + i, obj; - for (i in this._layers) { + for (var i = 0; i < this._layers.length; i++) { obj = this._layers[i]; this._addItem(obj); overlaysPresent = overlaysPresent || obj.overlay; @@ -162,17 +182,22 @@ L.Control.GroupedLayers = L.Control.extend({ }, _onLayerChange: function (e) { - var obj = this._layers[L.Util.stamp(e.layer)]; + var obj = this._getLayer(L.Util.stamp(e.layer)), + type; - if (!obj) { return; } + if (!obj) { + return; + } if (!this._handlingClick) { this._update(); } - var type = obj.overlay ? - (e.type === 'layeradd' ? 'overlayadd' : 'overlayremove') : - (e.type === 'layeradd' ? 'baselayerchange' : null); + if (obj.overlay) { + type = e.type === 'layeradd' ? 'overlayadd' : 'overlayremove'; + } else { + type = e.type === 'layeradd' ? 'baselayerchange' : null; + } if (type) { this._map.fire(type, obj); @@ -181,7 +206,6 @@ L.Control.GroupedLayers = L.Control.extend({ // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) _createRadioElement: function (name, checked) { - var radioHtml = ' 0) { - console.log(errorsFound + ' error(s) found.\n'); - fail(); - } else { - console.log('\tCheck passed'); - } -}; - - -function getSizeDelta(newContent, oldContent) { - if (!oldContent) { - return 'new'; - } - var newLen = newContent.replace(/\r\n?/g, '\n').length, - oldLen = oldContent.replace(/\r\n?/g, '\n').length, - delta = newLen - oldLen; - - return (delta >= 0 ? '+' : '') + delta; -} - -function loadSilently(path) { - try { - return fs.readFileSync(path, 'utf8'); - } catch (e) { - return null; - } -} - -function combineFiles(files) { - var content = ''; - for (var i = 0, len = files.length; i < len; i++) { - content += fs.readFileSync(files[i], 'utf8') + '\n\n'; - } - return content; -} - -exports.build = function (compsBase32, buildName) { - - var files = getFiles(compsBase32); - - console.log('Concatenating ' + files.length + ' files...'); - - var copy = fs.readFileSync('src/copyright.js', 'utf8'), - intro = '(function (window, document, undefined) {\n', - outro = '}(this, document));', - newSrc = copy + intro + combineFiles(files) + outro, - - pathPart = 'dist/leaflet.label' + (buildName ? '-' + buildName : ''), - srcPath = pathPart + '-src.js', - - oldSrc = loadSilently(srcPath), - srcDelta = getSizeDelta(newSrc, oldSrc); - - console.log('\tUncompressed size: ' + newSrc.length + ' bytes (' + srcDelta + ')'); - - if (newSrc === oldSrc) { - console.log('\tNo changes'); - } else { - fs.writeFileSync(srcPath, newSrc); - console.log('\tSaved to ' + srcPath); - } - - console.log('Compressing...'); - - var path = pathPart + '.js', - oldCompressed = loadSilently(path), - newCompressed = copy + UglifyJS.minify(newSrc, { - warnings: true, - fromString: true - }).code, - delta = getSizeDelta(newCompressed, oldCompressed); - - console.log('\tCompressed size: ' + newCompressed.length + ' bytes (' + delta + ')'); - - if (newCompressed === oldCompressed) { - console.log('\tNo changes'); - } else { - fs.writeFileSync(path, newCompressed); - console.log('\tSaved to ' + path); - } -}; diff --git a/mapentity/static/mapentity/Leaflet.label/build/deps.js b/mapentity/static/mapentity/Leaflet.label/build/deps.js deleted file mode 100644 index f9064aedf4..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/build/deps.js +++ /dev/null @@ -1,26 +0,0 @@ -var deps = { - Core: { - src: [ - 'Leaflet.label.js' - ], - desc: 'The core of the plugin. Currently only includes the version.' - }, - - Label: { - src: [ - 'Label.js', - 'BaseMarkerMethods.js', - 'Marker.Label.js', - 'CircleMarker.Label.js', - 'Path.Label.js', - 'Map.Label.js', - 'FeatureGroup.Label.js' - ], - desc: 'Leaflet.label plugin files.', - deps: ['Core'] - } -}; - -if (typeof exports !== 'undefined') { - exports.deps = deps; -} \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/build/hint.js b/mapentity/static/mapentity/Leaflet.label/build/hint.js deleted file mode 100644 index 464bbe115d..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/build/hint.js +++ /dev/null @@ -1,30 +0,0 @@ -var jshint = require('jshint').JSHINT, - fs = require('fs'), - config = require('./hintrc.js').config; - -function jshintSrc(path, src) { - jshint(src, config); - - var errors = jshint.errors, - i, len, e, line; - - for (i = 0, len = errors.length; i < len; i++) { - e = errors[i]; - //console.log(e.evidence); - console.log(path + '\tline ' + e.line + '\tcol ' + e.character + '\t ' + e.reason); - } - - return len; -} - -exports.jshint = function (files) { - var errorsFound = 0; - - for (var i = 0, len = files.length; i < len; i++) { - var src = fs.readFileSync(files[i], 'utf8'); - - errorsFound += jshintSrc(files[i], src); - } - - return errorsFound; -}; \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/build/hintrc.js b/mapentity/static/mapentity/Leaflet.label/build/hintrc.js deleted file mode 100644 index 618090c23d..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/build/hintrc.js +++ /dev/null @@ -1,51 +0,0 @@ -exports.config = { - "browser": true, - "node": true, - "predef": ["L"], - - "debug": false, - "devel": false, - - "es5": false, - "strict": false, - "globalstrict": false, - - "asi": false, - "laxbreak": false, - "bitwise": true, - "boss": false, - "curly": true, - "eqnull": false, - "evil": false, - "expr": false, - "forin": true, - "immed": true, - "latedef": true, - "loopfunc": false, - "noarg": true, - "regexp": true, - "regexdash": false, - "scripturl": false, - "shadow": false, - "supernew": false, - "undef": true, - "funcscope": false, - - "newcap": true, - "noempty": true, - "nonew": true, - "nomen": false, - "onevar": false, - "plusplus": false, - "sub": false, - "indent": 4, - - "eqeqeq": true, - "trailing": true, - "white": true, - "smarttabs": true -}; - -exports.globals = { - 'L': false -} diff --git a/mapentity/static/mapentity/Leaflet.label/dist/images/death.png b/mapentity/static/mapentity/Leaflet.label/dist/images/death.png deleted file mode 100644 index 6566ded238..0000000000 Binary files a/mapentity/static/mapentity/Leaflet.label/dist/images/death.png and /dev/null differ diff --git a/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label-src.js b/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label-src.js deleted file mode 100644 index 8f525fb7f3..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label-src.js +++ /dev/null @@ -1,542 +0,0 @@ -/* - Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps. - (c) 2012-2013, Jacob Toye, Smartrak - - https://github.com/Leaflet/Leaflet.label - http://leafletjs.com - https://github.com/jacobtoye -*/ -(function (window, document, undefined) { -/* - * Leaflet.label assumes that you have already included the Leaflet library. - */ - -L.labelVersion = '0.2.2-dev'; - -L.Label = L.Class.extend({ - - includes: L.Mixin.Events, - - options: { - className: '', - clickable: false, - direction: 'right', - noHide: false, - offset: [12, -15], // 6 (width of the label triangle) + 6 (padding) - opacity: 1, - zoomAnimation: true - }, - - initialize: function (options, source) { - L.setOptions(this, options); - - this._source = source; - this._animated = L.Browser.any3d && this.options.zoomAnimation; - this._isOpen = false; - }, - - onAdd: function (map) { - this._map = map; - - this._pane = this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane; - - if (!this._container) { - this._initLayout(); - } - - this._pane.appendChild(this._container); - - this._initInteraction(); - - this._update(); - - this.setOpacity(this.options.opacity); - - map - .on('moveend', this._onMoveEnd, this) - .on('viewreset', this._onViewReset, this); - - if (this._animated) { - map.on('zoomanim', this._zoomAnimation, this); - } - - if (L.Browser.touch && !this.options.noHide) { - L.DomEvent.on(this._container, 'click', this.close, this); - } - }, - - onRemove: function (map) { - this._pane.removeChild(this._container); - - map.off({ - zoomanim: this._zoomAnimation, - moveend: this._onMoveEnd, - viewreset: this._onViewReset - }, this); - - this._removeInteraction(); - - this._map = null; - }, - - setLatLng: function (latlng) { - this._latlng = L.latLng(latlng); - if (this._map) { - this._updatePosition(); - } - return this; - }, - - setContent: function (content) { - // Backup previous content and store new content - this._previousContent = this._content; - this._content = content; - - this._updateContent(); - - return this; - }, - - close: function () { - var map = this._map; - - if (map) { - if (L.Browser.touch && !this.options.noHide) { - L.DomEvent.off(this._container, 'click', this.close); - } - - map.removeLayer(this); - } - }, - - updateZIndex: function (zIndex) { - this._zIndex = zIndex; - - if (this._container && this._zIndex) { - this._container.style.zIndex = zIndex; - } - }, - - setOpacity: function (opacity) { - this.options.opacity = opacity; - - if (this._container) { - L.DomUtil.setOpacity(this._container, opacity); - } - }, - - _initLayout: function () { - this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated'); - this.updateZIndex(this._zIndex); - }, - - _update: function () { - if (!this._map) { return; } - - this._container.style.visibility = 'hidden'; - - this._updateContent(); - this._updatePosition(); - - this._container.style.visibility = ''; - }, - - _updateContent: function () { - if (!this._content || !this._map || this._prevContent === this._content) { - return; - } - - if (typeof this._content === 'string') { - this._container.innerHTML = this._content; - - this._prevContent = this._content; - - this._labelWidth = this._container.offsetWidth; - } - }, - - _updatePosition: function () { - var pos = this._map.latLngToLayerPoint(this._latlng); - - this._setPosition(pos); - }, - - _setPosition: function (pos) { - var map = this._map, - container = this._container, - centerPoint = map.latLngToContainerPoint(map.getCenter()), - labelPoint = map.layerPointToContainerPoint(pos), - direction = this.options.direction, - labelWidth = this._labelWidth, - offset = L.point(this.options.offset); - - // position to the right (right or auto & needs to) - if (direction === 'right' || direction === 'auto' && labelPoint.x < centerPoint.x) { - L.DomUtil.addClass(container, 'leaflet-label-right'); - L.DomUtil.removeClass(container, 'leaflet-label-left'); - - pos = pos.add(offset); - } else { // position to the left - L.DomUtil.addClass(container, 'leaflet-label-left'); - L.DomUtil.removeClass(container, 'leaflet-label-right'); - - pos = pos.add(L.point(-offset.x - labelWidth, offset.y)); - } - - L.DomUtil.setPosition(container, pos); - }, - - _zoomAnimation: function (opt) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); - - this._setPosition(pos); - }, - - _onMoveEnd: function () { - if (!this._animated || this.options.direction === 'auto') { - this._updatePosition(); - } - }, - - _onViewReset: function (e) { - /* if map resets hard, we must update the label */ - if (e && e.hard) { - this._update(); - } - }, - - _initInteraction: function () { - if (!this.options.clickable) { return; } - - var container = this._container, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; - - L.DomUtil.addClass(container, 'leaflet-clickable'); - L.DomEvent.on(container, 'click', this._onMouseClick, this); - - for (var i = 0; i < events.length; i++) { - L.DomEvent.on(container, events[i], this._fireMouseEvent, this); - } - }, - - _removeInteraction: function () { - if (!this.options.clickable) { return; } - - var container = this._container, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; - - L.DomUtil.removeClass(container, 'leaflet-clickable'); - L.DomEvent.off(container, 'click', this._onMouseClick, this); - - for (var i = 0; i < events.length; i++) { - L.DomEvent.off(container, events[i], this._fireMouseEvent, this); - } - }, - - _onMouseClick: function (e) { - if (this.hasEventListeners(e.type)) { - L.DomEvent.stopPropagation(e); - } - - this.fire(e.type, { - originalEvent: e - }); - }, - - _fireMouseEvent: function (e) { - this.fire(e.type, { - originalEvent: e - }); - - // TODO proper custom event propagation - // this line will always be called if marker is in a FeatureGroup - if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) { - L.DomEvent.preventDefault(e); - } - if (e.type !== 'mousedown') { - L.DomEvent.stopPropagation(e); - } else { - L.DomEvent.preventDefault(e); - } - } -}); - - -// This object is a mixin for L.Marker and L.CircleMarker. We declare it here as both need to include the contents. -L.BaseMarkerMethods = { - showLabel: function () { - if (this.label && this._map) { - this.label.setLatLng(this._latlng); - this._map.showLabel(this.label); - } - - return this; - }, - - hideLabel: function () { - if (this.label) { - this.label.close(); - } - return this; - }, - - setLabelNoHide: function (noHide) { - if (this._labelNoHide === noHide) { - return; - } - - this._labelNoHide = noHide; - - if (noHide) { - this._removeLabelRevealHandlers(); - this.showLabel(); - } else { - this._addLabelRevealHandlers(); - this.hideLabel(); - } - }, - - bindLabel: function (content, options) { - var labelAnchor = this.options.icon ? this.options.icon.options.labelAnchor : this.options.labelAnchor, - anchor = L.point(labelAnchor) || L.point(0, 0); - - anchor = anchor.add(L.Label.prototype.options.offset); - - if (options && options.offset) { - anchor = anchor.add(options.offset); - } - - options = L.Util.extend({offset: anchor}, options); - - this._labelNoHide = options.noHide; - - if (!this.label) { - if (!this._labelNoHide) { - this._addLabelRevealHandlers(); - } - - this - .on('remove', this.hideLabel, this) - .on('move', this._moveLabel, this) - .on('add', this._onMarkerAdd, this); - - this._hasLabelHandlers = true; - } - - this.label = new L.Label(options, this) - .setContent(content); - - return this; - }, - - unbindLabel: function () { - if (this.label) { - this.hideLabel(); - - this.label = null; - - if (this._hasLabelHandlers) { - if (!this._labelNoHide) { - this._removeLabelRevealHandlers(); - } - - this - .off('remove', this.hideLabel, this) - .off('move', this._moveLabel, this) - .off('add', this._onMarkerAdd, this); - } - - this._hasLabelHandlers = false; - } - return this; - }, - - updateLabelContent: function (content) { - if (this.label) { - this.label.setContent(content); - } - }, - - getLabel: function () { - return this.label; - }, - - _onMarkerAdd: function () { - if (this._labelNoHide) { - this.showLabel(); - } - }, - - _addLabelRevealHandlers: function () { - this - .on('mouseover', this.showLabel, this) - .on('mouseout', this.hideLabel, this); - - if (L.Browser.touch) { - this.on('click', this.showLabel, this); - } - }, - - _removeLabelRevealHandlers: function () { - this - .off('mouseover', this.showLabel, this) - .off('mouseout', this.hideLabel, this); - - if (L.Browser.touch) { - this.off('click', this.showLabel, this); - } - }, - - _moveLabel: function (e) { - this.label.setLatLng(e.latlng); - } -}; - -// Add in an option to icon that is used to set where the label anchor is -L.Icon.Default.mergeOptions({ - labelAnchor: new L.Point(9, -20) -}); - -// Have to do this since Leaflet is loaded before this plugin and initializes -// L.Marker.options.icon therefore missing our mixin above. -L.Marker.mergeOptions({ - icon: new L.Icon.Default() -}); - -L.Marker.include(L.BaseMarkerMethods); -L.Marker.include({ - _originalUpdateZIndex: L.Marker.prototype._updateZIndex, - - _updateZIndex: function (offset) { - var zIndex = this._zIndex + offset; - - this._originalUpdateZIndex(offset); - - if (this.label) { - this.label.updateZIndex(zIndex); - } - }, - - _originalSetOpacity: L.Marker.prototype.setOpacity, - - setOpacity: function (opacity, labelHasSemiTransparency) { - this.options.labelHasSemiTransparency = labelHasSemiTransparency; - - this._originalSetOpacity(opacity); - }, - - _originalUpdateOpacity: L.Marker.prototype._updateOpacity, - - _updateOpacity: function () { - var absoluteOpacity = this.options.opacity === 0 ? 0 : 1; - - this._originalUpdateOpacity(); - - if (this.label) { - this.label.setOpacity(this.options.labelHasSemiTransparency ? this.options.opacity : absoluteOpacity); - } - }, - - _originalSetLatLng: L.Marker.prototype.setLatLng, - - setLatLng: function (latlng) { - if (this.label && !this._labelNoHide) { - this.hideLabel(); - } - - return this._originalSetLatLng(latlng); - } -}); - -// Add in an option to icon that is used to set where the label anchor is -L.CircleMarker.mergeOptions({ - labelAnchor: new L.Point(0, 0) -}); - - -L.CircleMarker.include(L.BaseMarkerMethods); - -L.Path.include({ - bindLabel: function (content, options) { - if (!this.label || this.label.options !== options) { - this.label = new L.Label(options, this); - } - - this.label.setContent(content); - - if (!this._showLabelAdded) { - this - .on('mouseover', this._showLabel, this) - .on('mousemove', this._moveLabel, this) - .on('mouseout remove', this._hideLabel, this); - - if (L.Browser.touch) { - this.on('click', this._showLabel, this); - } - this._showLabelAdded = true; - } - - return this; - }, - - unbindLabel: function () { - if (this.label) { - this._hideLabel(); - this.label = null; - this._showLabelAdded = false; - this - .off('mouseover', this._showLabel, this) - .off('mousemove', this._moveLabel, this) - .off('mouseout remove', this._hideLabel, this); - } - return this; - }, - - updateLabelContent: function (content) { - if (this.label) { - this.label.setContent(content); - } - }, - - _showLabel: function (e) { - this.label.setLatLng(e.latlng); - this._map.showLabel(this.label); - }, - - _moveLabel: function (e) { - this.label.setLatLng(e.latlng); - }, - - _hideLabel: function () { - this.label.close(); - } -}); - -L.Map.include({ - showLabel: function (label) { - return this.addLayer(label); - } -}); - -L.FeatureGroup.include({ - // TODO: remove this when AOP is supported in Leaflet, need this as we cannot put code in removeLayer() - clearLayers: function () { - this.unbindLabel(); - this.eachLayer(this.removeLayer, this); - return this; - }, - - bindLabel: function (content, options) { - return this.invoke('bindLabel', content, options); - }, - - unbindLabel: function () { - return this.invoke('unbindLabel'); - }, - - updateLabelContent: function (content) { - this.invoke('updateLabelContent', content); - } -}); - -}(this, document)); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label.css b/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label.css deleted file mode 100644 index 95e6096467..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label.css +++ /dev/null @@ -1,54 +0,0 @@ -.leaflet-label { - background: rgb(235, 235, 235); - background: rgba(235, 235, 235, 0.81); - background-clip: padding-box; - border-color: #777; - border-color: rgba(0,0,0,0.25); - border-radius: 4px; - border-style: solid; - border-width: 4px; - color: #111; - display: block; - font: 12px/20px "Helvetica Neue", Arial, Helvetica, sans-serif; - font-weight: bold; - padding: 1px 6px; - position: absolute; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - pointer-events: none; - white-space: nowrap; - z-index: 6; -} - -.leaflet-label.leaflet-clickable { - cursor: pointer; - pointer-events: auto; -} - -.leaflet-label:before, -.leaflet-label:after { - border-top: 6px solid transparent; - border-bottom: 6px solid transparent; - content: none; - position: absolute; - top: 5px; -} - -.leaflet-label:before { - border-right: 6px solid black; - border-right-color: inherit; - left: -10px; -} - -.leaflet-label:after { - border-left: 6px solid black; - border-left-color: inherit; - right: -10px; -} - -.leaflet-label-right:before, -.leaflet-label-left:after { - content: ""; -} diff --git a/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label.js b/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label.js deleted file mode 100644 index b918ca577a..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/dist/leaflet.label.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps. - (c) 2012-2013, Jacob Toye, Smartrak - - https://github.com/Leaflet/Leaflet.label - http://leafletjs.com - https://github.com/jacobtoye -*/ -(function(){L.labelVersion="0.2.2-dev",L.Label=L.Class.extend({includes:L.Mixin.Events,options:{className:"",clickable:!1,direction:"right",noHide:!1,offset:[12,-15],opacity:1,zoomAnimation:!0},initialize:function(t,e){L.setOptions(this,t),this._source=e,this._animated=L.Browser.any3d&&this.options.zoomAnimation,this._isOpen=!1},onAdd:function(t){this._map=t,this._pane=this._source instanceof L.Marker?t._panes.markerPane:t._panes.popupPane,this._container||this._initLayout(),this._pane.appendChild(this._container),this._initInteraction(),this._update(),this.setOpacity(this.options.opacity),t.on("moveend",this._onMoveEnd,this).on("viewreset",this._onViewReset,this),this._animated&&t.on("zoomanim",this._zoomAnimation,this),L.Browser.touch&&!this.options.noHide&&L.DomEvent.on(this._container,"click",this.close,this)},onRemove:function(t){this._pane.removeChild(this._container),t.off({zoomanim:this._zoomAnimation,moveend:this._onMoveEnd,viewreset:this._onViewReset},this),this._removeInteraction(),this._map=null},setLatLng:function(t){return this._latlng=L.latLng(t),this._map&&this._updatePosition(),this},setContent:function(t){return this._previousContent=this._content,this._content=t,this._updateContent(),this},close:function(){var t=this._map;t&&(L.Browser.touch&&!this.options.noHide&&L.DomEvent.off(this._container,"click",this.close),t.removeLayer(this))},updateZIndex:function(t){this._zIndex=t,this._container&&this._zIndex&&(this._container.style.zIndex=t)},setOpacity:function(t){this.options.opacity=t,this._container&&L.DomUtil.setOpacity(this._container,t)},_initLayout:function(){this._container=L.DomUtil.create("div","leaflet-label "+this.options.className+" leaflet-zoom-animated"),this.updateZIndex(this._zIndex)},_update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updatePosition(),this._container.style.visibility="")},_updateContent:function(){this._content&&this._map&&this._prevContent!==this._content&&"string"==typeof this._content&&(this._container.innerHTML=this._content,this._prevContent=this._content,this._labelWidth=this._container.offsetWidth)},_updatePosition:function(){var t=this._map.latLngToLayerPoint(this._latlng);this._setPosition(t)},_setPosition:function(t){var e=this._map,i=this._container,n=e.latLngToContainerPoint(e.getCenter()),o=e.layerPointToContainerPoint(t),s=this.options.direction,a=this._labelWidth,l=L.point(this.options.offset);"right"===s||"auto"===s&&o.xi;i++)L.DomEvent.on(t,e[i],this._fireMouseEvent,this)}},_removeInteraction:function(){if(this.options.clickable){var t=this._container,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];L.DomUtil.removeClass(t,"leaflet-clickable"),L.DomEvent.off(t,"click",this._onMouseClick,this);for(var i=0;e.length>i;i++)L.DomEvent.off(t,e[i],this._fireMouseEvent,this)}},_onMouseClick:function(t){this.hasEventListeners(t.type)&&L.DomEvent.stopPropagation(t),this.fire(t.type,{originalEvent:t})},_fireMouseEvent:function(t){this.fire(t.type,{originalEvent:t}),"contextmenu"===t.type&&this.hasEventListeners(t.type)&&L.DomEvent.preventDefault(t),"mousedown"!==t.type?L.DomEvent.stopPropagation(t):L.DomEvent.preventDefault(t)}}),L.BaseMarkerMethods={showLabel:function(){return this.label&&this._map&&(this.label.setLatLng(this._latlng),this._map.showLabel(this.label)),this},hideLabel:function(){return this.label&&this.label.close(),this},setLabelNoHide:function(t){this._labelNoHide!==t&&(this._labelNoHide=t,t?(this._removeLabelRevealHandlers(),this.showLabel()):(this._addLabelRevealHandlers(),this.hideLabel()))},bindLabel:function(t,e){var i=this.options.icon?this.options.icon.options.labelAnchor:this.options.labelAnchor,n=L.point(i)||L.point(0,0);return n=n.add(L.Label.prototype.options.offset),e&&e.offset&&(n=n.add(e.offset)),e=L.Util.extend({offset:n},e),this._labelNoHide=e.noHide,this.label||(this._labelNoHide||this._addLabelRevealHandlers(),this.on("remove",this.hideLabel,this).on("move",this._moveLabel,this).on("add",this._onMarkerAdd,this),this._hasLabelHandlers=!0),this.label=new L.Label(e,this).setContent(t),this},unbindLabel:function(){return this.label&&(this.hideLabel(),this.label=null,this._hasLabelHandlers&&(this._labelNoHide||this._removeLabelRevealHandlers(),this.off("remove",this.hideLabel,this).off("move",this._moveLabel,this).off("add",this._onMarkerAdd,this)),this._hasLabelHandlers=!1),this},updateLabelContent:function(t){this.label&&this.label.setContent(t)},getLabel:function(){return this.label},_onMarkerAdd:function(){this._labelNoHide&&this.showLabel()},_addLabelRevealHandlers:function(){this.on("mouseover",this.showLabel,this).on("mouseout",this.hideLabel,this),L.Browser.touch&&this.on("click",this.showLabel,this)},_removeLabelRevealHandlers:function(){this.off("mouseover",this.showLabel,this).off("mouseout",this.hideLabel,this),L.Browser.touch&&this.off("click",this.showLabel,this)},_moveLabel:function(t){this.label.setLatLng(t.latlng)}},L.Icon.Default.mergeOptions({labelAnchor:new L.Point(9,-20)}),L.Marker.mergeOptions({icon:new L.Icon.Default}),L.Marker.include(L.BaseMarkerMethods),L.Marker.include({_originalUpdateZIndex:L.Marker.prototype._updateZIndex,_updateZIndex:function(t){var e=this._zIndex+t;this._originalUpdateZIndex(t),this.label&&this.label.updateZIndex(e)},_originalSetOpacity:L.Marker.prototype.setOpacity,setOpacity:function(t,e){this.options.labelHasSemiTransparency=e,this._originalSetOpacity(t)},_originalUpdateOpacity:L.Marker.prototype._updateOpacity,_updateOpacity:function(){var t=0===this.options.opacity?0:1;this._originalUpdateOpacity(),this.label&&this.label.setOpacity(this.options.labelHasSemiTransparency?this.options.opacity:t)},_originalSetLatLng:L.Marker.prototype.setLatLng,setLatLng:function(t){return this.label&&!this._labelNoHide&&this.hideLabel(),this._originalSetLatLng(t)}}),L.CircleMarker.mergeOptions({labelAnchor:new L.Point(0,0)}),L.CircleMarker.include(L.BaseMarkerMethods),L.Path.include({bindLabel:function(t,e){return this.label&&this.label.options===e||(this.label=new L.Label(e,this)),this.label.setContent(t),this._showLabelAdded||(this.on("mouseover",this._showLabel,this).on("mousemove",this._moveLabel,this).on("mouseout remove",this._hideLabel,this),L.Browser.touch&&this.on("click",this._showLabel,this),this._showLabelAdded=!0),this},unbindLabel:function(){return this.label&&(this._hideLabel(),this.label=null,this._showLabelAdded=!1,this.off("mouseover",this._showLabel,this).off("mousemove",this._moveLabel,this).off("mouseout remove",this._hideLabel,this)),this},updateLabelContent:function(t){this.label&&this.label.setContent(t)},_showLabel:function(t){this.label.setLatLng(t.latlng),this._map.showLabel(this.label)},_moveLabel:function(t){this.label.setLatLng(t.latlng)},_hideLabel:function(){this.label.close()}}),L.Map.include({showLabel:function(t){return this.addLayer(t)}}),L.FeatureGroup.include({clearLayers:function(){return this.unbindLabel(),this.eachLayer(this.removeLayer,this),this},bindLabel:function(t,e){return this.invoke("bindLabel",t,e)},unbindLabel:function(){return this.invoke("unbindLabel")},updateLabelContent:function(t){this.invoke("updateLabelContent",t)}})})(this,document); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/example/label.html b/mapentity/static/mapentity/Leaflet.label/example/label.html deleted file mode 100644 index f3264c4ca6..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/example/label.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - Leaflet.label example - - - - - - - - - - - - - - - - -
- - - diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/layers-2x.png b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/layers-2x.png deleted file mode 100644 index a2cf7f9efe..0000000000 Binary files a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/layers-2x.png and /dev/null differ diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/layers.png b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/layers.png deleted file mode 100755 index bca0a0e429..0000000000 Binary files a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/layers.png and /dev/null differ diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon-2x.png b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon-2x.png deleted file mode 100644 index 0015b6495f..0000000000 Binary files a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon-2x.png and /dev/null differ diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon.png b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon.png deleted file mode 100755 index e2e9f757f5..0000000000 Binary files a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon.png and /dev/null differ diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon@2x.png b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon@2x.png deleted file mode 100755 index 0015b6495f..0000000000 Binary files a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-icon@2x.png and /dev/null differ diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-shadow.png b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-shadow.png deleted file mode 100755 index d1e773c715..0000000000 Binary files a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/images/marker-shadow.png and /dev/null differ diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet-src.js b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet-src.js deleted file mode 100755 index 8152548393..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet-src.js +++ /dev/null @@ -1,9108 +0,0 @@ -/* - Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com - (c) 2010-2013, Vladimir Agafonkin - (c) 2010-2011, CloudMade -*/ -(function (window, document, undefined) { -var oldL = window.L, - L = {}; - -L.version = '0.7-dev'; - -// define Leaflet for Node module pattern loaders, including Browserify -if (typeof module === 'object' && typeof module.exports === 'object') { - module.exports = L; - -// define Leaflet as an AMD module -} else if (typeof define === 'function' && define.amd) { - define(L); -} - -// define Leaflet as a global L variable, saving the original L to restore later if needed - -L.noConflict = function () { - window.L = oldL; - return this; -}; - -window.L = L; - - -/* - * L.Util contains various utility functions used throughout Leaflet code. - */ - -L.Util = { - extend: function (dest) { // (Object[, Object, ...]) -> - var sources = Array.prototype.slice.call(arguments, 1), - i, j, len, src; - - for (j = 0, len = sources.length; j < len; j++) { - src = sources[j] || {}; - for (i in src) { - if (src.hasOwnProperty(i)) { - dest[i] = src[i]; - } - } - } - return dest; - }, - - bind: function (fn, obj) { // (Function, Object) -> Function - var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null; - return function () { - return fn.apply(obj, args || arguments); - }; - }, - - stamp: (function () { - var lastId = 0, - key = '_leaflet_id'; - return function (obj) { - obj[key] = obj[key] || ++lastId; - return obj[key]; - }; - }()), - - invokeEach: function (obj, method, context) { - var i, args; - - if (typeof obj === 'object') { - args = Array.prototype.slice.call(arguments, 3); - - for (i in obj) { - method.apply(context, [i, obj[i]].concat(args)); - } - return true; - } - - return false; - }, - - limitExecByInterval: function (fn, time, context) { - var lock, execOnUnlock; - - return function wrapperFn() { - var args = arguments; - - if (lock) { - execOnUnlock = true; - return; - } - - lock = true; - - setTimeout(function () { - lock = false; - - if (execOnUnlock) { - wrapperFn.apply(context, args); - execOnUnlock = false; - } - }, time); - - fn.apply(context, args); - }; - }, - - falseFn: function () { - return false; - }, - - formatNum: function (num, digits) { - var pow = Math.pow(10, digits || 5); - return Math.round(num * pow) / pow; - }, - - trim: function (str) { - return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); - }, - - splitWords: function (str) { - return L.Util.trim(str).split(/\s+/); - }, - - setOptions: function (obj, options) { - obj.options = L.extend({}, obj.options, options); - return obj.options; - }, - - getParamString: function (obj, existingUrl, uppercase) { - var params = []; - for (var i in obj) { - params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); - } - return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); - }, - - compileTemplate: function (str, data) { - // based on https://gist.github.com/padolsey/6008842 - str = str.replace(/"/g, '\\\"'); - str = str.replace(/\{ *([\w_]+) *\}/g, function (str, key) { - return '" + o["' + key + '"]' + (typeof data[key] === 'function' ? '(o)' : '') + ' + "'; - }); - // jshint evil: true - return new Function('o', 'return "' + str + '";'); - }, - - template: function (str, data) { - var cache = L.Util._templateCache = L.Util._templateCache || {}; - cache[str] = cache[str] || L.Util.compileTemplate(str, data); - return cache[str](data); - }, - - isArray: Array.isArray || function (obj) { - return (Object.prototype.toString.call(obj) === '[object Array]'); - }, - - emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' -}; - -(function () { - - // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ - - function getPrefixed(name) { - var i, fn, - prefixes = ['webkit', 'moz', 'o', 'ms']; - - for (i = 0; i < prefixes.length && !fn; i++) { - fn = window[prefixes[i] + name]; - } - - return fn; - } - - var lastTime = 0; - - function timeoutDefer(fn) { - var time = +new Date(), - timeToCall = Math.max(0, 16 - (time - lastTime)); - - lastTime = time + timeToCall; - return window.setTimeout(fn, timeToCall); - } - - var requestFn = window.requestAnimationFrame || - getPrefixed('RequestAnimationFrame') || timeoutDefer; - - var cancelFn = window.cancelAnimationFrame || - getPrefixed('CancelAnimationFrame') || - getPrefixed('CancelRequestAnimationFrame') || - function (id) { window.clearTimeout(id); }; - - - L.Util.requestAnimFrame = function (fn, context, immediate, element) { - fn = L.bind(fn, context); - - if (immediate && requestFn === timeoutDefer) { - fn(); - } else { - return requestFn.call(window, fn, element); - } - }; - - L.Util.cancelAnimFrame = function (id) { - if (id) { - cancelFn.call(window, id); - } - }; - -}()); - -// shortcuts for most used utility functions -L.extend = L.Util.extend; -L.bind = L.Util.bind; -L.stamp = L.Util.stamp; -L.setOptions = L.Util.setOptions; - - -/* - * L.Class powers the OOP facilities of the library. - * Thanks to John Resig and Dean Edwards for inspiration! - */ - -L.Class = function () {}; - -L.Class.extend = function (props) { - - // extended class with the new prototype - var NewClass = function () { - - // call the constructor - if (this.initialize) { - this.initialize.apply(this, arguments); - } - - // call all constructor hooks - if (this._initHooks) { - this.callInitHooks(); - } - }; - - // instantiate class without calling constructor - var F = function () {}; - F.prototype = this.prototype; - - var proto = new F(); - proto.constructor = NewClass; - - NewClass.prototype = proto; - - //inherit parent's statics - for (var i in this) { - if (this.hasOwnProperty(i) && i !== 'prototype') { - NewClass[i] = this[i]; - } - } - - // mix static properties into the class - if (props.statics) { - L.extend(NewClass, props.statics); - delete props.statics; - } - - // mix includes into the prototype - if (props.includes) { - L.Util.extend.apply(null, [proto].concat(props.includes)); - delete props.includes; - } - - // merge options - if (props.options && proto.options) { - props.options = L.extend({}, proto.options, props.options); - } - - // mix given properties into the prototype - L.extend(proto, props); - - proto._initHooks = []; - - var parent = this; - // jshint camelcase: false - NewClass.__super__ = parent.prototype; - - // add method for calling all hooks - proto.callInitHooks = function () { - - if (this._initHooksCalled) { return; } - - if (parent.prototype.callInitHooks) { - parent.prototype.callInitHooks.call(this); - } - - this._initHooksCalled = true; - - for (var i = 0, len = proto._initHooks.length; i < len; i++) { - proto._initHooks[i].call(this); - } - }; - - return NewClass; -}; - - -// method for adding properties to prototype -L.Class.include = function (props) { - L.extend(this.prototype, props); -}; - -// merge new default options to the Class -L.Class.mergeOptions = function (options) { - L.extend(this.prototype.options, options); -}; - -// add a constructor hook -L.Class.addInitHook = function (fn) { // (Function) || (String, args...) - var args = Array.prototype.slice.call(arguments, 1); - - var init = typeof fn === 'function' ? fn : function () { - this[fn].apply(this, args); - }; - - this.prototype._initHooks = this.prototype._initHooks || []; - this.prototype._initHooks.push(init); -}; - - -/* - * L.Mixin.Events is used to add custom events functionality to Leaflet classes. - */ - -var eventsKey = '_leaflet_events'; - -L.Mixin = {}; - -L.Mixin.Events = { - - addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object]) - - // types can be a map of types/handlers - if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; } - - var events = this[eventsKey] = this[eventsKey] || {}, - contextId = context && L.stamp(context), - i, len, event, type, indexKey, indexLenKey, typeIndex; - - // types can be a string of space-separated words - types = L.Util.splitWords(types); - - for (i = 0, len = types.length; i < len; i++) { - event = { - action: fn, - context: context || this - }; - type = types[i]; - - if (context) { - // store listeners of a particular context in a separate hash (if it has an id) - // gives a major performance boost when removing thousands of map layers - - indexKey = type + '_idx'; - indexLenKey = indexKey + '_len'; - - typeIndex = events[indexKey] = events[indexKey] || {}; - - if (!typeIndex[contextId]) { - typeIndex[contextId] = []; - - // keep track of the number of keys in the index to quickly check if it's empty - events[indexLenKey] = (events[indexLenKey] || 0) + 1; - } - - typeIndex[contextId].push(event); - - - } else { - events[type] = events[type] || []; - events[type].push(event); - } - } - - return this; - }, - - hasEventListeners: function (type) { // (String) -> Boolean - var events = this[eventsKey]; - return !!events && ((type in events && events[type].length > 0) || - (type + '_idx' in events && events[type + '_idx_len'] > 0)); - }, - - removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object]) - - if (!this[eventsKey]) { - return this; - } - - if (!types) { - return this.clearAllEventListeners(); - } - - if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; } - - var events = this[eventsKey], - contextId = context && L.stamp(context), - i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed; - - types = L.Util.splitWords(types); - - for (i = 0, len = types.length; i < len; i++) { - type = types[i]; - indexKey = type + '_idx'; - indexLenKey = indexKey + '_len'; - - typeIndex = events[indexKey]; - - if (!fn) { - // clear all listeners for a type if function isn't specified - delete events[type]; - delete events[indexKey]; - - } else { - listeners = context && typeIndex ? typeIndex[contextId] : events[type]; - - if (listeners) { - for (j = listeners.length - 1; j >= 0; j--) { - if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) { - removed = listeners.splice(j, 1); - // set the old action to a no-op, because it is possible - // that the listener is being iterated over as part of a dispatch - removed[0].action = L.Util.falseFn; - } - } - - if (context && typeIndex && (listeners.length === 0)) { - delete typeIndex[contextId]; - events[indexLenKey]--; - } - } - } - } - - return this; - }, - - clearAllEventListeners: function () { - delete this[eventsKey]; - return this; - }, - - fireEvent: function (type, data) { // (String[, Object]) - if (!this.hasEventListeners(type)) { - return this; - } - - var event = L.Util.extend({}, data, { type: type, target: this }); - - var events = this[eventsKey], - listeners, i, len, typeIndex, contextId; - - if (events[type]) { - // make sure adding/removing listeners inside other listeners won't cause infinite loop - listeners = events[type].slice(); - - for (i = 0, len = listeners.length; i < len; i++) { - listeners[i].action.call(listeners[i].context || this, event); - } - } - - // fire event for the context-indexed listeners as well - typeIndex = events[type + '_idx']; - - for (contextId in typeIndex) { - listeners = typeIndex[contextId].slice(); - - if (listeners) { - for (i = 0, len = listeners.length; i < len; i++) { - listeners[i].action.call(listeners[i].context || this, event); - } - } - } - - return this; - }, - - addOneTimeEventListener: function (types, fn, context) { - - if (L.Util.invokeEach(types, this.addOneTimeEventListener, this, fn, context)) { return this; } - - var handler = L.bind(function () { - this - .removeEventListener(types, fn, context) - .removeEventListener(types, handler, context); - }, this); - - return this - .addEventListener(types, fn, context) - .addEventListener(types, handler, context); - } -}; - -L.Mixin.Events.on = L.Mixin.Events.addEventListener; -L.Mixin.Events.off = L.Mixin.Events.removeEventListener; -L.Mixin.Events.once = L.Mixin.Events.addOneTimeEventListener; -L.Mixin.Events.fire = L.Mixin.Events.fireEvent; - - -/* - * L.Browser handles different browser and feature detections for internal Leaflet use. - */ - -(function () { - - var ie = 'ActiveXObject' in window, - ie6 = ie && !window.XMLHttpRequest, - ie7 = ie && !document.querySelector, - ielt9 = ie && !document.addEventListener, - - // terrible browser detection to work around Safari / iOS / Android browser bugs - ua = navigator.userAgent.toLowerCase(), - webkit = ua.indexOf('webkit') !== -1, - chrome = ua.indexOf('chrome') !== -1, - phantomjs = ua.indexOf('phantom') !== -1, - android = ua.indexOf('android') !== -1, - android23 = ua.search('android [23]') !== -1, - gecko = ua.indexOf('gecko') !== -1, - - mobile = typeof orientation !== undefined + '', - msPointer = window.navigator && window.navigator.msPointerEnabled && - window.navigator.msMaxTouchPoints && !window.PointerEvent, - pointer = (window.PointerEvent && window.navigator.pointerEnabled && window.navigator.maxTouchPoints) || - msPointer, - retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) || - ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') && - window.matchMedia('(min-resolution:144dpi)').matches), - - doc = document.documentElement, - ie3d = ie && ('transition' in doc.style), - webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()), - gecko3d = 'MozPerspective' in doc.style, - opera3d = 'OTransition' in doc.style, - any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d) && !phantomjs; - - - // PhantomJS has 'ontouchstart' in document.documentElement, but doesn't actually support touch. - // https://github.com/Leaflet/Leaflet/pull/1434#issuecomment-13843151 - - var touch = !window.L_NO_TOUCH && !phantomjs && (function () { - - var startName = 'ontouchstart'; - - // IE10+ (We simulate these into touch* events in L.DomEvent and L.DomEvent.Pointer) or WebKit, etc. - if (pointer || (startName in doc)) { - return true; - } - - // Firefox/Gecko - var div = document.createElement('div'), - supported = false; - - if (!div.setAttribute) { - return false; - } - div.setAttribute(startName, 'return;'); - - if (typeof div[startName] === 'function') { - supported = true; - } - - div.removeAttribute(startName); - div = null; - - return supported; - }()); - - - L.Browser = { - ie: ie, - ie6: ie6, - ie7: ie7, - ielt9: ielt9, - webkit: webkit, - gecko: gecko && !webkit && !window.opera && !ie, - - android: android, - android23: android23, - - chrome: chrome, - - ie3d: ie3d, - webkit3d: webkit3d, - gecko3d: gecko3d, - opera3d: opera3d, - any3d: any3d, - - mobile: mobile, - mobileWebkit: mobile && webkit, - mobileWebkit3d: mobile && webkit3d, - mobileOpera: mobile && window.opera, - - touch: touch, - msPointer: msPointer, - pointer: pointer, - - retina: retina - }; - -}()); - - -/* - * L.Point represents a point with x and y coordinates. - */ - -L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) { - this.x = (round ? Math.round(x) : x); - this.y = (round ? Math.round(y) : y); -}; - -L.Point.prototype = { - - clone: function () { - return new L.Point(this.x, this.y); - }, - - // non-destructive, returns a new point - add: function (point) { - return this.clone()._add(L.point(point)); - }, - - // destructive, used directly for performance in situations where it's safe to modify existing point - _add: function (point) { - this.x += point.x; - this.y += point.y; - return this; - }, - - subtract: function (point) { - return this.clone()._subtract(L.point(point)); - }, - - _subtract: function (point) { - this.x -= point.x; - this.y -= point.y; - return this; - }, - - divideBy: function (num) { - return this.clone()._divideBy(num); - }, - - _divideBy: function (num) { - this.x /= num; - this.y /= num; - return this; - }, - - multiplyBy: function (num) { - return this.clone()._multiplyBy(num); - }, - - _multiplyBy: function (num) { - this.x *= num; - this.y *= num; - return this; - }, - - round: function () { - return this.clone()._round(); - }, - - _round: function () { - this.x = Math.round(this.x); - this.y = Math.round(this.y); - return this; - }, - - floor: function () { - return this.clone()._floor(); - }, - - _floor: function () { - this.x = Math.floor(this.x); - this.y = Math.floor(this.y); - return this; - }, - - distanceTo: function (point) { - point = L.point(point); - - var x = point.x - this.x, - y = point.y - this.y; - - return Math.sqrt(x * x + y * y); - }, - - equals: function (point) { - point = L.point(point); - - return point.x === this.x && - point.y === this.y; - }, - - contains: function (point) { - point = L.point(point); - - return Math.abs(point.x) <= Math.abs(this.x) && - Math.abs(point.y) <= Math.abs(this.y); - }, - - toString: function () { - return 'Point(' + - L.Util.formatNum(this.x) + ', ' + - L.Util.formatNum(this.y) + ')'; - } -}; - -L.point = function (x, y, round) { - if (x instanceof L.Point) { - return x; - } - if (L.Util.isArray(x)) { - return new L.Point(x[0], x[1]); - } - if (x === undefined || x === null) { - return x; - } - return new L.Point(x, y, round); -}; - - -/* - * L.Bounds represents a rectangular area on the screen in pixel coordinates. - */ - -L.Bounds = function (a, b) { //(Point, Point) or Point[] - if (!a) { return; } - - var points = b ? [a, b] : a; - - for (var i = 0, len = points.length; i < len; i++) { - this.extend(points[i]); - } -}; - -L.Bounds.prototype = { - // extend the bounds to contain the given point - extend: function (point) { // (Point) - point = L.point(point); - - if (!this.min && !this.max) { - this.min = point.clone(); - this.max = point.clone(); - } else { - this.min.x = Math.min(point.x, this.min.x); - this.max.x = Math.max(point.x, this.max.x); - this.min.y = Math.min(point.y, this.min.y); - this.max.y = Math.max(point.y, this.max.y); - } - return this; - }, - - getCenter: function (round) { // (Boolean) -> Point - return new L.Point( - (this.min.x + this.max.x) / 2, - (this.min.y + this.max.y) / 2, round); - }, - - getBottomLeft: function () { // -> Point - return new L.Point(this.min.x, this.max.y); - }, - - getTopRight: function () { // -> Point - return new L.Point(this.max.x, this.min.y); - }, - - getSize: function () { - return this.max.subtract(this.min); - }, - - contains: function (obj) { // (Bounds) or (Point) -> Boolean - var min, max; - - if (typeof obj[0] === 'number' || obj instanceof L.Point) { - obj = L.point(obj); - } else { - obj = L.bounds(obj); - } - - if (obj instanceof L.Bounds) { - min = obj.min; - max = obj.max; - } else { - min = max = obj; - } - - return (min.x >= this.min.x) && - (max.x <= this.max.x) && - (min.y >= this.min.y) && - (max.y <= this.max.y); - }, - - intersects: function (bounds) { // (Bounds) -> Boolean - bounds = L.bounds(bounds); - - var min = this.min, - max = this.max, - min2 = bounds.min, - max2 = bounds.max, - xIntersects = (max2.x >= min.x) && (min2.x <= max.x), - yIntersects = (max2.y >= min.y) && (min2.y <= max.y); - - return xIntersects && yIntersects; - }, - - isValid: function () { - return !!(this.min && this.max); - } -}; - -L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[]) - if (!a || a instanceof L.Bounds) { - return a; - } - return new L.Bounds(a, b); -}; - - -/* - * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix. - */ - -L.Transformation = function (a, b, c, d) { - this._a = a; - this._b = b; - this._c = c; - this._d = d; -}; - -L.Transformation.prototype = { - transform: function (point, scale) { // (Point, Number) -> Point - return this._transform(point.clone(), scale); - }, - - // destructive transform (faster) - _transform: function (point, scale) { - scale = scale || 1; - point.x = scale * (this._a * point.x + this._b); - point.y = scale * (this._c * point.y + this._d); - return point; - }, - - untransform: function (point, scale) { - scale = scale || 1; - return new L.Point( - (point.x / scale - this._b) / this._a, - (point.y / scale - this._d) / this._c); - } -}; - - -/* - * L.DomUtil contains various utility functions for working with DOM. - */ - -L.DomUtil = { - get: function (id) { - return (typeof id === 'string' ? document.getElementById(id) : id); - }, - - getStyle: function (el, style) { - - var value = el.style[style]; - - if (!value && el.currentStyle) { - value = el.currentStyle[style]; - } - - if ((!value || value === 'auto') && document.defaultView) { - var css = document.defaultView.getComputedStyle(el, null); - value = css ? css[style] : null; - } - - return value === 'auto' ? null : value; - }, - - getViewportOffset: function (element) { - - var top = 0, - left = 0, - el = element, - docBody = document.body, - docEl = document.documentElement, - pos; - - do { - top += el.offsetTop || 0; - left += el.offsetLeft || 0; - - //add borders - top += parseInt(L.DomUtil.getStyle(el, 'borderTopWidth'), 10) || 0; - left += parseInt(L.DomUtil.getStyle(el, 'borderLeftWidth'), 10) || 0; - - pos = L.DomUtil.getStyle(el, 'position'); - - if (el.offsetParent === docBody && pos === 'absolute') { break; } - - if (pos === 'fixed') { - top += docBody.scrollTop || docEl.scrollTop || 0; - left += docBody.scrollLeft || docEl.scrollLeft || 0; - break; - } - - if (pos === 'relative' && !el.offsetLeft) { - var width = L.DomUtil.getStyle(el, 'width'), - maxWidth = L.DomUtil.getStyle(el, 'max-width'), - r = el.getBoundingClientRect(); - - if (width !== 'none' || maxWidth !== 'none') { - left += r.left + el.clientLeft; - } - - //calculate full y offset since we're breaking out of the loop - top += r.top + (docBody.scrollTop || docEl.scrollTop || 0); - - break; - } - - el = el.offsetParent; - - } while (el); - - el = element; - - do { - if (el === docBody) { break; } - - top -= el.scrollTop || 0; - left -= el.scrollLeft || 0; - - el = el.parentNode; - } while (el); - - return new L.Point(left, top); - }, - - documentIsLtr: function () { - if (!L.DomUtil._docIsLtrCached) { - L.DomUtil._docIsLtrCached = true; - L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === 'ltr'; - } - return L.DomUtil._docIsLtr; - }, - - create: function (tagName, className, container) { - - var el = document.createElement(tagName); - el.className = className; - - if (container) { - container.appendChild(el); - } - - return el; - }, - - hasClass: function (el, name) { - return (el.className.length > 0) && - new RegExp('(^|\\s)' + name + '(\\s|$)').test(el.className); - }, - - addClass: function (el, name) { - if (!L.DomUtil.hasClass(el, name)) { - el.className += (el.className ? ' ' : '') + name; - } - }, - - removeClass: function (el, name) { - el.className = L.Util.trim((' ' + el.className + ' ').replace(' ' + name + ' ', ' ')); - }, - - setOpacity: function (el, value) { - - if ('opacity' in el.style) { - el.style.opacity = value; - - } else if ('filter' in el.style) { - - var filter = false, - filterName = 'DXImageTransform.Microsoft.Alpha'; - - // filters collection throws an error if we try to retrieve a filter that doesn't exist - try { - filter = el.filters.item(filterName); - } catch (e) { - // don't set opacity to 1 if we haven't already set an opacity, - // it isn't needed and breaks transparent pngs. - if (value === 1) { return; } - } - - value = Math.round(value * 100); - - if (filter) { - filter.Enabled = (value !== 100); - filter.Opacity = value; - } else { - el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; - } - } - }, - - testProp: function (props) { - - var style = document.documentElement.style; - - for (var i = 0; i < props.length; i++) { - if (props[i] in style) { - return props[i]; - } - } - return false; - }, - - getTranslateString: function (point) { - // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate - // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care - // (same speed either way), Opera 12 doesn't support translate3d - - var is3d = L.Browser.webkit3d, - open = 'translate' + (is3d ? '3d' : '') + '(', - close = (is3d ? ',0' : '') + ')'; - - return open + point.x + 'px,' + point.y + 'px' + close; - }, - - getScaleString: function (scale, origin) { - - var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))), - scaleStr = ' scale(' + scale + ') '; - - return preTranslateStr + scaleStr; - }, - - setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean]) - - // jshint camelcase: false - el._leaflet_pos = point; - - if (!disable3D && L.Browser.any3d) { - el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point); - - // workaround for Android 2/3 stability (https://github.com/CloudMade/Leaflet/issues/69) - if (L.Browser.mobileWebkit3d) { - el.style.WebkitBackfaceVisibility = 'hidden'; - } - } else { - el.style.left = point.x + 'px'; - el.style.top = point.y + 'px'; - } - }, - - getPosition: function (el) { - // this method is only used for elements previously positioned using setPosition, - // so it's safe to cache the position for performance - - // jshint camelcase: false - return el._leaflet_pos; - } -}; - - -// prefix style property names - -L.DomUtil.TRANSFORM = L.DomUtil.testProp( - ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); - -// webkitTransition comes first because some browser versions that drop vendor prefix don't do -// the same for the transitionend event, in particular the Android 4.1 stock browser - -L.DomUtil.TRANSITION = L.DomUtil.testProp( - ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); - -L.DomUtil.TRANSITION_END = - L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ? - L.DomUtil.TRANSITION + 'End' : 'transitionend'; - -(function () { - var userSelectProperty = L.DomUtil.testProp( - ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); - - L.extend(L.DomUtil, { - disableTextSelection: function () { - L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault); - if (userSelectProperty) { - var style = document.documentElement.style; - this._userSelect = style[userSelectProperty]; - style[userSelectProperty] = 'none'; - } - }, - - enableTextSelection: function () { - L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault); - if (userSelectProperty) { - document.documentElement.style[userSelectProperty] = this._userSelect; - delete this._userSelect; - } - }, - - disableImageDrag: function () { - L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault); - }, - - enableImageDrag: function () { - L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault); - } - }); -})(); - - -/* - * L.LatLng represents a geographical point with latitude and longitude coordinates. - */ - -L.LatLng = function (rawLat, rawLng) { // (Number, Number) - var lat = parseFloat(rawLat), - lng = parseFloat(rawLng); - - if (isNaN(lat) || isNaN(lng)) { - throw new Error('Invalid LatLng object: (' + rawLat + ', ' + rawLng + ')'); - } - - this.lat = lat; - this.lng = lng; -}; - -L.extend(L.LatLng, { - DEG_TO_RAD: Math.PI / 180, - RAD_TO_DEG: 180 / Math.PI, - MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check -}); - -L.LatLng.prototype = { - equals: function (obj) { // (LatLng) -> Boolean - if (!obj) { return false; } - - obj = L.latLng(obj); - - var margin = Math.max( - Math.abs(this.lat - obj.lat), - Math.abs(this.lng - obj.lng)); - - return margin <= L.LatLng.MAX_MARGIN; - }, - - toString: function (precision) { // (Number) -> String - return 'LatLng(' + - L.Util.formatNum(this.lat, precision) + ', ' + - L.Util.formatNum(this.lng, precision) + ')'; - }, - - // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula - // TODO move to projection code, LatLng shouldn't know about Earth - distanceTo: function (other) { // (LatLng) -> Number - other = L.latLng(other); - - var R = 6378137, // earth radius in meters - d2r = L.LatLng.DEG_TO_RAD, - dLat = (other.lat - this.lat) * d2r, - dLon = (other.lng - this.lng) * d2r, - lat1 = this.lat * d2r, - lat2 = other.lat * d2r, - sin1 = Math.sin(dLat / 2), - sin2 = Math.sin(dLon / 2); - - var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2); - - return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - }, - - wrap: function (a, b) { // (Number, Number) -> LatLng - var lng = this.lng; - - a = a || -180; - b = b || 180; - - lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a); - - return new L.LatLng(this.lat, lng); - } -}; - -L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number) - if (a instanceof L.LatLng) { - return a; - } - if (L.Util.isArray(a)) { - if (typeof a[0] === 'number' || typeof a[0] === 'string') { - return new L.LatLng(a[0], a[1]); - } else { - return null; - } - } - if (a === undefined || a === null) { - return a; - } - if (typeof a === 'object' && 'lat' in a) { - return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon); - } - if (b === undefined) { - return null; - } - return new L.LatLng(a, b); -}; - - - -/* - * L.LatLngBounds represents a rectangular area on the map in geographical coordinates. - */ - -L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) - if (!southWest) { return; } - - var latlngs = northEast ? [southWest, northEast] : southWest; - - for (var i = 0, len = latlngs.length; i < len; i++) { - this.extend(latlngs[i]); - } -}; - -L.LatLngBounds.prototype = { - // extend the bounds to contain the given point or bounds - extend: function (obj) { // (LatLng) or (LatLngBounds) - if (!obj) { return this; } - - var latLng = L.latLng(obj); - if (latLng !== null) { - obj = latLng; - } else { - obj = L.latLngBounds(obj); - } - - if (obj instanceof L.LatLng) { - if (!this._southWest && !this._northEast) { - this._southWest = new L.LatLng(obj.lat, obj.lng); - this._northEast = new L.LatLng(obj.lat, obj.lng); - } else { - this._southWest.lat = Math.min(obj.lat, this._southWest.lat); - this._southWest.lng = Math.min(obj.lng, this._southWest.lng); - - this._northEast.lat = Math.max(obj.lat, this._northEast.lat); - this._northEast.lng = Math.max(obj.lng, this._northEast.lng); - } - } else if (obj instanceof L.LatLngBounds) { - this.extend(obj._southWest); - this.extend(obj._northEast); - } - return this; - }, - - // extend the bounds by a percentage - pad: function (bufferRatio) { // (Number) -> LatLngBounds - var sw = this._southWest, - ne = this._northEast, - heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, - widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; - - return new L.LatLngBounds( - new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), - new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); - }, - - getCenter: function () { // -> LatLng - return new L.LatLng( - (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 L.LatLng(this.getNorth(), this.getWest()); - }, - - getSouthEast: function () { - return new L.LatLng(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 (obj) { // (LatLngBounds) or (LatLng) -> Boolean - if (typeof obj[0] === 'number' || obj instanceof L.LatLng) { - obj = L.latLng(obj); - } else { - obj = L.latLngBounds(obj); - } - - var sw = this._southWest, - ne = this._northEast, - sw2, ne2; - - if (obj instanceof L.LatLngBounds) { - sw2 = obj.getSouthWest(); - ne2 = obj.getNorthEast(); - } else { - sw2 = ne2 = obj; - } - - return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && - (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); - }, - - intersects: function (bounds) { // (LatLngBounds) - bounds = L.latLngBounds(bounds); - - var sw = this._southWest, - ne = this._northEast, - sw2 = bounds.getSouthWest(), - ne2 = bounds.getNorthEast(), - - latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), - lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); - - return latIntersects && lngIntersects; - }, - - toBBoxString: function () { - return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); - }, - - equals: function (bounds) { // (LatLngBounds) - if (!bounds) { return false; } - - bounds = L.latLngBounds(bounds); - - return this._southWest.equals(bounds.getSouthWest()) && - this._northEast.equals(bounds.getNorthEast()); - }, - - isValid: function () { - return !!(this._southWest && this._northEast); - } -}; - -//TODO International date line? - -L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng) - if (!a || a instanceof L.LatLngBounds) { - return a; - } - return new L.LatLngBounds(a, b); -}; - - -/* - * L.Projection contains various geographical projections used by CRS classes. - */ - -L.Projection = {}; - - -/* - * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default. - */ - -L.Projection.SphericalMercator = { - MAX_LATITUDE: 85.0511287798, - - project: function (latlng) { // (LatLng) -> Point - var d = L.LatLng.DEG_TO_RAD, - max = this.MAX_LATITUDE, - lat = Math.max(Math.min(max, latlng.lat), -max), - x = latlng.lng * d, - y = lat * d; - - y = Math.log(Math.tan((Math.PI / 4) + (y / 2))); - - return new L.Point(x, y); - }, - - unproject: function (point) { // (Point, Boolean) -> LatLng - var d = L.LatLng.RAD_TO_DEG, - lng = point.x * d, - lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d; - - return new L.LatLng(lat, lng); - } -}; - - -/* - * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple. - */ - -L.Projection.LonLat = { - project: function (latlng) { - return new L.Point(latlng.lng, latlng.lat); - }, - - unproject: function (point) { - return new L.LatLng(point.y, point.x); - } -}; - - -/* - * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet. - */ - -L.CRS = { - latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point - var projectedPoint = this.projection.project(latlng), - scale = this.scale(zoom); - - return this.transformation._transform(projectedPoint, scale); - }, - - pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng - var scale = this.scale(zoom), - untransformedPoint = this.transformation.untransform(point, scale); - - return this.projection.unproject(untransformedPoint); - }, - - project: function (latlng) { - return this.projection.project(latlng); - }, - - scale: function (zoom) { - return 256 * Math.pow(2, zoom); - } -}; - - -/* - * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps. - */ - -L.CRS.Simple = L.extend({}, L.CRS, { - projection: L.Projection.LonLat, - transformation: new L.Transformation(1, 0, -1, 0), - - scale: function (zoom) { - return Math.pow(2, zoom); - } -}); - - -/* - * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping - * and is used by Leaflet by default. - */ - -L.CRS.EPSG3857 = L.extend({}, L.CRS, { - code: 'EPSG:3857', - - projection: L.Projection.SphericalMercator, - transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5), - - project: function (latlng) { // (LatLng) -> Point - var projectedPoint = this.projection.project(latlng), - earthRadius = 6378137; - return projectedPoint.multiplyBy(earthRadius); - } -}); - -L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, { - code: 'EPSG:900913' -}); - - -/* - * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists. - */ - -L.CRS.EPSG4326 = L.extend({}, L.CRS, { - code: 'EPSG:4326', - - projection: L.Projection.LonLat, - transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5) -}); - - -/* - * L.Map is the central class of the API - it is used to create a map. - */ - -L.Map = L.Class.extend({ - - includes: L.Mixin.Events, - - options: { - crs: L.CRS.EPSG3857, - - /* - center: LatLng, - zoom: Number, - layers: Array, - */ - - fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23, - trackResize: true, - markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d - }, - - initialize: function (id, options) { // (HTMLElement or String, Object) - options = L.setOptions(this, options); - - - this._initContainer(id); - this._initLayout(); - - // hack for https://github.com/Leaflet/Leaflet/issues/1980 - this._onResize = L.bind(this._onResize, this); - - this._initEvents(); - - if (options.maxBounds) { - this.setMaxBounds(options.maxBounds); - } - - if (options.center && options.zoom !== undefined) { - this.setView(L.latLng(options.center), options.zoom, {reset: true}); - } - - this._handlers = []; - - this._layers = {}; - this._zoomBoundLayers = {}; - this._tileLayersNum = 0; - - this.callInitHooks(); - - this._addLayers(options.layers); - }, - - - // public methods that modify map state - - // replaced by animation-powered implementation in Map.PanAnimation.js - setView: function (center, zoom) { - zoom = zoom === undefined ? this.getZoom() : zoom; - this._resetView(L.latLng(center), this._limitZoom(zoom)); - return this; - }, - - setZoom: function (zoom, options) { - if (!this._loaded) { - this._zoom = this._limitZoom(zoom); - return this; - } - return this.setView(this.getCenter(), zoom, {zoom: options}); - }, - - zoomIn: function (delta, options) { - return this.setZoom(this._zoom + (delta || 1), options); - }, - - zoomOut: function (delta, options) { - return this.setZoom(this._zoom - (delta || 1), options); - }, - - setZoomAround: function (latlng, zoom, options) { - var scale = this.getZoomScale(zoom), - viewHalf = this.getSize().divideBy(2), - containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng), - - centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), - newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); - - return this.setView(newCenter, zoom, {zoom: options}); - }, - - fitBounds: function (bounds, options) { - - options = options || {}; - bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds); - - var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]), - paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]), - - zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)), - paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), - - swPoint = this.project(bounds.getSouthWest(), zoom), - nePoint = this.project(bounds.getNorthEast(), zoom), - center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); - - zoom = options && options.maxZoom ? Math.min(options.maxZoom, zoom) : zoom; - - return this.setView(center, zoom, options); - }, - - fitWorld: function (options) { - return this.fitBounds([[-90, -180], [90, 180]], options); - }, - - panTo: function (center, options) { // (LatLng) - return this.setView(center, this._zoom, {pan: options}); - }, - - panBy: function (offset) { // (Point) - // replaced with animated panBy in Map.Animation.js - this.fire('movestart'); - - this._rawPanBy(L.point(offset)); - - this.fire('move'); - return this.fire('moveend'); - }, - - setMaxBounds: function (bounds, options) { - bounds = L.latLngBounds(bounds); - - this.options.maxBounds = bounds; - - if (!bounds) { - this._boundsMinZoom = null; - this.off('moveend', this._panInsideMaxBounds, this); - return this; - } - - var minZoom = this.getBoundsZoom(bounds, true); - - this._boundsMinZoom = minZoom; - - if (this._loaded) { - if (this._zoom < minZoom) { - this.setView(bounds.getCenter(), minZoom, options); - } else { - this.panInsideBounds(bounds); - } - } - - this.on('moveend', this._panInsideMaxBounds, this); - - return this; - }, - - panInsideBounds: function (bounds) { - bounds = L.latLngBounds(bounds); - - var viewBounds = this.getPixelBounds(), - viewSw = viewBounds.getBottomLeft(), - viewNe = viewBounds.getTopRight(), - sw = this.project(bounds.getSouthWest()), - ne = this.project(bounds.getNorthEast()), - dx = 0, - dy = 0; - - if (viewNe.y < ne.y) { // north - dy = Math.ceil(ne.y - viewNe.y); - } - if (viewNe.x > ne.x) { // east - dx = Math.floor(ne.x - viewNe.x); - } - if (viewSw.y > sw.y) { // south - dy = Math.floor(sw.y - viewSw.y); - } - if (viewSw.x < sw.x) { // west - dx = Math.ceil(sw.x - viewSw.x); - } - - if (dx || dy) { - return this.panBy([dx, dy]); - } - - return this; - }, - - addLayer: function (layer) { - // TODO method is too big, refactor - - var id = L.stamp(layer); - - if (this._layers[id]) { return this; } - - this._layers[id] = layer; - - // TODO getMaxZoom, getMinZoom in ILayer (instead of options) - if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) { - this._zoomBoundLayers[id] = layer; - this._updateZoomLevels(); - } - - // TODO looks ugly, refactor!!! - if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { - this._tileLayersNum++; - this._tileLayersToLoad++; - layer.on('load', this._onTileLayerLoad, this); - } - - if (this._loaded) { - this._layerAdd(layer); - } - - return this; - }, - - removeLayer: function (layer) { - var id = L.stamp(layer); - - if (!this._layers[id]) { return this; } - - if (this._loaded) { - layer.onRemove(this); - } - - delete this._layers[id]; - - if (this._loaded) { - this.fire('layerremove', {layer: layer}); - } - - if (this._zoomBoundLayers[id]) { - delete this._zoomBoundLayers[id]; - this._updateZoomLevels(); - } - - // TODO looks ugly, refactor - if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { - this._tileLayersNum--; - this._tileLayersToLoad--; - layer.off('load', this._onTileLayerLoad, this); - } - - return this; - }, - - hasLayer: function (layer) { - if (!layer) { return false; } - - return (L.stamp(layer) in this._layers); - }, - - eachLayer: function (method, context) { - for (var i in this._layers) { - method.call(context, this._layers[i]); - } - return this; - }, - - invalidateSize: function (options) { - options = L.extend({ - animate: false, - pan: true - }, options === true ? {animate: true} : options); - - var oldSize = this.getSize(); - this._sizeChanged = true; - this._initialCenter = null; - - if (this.options.maxBounds) { - this.setMaxBounds(this.options.maxBounds); - } - - if (!this._loaded) { return this; } - - var newSize = this.getSize(), - oldCenter = oldSize.divideBy(2).round(), - newCenter = newSize.divideBy(2).round(), - offset = oldCenter.subtract(newCenter); - - if (!offset.x && !offset.y) { return this; } - - if (options.animate && options.pan) { - this.panBy(offset); - - } else { - if (options.pan) { - this._rawPanBy(offset); - } - - this.fire('move'); - - // make sure moveend is not fired too often on resize - clearTimeout(this._sizeTimer); - this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200); - } - - return this.fire('resize', { - oldSize: oldSize, - newSize: newSize - }); - }, - - // TODO handler.addTo - addHandler: function (name, HandlerClass) { - if (!HandlerClass) { return this; } - - var handler = this[name] = new HandlerClass(this); - - this._handlers.push(handler); - - if (this.options[name]) { - handler.enable(); - } - - return this; - }, - - remove: function () { - if (this._loaded) { - this.fire('unload'); - } - - this._initEvents('off'); - - try { - // throws error in IE6-8 - delete this._container._leaflet; - } catch (e) { - this._container._leaflet = undefined; - } - - this._clearPanes(); - if (this._clearControlPos) { - this._clearControlPos(); - } - - this._clearHandlers(); - - return this; - }, - - - // public methods for getting map state - - getCenter: function () { // (Boolean) -> LatLng - this._checkIfLoaded(); - - if (this._initialCenter && !this._moved()) { - return this._initialCenter; - } - return this.layerPointToLatLng(this._getCenterLayerPoint()); - }, - - getZoom: function () { - return this._zoom; - }, - - getBounds: function () { - var bounds = this.getPixelBounds(), - sw = this.unproject(bounds.getBottomLeft()), - ne = this.unproject(bounds.getTopRight()); - - return new L.LatLngBounds(sw, ne); - }, - - getMinZoom: function () { - var z1 = this._layersMinZoom === undefined ? 0 : this._layersMinZoom, - z2 = this._boundsMinZoom === undefined ? 0 : this._boundsMinZoom; - return this.options.minZoom === undefined ? Math.max(z1, z2) : this.options.minZoom; - }, - - getMaxZoom: function () { - return this.options.maxZoom === undefined ? - (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : - this.options.maxZoom; - }, - - getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number - bounds = L.latLngBounds(bounds); - - var zoom = this.getMinZoom() - (inside ? 1 : 0), - maxZoom = this.getMaxZoom(), - size = this.getSize(), - - nw = bounds.getNorthWest(), - se = bounds.getSouthEast(), - - zoomNotFound = true, - boundsSize; - - padding = L.point(padding || [0, 0]); - - do { - zoom++; - boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding); - zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y; - - } while (zoomNotFound && zoom <= maxZoom); - - if (zoomNotFound && inside) { - return null; - } - - return inside ? zoom : zoom - 1; - }, - - getSize: function () { - if (!this._size || this._sizeChanged) { - this._size = new L.Point( - this._container.clientWidth, - this._container.clientHeight); - - this._sizeChanged = false; - } - return this._size.clone(); - }, - - getPixelBounds: function () { - var topLeftPoint = this._getTopLeftPoint(); - return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); - }, - - getPixelOrigin: function () { - this._checkIfLoaded(); - return this._initialTopLeftPoint; - }, - - getPanes: function () { - return this._panes; - }, - - getContainer: function () { - return this._container; - }, - - - // TODO replace with universal implementation after refactoring projections - - getZoomScale: function (toZoom) { - var crs = this.options.crs; - return crs.scale(toZoom) / crs.scale(this._zoom); - }, - - getScaleZoom: function (scale) { - return this._zoom + (Math.log(scale) / Math.LN2); - }, - - - // conversion methods - - project: function (latlng, zoom) { // (LatLng[, Number]) -> Point - zoom = zoom === undefined ? this._zoom : zoom; - return this.options.crs.latLngToPoint(L.latLng(latlng), zoom); - }, - - unproject: function (point, zoom) { // (Point[, Number]) -> LatLng - zoom = zoom === undefined ? this._zoom : zoom; - return this.options.crs.pointToLatLng(L.point(point), zoom); - }, - - layerPointToLatLng: function (point) { // (Point) - var projectedPoint = L.point(point).add(this.getPixelOrigin()); - return this.unproject(projectedPoint); - }, - - latLngToLayerPoint: function (latlng) { // (LatLng) - var projectedPoint = this.project(L.latLng(latlng))._round(); - return projectedPoint._subtract(this.getPixelOrigin()); - }, - - containerPointToLayerPoint: function (point) { // (Point) - return L.point(point).subtract(this._getMapPanePos()); - }, - - layerPointToContainerPoint: function (point) { // (Point) - return L.point(point).add(this._getMapPanePos()); - }, - - containerPointToLatLng: function (point) { - var layerPoint = this.containerPointToLayerPoint(L.point(point)); - return this.layerPointToLatLng(layerPoint); - }, - - latLngToContainerPoint: function (latlng) { - return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng))); - }, - - mouseEventToContainerPoint: function (e) { // (MouseEvent) - return L.DomEvent.getMousePosition(e, this._container); - }, - - mouseEventToLayerPoint: function (e) { // (MouseEvent) - return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); - }, - - mouseEventToLatLng: function (e) { // (MouseEvent) - return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); - }, - - - // map initialization methods - - _initContainer: function (id) { - var container = this._container = L.DomUtil.get(id); - - if (!container) { - throw new Error('Map container not found.'); - } else if (container._leaflet) { - throw new Error('Map container is already initialized.'); - } - - container._leaflet = true; - }, - - _initLayout: function () { - var container = this._container; - - L.DomUtil.addClass(container, 'leaflet-container' + - (L.Browser.touch ? ' leaflet-touch' : '') + - (L.Browser.retina ? ' leaflet-retina' : '') + - (this.options.fadeAnimation ? ' leaflet-fade-anim' : '')); - - var position = L.DomUtil.getStyle(container, 'position'); - - if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { - container.style.position = 'relative'; - } - - this._initPanes(); - - if (this._initControlPos) { - this._initControlPos(); - } - }, - - _initPanes: function () { - var panes = this._panes = {}; - - this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container); - - this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane); - panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane); - panes.shadowPane = this._createPane('leaflet-shadow-pane'); - panes.overlayPane = this._createPane('leaflet-overlay-pane'); - panes.markerPane = this._createPane('leaflet-marker-pane'); - panes.popupPane = this._createPane('leaflet-popup-pane'); - - var zoomHide = ' leaflet-zoom-hide'; - - if (!this.options.markerZoomAnimation) { - L.DomUtil.addClass(panes.markerPane, zoomHide); - L.DomUtil.addClass(panes.shadowPane, zoomHide); - L.DomUtil.addClass(panes.popupPane, zoomHide); - } - }, - - _createPane: function (className, container) { - return L.DomUtil.create('div', className, container || this._panes.objectsPane); - }, - - _clearPanes: function () { - this._container.removeChild(this._mapPane); - }, - - _addLayers: function (layers) { - layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : []; - - for (var i = 0, len = layers.length; i < len; i++) { - this.addLayer(layers[i]); - } - }, - - - // private methods that modify map state - - _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) { - - var zoomChanged = (this._zoom !== zoom); - - if (!afterZoomAnim) { - this.fire('movestart'); - - if (zoomChanged) { - this.fire('zoomstart'); - } - } - - this._zoom = zoom; - this._initialCenter = center; - - this._initialTopLeftPoint = this._getNewTopLeftPoint(center); - - if (!preserveMapOffset) { - L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0)); - } else { - this._initialTopLeftPoint._add(this._getMapPanePos()); - } - - this._tileLayersToLoad = this._tileLayersNum; - - var loading = !this._loaded; - this._loaded = true; - - if (loading) { - this.fire('load'); - this.eachLayer(this._layerAdd, this); - } - - this.fire('viewreset', {hard: !preserveMapOffset}); - - this.fire('move'); - - if (zoomChanged || afterZoomAnim) { - this.fire('zoomend'); - } - - this.fire('moveend', {hard: !preserveMapOffset}); - }, - - _rawPanBy: function (offset) { - L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); - }, - - _getZoomSpan: function () { - return this.getMaxZoom() - this.getMinZoom(); - }, - - _updateZoomLevels: function () { - var i, - minZoom = Infinity, - maxZoom = -Infinity, - oldZoomSpan = this._getZoomSpan(); - - for (i in this._zoomBoundLayers) { - var layer = this._zoomBoundLayers[i]; - if (!isNaN(layer.options.minZoom)) { - minZoom = Math.min(minZoom, layer.options.minZoom); - } - if (!isNaN(layer.options.maxZoom)) { - maxZoom = Math.max(maxZoom, layer.options.maxZoom); - } - } - - if (i === undefined) { // we have no tilelayers - this._layersMaxZoom = this._layersMinZoom = undefined; - } else { - this._layersMaxZoom = maxZoom; - this._layersMinZoom = minZoom; - } - - if (oldZoomSpan !== this._getZoomSpan()) { - this.fire('zoomlevelschange'); - } - }, - - _panInsideMaxBounds: function () { - this.panInsideBounds(this.options.maxBounds); - }, - - _checkIfLoaded: function () { - if (!this._loaded) { - throw new Error('Set map center and zoom first.'); - } - }, - - // map events - - _initEvents: function (onOff) { - if (!L.DomEvent) { return; } - - onOff = onOff || 'on'; - - L.DomEvent[onOff](this._container, 'click', this._onMouseClick, this); - - var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter', - 'mouseleave', 'mousemove', 'contextmenu'], - i, len; - - for (i = 0, len = events.length; i < len; i++) { - L.DomEvent[onOff](this._container, events[i], this._fireMouseEvent, this); - } - - if (this.options.trackResize) { - L.DomEvent[onOff](window, 'resize', this._onResize, this); - } - }, - - _onResize: function () { - L.Util.cancelAnimFrame(this._resizeRequest); - this._resizeRequest = L.Util.requestAnimFrame( - this.invalidateSize, this, false, this._container); - }, - - _onMouseClick: function (e) { - if (!this._loaded || (!e._simulated && - ((this.dragging && this.dragging.moved()) || - (this.boxZoom && this.boxZoom.moved()))) || - L.DomEvent._skipped(e)) { return; } - - this.fire('preclick'); - this._fireMouseEvent(e); - }, - - _fireMouseEvent: function (e) { - if (!this._loaded || L.DomEvent._skipped(e)) { return; } - - var type = e.type; - - type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type)); - - if (!this.hasEventListeners(type)) { return; } - - if (type === 'contextmenu') { - L.DomEvent.preventDefault(e); - } - - var containerPoint = this.mouseEventToContainerPoint(e), - layerPoint = this.containerPointToLayerPoint(containerPoint), - latlng = this.layerPointToLatLng(layerPoint); - - this.fire(type, { - latlng: latlng, - layerPoint: layerPoint, - containerPoint: containerPoint, - originalEvent: e - }); - }, - - _onTileLayerLoad: function () { - this._tileLayersToLoad--; - if (this._tileLayersNum && !this._tileLayersToLoad) { - this.fire('tilelayersload'); - } - }, - - _clearHandlers: function () { - for (var i = 0, len = this._handlers.length; i < len; i++) { - this._handlers[i].disable(); - } - }, - - whenReady: function (callback, context) { - if (this._loaded) { - callback.call(context || this, this); - } else { - this.on('load', callback, context); - } - return this; - }, - - _layerAdd: function (layer) { - layer.onAdd(this); - this.fire('layeradd', {layer: layer}); - }, - - - // private methods for getting map state - - _getMapPanePos: function () { - return L.DomUtil.getPosition(this._mapPane); - }, - - _moved: function () { - var pos = this._getMapPanePos(); - return pos && !pos.equals([0, 0]); - }, - - _getTopLeftPoint: function () { - return this.getPixelOrigin().subtract(this._getMapPanePos()); - }, - - _getNewTopLeftPoint: function (center, zoom) { - var viewHalf = this.getSize()._divideBy(2); - // TODO round on display, not calculation to increase precision? - return this.project(center, zoom)._subtract(viewHalf)._round(); - }, - - _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) { - var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos()); - return this.project(latlng, newZoom)._subtract(topLeft); - }, - - // layer point of the current center - _getCenterLayerPoint: function () { - return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); - }, - - // offset of the specified place to the current center in pixels - _getCenterOffset: function (latlng) { - return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); - }, - - _limitZoom: function (zoom) { - var min = this.getMinZoom(), - max = this.getMaxZoom(); - - return Math.max(min, Math.min(max, zoom)); - } -}); - -L.map = function (id, options) { - return new L.Map(id, options); -}; - - -/* - * Mercator projection that takes into account that the Earth is not a perfect sphere. - * Less popular than spherical mercator; used by projections like EPSG:3395. - */ - -L.Projection.Mercator = { - MAX_LATITUDE: 85.0840591556, - - R_MINOR: 6356752.314245179, - R_MAJOR: 6378137, - - project: function (latlng) { // (LatLng) -> Point - var d = L.LatLng.DEG_TO_RAD, - max = this.MAX_LATITUDE, - lat = Math.max(Math.min(max, latlng.lat), -max), - r = this.R_MAJOR, - r2 = this.R_MINOR, - x = latlng.lng * d * r, - y = lat * d, - tmp = r2 / r, - eccent = Math.sqrt(1.0 - tmp * tmp), - con = eccent * Math.sin(y); - - con = Math.pow((1 - con) / (1 + con), eccent * 0.5); - - var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con; - y = -r * Math.log(ts); - - return new L.Point(x, y); - }, - - unproject: function (point) { // (Point, Boolean) -> LatLng - var d = L.LatLng.RAD_TO_DEG, - r = this.R_MAJOR, - r2 = this.R_MINOR, - lng = point.x * d / r, - tmp = r2 / r, - eccent = Math.sqrt(1 - (tmp * tmp)), - ts = Math.exp(- point.y / r), - phi = (Math.PI / 2) - 2 * Math.atan(ts), - numIter = 15, - tol = 1e-7, - i = numIter, - dphi = 0.1, - con; - - while ((Math.abs(dphi) > tol) && (--i > 0)) { - con = eccent * Math.sin(phi); - dphi = (Math.PI / 2) - 2 * Math.atan(ts * - Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi; - phi += dphi; - } - - return new L.LatLng(phi * d, lng); - } -}; - - - -L.CRS.EPSG3395 = L.extend({}, L.CRS, { - code: 'EPSG:3395', - - projection: L.Projection.Mercator, - - transformation: (function () { - var m = L.Projection.Mercator, - r = m.R_MAJOR, - scale = 0.5 / (Math.PI * r); - - return new L.Transformation(scale, 0.5, -scale, 0.5); - }()) -}); - - -/* - * L.TileLayer is used for standard xyz-numbered tile layers. - */ - -L.TileLayer = L.Class.extend({ - includes: L.Mixin.Events, - - options: { - minZoom: 0, - maxZoom: 18, - tileSize: 256, - subdomains: 'abc', - errorTileUrl: '', - attribution: '', - zoomOffset: 0, - opacity: 1, - /* - maxNativeZoom: null, - zIndex: null, - tms: false, - continuousWorld: false, - noWrap: false, - zoomReverse: false, - detectRetina: false, - reuseTiles: false, - bounds: false, - */ - unloadInvisibleTiles: L.Browser.mobile, - updateWhenIdle: L.Browser.mobile - }, - - initialize: function (url, options) { - options = L.setOptions(this, options); - - // detecting retina displays, adjusting tileSize and zoom levels - if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) { - - options.tileSize = Math.floor(options.tileSize / 2); - options.zoomOffset++; - - if (options.minZoom > 0) { - options.minZoom--; - } - this.options.maxZoom--; - } - - if (options.bounds) { - options.bounds = L.latLngBounds(options.bounds); - } - - this._url = url; - - var subdomains = this.options.subdomains; - - if (typeof subdomains === 'string') { - this.options.subdomains = subdomains.split(''); - } - }, - - onAdd: function (map) { - this._map = map; - this._animated = map._zoomAnimated; - - // create a container div for tiles - this._initContainer(); - - // set up events - map.on({ - 'viewreset': this._reset, - 'moveend': this._update - }, this); - - if (this._animated) { - map.on({ - 'zoomanim': this._animateZoom, - 'zoomend': this._endZoomAnim - }, this); - } - - if (!this.options.updateWhenIdle) { - this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this); - map.on('move', this._limitedUpdate, this); - } - - this._reset(); - this._update(); - }, - - addTo: function (map) { - map.addLayer(this); - return this; - }, - - onRemove: function (map) { - this._container.parentNode.removeChild(this._container); - - map.off({ - 'viewreset': this._reset, - 'moveend': this._update - }, this); - - if (this._animated) { - map.off({ - 'zoomanim': this._animateZoom, - 'zoomend': this._endZoomAnim - }, this); - } - - if (!this.options.updateWhenIdle) { - map.off('move', this._limitedUpdate, this); - } - - this._container = null; - this._map = null; - }, - - bringToFront: function () { - var pane = this._map._panes.tilePane; - - if (this._container) { - pane.appendChild(this._container); - this._setAutoZIndex(pane, Math.max); - } - - return this; - }, - - bringToBack: function () { - var pane = this._map._panes.tilePane; - - if (this._container) { - pane.insertBefore(this._container, pane.firstChild); - this._setAutoZIndex(pane, Math.min); - } - - return this; - }, - - getAttribution: function () { - return this.options.attribution; - }, - - getContainer: function () { - return this._container; - }, - - setOpacity: function (opacity) { - this.options.opacity = opacity; - - if (this._map) { - this._updateOpacity(); - } - - return this; - }, - - setZIndex: function (zIndex) { - this.options.zIndex = zIndex; - this._updateZIndex(); - - return this; - }, - - setUrl: function (url, noRedraw) { - this._url = url; - - if (!noRedraw) { - this.redraw(); - } - - return this; - }, - - redraw: function () { - if (this._map) { - this._reset({hard: true}); - this._update(); - } - return this; - }, - - _updateZIndex: function () { - if (this._container && this.options.zIndex !== undefined) { - this._container.style.zIndex = this.options.zIndex; - } - }, - - _setAutoZIndex: function (pane, compare) { - - var layers = pane.children, - edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min - zIndex, i, len; - - for (i = 0, len = layers.length; i < len; i++) { - - if (layers[i] !== this._container) { - zIndex = parseInt(layers[i].style.zIndex, 10); - - if (!isNaN(zIndex)) { - edgeZIndex = compare(edgeZIndex, zIndex); - } - } - } - - this.options.zIndex = this._container.style.zIndex = - (isFinite(edgeZIndex) ? edgeZIndex : 0) + compare(1, -1); - }, - - _updateOpacity: function () { - var i, - tiles = this._tiles; - - if (L.Browser.ielt9) { - for (i in tiles) { - L.DomUtil.setOpacity(tiles[i], this.options.opacity); - } - } else { - L.DomUtil.setOpacity(this._container, this.options.opacity); - } - }, - - _initContainer: function () { - var tilePane = this._map._panes.tilePane; - - if (!this._container) { - this._container = L.DomUtil.create('div', 'leaflet-layer'); - - this._updateZIndex(); - - if (this._animated) { - var className = 'leaflet-tile-container'; - - this._bgBuffer = L.DomUtil.create('div', className, this._container); - this._tileContainer = L.DomUtil.create('div', className, this._container); - - } else { - this._tileContainer = this._container; - } - - tilePane.appendChild(this._container); - - if (this.options.opacity < 1) { - this._updateOpacity(); - } - } - }, - - _reset: function (e) { - for (var key in this._tiles) { - this.fire('tileunload', {tile: this._tiles[key]}); - } - - this._tiles = {}; - this._tilesToLoad = 0; - - if (this.options.reuseTiles) { - this._unusedTiles = []; - } - - this._tileContainer.innerHTML = ''; - - if (this._animated && e && e.hard) { - this._clearBgBuffer(); - } - - this._initContainer(); - }, - - _getTileSize: function () { - var map = this._map, - zoom = map.getZoom(), - zoomN = this.options.maxNativeZoom, - tileSize = this.options.tileSize; - - if (zoomN && zoom > zoomN) { - tileSize = Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * tileSize); - } - - return tileSize; - }, - - _update: function () { - - if (!this._map) { return; } - - var map = this._map, - bounds = map.getPixelBounds(), - zoom = map.getZoom(), - tileSize = this._getTileSize(); - - if (zoom > this.options.maxZoom || zoom < this.options.minZoom) { - return; - } - - var tileBounds = L.bounds( - bounds.min.divideBy(tileSize)._floor(), - bounds.max.divideBy(tileSize)._floor()); - - this._addTilesFromCenterOut(tileBounds); - - if (this.options.unloadInvisibleTiles || this.options.reuseTiles) { - this._removeOtherTiles(tileBounds); - } - }, - - _addTilesFromCenterOut: function (bounds) { - var queue = [], - center = bounds.getCenter(); - - var j, i, point; - - for (j = bounds.min.y; j <= bounds.max.y; j++) { - for (i = bounds.min.x; i <= bounds.max.x; i++) { - point = new L.Point(i, j); - - if (this._tileShouldBeLoaded(point)) { - queue.push(point); - } - } - } - - var tilesToLoad = queue.length; - - if (tilesToLoad === 0) { return; } - - // load tiles in order of their distance to center - queue.sort(function (a, b) { - return a.distanceTo(center) - b.distanceTo(center); - }); - - var fragment = document.createDocumentFragment(); - - // if its the first batch of tiles to load - if (!this._tilesToLoad) { - this.fire('loading'); - } - - this._tilesToLoad += tilesToLoad; - - for (i = 0; i < tilesToLoad; i++) { - this._addTile(queue[i], fragment); - } - - this._tileContainer.appendChild(fragment); - }, - - _tileShouldBeLoaded: function (tilePoint) { - if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) { - return false; // already loaded - } - - var options = this.options; - - if (!options.continuousWorld) { - var limit = this._getWrapTileNum(); - - // don't load if exceeds world bounds - if ((options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit)) || - tilePoint.y < 0 || tilePoint.y >= limit) { return false; } - } - - if (options.bounds) { - var tileSize = options.tileSize, - nwPoint = tilePoint.multiplyBy(tileSize), - sePoint = nwPoint.add([tileSize, tileSize]), - nw = this._map.unproject(nwPoint), - se = this._map.unproject(sePoint); - - // TODO temporary hack, will be removed after refactoring projections - // https://github.com/Leaflet/Leaflet/issues/1618 - if (!options.continuousWorld && !options.noWrap) { - nw = nw.wrap(); - se = se.wrap(); - } - - if (!options.bounds.intersects([nw, se])) { return false; } - } - - return true; - }, - - _removeOtherTiles: function (bounds) { - var kArr, x, y, key; - - for (key in this._tiles) { - kArr = key.split(':'); - x = parseInt(kArr[0], 10); - y = parseInt(kArr[1], 10); - - // remove tile if it's out of bounds - if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) { - this._removeTile(key); - } - } - }, - - _removeTile: function (key) { - var tile = this._tiles[key]; - - this.fire('tileunload', {tile: tile, url: tile.src}); - - if (this.options.reuseTiles) { - L.DomUtil.removeClass(tile, 'leaflet-tile-loaded'); - this._unusedTiles.push(tile); - - } else if (tile.parentNode === this._tileContainer) { - this._tileContainer.removeChild(tile); - } - - // for https://github.com/CloudMade/Leaflet/issues/137 - if (!L.Browser.android) { - tile.onload = null; - tile.src = L.Util.emptyImageUrl; - } - - delete this._tiles[key]; - }, - - _addTile: function (tilePoint, container) { - var tilePos = this._getTilePos(tilePoint); - - // get unused tile - or create a new tile - var tile = this._getTile(); - - /* - Chrome 20 layouts much faster with top/left (verify with timeline, frames) - Android 4 browser has display issues with top/left and requires transform instead - Android 2 browser requires top/left or tiles disappear on load or first drag - (reappear after zoom) https://github.com/CloudMade/Leaflet/issues/866 - (other browsers don't currently care) - see debug/hacks/jitter.html for an example - */ - L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome || L.Browser.android23); - - this._tiles[tilePoint.x + ':' + tilePoint.y] = tile; - - this._loadTile(tile, tilePoint); - - if (tile.parentNode !== this._tileContainer) { - container.appendChild(tile); - } - }, - - _getZoomForUrl: function () { - - var options = this.options, - zoom = this._map.getZoom(); - - if (options.zoomReverse) { - zoom = options.maxZoom - zoom; - } - - zoom += options.zoomOffset; - - return options.maxNativeZoom ? Math.min(zoom, options.maxNativeZoom) : zoom; - }, - - _getTilePos: function (tilePoint) { - var origin = this._map.getPixelOrigin(), - tileSize = this._getTileSize(); - - return tilePoint.multiplyBy(tileSize).subtract(origin); - }, - - // image-specific code (override to implement e.g. Canvas or SVG tile layer) - - getTileUrl: function (tilePoint) { - return L.Util.template(this._url, L.extend({ - s: this._getSubdomain(tilePoint), - z: tilePoint.z, - x: tilePoint.x, - y: tilePoint.y - }, this.options)); - }, - - _getWrapTileNum: function () { - // TODO refactor, limit is not valid for non-standard projections - return Math.pow(2, this._getZoomForUrl()); - }, - - _adjustTilePoint: function (tilePoint) { - - var limit = this._getWrapTileNum(); - - // wrap tile coordinates - if (!this.options.continuousWorld && !this.options.noWrap) { - tilePoint.x = ((tilePoint.x % limit) + limit) % limit; - } - - if (this.options.tms) { - tilePoint.y = limit - tilePoint.y - 1; - } - - tilePoint.z = this._getZoomForUrl(); - }, - - _getSubdomain: function (tilePoint) { - var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; - return this.options.subdomains[index]; - }, - - _getTile: function () { - if (this.options.reuseTiles && this._unusedTiles.length > 0) { - var tile = this._unusedTiles.pop(); - this._resetTile(tile); - return tile; - } - return this._createTile(); - }, - - // Override if data stored on a tile needs to be cleaned up before reuse - _resetTile: function (/*tile*/) {}, - - _createTile: function () { - var tile = L.DomUtil.create('img', 'leaflet-tile'); - tile.style.width = tile.style.height = this._getTileSize() + 'px'; - tile.galleryimg = 'no'; - - tile.onselectstart = tile.onmousemove = L.Util.falseFn; - - if (L.Browser.ielt9 && this.options.opacity !== undefined) { - L.DomUtil.setOpacity(tile, this.options.opacity); - } - return tile; - }, - - _loadTile: function (tile, tilePoint) { - tile._layer = this; - tile.onload = this._tileOnLoad; - tile.onerror = this._tileOnError; - - this._adjustTilePoint(tilePoint); - tile.src = this.getTileUrl(tilePoint); - - this.fire('tileloadstart', { - tile: tile, - url: tile.src - }); - }, - - _tileLoaded: function () { - this._tilesToLoad--; - - if (this._animated) { - L.DomUtil.addClass(this._tileContainer, 'leaflet-zoom-animated'); - } - - if (!this._tilesToLoad) { - this.fire('load'); - - if (this._animated) { - // clear scaled tiles after all new tiles are loaded (for performance) - clearTimeout(this._clearBgBufferTimer); - this._clearBgBufferTimer = setTimeout(L.bind(this._clearBgBuffer, this), 500); - } - } - }, - - _tileOnLoad: function () { - var layer = this._layer; - - //Only if we are loading an actual image - if (this.src !== L.Util.emptyImageUrl) { - L.DomUtil.addClass(this, 'leaflet-tile-loaded'); - - layer.fire('tileload', { - tile: this, - url: this.src - }); - } - - layer._tileLoaded(); - }, - - _tileOnError: function () { - var layer = this._layer; - - layer.fire('tileerror', { - tile: this, - url: this.src - }); - - var newUrl = layer.options.errorTileUrl; - if (newUrl) { - this.src = newUrl; - } - - layer._tileLoaded(); - } -}); - -L.tileLayer = function (url, options) { - return new L.TileLayer(url, options); -}; - - -/* - * L.TileLayer.WMS is used for putting WMS tile layers on the map. - */ - -L.TileLayer.WMS = L.TileLayer.extend({ - - defaultWmsParams: { - service: 'WMS', - request: 'GetMap', - version: '1.1.1', - layers: '', - styles: '', - format: 'image/jpeg', - transparent: false - }, - - initialize: function (url, options) { // (String, Object) - - this._url = url; - - var wmsParams = L.extend({}, this.defaultWmsParams), - tileSize = options.tileSize || this.options.tileSize; - - if (options.detectRetina && L.Browser.retina) { - wmsParams.width = wmsParams.height = tileSize * 2; - } else { - wmsParams.width = wmsParams.height = tileSize; - } - - for (var i in options) { - // all keys that are not TileLayer options go to WMS params - if (!this.options.hasOwnProperty(i) && i !== 'crs') { - wmsParams[i] = options[i]; - } - } - - this.wmsParams = wmsParams; - - L.setOptions(this, options); - }, - - onAdd: function (map) { - - this._crs = this.options.crs || map.options.crs; - - this._wmsVersion = parseFloat(this.wmsParams.version); - - var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; - this.wmsParams[projectionKey] = this._crs.code; - - L.TileLayer.prototype.onAdd.call(this, map); - }, - - getTileUrl: function (tilePoint) { // (Point, Number) -> String - - var map = this._map, - tileSize = this.options.tileSize, - - nwPoint = tilePoint.multiplyBy(tileSize), - sePoint = nwPoint.add([tileSize, tileSize]), - - nw = this._crs.project(map.unproject(nwPoint, tilePoint.z)), - se = this._crs.project(map.unproject(sePoint, tilePoint.z)), - bbox = this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ? - [se.y, nw.x, nw.y, se.x].join(',') : - [nw.x, se.y, se.x, nw.y].join(','), - - url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)}); - - return url + L.Util.getParamString(this.wmsParams, url, true) + '&BBOX=' + bbox; - }, - - setParams: function (params, noRedraw) { - - L.extend(this.wmsParams, params); - - if (!noRedraw) { - this.redraw(); - } - - return this; - } -}); - -L.tileLayer.wms = function (url, options) { - return new L.TileLayer.WMS(url, options); -}; - - -/* - * L.TileLayer.Canvas is a class that you can use as a base for creating - * dynamically drawn Canvas-based tile layers. - */ - -L.TileLayer.Canvas = L.TileLayer.extend({ - options: { - async: false - }, - - initialize: function (options) { - L.setOptions(this, options); - }, - - redraw: function () { - if (this._map) { - this._reset({hard: true}); - this._update(); - } - - for (var i in this._tiles) { - this._redrawTile(this._tiles[i]); - } - return this; - }, - - _redrawTile: function (tile) { - this.drawTile(tile, tile._tilePoint, this._map._zoom); - }, - - _createTile: function () { - var tile = L.DomUtil.create('canvas', 'leaflet-tile'); - tile.width = tile.height = this.options.tileSize; - tile.onselectstart = tile.onmousemove = L.Util.falseFn; - return tile; - }, - - _loadTile: function (tile, tilePoint) { - tile._layer = this; - tile._tilePoint = tilePoint; - - this._redrawTile(tile); - - if (!this.options.async) { - this.tileDrawn(tile); - } - }, - - drawTile: function (/*tile, tilePoint*/) { - // override with rendering code - }, - - tileDrawn: function (tile) { - this._tileOnLoad.call(tile); - } -}); - - -L.tileLayer.canvas = function (options) { - return new L.TileLayer.Canvas(options); -}; - - -/* - * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds). - */ - -L.ImageOverlay = L.Class.extend({ - includes: L.Mixin.Events, - - options: { - opacity: 1 - }, - - initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) - this._url = url; - this._bounds = L.latLngBounds(bounds); - - L.setOptions(this, options); - }, - - onAdd: function (map) { - this._map = map; - - if (!this._image) { - this._initImage(); - } - - map._panes.overlayPane.appendChild(this._image); - - map.on('viewreset', this._reset, this); - - if (map.options.zoomAnimation && L.Browser.any3d) { - map.on('zoomanim', this._animateZoom, this); - } - - this._reset(); - }, - - onRemove: function (map) { - map.getPanes().overlayPane.removeChild(this._image); - - map.off('viewreset', this._reset, this); - - if (map.options.zoomAnimation) { - map.off('zoomanim', this._animateZoom, this); - } - }, - - addTo: function (map) { - map.addLayer(this); - return this; - }, - - setOpacity: function (opacity) { - this.options.opacity = opacity; - this._updateOpacity(); - return this; - }, - - // TODO remove bringToFront/bringToBack duplication from TileLayer/Path - bringToFront: function () { - if (this._image) { - this._map._panes.overlayPane.appendChild(this._image); - } - return this; - }, - - bringToBack: function () { - var pane = this._map._panes.overlayPane; - if (this._image) { - pane.insertBefore(this._image, pane.firstChild); - } - return this; - }, - - setUrl: function (url) { - this._url = url; - this._image.src = this._url; - }, - - getAttribution: function () { - return this.options.attribution; - }, - - _initImage: function () { - this._image = L.DomUtil.create('img', 'leaflet-image-layer'); - - if (this._map.options.zoomAnimation && L.Browser.any3d) { - L.DomUtil.addClass(this._image, 'leaflet-zoom-animated'); - } else { - L.DomUtil.addClass(this._image, 'leaflet-zoom-hide'); - } - - this._updateOpacity(); - - //TODO createImage util method to remove duplication - L.extend(this._image, { - galleryimg: 'no', - onselectstart: L.Util.falseFn, - onmousemove: L.Util.falseFn, - onload: L.bind(this._onImageLoad, this), - src: this._url - }); - }, - - _animateZoom: function (e) { - var map = this._map, - image = this._image, - scale = map.getZoomScale(e.zoom), - nw = this._bounds.getNorthWest(), - se = this._bounds.getSouthEast(), - - topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center), - size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft), - origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale))); - - image.style[L.DomUtil.TRANSFORM] = - L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') '; - }, - - _reset: function () { - var image = this._image, - topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()), - size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft); - - L.DomUtil.setPosition(image, topLeft); - - image.style.width = size.x + 'px'; - image.style.height = size.y + 'px'; - }, - - _onImageLoad: function () { - this.fire('load'); - }, - - _updateOpacity: function () { - L.DomUtil.setOpacity(this._image, this.options.opacity); - } -}); - -L.imageOverlay = function (url, bounds, options) { - return new L.ImageOverlay(url, bounds, options); -}; - - -/* - * L.Icon is an image-based icon class that you can use with L.Marker for custom markers. - */ - -L.Icon = L.Class.extend({ - options: { - /* - iconUrl: (String) (required) - iconRetinaUrl: (String) (optional, used for retina devices if detected) - iconSize: (Point) (can be set through CSS) - iconAnchor: (Point) (centered by default, can be set in CSS with negative margins) - popupAnchor: (Point) (if not specified, popup opens in the anchor point) - shadowUrl: (String) (no shadow by default) - shadowRetinaUrl: (String) (optional, used for retina devices if detected) - shadowSize: (Point) - shadowAnchor: (Point) - */ - className: '' - }, - - initialize: function (options) { - L.setOptions(this, options); - }, - - createIcon: function (oldIcon) { - return this._createIcon('icon', oldIcon); - }, - - createShadow: function (oldIcon) { - return this._createIcon('shadow', oldIcon); - }, - - _createIcon: function (name, oldIcon) { - var src = this._getIconUrl(name); - - if (!src) { - if (name === 'icon') { - throw new Error('iconUrl not set in Icon options (see the docs).'); - } - return null; - } - - var img; - if (!oldIcon || oldIcon.tagName !== 'IMG') { - img = this._createImg(src); - } else { - img = this._createImg(src, oldIcon); - } - this._setIconStyles(img, name); - - return img; - }, - - _setIconStyles: function (img, name) { - var options = this.options, - size = L.point(options[name + 'Size']), - anchor; - - if (name === 'shadow') { - anchor = L.point(options.shadowAnchor || options.iconAnchor); - } else { - anchor = L.point(options.iconAnchor); - } - - if (!anchor && size) { - anchor = size.divideBy(2, true); - } - - img.className = 'leaflet-marker-' + name + ' ' + options.className; - - if (anchor) { - img.style.marginLeft = (-anchor.x) + 'px'; - img.style.marginTop = (-anchor.y) + 'px'; - } - - if (size) { - img.style.width = size.x + 'px'; - img.style.height = size.y + 'px'; - } - }, - - _createImg: function (src, el) { - - if (!L.Browser.ie6) { - if (!el) { - el = document.createElement('img'); - } - el.src = src; - } else { - if (!el) { - el = document.createElement('div'); - } - el.style.filter = - 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + src + '")'; - } - return el; - }, - - _getIconUrl: function (name) { - if (L.Browser.retina && this.options[name + 'RetinaUrl']) { - return this.options[name + 'RetinaUrl']; - } - return this.options[name + 'Url']; - } -}); - -L.icon = function (options) { - return new L.Icon(options); -}; - - -/* - * L.Icon.Default is the blue marker icon used by default in Leaflet. - */ - -L.Icon.Default = L.Icon.extend({ - - options: { - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - - shadowSize: [41, 41] - }, - - _getIconUrl: function (name) { - var key = name + 'Url'; - - if (this.options[key]) { - return this.options[key]; - } - - if (L.Browser.retina && name === 'icon') { - name += '-2x'; - } - - var path = L.Icon.Default.imagePath; - - if (!path) { - throw new Error('Couldn\'t autodetect L.Icon.Default.imagePath, set it manually.'); - } - - return path + '/marker-' + name + '.png'; - } -}); - -L.Icon.Default.imagePath = (function () { - var scripts = document.getElementsByTagName('script'), - leafletRe = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/; - - var i, len, src, matches, path; - - for (i = 0, len = scripts.length; i < len; i++) { - src = scripts[i].src; - matches = src.match(leafletRe); - - if (matches) { - path = src.split(leafletRe)[0]; - return (path ? path + '/' : '') + 'images'; - } - } -}()); - - -/* - * L.Marker is used to display clickable/draggable icons on the map. - */ - -L.Marker = L.Class.extend({ - - includes: L.Mixin.Events, - - options: { - icon: new L.Icon.Default(), - title: '', - alt: '', - clickable: true, - draggable: false, - keyboard: true, - zIndexOffset: 0, - opacity: 1, - riseOnHover: false, - riseOffset: 250 - }, - - initialize: function (latlng, options) { - L.setOptions(this, options); - this._latlng = L.latLng(latlng); - }, - - onAdd: function (map) { - this._map = map; - - map.on('viewreset', this.update, this); - - this._initIcon(); - this.update(); - this.fire('add'); - - if (map.options.zoomAnimation && map.options.markerZoomAnimation) { - map.on('zoomanim', this._animateZoom, this); - } - }, - - addTo: function (map) { - map.addLayer(this); - return this; - }, - - onRemove: function (map) { - if (this.dragging) { - this.dragging.disable(); - } - - this._removeIcon(); - this._removeShadow(); - - this.fire('remove'); - - map.off({ - 'viewreset': this.update, - 'zoomanim': this._animateZoom - }, this); - - this._map = null; - }, - - getLatLng: function () { - return this._latlng; - }, - - setLatLng: function (latlng) { - this._latlng = L.latLng(latlng); - - this.update(); - - return this.fire('move', { latlng: this._latlng }); - }, - - setZIndexOffset: function (offset) { - this.options.zIndexOffset = offset; - this.update(); - - return this; - }, - - setIcon: function (icon) { - - this.options.icon = icon; - - if (this._map) { - this._initIcon(); - this.update(); - } - - if (this._popup) { - this.bindPopup(this._popup); - } - - return this; - }, - - update: function () { - if (this._icon) { - var pos = this._map.latLngToLayerPoint(this._latlng).round(); - this._setPos(pos); - } - - return this; - }, - - _initIcon: function () { - var options = this.options, - map = this._map, - animation = (map.options.zoomAnimation && map.options.markerZoomAnimation), - classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-hide'; - - var icon = options.icon.createIcon(this._icon), - addIcon = false; - - // if we're not reusing the icon, remove the old one and init new one - if (icon !== this._icon) { - if (this._icon) { - this._removeIcon(); - } - addIcon = true; - - if (options.title) { - icon.title = options.title; - } - - if (options.alt) { - icon.alt = options.alt; - } - } - - L.DomUtil.addClass(icon, classToAdd); - - if (options.keyboard) { - icon.tabIndex = '0'; - } - - this._icon = icon; - - this._initInteraction(); - - if (options.riseOnHover) { - L.DomEvent - .on(icon, 'mouseover', this._bringToFront, this) - .on(icon, 'mouseout', this._resetZIndex, this); - } - - var newShadow = options.icon.createShadow(this._shadow), - addShadow = false; - - if (newShadow !== this._shadow) { - this._removeShadow(); - addShadow = true; - } - - if (newShadow) { - L.DomUtil.addClass(newShadow, classToAdd); - } - this._shadow = newShadow; - - - if (options.opacity < 1) { - this._updateOpacity(); - } - - - var panes = this._map._panes; - - if (addIcon) { - panes.markerPane.appendChild(this._icon); - } - - if (newShadow && addShadow) { - panes.shadowPane.appendChild(this._shadow); - } - }, - - _removeIcon: function () { - if (this.options.riseOnHover) { - L.DomEvent - .off(this._icon, 'mouseover', this._bringToFront) - .off(this._icon, 'mouseout', this._resetZIndex); - } - - this._map._panes.markerPane.removeChild(this._icon); - - this._icon = null; - }, - - _removeShadow: function () { - if (this._shadow) { - this._map._panes.shadowPane.removeChild(this._shadow); - } - this._shadow = null; - }, - - _setPos: function (pos) { - L.DomUtil.setPosition(this._icon, pos); - - if (this._shadow) { - L.DomUtil.setPosition(this._shadow, pos); - } - - this._zIndex = pos.y + this.options.zIndexOffset; - - this._resetZIndex(); - }, - - _updateZIndex: function (offset) { - this._icon.style.zIndex = this._zIndex + offset; - }, - - _animateZoom: function (opt) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); - - this._setPos(pos); - }, - - _initInteraction: function () { - - if (!this.options.clickable) { return; } - - // TODO refactor into something shared with Map/Path/etc. to DRY it up - - var icon = this._icon, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; - - L.DomUtil.addClass(icon, 'leaflet-clickable'); - L.DomEvent.on(icon, 'click', this._onMouseClick, this); - L.DomEvent.on(icon, 'keypress', this._onKeyPress, this); - - for (var i = 0; i < events.length; i++) { - L.DomEvent.on(icon, events[i], this._fireMouseEvent, this); - } - - if (L.Handler.MarkerDrag) { - this.dragging = new L.Handler.MarkerDrag(this); - - if (this.options.draggable) { - this.dragging.enable(); - } - } - }, - - _onMouseClick: function (e) { - var wasDragged = this.dragging && this.dragging.moved(); - - if (this.hasEventListeners(e.type) || wasDragged) { - L.DomEvent.stopPropagation(e); - } - - if (wasDragged) { return; } - - if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; } - - this.fire(e.type, { - originalEvent: e, - latlng: this._latlng - }); - }, - - _onKeyPress: function (e) { - if (e.keyCode === 13) { - this.fire('click', { - originalEvent: e, - latlng: this._latlng - }); - } - }, - - _fireMouseEvent: function (e) { - - this.fire(e.type, { - originalEvent: e, - latlng: this._latlng - }); - - // TODO proper custom event propagation - // this line will always be called if marker is in a FeatureGroup - if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) { - L.DomEvent.preventDefault(e); - } - if (e.type !== 'mousedown') { - L.DomEvent.stopPropagation(e); - } else { - L.DomEvent.preventDefault(e); - } - }, - - setOpacity: function (opacity) { - this.options.opacity = opacity; - if (this._map) { - this._updateOpacity(); - } - - return this; - }, - - _updateOpacity: function () { - L.DomUtil.setOpacity(this._icon, this.options.opacity); - if (this._shadow) { - L.DomUtil.setOpacity(this._shadow, this.options.opacity); - } - }, - - _bringToFront: function () { - this._updateZIndex(this.options.riseOffset); - }, - - _resetZIndex: function () { - this._updateZIndex(0); - } -}); - -L.marker = function (latlng, options) { - return new L.Marker(latlng, options); -}; - - -/* - * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon) - * to use with L.Marker. - */ - -L.DivIcon = L.Icon.extend({ - options: { - iconSize: [12, 12], // also can be set through CSS - /* - iconAnchor: (Point) - popupAnchor: (Point) - html: (String) - bgPos: (Point) - */ - className: 'leaflet-div-icon', - html: false - }, - - createIcon: function (oldIcon) { - var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), - options = this.options; - - if (options.html !== false) { - div.innerHTML = options.html; - } else { - div.innerHTML = ''; - } - - if (options.bgPos) { - div.style.backgroundPosition = - (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px'; - } - - this._setIconStyles(div, 'icon'); - return div; - }, - - createShadow: function () { - return null; - } -}); - -L.divIcon = function (options) { - return new L.DivIcon(options); -}; - - -/* - * L.Popup is used for displaying popups on the map. - */ - -L.Map.mergeOptions({ - closePopupOnClick: true -}); - -L.Popup = L.Class.extend({ - includes: L.Mixin.Events, - - options: { - minWidth: 50, - maxWidth: 300, - // maxHeight: null, - autoPan: true, - closeButton: true, - offset: [0, 7], - autoPanPadding: [5, 5], - // autoPanPaddingTopLeft: null, - // autoPanPaddingBottomRight: null, - keepInView: false, - className: '', - zoomAnimation: true - }, - - initialize: function (options, source) { - L.setOptions(this, options); - - this._source = source; - this._animated = L.Browser.any3d && this.options.zoomAnimation; - this._isOpen = false; - }, - - onAdd: function (map) { - this._map = map; - - if (!this._container) { - this._initLayout(); - } - - var animFade = map.options.fadeAnimation; - - if (animFade) { - L.DomUtil.setOpacity(this._container, 0); - } - map._panes.popupPane.appendChild(this._container); - - map.on(this._getEvents(), this); - - this.update(); - - if (animFade) { - L.DomUtil.setOpacity(this._container, 1); - } - - this.fire('open'); - - map.fire('popupopen', {popup: this}); - - if (this._source) { - this._source.fire('popupopen', {popup: this}); - } - }, - - addTo: function (map) { - map.addLayer(this); - return this; - }, - - openOn: function (map) { - map.openPopup(this); - return this; - }, - - onRemove: function (map) { - map._panes.popupPane.removeChild(this._container); - - L.Util.falseFn(this._container.offsetWidth); // force reflow - - map.off(this._getEvents(), this); - - if (map.options.fadeAnimation) { - L.DomUtil.setOpacity(this._container, 0); - } - - this._map = null; - - this.fire('close'); - - map.fire('popupclose', {popup: this}); - - if (this._source) { - this._source.fire('popupclose', {popup: this}); - } - }, - - getLatLng: function () { - return this._latlng; - }, - - setLatLng: function (latlng) { - this._latlng = L.latLng(latlng); - this.update(); - return this; - }, - - getContent: function () { - return this._content; - }, - - setContent: function (content) { - this._content = content; - this.update(); - return this; - }, - - update: function () { - if (!this._map) { return; } - - this._container.style.visibility = 'hidden'; - - this._updateContent(); - this._updateLayout(); - this._updatePosition(); - - this._container.style.visibility = ''; - - this._adjustPan(); - }, - - _getEvents: function () { - var events = { - viewreset: this._updatePosition - }; - - if (this._animated) { - events.zoomanim = this._zoomAnimation; - } - if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) { - events.preclick = this._close; - } - if (this.options.keepInView) { - events.moveend = this._adjustPan; - } - - return events; - }, - - _close: function () { - if (this._map) { - this._map.closePopup(this); - } - }, - - _initLayout: function () { - var prefix = 'leaflet-popup', - containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' + - (this._animated ? 'animated' : 'hide'), - container = this._container = L.DomUtil.create('div', containerClass), - closeButton; - - if (this.options.closeButton) { - closeButton = this._closeButton = - L.DomUtil.create('a', prefix + '-close-button', container); - closeButton.href = '#close'; - closeButton.innerHTML = '×'; - L.DomEvent.disableClickPropagation(closeButton); - - L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this); - } - - var wrapper = this._wrapper = - L.DomUtil.create('div', prefix + '-content-wrapper', container); - L.DomEvent.disableClickPropagation(wrapper); - - this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper); - - L.DomEvent.disableScrollPropagation(this._contentNode); - L.DomEvent.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation); - - this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container); - this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer); - }, - - _updateContent: function () { - if (!this._content) { return; } - - if (typeof this._content === 'string') { - this._contentNode.innerHTML = this._content; - } else { - while (this._contentNode.hasChildNodes()) { - this._contentNode.removeChild(this._contentNode.firstChild); - } - this._contentNode.appendChild(this._content); - } - this.fire('contentupdate'); - }, - - _updateLayout: function () { - var container = this._contentNode, - style = container.style; - - style.width = ''; - style.whiteSpace = 'nowrap'; - - var width = container.offsetWidth; - width = Math.min(width, this.options.maxWidth); - width = Math.max(width, this.options.minWidth); - - style.width = (width + 1) + 'px'; - style.whiteSpace = ''; - - style.height = ''; - - var height = container.offsetHeight, - maxHeight = this.options.maxHeight, - scrolledClass = 'leaflet-popup-scrolled'; - - if (maxHeight && height > maxHeight) { - style.height = maxHeight + 'px'; - L.DomUtil.addClass(container, scrolledClass); - } else { - L.DomUtil.removeClass(container, scrolledClass); - } - - this._containerWidth = this._container.offsetWidth; - }, - - _updatePosition: function () { - if (!this._map) { return; } - - var pos = this._map.latLngToLayerPoint(this._latlng), - animated = this._animated, - offset = L.point(this.options.offset); - - if (animated) { - L.DomUtil.setPosition(this._container, pos); - } - - this._containerBottom = -offset.y - (animated ? 0 : pos.y); - this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x); - - // bottom position the popup in case the height of the popup changes (images loading etc) - this._container.style.bottom = this._containerBottom + 'px'; - this._container.style.left = this._containerLeft + 'px'; - }, - - _zoomAnimation: function (opt) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center); - - L.DomUtil.setPosition(this._container, pos); - }, - - _adjustPan: function () { - if (!this.options.autoPan) { return; } - - var map = this._map, - containerHeight = this._container.offsetHeight, - containerWidth = this._containerWidth, - - layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom); - - if (this._animated) { - layerPos._add(L.DomUtil.getPosition(this._container)); - } - - var containerPos = map.layerPointToContainerPoint(layerPos), - padding = L.point(this.options.autoPanPadding), - paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding), - paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding), - size = map.getSize(), - dx = 0, - dy = 0; - - if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right - dx = containerPos.x + containerWidth - size.x + paddingBR.x; - } - if (containerPos.x - dx - paddingTL.x < 0) { // left - dx = containerPos.x - paddingTL.x; - } - if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom - dy = containerPos.y + containerHeight - size.y + paddingBR.y; - } - if (containerPos.y - dy - paddingTL.y < 0) { // top - dy = containerPos.y - paddingTL.y; - } - - if (dx || dy) { - map - .fire('autopanstart') - .panBy([dx, dy]); - } - }, - - _onCloseButtonClick: function (e) { - this._close(); - L.DomEvent.stop(e); - } -}); - -L.popup = function (options, source) { - return new L.Popup(options, source); -}; - - -L.Map.include({ - openPopup: function (popup, latlng, options) { // (Popup) or (String || HTMLElement, LatLng[, Object]) - this.closePopup(); - - if (!(popup instanceof L.Popup)) { - var content = popup; - - popup = new L.Popup(options) - .setLatLng(latlng) - .setContent(content); - } - popup._isOpen = true; - - this._popup = popup; - return this.addLayer(popup); - }, - - closePopup: function (popup) { - if (!popup || popup === this._popup) { - popup = this._popup; - this._popup = null; - } - if (popup) { - this.removeLayer(popup); - popup._isOpen = false; - } - return this; - } -}); - - -/* - * Popup extension to L.Marker, adding popup-related methods. - */ - -L.Marker.include({ - openPopup: function () { - if (this._popup && this._map && !this._map.hasLayer(this._popup)) { - this._popup.setLatLng(this._latlng); - this._map.openPopup(this._popup); - } - - return this; - }, - - closePopup: function () { - if (this._popup) { - this._popup._close(); - } - return this; - }, - - togglePopup: function () { - if (this._popup) { - if (this._popup._isOpen) { - this.closePopup(); - } else { - this.openPopup(); - } - } - return this; - }, - - bindPopup: function (content, options) { - var anchor = L.point(this.options.icon.options.popupAnchor || [0, 0]); - - anchor = anchor.add(L.Popup.prototype.options.offset); - - if (options && options.offset) { - anchor = anchor.add(options.offset); - } - - options = L.extend({offset: anchor}, options); - - if (!this._popupHandlersAdded) { - this - .on('click', this.togglePopup, this) - .on('remove', this.closePopup, this) - .on('move', this._movePopup, this); - this._popupHandlersAdded = true; - } - - if (content instanceof L.Popup) { - L.setOptions(content, options); - this._popup = content; - } else { - this._popup = new L.Popup(options, this) - .setContent(content); - } - - return this; - }, - - setPopupContent: function (content) { - if (this._popup) { - this._popup.setContent(content); - } - return this; - }, - - unbindPopup: function () { - if (this._popup) { - this._popup = null; - this - .off('click', this.togglePopup, this) - .off('remove', this.closePopup, this) - .off('move', this._movePopup, this); - this._popupHandlersAdded = false; - } - return this; - }, - - getPopup: function () { - return this._popup; - }, - - _movePopup: function (e) { - this._popup.setLatLng(e.latlng); - } -}); - - -/* - * L.LayerGroup is a class to combine several layers into one so that - * you can manipulate the group (e.g. add/remove it) as one layer. - */ - -L.LayerGroup = L.Class.extend({ - initialize: function (layers) { - this._layers = {}; - - var i, len; - - if (layers) { - for (i = 0, len = layers.length; i < len; i++) { - this.addLayer(layers[i]); - } - } - }, - - addLayer: function (layer) { - var id = this.getLayerId(layer); - - this._layers[id] = layer; - - if (this._map) { - this._map.addLayer(layer); - } - - return this; - }, - - removeLayer: function (layer) { - var id = layer in this._layers ? layer : this.getLayerId(layer); - - if (this._map && this._layers[id]) { - this._map.removeLayer(this._layers[id]); - } - - delete this._layers[id]; - - return this; - }, - - hasLayer: function (layer) { - if (!layer) { return false; } - - return (layer in this._layers || this.getLayerId(layer) in this._layers); - }, - - clearLayers: function () { - this.eachLayer(this.removeLayer, this); - return this; - }, - - invoke: function (methodName) { - var args = Array.prototype.slice.call(arguments, 1), - i, layer; - - for (i in this._layers) { - layer = this._layers[i]; - - if (layer[methodName]) { - layer[methodName].apply(layer, args); - } - } - - return this; - }, - - onAdd: function (map) { - this._map = map; - this.eachLayer(map.addLayer, map); - }, - - onRemove: function (map) { - this.eachLayer(map.removeLayer, map); - this._map = null; - }, - - addTo: function (map) { - map.addLayer(this); - return this; - }, - - eachLayer: function (method, context) { - for (var i in this._layers) { - method.call(context, this._layers[i]); - } - return this; - }, - - getLayer: function (id) { - return this._layers[id]; - }, - - getLayers: function () { - var layers = []; - - for (var i in this._layers) { - layers.push(this._layers[i]); - } - return layers; - }, - - setZIndex: function (zIndex) { - return this.invoke('setZIndex', zIndex); - }, - - getLayerId: function (layer) { - return L.stamp(layer); - } -}); - -L.layerGroup = function (layers) { - return new L.LayerGroup(layers); -}; - - -/* - * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods - * shared between a group of interactive layers (like vectors or markers). - */ - -L.FeatureGroup = L.LayerGroup.extend({ - includes: L.Mixin.Events, - - statics: { - EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose' - }, - - addLayer: function (layer) { - if (this.hasLayer(layer)) { - return this; - } - - if ('on' in layer) { - layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this); - } - - L.LayerGroup.prototype.addLayer.call(this, layer); - - if (this._popupContent && layer.bindPopup) { - layer.bindPopup(this._popupContent, this._popupOptions); - } - - return this.fire('layeradd', {layer: layer}); - }, - - removeLayer: function (layer) { - if (!this.hasLayer(layer)) { - return this; - } - if (layer in this._layers) { - layer = this._layers[layer]; - } - - layer.off(L.FeatureGroup.EVENTS, this._propagateEvent, this); - - L.LayerGroup.prototype.removeLayer.call(this, layer); - - if (this._popupContent) { - this.invoke('unbindPopup'); - } - - return this.fire('layerremove', {layer: layer}); - }, - - bindPopup: function (content, options) { - this._popupContent = content; - this._popupOptions = options; - return this.invoke('bindPopup', content, options); - }, - - setStyle: function (style) { - return this.invoke('setStyle', style); - }, - - bringToFront: function () { - return this.invoke('bringToFront'); - }, - - bringToBack: function () { - return this.invoke('bringToBack'); - }, - - getBounds: function () { - var bounds = new L.LatLngBounds(); - - this.eachLayer(function (layer) { - bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds()); - }); - - return bounds; - }, - - _propagateEvent: function (e) { - if (!e.layer) { - e.layer = e.target; - } - e.target = this; - - this.fire(e.type, e); - } -}); - -L.featureGroup = function (layers) { - return new L.FeatureGroup(layers); -}; - - -/* - * L.Path is a base class for rendering vector paths on a map. Inherited by Polyline, Circle, etc. - */ - -L.Path = L.Class.extend({ - includes: [L.Mixin.Events], - - statics: { - // how much to extend the clip area around the map view - // (relative to its size, e.g. 0.5 is half the screen in each direction) - // set it so that SVG element doesn't exceed 1280px (vectors flicker on dragend if it is) - CLIP_PADDING: (function () { - var max = L.Browser.mobile ? 1280 : 2000, - target = (max / Math.max(window.outerWidth, window.outerHeight) - 1) / 2; - return Math.max(0, Math.min(0.5, target)); - })() - }, - - options: { - stroke: true, - color: '#0033ff', - dashArray: null, - lineCap: null, - lineJoin: null, - weight: 5, - opacity: 0.5, - - fill: false, - fillColor: null, //same as color by default - fillOpacity: 0.2, - - clickable: true - }, - - initialize: function (options) { - L.setOptions(this, options); - }, - - onAdd: function (map) { - this._map = map; - - if (!this._container) { - this._initElements(); - this._initEvents(); - } - - this.projectLatlngs(); - this._updatePath(); - - if (this._container) { - this._map._pathRoot.appendChild(this._container); - } - - this.fire('add'); - - map.on({ - 'viewreset': this.projectLatlngs, - 'moveend': this._updatePath - }, this); - }, - - addTo: function (map) { - map.addLayer(this); - return this; - }, - - onRemove: function (map) { - map._pathRoot.removeChild(this._container); - - // Need to fire remove event before we set _map to null as the event hooks might need the object - this.fire('remove'); - this._map = null; - - if (L.Browser.vml) { - this._container = null; - this._stroke = null; - this._fill = null; - } - - map.off({ - 'viewreset': this.projectLatlngs, - 'moveend': this._updatePath - }, this); - }, - - projectLatlngs: function () { - // do all projection stuff here - }, - - setStyle: function (style) { - L.setOptions(this, style); - - if (this._container) { - this._updateStyle(); - } - - return this; - }, - - redraw: function () { - if (this._map) { - this.projectLatlngs(); - this._updatePath(); - } - return this; - } -}); - -L.Map.include({ - _updatePathViewport: function () { - var p = L.Path.CLIP_PADDING, - size = this.getSize(), - panePos = L.DomUtil.getPosition(this._mapPane), - min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)._round()), - max = min.add(size.multiplyBy(1 + p * 2)._round()); - - this._pathViewport = new L.Bounds(min, max); - } -}); - - -/* - * Extends L.Path with SVG-specific rendering code. - */ - -L.Path.SVG_NS = 'http://www.w3.org/2000/svg'; - -L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect); - -L.Path = L.Path.extend({ - statics: { - SVG: L.Browser.svg - }, - - bringToFront: function () { - var root = this._map._pathRoot, - path = this._container; - - if (path && root.lastChild !== path) { - root.appendChild(path); - } - return this; - }, - - bringToBack: function () { - var root = this._map._pathRoot, - path = this._container, - first = root.firstChild; - - if (path && first !== path) { - root.insertBefore(path, first); - } - return this; - }, - - getPathString: function () { - // form path string here - }, - - _createElement: function (name) { - return document.createElementNS(L.Path.SVG_NS, name); - }, - - _initElements: function () { - this._map._initPathRoot(); - this._initPath(); - this._initStyle(); - }, - - _initPath: function () { - this._container = this._createElement('g'); - - this._path = this._createElement('path'); - this._container.appendChild(this._path); - }, - - _initStyle: function () { - if (this.options.stroke) { - this._path.setAttribute('stroke-linejoin', 'round'); - this._path.setAttribute('stroke-linecap', 'round'); - } - if (this.options.fill) { - this._path.setAttribute('fill-rule', 'evenodd'); - } - if (this.options.pointerEvents) { - this._path.setAttribute('pointer-events', this.options.pointerEvents); - } - if (!this.options.clickable && !this.options.pointerEvents) { - this._path.setAttribute('pointer-events', 'none'); - } - this._updateStyle(); - }, - - _updateStyle: function () { - if (this.options.stroke) { - this._path.setAttribute('stroke', this.options.color); - this._path.setAttribute('stroke-opacity', this.options.opacity); - this._path.setAttribute('stroke-width', this.options.weight); - if (this.options.dashArray) { - this._path.setAttribute('stroke-dasharray', this.options.dashArray); - } else { - this._path.removeAttribute('stroke-dasharray'); - } - if (this.options.lineCap) { - this._path.setAttribute('stroke-linecap', this.options.lineCap); - } - if (this.options.lineJoin) { - this._path.setAttribute('stroke-linejoin', this.options.lineJoin); - } - } else { - this._path.setAttribute('stroke', 'none'); - } - if (this.options.fill) { - this._path.setAttribute('fill', this.options.fillColor || this.options.color); - this._path.setAttribute('fill-opacity', this.options.fillOpacity); - } else { - this._path.setAttribute('fill', 'none'); - } - }, - - _updatePath: function () { - var str = this.getPathString(); - if (!str) { - // fix webkit empty string parsing bug - str = 'M0 0'; - } - this._path.setAttribute('d', str); - }, - - // TODO remove duplication with L.Map - _initEvents: function () { - if (this.options.clickable) { - if (L.Browser.svg || !L.Browser.vml) { - this._path.setAttribute('class', 'leaflet-clickable'); - } - - L.DomEvent.on(this._container, 'click', this._onMouseClick, this); - - var events = ['dblclick', 'mousedown', 'mouseover', - 'mouseout', 'mousemove', 'contextmenu']; - for (var i = 0; i < events.length; i++) { - L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this); - } - } - }, - - _onMouseClick: function (e) { - if (this._map.dragging && this._map.dragging.moved()) { return; } - - this._fireMouseEvent(e); - }, - - _fireMouseEvent: function (e) { - if (!this.hasEventListeners(e.type)) { return; } - - var map = this._map, - containerPoint = map.mouseEventToContainerPoint(e), - layerPoint = map.containerPointToLayerPoint(containerPoint), - latlng = map.layerPointToLatLng(layerPoint); - - this.fire(e.type, { - latlng: latlng, - layerPoint: layerPoint, - containerPoint: containerPoint, - originalEvent: e - }); - - if (e.type === 'contextmenu') { - L.DomEvent.preventDefault(e); - } - if (e.type !== 'mousemove') { - L.DomEvent.stopPropagation(e); - } - } -}); - -L.Map.include({ - _initPathRoot: function () { - if (!this._pathRoot) { - this._pathRoot = L.Path.prototype._createElement('svg'); - this._panes.overlayPane.appendChild(this._pathRoot); - - if (this.options.zoomAnimation && L.Browser.any3d) { - this._pathRoot.setAttribute('class', ' leaflet-zoom-animated'); - - this.on({ - 'zoomanim': this._animatePathZoom, - 'zoomend': this._endPathZoom - }); - } else { - this._pathRoot.setAttribute('class', ' leaflet-zoom-hide'); - } - - this.on('moveend', this._updateSvgViewport); - this._updateSvgViewport(); - } - }, - - _animatePathZoom: function (e) { - var scale = this.getZoomScale(e.zoom), - offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min); - - this._pathRoot.style[L.DomUtil.TRANSFORM] = - L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') '; - - this._pathZooming = true; - }, - - _endPathZoom: function () { - this._pathZooming = false; - }, - - _updateSvgViewport: function () { - - if (this._pathZooming) { - // Do not update SVGs while a zoom animation is going on otherwise the animation will break. - // When the zoom animation ends we will be updated again anyway - // This fixes the case where you do a momentum move and zoom while the move is still ongoing. - return; - } - - this._updatePathViewport(); - - var vp = this._pathViewport, - min = vp.min, - max = vp.max, - width = max.x - min.x, - height = max.y - min.y, - root = this._pathRoot, - pane = this._panes.overlayPane; - - // Hack to make flicker on drag end on mobile webkit less irritating - if (L.Browser.mobileWebkit) { - pane.removeChild(root); - } - - L.DomUtil.setPosition(root, min); - root.setAttribute('width', width); - root.setAttribute('height', height); - root.setAttribute('viewBox', [min.x, min.y, width, height].join(' ')); - - if (L.Browser.mobileWebkit) { - pane.appendChild(root); - } - } -}); - - -/* - * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods. - */ - -L.Path.include({ - - bindPopup: function (content, options) { - - if (content instanceof L.Popup) { - this._popup = content; - } else { - if (!this._popup || options) { - this._popup = new L.Popup(options, this); - } - this._popup.setContent(content); - } - - if (!this._popupHandlersAdded) { - this - .on('click', this._openPopup, this) - .on('remove', this.closePopup, this); - - this._popupHandlersAdded = true; - } - - return this; - }, - - unbindPopup: function () { - if (this._popup) { - this._popup = null; - this - .off('click', this._openPopup) - .off('remove', this.closePopup); - - this._popupHandlersAdded = false; - } - return this; - }, - - openPopup: function (latlng) { - - if (this._popup) { - // open the popup from one of the path's points if not specified - latlng = latlng || this._latlng || - this._latlngs[Math.floor(this._latlngs.length / 2)]; - - this._openPopup({latlng: latlng}); - } - - return this; - }, - - closePopup: function () { - if (this._popup) { - this._popup._close(); - } - return this; - }, - - _openPopup: function (e) { - this._popup.setLatLng(e.latlng); - this._map.openPopup(this._popup); - } -}); - - -/* - * Vector rendering for IE6-8 through VML. - * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! - */ - -L.Browser.vml = !L.Browser.svg && (function () { - try { - var div = document.createElement('div'); - div.innerHTML = ''; - - var shape = div.firstChild; - shape.style.behavior = 'url(#default#VML)'; - - return shape && (typeof shape.adj === 'object'); - - } catch (e) { - return false; - } -}()); - -L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ - statics: { - VML: true, - CLIP_PADDING: 0.02 - }, - - _createElement: (function () { - try { - document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); - return function (name) { - return document.createElement(''); - }; - } catch (e) { - return function (name) { - return document.createElement( - '<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); - }; - } - }()), - - _initPath: function () { - var container = this._container = this._createElement('shape'); - L.DomUtil.addClass(container, 'leaflet-vml-shape'); - if (this.options.clickable) { - L.DomUtil.addClass(container, 'leaflet-clickable'); - } - container.coordsize = '1 1'; - - this._path = this._createElement('path'); - container.appendChild(this._path); - - this._map._pathRoot.appendChild(container); - }, - - _initStyle: function () { - this._updateStyle(); - }, - - _updateStyle: function () { - var stroke = this._stroke, - fill = this._fill, - options = this.options, - container = this._container; - - container.stroked = options.stroke; - container.filled = options.fill; - - if (options.stroke) { - if (!stroke) { - stroke = this._stroke = this._createElement('stroke'); - stroke.endcap = 'round'; - container.appendChild(stroke); - } - stroke.weight = options.weight + 'px'; - stroke.color = options.color; - stroke.opacity = options.opacity; - - if (options.dashArray) { - stroke.dashStyle = L.Util.isArray(options.dashArray) ? - options.dashArray.join(' ') : - options.dashArray.replace(/( *, *)/g, ' '); - } else { - stroke.dashStyle = ''; - } - if (options.lineCap) { - stroke.endcap = options.lineCap.replace('butt', 'flat'); - } - if (options.lineJoin) { - stroke.joinstyle = options.lineJoin; - } - - } else if (stroke) { - container.removeChild(stroke); - this._stroke = null; - } - - if (options.fill) { - if (!fill) { - fill = this._fill = this._createElement('fill'); - container.appendChild(fill); - } - fill.color = options.fillColor || options.color; - fill.opacity = options.fillOpacity; - - } else if (fill) { - container.removeChild(fill); - this._fill = null; - } - }, - - _updatePath: function () { - var style = this._container.style; - - style.display = 'none'; - this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug - style.display = ''; - } -}); - -L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : { - _initPathRoot: function () { - if (this._pathRoot) { return; } - - var root = this._pathRoot = document.createElement('div'); - root.className = 'leaflet-vml-container'; - this._panes.overlayPane.appendChild(root); - - this.on('moveend', this._updatePathViewport); - this._updatePathViewport(); - } -}); - - -/* - * Vector rendering for all browsers that support canvas. - */ - -L.Browser.canvas = (function () { - return !!document.createElement('canvas').getContext; -}()); - -L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({ - statics: { - //CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value - CANVAS: true, - SVG: false - }, - - redraw: function () { - if (this._map) { - this.projectLatlngs(); - this._requestUpdate(); - } - return this; - }, - - setStyle: function (style) { - L.setOptions(this, style); - - if (this._map) { - this._updateStyle(); - this._requestUpdate(); - } - return this; - }, - - onRemove: function (map) { - map - .off('viewreset', this.projectLatlngs, this) - .off('moveend', this._updatePath, this); - - if (this.options.clickable) { - this._map.off('click', this._onClick, this); - this._map.off('mousemove', this._onMouseMove, this); - } - - this._requestUpdate(); - - this._map = null; - }, - - _requestUpdate: function () { - if (this._map && !L.Path._updateRequest) { - L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map); - } - }, - - _fireMapMoveEnd: function () { - L.Path._updateRequest = null; - this.fire('moveend'); - }, - - _initElements: function () { - this._map._initPathRoot(); - this._ctx = this._map._canvasCtx; - }, - - _updateStyle: function () { - var options = this.options; - - if (options.stroke) { - this._ctx.lineWidth = options.weight; - this._ctx.strokeStyle = options.color; - } - if (options.fill) { - this._ctx.fillStyle = options.fillColor || options.color; - } - }, - - _drawPath: function () { - var i, j, len, len2, point, drawMethod; - - this._ctx.beginPath(); - - for (i = 0, len = this._parts.length; i < len; i++) { - for (j = 0, len2 = this._parts[i].length; j < len2; j++) { - point = this._parts[i][j]; - drawMethod = (j === 0 ? 'move' : 'line') + 'To'; - - this._ctx[drawMethod](point.x, point.y); - } - // TODO refactor ugly hack - if (this instanceof L.Polygon) { - this._ctx.closePath(); - } - } - }, - - _checkIfEmpty: function () { - return !this._parts.length; - }, - - _updatePath: function () { - if (this._checkIfEmpty()) { return; } - - var ctx = this._ctx, - options = this.options; - - this._drawPath(); - ctx.save(); - this._updateStyle(); - - if (options.fill) { - ctx.globalAlpha = options.fillOpacity; - ctx.fill(); - } - - if (options.stroke) { - ctx.globalAlpha = options.opacity; - ctx.stroke(); - } - - ctx.restore(); - - // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature - }, - - _initEvents: function () { - if (this.options.clickable) { - // TODO dblclick - this._map.on('mousemove', this._onMouseMove, this); - this._map.on('click', this._onClick, this); - } - }, - - _onClick: function (e) { - if (this._containsPoint(e.layerPoint)) { - this.fire('click', e); - } - }, - - _onMouseMove: function (e) { - if (!this._map || this._map._animatingZoom) { return; } - - // TODO don't do on each move - if (this._containsPoint(e.layerPoint)) { - this._ctx.canvas.style.cursor = 'pointer'; - this._mouseInside = true; - this.fire('mouseover', e); - - } else if (this._mouseInside) { - this._ctx.canvas.style.cursor = ''; - this._mouseInside = false; - this.fire('mouseout', e); - } - } -}); - -L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : { - _initPathRoot: function () { - var root = this._pathRoot, - ctx; - - if (!root) { - root = this._pathRoot = document.createElement('canvas'); - root.style.position = 'absolute'; - ctx = this._canvasCtx = root.getContext('2d'); - - ctx.lineCap = 'round'; - ctx.lineJoin = 'round'; - - this._panes.overlayPane.appendChild(root); - - if (this.options.zoomAnimation) { - this._pathRoot.className = 'leaflet-zoom-animated'; - this.on('zoomanim', this._animatePathZoom); - this.on('zoomend', this._endPathZoom); - } - this.on('moveend', this._updateCanvasViewport); - this._updateCanvasViewport(); - } - }, - - _updateCanvasViewport: function () { - // don't redraw while zooming. See _updateSvgViewport for more details - if (this._pathZooming) { return; } - this._updatePathViewport(); - - var vp = this._pathViewport, - min = vp.min, - size = vp.max.subtract(min), - root = this._pathRoot; - - //TODO check if this works properly on mobile webkit - L.DomUtil.setPosition(root, min); - root.width = size.x; - root.height = size.y; - root.getContext('2d').translate(-min.x, -min.y); - } -}); - - -/* - * L.LineUtil contains different utility functions for line segments - * and polylines (clipping, simplification, distances, etc.) - */ - -/*jshint bitwise:false */ // allow bitwise oprations for this file - -L.LineUtil = { - - // Simplify polyline with vertex reduction and Douglas-Peucker simplification. - // Improves rendering performance dramatically by lessening the number of points to draw. - - simplify: function (/*Point[]*/ points, /*Number*/ tolerance) { - if (!tolerance || !points.length) { - return points.slice(); - } - - var sqTolerance = tolerance * tolerance; - - // stage 1: vertex reduction - points = this._reducePoints(points, sqTolerance); - - // stage 2: Douglas-Peucker simplification - points = this._simplifyDP(points, sqTolerance); - - return points; - }, - - // distance from a point to a segment between two points - pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { - return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true)); - }, - - closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { - return this._sqClosestPointOnSegment(p, p1, p2); - }, - - // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm - _simplifyDP: function (points, sqTolerance) { - - var len = points.length, - ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, - markers = new ArrayConstructor(len); - - markers[0] = markers[len - 1] = 1; - - this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1); - - var i, - newPoints = []; - - for (i = 0; i < len; i++) { - if (markers[i]) { - newPoints.push(points[i]); - } - } - - return newPoints; - }, - - _simplifyDPStep: function (points, markers, sqTolerance, first, last) { - - var maxSqDist = 0, - index, i, sqDist; - - for (i = first + 1; i <= last - 1; i++) { - sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true); - - if (sqDist > maxSqDist) { - index = i; - maxSqDist = sqDist; - } - } - - if (maxSqDist > sqTolerance) { - markers[index] = 1; - - this._simplifyDPStep(points, markers, sqTolerance, first, index); - this._simplifyDPStep(points, markers, sqTolerance, index, last); - } - }, - - // reduce points that are too close to each other to a single point - _reducePoints: function (points, sqTolerance) { - var reducedPoints = [points[0]]; - - for (var i = 1, prev = 0, len = points.length; i < len; i++) { - if (this._sqDist(points[i], points[prev]) > sqTolerance) { - reducedPoints.push(points[i]); - prev = i; - } - } - if (prev < len - 1) { - reducedPoints.push(points[len - 1]); - } - return reducedPoints; - }, - - // Cohen-Sutherland line clipping algorithm. - // Used to avoid rendering parts of a polyline that are not currently visible. - - clipSegment: function (a, b, bounds, useLastCode) { - var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), - codeB = this._getBitCode(b, bounds), - - codeOut, p, newCode; - - // save 2nd code to avoid calculating it on the next segment - this._lastCode = codeB; - - while (true) { - // if a,b is inside the clip window (trivial accept) - if (!(codeA | codeB)) { - return [a, b]; - // if a,b is outside the clip window (trivial reject) - } else if (codeA & codeB) { - return false; - // other cases - } else { - codeOut = codeA || codeB; - p = this._getEdgeIntersection(a, b, codeOut, bounds); - newCode = this._getBitCode(p, bounds); - - if (codeOut === codeA) { - a = p; - codeA = newCode; - } else { - b = p; - codeB = newCode; - } - } - } - }, - - _getEdgeIntersection: function (a, b, code, bounds) { - var dx = b.x - a.x, - dy = b.y - a.y, - min = bounds.min, - max = bounds.max; - - if (code & 8) { // top - return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y); - } else if (code & 4) { // bottom - return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y); - } else if (code & 2) { // right - return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx); - } else if (code & 1) { // left - return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx); - } - }, - - _getBitCode: function (/*Point*/ p, bounds) { - var code = 0; - - if (p.x < bounds.min.x) { // left - code |= 1; - } else if (p.x > bounds.max.x) { // right - code |= 2; - } - if (p.y < bounds.min.y) { // bottom - code |= 4; - } else if (p.y > bounds.max.y) { // top - code |= 8; - } - - return code; - }, - - // square distance (to avoid unnecessary Math.sqrt calls) - _sqDist: function (p1, p2) { - var dx = p2.x - p1.x, - dy = p2.y - p1.y; - return dx * dx + dy * dy; - }, - - // return closest point on segment or distance to that point - _sqClosestPointOnSegment: function (p, p1, p2, sqDist) { - var x = p1.x, - y = p1.y, - dx = p2.x - x, - dy = p2.y - y, - dot = dx * dx + dy * dy, - t; - - if (dot > 0) { - t = ((p.x - x) * dx + (p.y - y) * dy) / dot; - - if (t > 1) { - x = p2.x; - y = p2.y; - } else if (t > 0) { - x += dx * t; - y += dy * t; - } - } - - dx = p.x - x; - dy = p.y - y; - - return sqDist ? dx * dx + dy * dy : new L.Point(x, y); - } -}; - - -/* - * L.Polyline is used to display polylines on a map. - */ - -L.Polyline = L.Path.extend({ - initialize: function (latlngs, options) { - L.Path.prototype.initialize.call(this, options); - - this._latlngs = this._convertLatLngs(latlngs); - }, - - options: { - // how much to simplify the polyline on each zoom level - // more = better performance and smoother look, less = more accurate - smoothFactor: 1.0, - noClip: false - }, - - projectLatlngs: function () { - this._originalPoints = []; - - for (var i = 0, len = this._latlngs.length; i < len; i++) { - this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]); - } - }, - - getPathString: function () { - for (var i = 0, len = this._parts.length, str = ''; i < len; i++) { - str += this._getPathPartStr(this._parts[i]); - } - return str; - }, - - getLatLngs: function () { - return this._latlngs; - }, - - setLatLngs: function (latlngs) { - this._latlngs = this._convertLatLngs(latlngs); - return this.redraw(); - }, - - addLatLng: function (latlng) { - this._latlngs.push(L.latLng(latlng)); - return this.redraw(); - }, - - spliceLatLngs: function () { // (Number index, Number howMany) - var removed = [].splice.apply(this._latlngs, arguments); - this._convertLatLngs(this._latlngs, true); - this.redraw(); - return removed; - }, - - closestLayerPoint: function (p) { - var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null; - - for (var j = 0, jLen = parts.length; j < jLen; j++) { - var points = parts[j]; - for (var i = 1, len = points.length; i < len; i++) { - p1 = points[i - 1]; - p2 = points[i]; - var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true); - if (sqDist < minDistance) { - minDistance = sqDist; - minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2); - } - } - } - if (minPoint) { - minPoint.distance = Math.sqrt(minDistance); - } - return minPoint; - }, - - getBounds: function () { - return new L.LatLngBounds(this.getLatLngs()); - }, - - _convertLatLngs: function (latlngs, overwrite) { - var i, len, target = overwrite ? latlngs : []; - - for (i = 0, len = latlngs.length; i < len; i++) { - if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') { - return; - } - target[i] = L.latLng(latlngs[i]); - } - return target; - }, - - _initEvents: function () { - L.Path.prototype._initEvents.call(this); - }, - - _getPathPartStr: function (points) { - var round = L.Path.VML; - - for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) { - p = points[j]; - if (round) { - p._round(); - } - str += (j ? 'L' : 'M') + p.x + ' ' + p.y; - } - return str; - }, - - _clipPoints: function () { - var points = this._originalPoints, - len = points.length, - i, k, segment; - - if (this.options.noClip) { - this._parts = [points]; - return; - } - - this._parts = []; - - var parts = this._parts, - vp = this._map._pathViewport, - lu = L.LineUtil; - - for (i = 0, k = 0; i < len - 1; i++) { - segment = lu.clipSegment(points[i], points[i + 1], vp, i); - if (!segment) { - continue; - } - - parts[k] = parts[k] || []; - parts[k].push(segment[0]); - - // if segment goes out of screen, or it's the last one, it's the end of the line part - if ((segment[1] !== points[i + 1]) || (i === len - 2)) { - parts[k].push(segment[1]); - k++; - } - } - }, - - // simplify each clipped part of the polyline - _simplifyPoints: function () { - var parts = this._parts, - lu = L.LineUtil; - - for (var i = 0, len = parts.length; i < len; i++) { - parts[i] = lu.simplify(parts[i], this.options.smoothFactor); - } - }, - - _updatePath: function () { - if (!this._map) { return; } - - this._clipPoints(); - this._simplifyPoints(); - - L.Path.prototype._updatePath.call(this); - } -}); - -L.polyline = function (latlngs, options) { - return new L.Polyline(latlngs, options); -}; - - -/* - * L.PolyUtil contains utility functions for polygons (clipping, etc.). - */ - -/*jshint bitwise:false */ // allow bitwise operations here - -L.PolyUtil = {}; - -/* - * Sutherland-Hodgeman polygon clipping algorithm. - * Used to avoid rendering parts of a polygon that are not currently visible. - */ -L.PolyUtil.clipPolygon = function (points, bounds) { - var clippedPoints, - edges = [1, 4, 2, 8], - i, j, k, - a, b, - len, edge, p, - lu = L.LineUtil; - - for (i = 0, len = points.length; i < len; i++) { - points[i]._code = lu._getBitCode(points[i], bounds); - } - - // for each edge (left, bottom, right, top) - for (k = 0; k < 4; k++) { - edge = edges[k]; - clippedPoints = []; - - for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { - a = points[i]; - b = points[j]; - - // if a is inside the clip window - if (!(a._code & edge)) { - // if b is outside the clip window (a->b goes out of screen) - if (b._code & edge) { - p = lu._getEdgeIntersection(b, a, edge, bounds); - p._code = lu._getBitCode(p, bounds); - clippedPoints.push(p); - } - clippedPoints.push(a); - - // else if b is inside the clip window (a->b enters the screen) - } else if (!(b._code & edge)) { - p = lu._getEdgeIntersection(b, a, edge, bounds); - p._code = lu._getBitCode(p, bounds); - clippedPoints.push(p); - } - } - points = clippedPoints; - } - - return points; -}; - - -/* - * L.Polygon is used to display polygons on a map. - */ - -L.Polygon = L.Polyline.extend({ - options: { - fill: true - }, - - initialize: function (latlngs, options) { - L.Polyline.prototype.initialize.call(this, latlngs, options); - this._initWithHoles(latlngs); - }, - - _initWithHoles: function (latlngs) { - var i, len, hole; - if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { - this._latlngs = this._convertLatLngs(latlngs[0]); - this._holes = latlngs.slice(1); - - for (i = 0, len = this._holes.length; i < len; i++) { - hole = this._holes[i] = this._convertLatLngs(this._holes[i]); - if (hole[0].equals(hole[hole.length - 1])) { - hole.pop(); - } - } - } - - // filter out last point if its equal to the first one - latlngs = this._latlngs; - - if (latlngs.length >= 2 && latlngs[0].equals(latlngs[latlngs.length - 1])) { - latlngs.pop(); - } - }, - - projectLatlngs: function () { - L.Polyline.prototype.projectLatlngs.call(this); - - // project polygon holes points - // TODO move this logic to Polyline to get rid of duplication - this._holePoints = []; - - if (!this._holes) { return; } - - var i, j, len, len2; - - for (i = 0, len = this._holes.length; i < len; i++) { - this._holePoints[i] = []; - - for (j = 0, len2 = this._holes[i].length; j < len2; j++) { - this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]); - } - } - }, - - setLatLngs: function (latlngs) { - if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { - this._initWithHoles(latlngs); - return this.redraw(); - } else { - return L.Polyline.prototype.setLatLngs.call(this, latlngs); - } - }, - - _clipPoints: function () { - var points = this._originalPoints, - newParts = []; - - this._parts = [points].concat(this._holePoints); - - if (this.options.noClip) { return; } - - for (var i = 0, len = this._parts.length; i < len; i++) { - var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport); - if (clipped.length) { - newParts.push(clipped); - } - } - - this._parts = newParts; - }, - - _getPathPartStr: function (points) { - var str = L.Polyline.prototype._getPathPartStr.call(this, points); - return str + (L.Browser.svg ? 'z' : 'x'); - } -}); - -L.polygon = function (latlngs, options) { - return new L.Polygon(latlngs, options); -}; - - -/* - * Contains L.MultiPolyline and L.MultiPolygon layers. - */ - -(function () { - function createMulti(Klass) { - - return L.FeatureGroup.extend({ - - initialize: function (latlngs, options) { - this._layers = {}; - this._options = options; - this.setLatLngs(latlngs); - }, - - setLatLngs: function (latlngs) { - var i = 0, - len = latlngs.length; - - this.eachLayer(function (layer) { - if (i < len) { - layer.setLatLngs(latlngs[i++]); - } else { - this.removeLayer(layer); - } - }, this); - - while (i < len) { - this.addLayer(new Klass(latlngs[i++], this._options)); - } - - return this; - }, - - getLatLngs: function () { - var latlngs = []; - - this.eachLayer(function (layer) { - latlngs.push(layer.getLatLngs()); - }); - - return latlngs; - } - }); - } - - L.MultiPolyline = createMulti(L.Polyline); - L.MultiPolygon = createMulti(L.Polygon); - - L.multiPolyline = function (latlngs, options) { - return new L.MultiPolyline(latlngs, options); - }; - - L.multiPolygon = function (latlngs, options) { - return new L.MultiPolygon(latlngs, options); - }; -}()); - - -/* - * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. - */ - -L.Rectangle = L.Polygon.extend({ - initialize: function (latLngBounds, options) { - L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); - }, - - setBounds: function (latLngBounds) { - this.setLatLngs(this._boundsToLatLngs(latLngBounds)); - }, - - _boundsToLatLngs: function (latLngBounds) { - latLngBounds = L.latLngBounds(latLngBounds); - return [ - latLngBounds.getSouthWest(), - latLngBounds.getNorthWest(), - latLngBounds.getNorthEast(), - latLngBounds.getSouthEast() - ]; - } -}); - -L.rectangle = function (latLngBounds, options) { - return new L.Rectangle(latLngBounds, options); -}; - - -/* - * L.Circle is a circle overlay (with a certain radius in meters). - */ - -L.Circle = L.Path.extend({ - initialize: function (latlng, radius, options) { - L.Path.prototype.initialize.call(this, options); - - this._latlng = L.latLng(latlng); - this._mRadius = radius; - }, - - options: { - fill: true - }, - - setLatLng: function (latlng) { - this._latlng = L.latLng(latlng); - return this.redraw(); - }, - - setRadius: function (radius) { - this._mRadius = radius; - return this.redraw(); - }, - - projectLatlngs: function () { - var lngRadius = this._getLngRadius(), - latlng = this._latlng, - pointLeft = this._map.latLngToLayerPoint([latlng.lat, latlng.lng - lngRadius]); - - this._point = this._map.latLngToLayerPoint(latlng); - this._radius = Math.max(this._point.x - pointLeft.x, 1); - }, - - getBounds: function () { - var lngRadius = this._getLngRadius(), - latRadius = (this._mRadius / 40075017) * 360, - latlng = this._latlng; - - return new L.LatLngBounds( - [latlng.lat - latRadius, latlng.lng - lngRadius], - [latlng.lat + latRadius, latlng.lng + lngRadius]); - }, - - getLatLng: function () { - return this._latlng; - }, - - getPathString: function () { - var p = this._point, - r = this._radius; - - if (this._checkIfEmpty()) { - return ''; - } - - if (L.Browser.svg) { - return 'M' + p.x + ',' + (p.y - r) + - 'A' + r + ',' + r + ',0,1,1,' + - (p.x - 0.1) + ',' + (p.y - r) + ' z'; - } else { - p._round(); - r = Math.round(r); - return 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r + ' 0,' + (65535 * 360); - } - }, - - getRadius: function () { - return this._mRadius; - }, - - // TODO Earth hardcoded, move into projection code! - - _getLatRadius: function () { - return (this._mRadius / 40075017) * 360; - }, - - _getLngRadius: function () { - return this._getLatRadius() / Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat); - }, - - _checkIfEmpty: function () { - if (!this._map) { - return false; - } - var vp = this._map._pathViewport, - r = this._radius, - p = this._point; - - return p.x - r > vp.max.x || p.y - r > vp.max.y || - p.x + r < vp.min.x || p.y + r < vp.min.y; - } -}); - -L.circle = function (latlng, radius, options) { - return new L.Circle(latlng, radius, options); -}; - - -/* - * L.CircleMarker is a circle overlay with a permanent pixel radius. - */ - -L.CircleMarker = L.Circle.extend({ - options: { - radius: 10, - weight: 2 - }, - - initialize: function (latlng, options) { - L.Circle.prototype.initialize.call(this, latlng, null, options); - this._radius = this.options.radius; - }, - - projectLatlngs: function () { - this._point = this._map.latLngToLayerPoint(this._latlng); - }, - - _updateStyle : function () { - L.Circle.prototype._updateStyle.call(this); - this.setRadius(this.options.radius); - }, - - setLatLng: function (latlng) { - L.Circle.prototype.setLatLng.call(this, latlng); - if (this._popup && this._popup._isOpen) { - this._popup.setLatLng(latlng); - } - }, - - setRadius: function (radius) { - this.options.radius = this._radius = radius; - return this.redraw(); - }, - - getRadius: function () { - return this._radius; - } -}); - -L.circleMarker = function (latlng, options) { - return new L.CircleMarker(latlng, options); -}; - - -/* - * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines. - */ - -L.Polyline.include(!L.Path.CANVAS ? {} : { - _containsPoint: function (p, closed) { - var i, j, k, len, len2, dist, part, - w = this.options.weight / 2; - - if (L.Browser.touch) { - w += 10; // polyline click tolerance on touch devices - } - - for (i = 0, len = this._parts.length; i < len; i++) { - part = this._parts[i]; - for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { - if (!closed && (j === 0)) { - continue; - } - - dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]); - - if (dist <= w) { - return true; - } - } - } - return false; - } -}); - - -/* - * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons. - */ - -L.Polygon.include(!L.Path.CANVAS ? {} : { - _containsPoint: function (p) { - var inside = false, - part, p1, p2, - i, j, k, - len, len2; - - // TODO optimization: check if within bounds first - - if (L.Polyline.prototype._containsPoint.call(this, p, true)) { - // click on polygon border - return true; - } - - // ray casting algorithm for detecting if point is in polygon - - for (i = 0, len = this._parts.length; i < len; i++) { - part = this._parts[i]; - - for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { - p1 = part[j]; - p2 = part[k]; - - 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)) { - inside = !inside; - } - } - } - - return inside; - } -}); - - -/* - * Extends L.Circle with Canvas-specific code. - */ - -L.Circle.include(!L.Path.CANVAS ? {} : { - _drawPath: function () { - var p = this._point; - this._ctx.beginPath(); - this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false); - }, - - _containsPoint: function (p) { - var center = this._point, - w2 = this.options.stroke ? this.options.weight / 2 : 0; - - return (p.distanceTo(center) <= this._radius + w2); - } -}); - - -/* - * CircleMarker canvas specific drawing parts. - */ - -L.CircleMarker.include(!L.Path.CANVAS ? {} : { - _updateStyle: function () { - L.Path.prototype._updateStyle.call(this); - } -}); - - -/* - * L.GeoJSON turns any GeoJSON data into a Leaflet layer. - */ - -L.GeoJSON = L.FeatureGroup.extend({ - - initialize: function (geojson, options) { - L.setOptions(this, options); - - this._layers = {}; - - if (geojson) { - this.addData(geojson); - } - }, - - addData: function (geojson) { - var features = L.Util.isArray(geojson) ? geojson : geojson.features, - i, len, feature; - - if (features) { - for (i = 0, len = features.length; i < len; i++) { - // Only add this if geometry or geometries are set and not null - feature = features[i]; - if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { - this.addData(features[i]); - } - } - return this; - } - - var options = this.options; - - if (options.filter && !options.filter(geojson)) { return; } - - var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng, options); - layer.feature = L.GeoJSON.asFeature(geojson); - - layer.defaultOptions = layer.options; - this.resetStyle(layer); - - if (options.onEachFeature) { - options.onEachFeature(geojson, layer); - } - - return this.addLayer(layer); - }, - - resetStyle: function (layer) { - var style = this.options.style; - if (style) { - // reset any custom styles - L.Util.extend(layer.options, layer.defaultOptions); - - this._setLayerStyle(layer, style); - } - }, - - setStyle: function (style) { - this.eachLayer(function (layer) { - this._setLayerStyle(layer, style); - }, this); - }, - - _setLayerStyle: function (layer, style) { - if (typeof style === 'function') { - style = style(layer.feature); - } - if (layer.setStyle) { - layer.setStyle(style); - } - } -}); - -L.extend(L.GeoJSON, { - geometryToLayer: function (geojson, pointToLayer, coordsToLatLng, vectorOptions) { - var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, - coords = geometry.coordinates, - layers = [], - latlng, latlngs, i, len; - - coordsToLatLng = coordsToLatLng || this.coordsToLatLng; - - switch (geometry.type) { - case 'Point': - latlng = coordsToLatLng(coords); - return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng); - - case 'MultiPoint': - for (i = 0, len = coords.length; i < len; i++) { - latlng = coordsToLatLng(coords[i]); - layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng)); - } - return new L.FeatureGroup(layers); - - case 'LineString': - latlngs = this.coordsToLatLngs(coords, 0, coordsToLatLng); - return new L.Polyline(latlngs, vectorOptions); - - case 'Polygon': - if (coords.length === 2 && !coords[1].length) { - throw new Error('Invalid GeoJSON object.'); - } - latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); - return new L.Polygon(latlngs, vectorOptions); - - case 'MultiLineString': - latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); - return new L.MultiPolyline(latlngs, vectorOptions); - - case 'MultiPolygon': - latlngs = this.coordsToLatLngs(coords, 2, coordsToLatLng); - return new L.MultiPolygon(latlngs, vectorOptions); - - case 'GeometryCollection': - for (i = 0, len = geometry.geometries.length; i < len; i++) { - - layers.push(this.geometryToLayer({ - geometry: geometry.geometries[i], - type: 'Feature', - properties: geojson.properties - }, pointToLayer, coordsToLatLng, vectorOptions)); - } - return new L.FeatureGroup(layers); - - default: - throw new Error('Invalid GeoJSON object.'); - } - }, - - coordsToLatLng: function (coords) { // (Array[, Boolean]) -> LatLng - return new L.LatLng(coords[1], coords[0]); - }, - - coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) { // (Array[, Number, Function]) -> Array - var latlng, i, len, - latlngs = []; - - for (i = 0, len = coords.length; i < len; i++) { - latlng = levelsDeep ? - this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) : - (coordsToLatLng || this.coordsToLatLng)(coords[i]); - - latlngs.push(latlng); - } - - return latlngs; - }, - - latLngToCoords: function (latLng) { - return [latLng.lng, latLng.lat]; - }, - - latLngsToCoords: function (latLngs) { - var coords = []; - - for (var i = 0, len = latLngs.length; i < len; i++) { - coords.push(L.GeoJSON.latLngToCoords(latLngs[i])); - } - - return coords; - }, - - getFeature: function (layer, newGeometry) { - return layer.feature ? L.extend({}, layer.feature, {geometry: newGeometry}) : L.GeoJSON.asFeature(newGeometry); - }, - - asFeature: function (geoJSON) { - if (geoJSON.type === 'Feature') { - return geoJSON; - } - - return { - type: 'Feature', - properties: {}, - geometry: geoJSON - }; - } -}); - -var PointToGeoJSON = { - toGeoJSON: function () { - return L.GeoJSON.getFeature(this, { - type: 'Point', - coordinates: L.GeoJSON.latLngToCoords(this.getLatLng()) - }); - } -}; - -L.Marker.include(PointToGeoJSON); -L.Circle.include(PointToGeoJSON); -L.CircleMarker.include(PointToGeoJSON); - -L.Polyline.include({ - toGeoJSON: function () { - return L.GeoJSON.getFeature(this, { - type: 'LineString', - coordinates: L.GeoJSON.latLngsToCoords(this.getLatLngs()) - }); - } -}); - -L.Polygon.include({ - toGeoJSON: function () { - var coords = [L.GeoJSON.latLngsToCoords(this.getLatLngs())], - i, len, hole; - - coords[0].push(coords[0][0]); - - if (this._holes) { - for (i = 0, len = this._holes.length; i < len; i++) { - hole = L.GeoJSON.latLngsToCoords(this._holes[i]); - hole.push(hole[0]); - coords.push(hole); - } - } - - return L.GeoJSON.getFeature(this, { - type: 'Polygon', - coordinates: coords - }); - } -}); - -(function () { - function multiToGeoJSON(type) { - return function () { - var coords = []; - - this.eachLayer(function (layer) { - coords.push(layer.toGeoJSON().geometry.coordinates); - }); - - return L.GeoJSON.getFeature(this, { - type: type, - coordinates: coords - }); - }; - } - - L.MultiPolyline.include({toGeoJSON: multiToGeoJSON('MultiLineString')}); - L.MultiPolygon.include({toGeoJSON: multiToGeoJSON('MultiPolygon')}); - - L.LayerGroup.include({ - toGeoJSON: function () { - - var geometry = this.feature && this.feature.geometry, - jsons = [], - json; - - if (geometry && geometry.type === 'MultiPoint') { - return multiToGeoJSON('MultiPoint').call(this); - } - - var isGeometryCollection = geometry && geometry.type === 'GeometryCollection'; - - this.eachLayer(function (layer) { - if (layer.toGeoJSON) { - json = layer.toGeoJSON(); - jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json)); - } - }); - - if (isGeometryCollection) { - return L.GeoJSON.getFeature(this, { - geometries: jsons, - type: 'GeometryCollection' - }); - } - - return { - type: 'FeatureCollection', - features: jsons - }; - } - }); -}()); - -L.geoJson = function (geojson, options) { - return new L.GeoJSON(geojson, options); -}; - - -/* - * L.DomEvent contains functions for working with DOM events. - */ - -L.DomEvent = { - /* inspired by John Resig, Dean Edwards and YUI addEvent implementations */ - addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object]) - - var id = L.stamp(fn), - key = '_leaflet_' + type + id, - handler, originalHandler, newType; - - if (obj[key]) { return this; } - - handler = function (e) { - return fn.call(context || obj, e || L.DomEvent._getEvent()); - }; - - if (L.Browser.pointer && type.indexOf('touch') === 0) { - return this.addPointerListener(obj, type, handler, id); - } - if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { - this.addDoubleTapListener(obj, handler, id); - } - - if ('addEventListener' in obj) { - - if (type === 'mousewheel') { - obj.addEventListener('DOMMouseScroll', handler, false); - obj.addEventListener(type, handler, false); - - } else if ((type === 'mouseenter') || (type === 'mouseleave')) { - - originalHandler = handler; - newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout'); - - handler = function (e) { - if (!L.DomEvent._checkMouse(obj, e)) { return; } - return originalHandler(e); - }; - - obj.addEventListener(newType, handler, false); - - } else if (type === 'click' && L.Browser.android) { - originalHandler = handler; - handler = function (e) { - return L.DomEvent._filterClick(e, originalHandler); - }; - - obj.addEventListener(type, handler, false); - } else { - obj.addEventListener(type, handler, false); - } - - } else if ('attachEvent' in obj) { - obj.attachEvent('on' + type, handler); - } - - obj[key] = handler; - - return this; - }, - - removeListener: function (obj, type, fn) { // (HTMLElement, String, Function) - - var id = L.stamp(fn), - key = '_leaflet_' + type + id, - handler = obj[key]; - - if (!handler) { return this; } - - if (L.Browser.pointer && type.indexOf('touch') === 0) { - this.removePointerListener(obj, type, id); - } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) { - this.removeDoubleTapListener(obj, id); - - } else if ('removeEventListener' in obj) { - - if (type === 'mousewheel') { - obj.removeEventListener('DOMMouseScroll', handler, false); - obj.removeEventListener(type, handler, false); - - } else if ((type === 'mouseenter') || (type === 'mouseleave')) { - obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false); - } else { - obj.removeEventListener(type, handler, false); - } - } else if ('detachEvent' in obj) { - obj.detachEvent('on' + type, handler); - } - - obj[key] = null; - - return this; - }, - - stopPropagation: function (e) { - - if (e.stopPropagation) { - e.stopPropagation(); - } else { - e.cancelBubble = true; - } - L.DomEvent._skipped(e); - - return this; - }, - - disableScrollPropagation: function (el) { - var stop = L.DomEvent.stopPropagation; - - return L.DomEvent - .on(el, 'mousewheel', stop) - .on(el, 'MozMousePixelScroll', stop); - }, - - disableClickPropagation: function (el) { - var stop = L.DomEvent.stopPropagation; - - for (var i = L.Draggable.START.length - 1; i >= 0; i--) { - L.DomEvent.on(el, L.Draggable.START[i], stop); - } - - return L.DomEvent - .on(el, 'click', L.DomEvent._fakeStop) - .on(el, 'dblclick', stop); - }, - - preventDefault: function (e) { - - if (e.preventDefault) { - e.preventDefault(); - } else { - e.returnValue = false; - } - return this; - }, - - stop: function (e) { - return L.DomEvent - .preventDefault(e) - .stopPropagation(e); - }, - - getMousePosition: function (e, container) { - var body = document.body, - docEl = document.documentElement, - //gecko makes scrollLeft more negative as you scroll in rtl, other browsers don't - //ref: https://code.google.com/p/closure-library/source/browse/closure/goog/style/bidi.js - x = L.DomUtil.documentIsLtr() ? - (e.pageX ? e.pageX - body.scrollLeft - docEl.scrollLeft : e.clientX) : - (L.Browser.gecko ? e.pageX - body.scrollLeft - docEl.scrollLeft : - e.pageX ? e.pageX - body.scrollLeft + docEl.scrollLeft : e.clientX), - y = e.pageY ? e.pageY - body.scrollTop - docEl.scrollTop: e.clientY, - pos = new L.Point(x, y); - - if (!container) { - return pos; - } - - var rect = container.getBoundingClientRect(), - left = rect.left - container.clientLeft, - top = rect.top - container.clientTop; - - return pos._subtract(new L.Point(left, top)); - }, - - getWheelDelta: function (e) { - - var delta = 0; - - if (e.wheelDelta) { - delta = e.wheelDelta / 120; - } - if (e.detail) { - delta = -e.detail / 3; - } - return delta; - }, - - _skipEvents: {}, - - _fakeStop: function (e) { - // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e) - L.DomEvent._skipEvents[e.type] = true; - }, - - _skipped: function (e) { - var skipped = this._skipEvents[e.type]; - // reset when checking, as it's only used in map container and propagates outside of the map - this._skipEvents[e.type] = false; - return skipped; - }, - - // check if element really left/entered the event target (for mouseenter/mouseleave) - _checkMouse: function (el, e) { - - var related = e.relatedTarget; - - if (!related) { return true; } - - try { - while (related && (related !== el)) { - related = related.parentNode; - } - } catch (err) { - return false; - } - return (related !== el); - }, - - _getEvent: function () { // evil magic for IE - /*jshint noarg:false */ - var e = window.event; - if (!e) { - var caller = arguments.callee.caller; - while (caller) { - e = caller['arguments'][0]; - if (e && window.Event === e.constructor) { - break; - } - caller = caller.caller; - } - } - return e; - }, - - // this is a horrible workaround for a bug in Android where a single touch triggers two click events - _filterClick: function (e, handler) { - var timeStamp = (e.timeStamp || e.originalEvent.timeStamp), - elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); - - // are they closer together than 1000ms yet more than 100ms? - // Android typically triggers them ~300ms apart while multiple listeners - // on the same event should be triggered far faster; - // or check if click is simulated on the element, and if it is, reject any non-simulated events - - if ((elapsed && elapsed > 100 && elapsed < 1000) || (e.target._simulatedClick && !e._simulated)) { - L.DomEvent.stop(e); - return; - } - L.DomEvent._lastClick = timeStamp; - - return handler(e); - } -}; - -L.DomEvent.on = L.DomEvent.addListener; -L.DomEvent.off = L.DomEvent.removeListener; - - -/* - * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too. - */ - -L.Draggable = L.Class.extend({ - includes: L.Mixin.Events, - - statics: { - START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'], - END: { - mousedown: 'mouseup', - touchstart: 'touchend', - pointerdown: 'touchend', - MSPointerDown: 'touchend' - }, - MOVE: { - mousedown: 'mousemove', - touchstart: 'touchmove', - pointerdown: 'touchmove', - MSPointerDown: 'touchmove' - } - }, - - initialize: function (element, dragStartTarget) { - this._element = element; - this._dragStartTarget = dragStartTarget || element; - }, - - enable: function () { - if (this._enabled) { return; } - - for (var i = L.Draggable.START.length - 1; i >= 0; i--) { - L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); - } - - this._enabled = true; - }, - - disable: function () { - if (!this._enabled) { return; } - - for (var i = L.Draggable.START.length - 1; i >= 0; i--) { - L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); - } - - this._enabled = false; - this._moved = false; - }, - - _onDown: function (e) { - this._moved = false; - - if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } - - L.DomEvent.stopPropagation(e); - - if (L.Draggable._disabled) { return; } - - L.DomUtil.disableImageDrag(); - L.DomUtil.disableTextSelection(); - - if (this._moving) { return; } - - var first = e.touches ? e.touches[0] : e; - - this._startPoint = new L.Point(first.clientX, first.clientY); - this._startPos = this._newPos = L.DomUtil.getPosition(this._element); - - L.DomEvent - .on(document, L.Draggable.MOVE[e.type], this._onMove, this) - .on(document, L.Draggable.END[e.type], this._onUp, this); - }, - - _onMove: function (e) { - if (e.touches && e.touches.length > 1) { - this._moved = true; - return; - } - - var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), - newPoint = new L.Point(first.clientX, first.clientY), - offset = newPoint.subtract(this._startPoint); - - if (!offset.x && !offset.y) { return; } - - L.DomEvent.preventDefault(e); - - if (!this._moved) { - this.fire('dragstart'); - - this._moved = true; - this._startPos = L.DomUtil.getPosition(this._element).subtract(offset); - - if (!L.Browser.touch) { - L.DomUtil.addClass(document.body, 'leaflet-dragging'); - } - } - - this._newPos = this._startPos.add(offset); - this._moving = true; - - L.Util.cancelAnimFrame(this._animRequest); - this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget); - }, - - _updatePosition: function () { - this.fire('predrag'); - L.DomUtil.setPosition(this._element, this._newPos); - this.fire('drag'); - }, - - _onUp: function () { - if (!L.Browser.touch) { - L.DomUtil.removeClass(document.body, 'leaflet-dragging'); - } - - for (var i in L.Draggable.MOVE) { - L.DomEvent - .off(document, L.Draggable.MOVE[i], this._onMove) - .off(document, L.Draggable.END[i], this._onUp); - } - - L.DomUtil.enableImageDrag(); - L.DomUtil.enableTextSelection(); - - if (this._moved) { - // ensure drag is not fired after dragend - L.Util.cancelAnimFrame(this._animRequest); - - this.fire('dragend'); - } - - this._moving = false; - } -}); - - -/* - L.Handler is a base class for handler classes that are used internally to inject - interaction features like dragging to classes like Map and Marker. -*/ - -L.Handler = L.Class.extend({ - initialize: function (map) { - this._map = map; - }, - - enable: function () { - if (this._enabled) { return; } - - this._enabled = true; - this.addHooks(); - }, - - disable: function () { - if (!this._enabled) { return; } - - this._enabled = false; - this.removeHooks(); - }, - - enabled: function () { - return !!this._enabled; - } -}); - - -/* - * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. - */ - -L.Map.mergeOptions({ - dragging: true, - - inertia: !L.Browser.android23, - inertiaDeceleration: 3400, // px/s^2 - inertiaMaxSpeed: Infinity, // px/s - inertiaThreshold: L.Browser.touch ? 32 : 18, // ms - easeLinearity: 0.25, - - // TODO refactor, move to CRS - worldCopyJump: false -}); - -L.Map.Drag = L.Handler.extend({ - addHooks: function () { - if (!this._draggable) { - var map = this._map; - - this._draggable = new L.Draggable(map._mapPane, map._container); - - this._draggable.on({ - 'dragstart': this._onDragStart, - 'drag': this._onDrag, - 'dragend': this._onDragEnd - }, this); - - if (map.options.worldCopyJump) { - this._draggable.on('predrag', this._onPreDrag, this); - map.on('viewreset', this._onViewReset, this); - - map.whenReady(this._onViewReset, this); - } - } - this._draggable.enable(); - }, - - removeHooks: function () { - this._draggable.disable(); - }, - - moved: function () { - return this._draggable && this._draggable._moved; - }, - - _onDragStart: function () { - var map = this._map; - - if (map._panAnim) { - map._panAnim.stop(); - } - - map - .fire('movestart') - .fire('dragstart'); - - if (map.options.inertia) { - this._positions = []; - this._times = []; - } - }, - - _onDrag: function () { - if (this._map.options.inertia) { - var time = this._lastTime = +new Date(), - pos = this._lastPos = this._draggable._newPos; - - this._positions.push(pos); - this._times.push(time); - - if (time - this._times[0] > 200) { - this._positions.shift(); - this._times.shift(); - } - } - - this._map - .fire('move') - .fire('drag'); - }, - - _onViewReset: function () { - // TODO fix hardcoded Earth values - var pxCenter = this._map.getSize()._divideBy(2), - pxWorldCenter = this._map.latLngToLayerPoint([0, 0]); - - this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; - this._worldWidth = this._map.project([0, 180]).x; - }, - - _onPreDrag: function () { - // TODO refactor to be able to adjust map pane position after zoom - var worldWidth = this._worldWidth, - halfWidth = Math.round(worldWidth / 2), - dx = this._initialWorldOffset, - x = this._draggable._newPos.x, - newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, - newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, - newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; - - this._draggable._newPos.x = newX; - }, - - _onDragEnd: function () { - var map = this._map, - options = map.options, - delay = +new Date() - this._lastTime, - - noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0]; - - map.fire('dragend'); - - if (noInertia) { - map.fire('moveend'); - - } else { - - var direction = this._lastPos.subtract(this._positions[0]), - duration = (this._lastTime + delay - this._times[0]) / 1000, - ease = options.easeLinearity, - - speedVector = direction.multiplyBy(ease / duration), - speed = speedVector.distanceTo([0, 0]), - - limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), - limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), - - decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), - offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); - - if (!offset.x || !offset.y) { - map.fire('moveend'); - - } else { - L.Util.requestAnimFrame(function () { - map.panBy(offset, { - duration: decelerationDuration, - easeLinearity: ease, - noMoveStart: true - }); - }); - } - } - } -}); - -L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag); - - -/* - * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. - */ - -L.Map.mergeOptions({ - doubleClickZoom: true -}); - -L.Map.DoubleClickZoom = L.Handler.extend({ - addHooks: function () { - this._map.on('dblclick', this._onDoubleClick, this); - }, - - removeHooks: function () { - this._map.off('dblclick', this._onDoubleClick, this); - }, - - _onDoubleClick: function (e) { - var map = this._map, - zoom = map.getZoom() + 1; - - if (map.options.doubleClickZoom === 'center') { - map.setZoom(zoom); - } else { - map.setZoomAround(e.containerPoint, zoom); - } - } -}); - -L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); - - -/* - * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. - */ - -L.Map.mergeOptions({ - scrollWheelZoom: true -}); - -L.Map.ScrollWheelZoom = L.Handler.extend({ - addHooks: function () { - L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this); - L.DomEvent.on(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault); - this._delta = 0; - }, - - removeHooks: function () { - L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll); - L.DomEvent.off(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault); - }, - - _onWheelScroll: function (e) { - var delta = L.DomEvent.getWheelDelta(e); - - this._delta += delta; - this._lastMousePos = this._map.mouseEventToContainerPoint(e); - - if (!this._startTime) { - this._startTime = +new Date(); - } - - var left = Math.max(40 - (+new Date() - this._startTime), 0); - - clearTimeout(this._timer); - this._timer = setTimeout(L.bind(this._performZoom, this), left); - - L.DomEvent.preventDefault(e); - L.DomEvent.stopPropagation(e); - }, - - _performZoom: function () { - var map = this._map, - delta = this._delta, - zoom = map.getZoom(); - - delta = delta > 0 ? Math.ceil(delta) : Math.floor(delta); - delta = Math.max(Math.min(delta, 4), -4); - delta = map._limitZoom(zoom + delta) - zoom; - - this._delta = 0; - this._startTime = null; - - if (!delta) { return; } - - if (map.options.scrollWheelZoom === 'center') { - map.setZoom(zoom + delta); - } else { - map.setZoomAround(this._lastMousePos, zoom + delta); - } - } -}); - -L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom); - - -/* - * Extends the event handling code with double tap support for mobile browsers. - */ - -L.extend(L.DomEvent, { - - _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart', - _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend', - - // inspired by Zepto touch code by Thomas Fuchs - addDoubleTapListener: function (obj, handler, id) { - var last, - doubleTap = false, - delay = 250, - touch, - pre = '_leaflet_', - touchstart = this._touchstart, - touchend = this._touchend, - trackedTouches = []; - - function onTouchStart(e) { - var count; - - if (L.Browser.pointer) { - trackedTouches.push(e.pointerId); - count = trackedTouches.length; - } else { - count = e.touches.length; - } - if (count > 1) { - return; - } - - var now = Date.now(), - delta = now - (last || now); - - touch = e.touches ? e.touches[0] : e; - doubleTap = (delta > 0 && delta <= delay); - last = now; - } - - function onTouchEnd(e) { - if (L.Browser.pointer) { - var idx = trackedTouches.indexOf(e.pointerId); - if (idx === -1) { - return; - } - trackedTouches.splice(idx, 1); - } - - if (doubleTap) { - if (L.Browser.pointer) { - // work around .type being readonly with MSPointer* events - var newTouch = { }, - prop; - - // jshint forin:false - for (var i in touch) { - prop = touch[i]; - if (typeof prop === 'function') { - newTouch[i] = prop.bind(touch); - } else { - newTouch[i] = prop; - } - } - touch = newTouch; - } - touch.type = 'dblclick'; - handler(touch); - last = null; - } - } - obj[pre + touchstart + id] = onTouchStart; - obj[pre + touchend + id] = onTouchEnd; - - // on pointer we need to listen on the document, otherwise a drag starting on the map and moving off screen - // will not come through to us, so we will lose track of how many touches are ongoing - var endElement = L.Browser.pointer ? document.documentElement : obj; - - obj.addEventListener(touchstart, onTouchStart, false); - endElement.addEventListener(touchend, onTouchEnd, false); - - if (L.Browser.pointer) { - endElement.addEventListener(L.DomEvent.POINTER_CANCEL, onTouchEnd, false); - } - - return this; - }, - - removeDoubleTapListener: function (obj, id) { - var pre = '_leaflet_'; - - obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false); - (L.Browser.pointer ? document.documentElement : obj).removeEventListener( - this._touchend, obj[pre + this._touchend + id], false); - - if (L.Browser.pointer) { - document.documentElement.removeEventListener(L.DomEvent.POINTER_CANCEL, obj[pre + this._touchend + id], - false); - } - - return this; - } -}); - - -/* - * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. - */ - -L.extend(L.DomEvent, { - - //static - POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown', - POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove', - POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup', - POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel', - - _pointers: [], - _pointerDocumentListener: false, - - // Provides a touch events wrapper for (ms)pointer events. - // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019 - //ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 - - addPointerListener: function (obj, type, handler, id) { - - switch (type) { - case 'touchstart': - return this.addPointerListenerStart(obj, type, handler, id); - case 'touchend': - return this.addPointerListenerEnd(obj, type, handler, id); - case 'touchmove': - return this.addPointerListenerMove(obj, type, handler, id); - default: - throw 'Unknown touch event type'; - } - }, - - addPointerListenerStart: function (obj, type, handler, id) { - var pre = '_leaflet_', - pointers = this._pointers; - - var cb = function (e) { - - L.DomEvent.preventDefault(e); - - var alreadyInArray = false; - for (var i = 0; i < pointers.length; i++) { - if (pointers[i].pointerId === e.pointerId) { - alreadyInArray = true; - break; - } - } - if (!alreadyInArray) { - pointers.push(e); - } - - e.touches = pointers.slice(); - e.changedTouches = [e]; - - handler(e); - }; - - obj[pre + 'touchstart' + id] = cb; - obj.addEventListener(this.POINTER_DOWN, cb, false); - - // need to also listen for end events to keep the _pointers list accurate - // this needs to be on the body and never go away - if (!this._pointerDocumentListener) { - var internalCb = function (e) { - for (var i = 0; i < pointers.length; i++) { - if (pointers[i].pointerId === e.pointerId) { - pointers.splice(i, 1); - break; - } - } - }; - //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there - document.documentElement.addEventListener(this.POINTER_UP, internalCb, false); - document.documentElement.addEventListener(this.POINTER_CANCEL, internalCb, false); - - this._pointerDocumentListener = true; - } - - return this; - }, - - addPointerListenerMove: function (obj, type, handler, id) { - var pre = '_leaflet_', - touches = this._pointers; - - function cb(e) { - - // don't fire touch moves when mouse isn't down - if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } - - for (var i = 0; i < touches.length; i++) { - if (touches[i].pointerId === e.pointerId) { - touches[i] = e; - break; - } - } - - e.touches = touches.slice(); - e.changedTouches = [e]; - - handler(e); - } - - obj[pre + 'touchmove' + id] = cb; - obj.addEventListener(this.POINTER_MOVE, cb, false); - - return this; - }, - - addPointerListenerEnd: function (obj, type, handler, id) { - var pre = '_leaflet_', - touches = this._pointers; - - var cb = function (e) { - for (var i = 0; i < touches.length; i++) { - if (touches[i].pointerId === e.pointerId) { - touches.splice(i, 1); - break; - } - } - - e.touches = touches.slice(); - e.changedTouches = [e]; - - handler(e); - }; - - obj[pre + 'touchend' + id] = cb; - obj.addEventListener(this.POINTER_UP, cb, false); - obj.addEventListener(this.POINTER_CANCEL, cb, false); - - return this; - }, - - removePointerListener: function (obj, type, id) { - var pre = '_leaflet_', - cb = obj[pre + type + id]; - - switch (type) { - case 'touchstart': - obj.removeEventListener(this.POINTER_DOWN, cb, false); - break; - case 'touchmove': - obj.removeEventListener(this.POINTER_MOVE, cb, false); - break; - case 'touchend': - obj.removeEventListener(this.POINTER_UP, cb, false); - obj.removeEventListener(this.POINTER_CANCEL, cb, false); - break; - } - - return this; - } -}); - - -/* - * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers. - */ - -L.Map.mergeOptions({ - touchZoom: L.Browser.touch && !L.Browser.android23, - bounceAtZoomLimits: true -}); - -L.Map.TouchZoom = L.Handler.extend({ - addHooks: function () { - L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this); - }, - - removeHooks: function () { - L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this); - }, - - _onTouchStart: function (e) { - var map = this._map; - - if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; } - - var p1 = map.mouseEventToLayerPoint(e.touches[0]), - p2 = map.mouseEventToLayerPoint(e.touches[1]), - viewCenter = map._getCenterLayerPoint(); - - this._startCenter = p1.add(p2)._divideBy(2); - this._startDist = p1.distanceTo(p2); - - this._moved = false; - this._zooming = true; - - this._centerOffset = viewCenter.subtract(this._startCenter); - - if (map._panAnim) { - map._panAnim.stop(); - } - - L.DomEvent - .on(document, 'touchmove', this._onTouchMove, this) - .on(document, 'touchend', this._onTouchEnd, this); - - L.DomEvent.preventDefault(e); - }, - - _onTouchMove: function (e) { - var map = this._map; - - if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; } - - var p1 = map.mouseEventToLayerPoint(e.touches[0]), - p2 = map.mouseEventToLayerPoint(e.touches[1]); - - this._scale = p1.distanceTo(p2) / this._startDist; - this._delta = p1._add(p2)._divideBy(2)._subtract(this._startCenter); - - if (this._scale === 1) { return; } - - if (!map.options.bounceAtZoomLimits) { - if ((map.getZoom() === map.getMinZoom() && this._scale < 1) || - (map.getZoom() === map.getMaxZoom() && this._scale > 1)) { return; } - } - - if (!this._moved) { - L.DomUtil.addClass(map._mapPane, 'leaflet-touching'); - - map - .fire('movestart') - .fire('zoomstart'); - - this._moved = true; - } - - L.Util.cancelAnimFrame(this._animRequest); - this._animRequest = L.Util.requestAnimFrame( - this._updateOnMove, this, true, this._map._container); - - L.DomEvent.preventDefault(e); - }, - - _updateOnMove: function () { - var map = this._map, - origin = this._getScaleOrigin(), - center = map.layerPointToLatLng(origin), - zoom = map.getScaleZoom(this._scale); - - map._animateZoom(center, zoom, this._startCenter, this._scale, this._delta); - }, - - _onTouchEnd: function () { - if (!this._moved || !this._zooming) { - this._zooming = false; - return; - } - - var map = this._map; - - this._zooming = false; - L.DomUtil.removeClass(map._mapPane, 'leaflet-touching'); - L.Util.cancelAnimFrame(this._animRequest); - - L.DomEvent - .off(document, 'touchmove', this._onTouchMove) - .off(document, 'touchend', this._onTouchEnd); - - var origin = this._getScaleOrigin(), - center = map.layerPointToLatLng(origin), - - oldZoom = map.getZoom(), - floatZoomDelta = map.getScaleZoom(this._scale) - oldZoom, - roundZoomDelta = (floatZoomDelta > 0 ? - Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)), - - zoom = map._limitZoom(oldZoom + roundZoomDelta), - scale = map.getZoomScale(zoom) / this._scale; - - map._animateZoom(center, zoom, origin, scale); - }, - - _getScaleOrigin: function () { - var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale); - return this._startCenter.add(centerOffset); - } -}); - -L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom); - - -/* - * L.Map.Tap is used to enable mobile hacks like quick taps and long hold. - */ - -L.Map.mergeOptions({ - tap: true, - tapTolerance: 15 -}); - -L.Map.Tap = L.Handler.extend({ - addHooks: function () { - L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this); - }, - - removeHooks: function () { - L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this); - }, - - _onDown: function (e) { - if (!e.touches) { return; } - - L.DomEvent.preventDefault(e); - - this._fireClick = true; - - // don't simulate click or track longpress if more than 1 touch - if (e.touches.length > 1) { - this._fireClick = false; - clearTimeout(this._holdTimeout); - return; - } - - var first = e.touches[0], - el = first.target; - - this._startPos = this._newPos = new L.Point(first.clientX, first.clientY); - - // if touching a link, highlight it - if (el.tagName && el.tagName.toLowerCase() === 'a') { - L.DomUtil.addClass(el, 'leaflet-active'); - } - - // simulate long hold but setting a timeout - this._holdTimeout = setTimeout(L.bind(function () { - if (this._isTapValid()) { - this._fireClick = false; - this._onUp(); - this._simulateEvent('contextmenu', first); - } - }, this), 1000); - - L.DomEvent - .on(document, 'touchmove', this._onMove, this) - .on(document, 'touchend', this._onUp, this); - }, - - _onUp: function (e) { - clearTimeout(this._holdTimeout); - - L.DomEvent - .off(document, 'touchmove', this._onMove, this) - .off(document, 'touchend', this._onUp, this); - - if (this._fireClick && e && e.changedTouches) { - - var first = e.changedTouches[0], - el = first.target; - - if (el && el.tagName && el.tagName.toLowerCase() === 'a') { - L.DomUtil.removeClass(el, 'leaflet-active'); - } - - // simulate click if the touch didn't move too much - if (this._isTapValid()) { - this._simulateEvent('click', first); - } - } - }, - - _isTapValid: function () { - return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; - }, - - _onMove: function (e) { - var first = e.touches[0]; - this._newPos = new L.Point(first.clientX, first.clientY); - }, - - _simulateEvent: function (type, e) { - var simulatedEvent = document.createEvent('MouseEvents'); - - simulatedEvent._simulated = true; - e.target._simulatedClick = true; - - simulatedEvent.initMouseEvent( - type, true, true, window, 1, - e.screenX, e.screenY, - e.clientX, e.clientY, - false, false, false, false, 0, null); - - e.target.dispatchEvent(simulatedEvent); - } -}); - -if (L.Browser.touch && !L.Browser.pointer) { - L.Map.addInitHook('addHandler', 'tap', L.Map.Tap); -} - - -/* - * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map - * (zoom to a selected bounding box), enabled by default. - */ - -L.Map.mergeOptions({ - boxZoom: true -}); - -L.Map.BoxZoom = L.Handler.extend({ - initialize: function (map) { - this._map = map; - this._container = map._container; - this._pane = map._panes.overlayPane; - this._moved = false; - }, - - addHooks: function () { - L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this); - }, - - removeHooks: function () { - L.DomEvent.off(this._container, 'mousedown', this._onMouseDown); - this._moved = false; - }, - - moved: function () { - return this._moved; - }, - - _onMouseDown: function (e) { - this._moved = false; - - if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } - - L.DomUtil.disableTextSelection(); - L.DomUtil.disableImageDrag(); - - this._startLayerPoint = this._map.mouseEventToLayerPoint(e); - - this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane); - L.DomUtil.setPosition(this._box, this._startLayerPoint); - - //TODO refactor: move cursor to styles - this._container.style.cursor = 'crosshair'; - - L.DomEvent - .on(document, 'mousemove', this._onMouseMove, this) - .on(document, 'mouseup', this._onMouseUp, this) - .on(document, 'keydown', this._onKeyDown, this); - - this._map.fire('boxzoomstart'); - }, - - _onMouseMove: function (e) { - var startPoint = this._startLayerPoint, - box = this._box, - - layerPoint = this._map.mouseEventToLayerPoint(e), - offset = layerPoint.subtract(startPoint), - - newPos = new L.Point( - Math.min(layerPoint.x, startPoint.x), - Math.min(layerPoint.y, startPoint.y)); - - L.DomUtil.setPosition(box, newPos); - - this._moved = true; - - // TODO refactor: remove hardcoded 4 pixels - box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px'; - box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px'; - }, - - _finish: function () { - this._pane.removeChild(this._box); - this._container.style.cursor = ''; - - L.DomUtil.enableTextSelection(); - L.DomUtil.enableImageDrag(); - - L.DomEvent - .off(document, 'mousemove', this._onMouseMove) - .off(document, 'mouseup', this._onMouseUp) - .off(document, 'keydown', this._onKeyDown); - }, - - _onMouseUp: function (e) { - - this._finish(); - - var map = this._map, - layerPoint = map.mouseEventToLayerPoint(e); - - if (this._startLayerPoint.equals(layerPoint)) { return; } - - var bounds = new L.LatLngBounds( - map.layerPointToLatLng(this._startLayerPoint), - map.layerPointToLatLng(layerPoint)); - - map.fitBounds(bounds); - - map.fire('boxzoomend', { - boxZoomBounds: bounds - }); - }, - - _onKeyDown: function (e) { - if (e.keyCode === 27) { - this._finish(); - } - } -}); - -L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom); - - -/* - * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. - */ - -L.Map.mergeOptions({ - keyboard: true, - keyboardPanOffset: 80, - keyboardZoomOffset: 1 -}); - -L.Map.Keyboard = L.Handler.extend({ - - keyCodes: { - left: [37], - right: [39], - down: [40], - up: [38], - zoomIn: [187, 107, 61, 171], - zoomOut: [189, 109, 173] - }, - - initialize: function (map) { - this._map = map; - - this._setPanOffset(map.options.keyboardPanOffset); - this._setZoomOffset(map.options.keyboardZoomOffset); - }, - - addHooks: function () { - var container = this._map._container; - - // make the container focusable by tabbing - if (container.tabIndex === -1) { - container.tabIndex = '0'; - } - - L.DomEvent - .on(container, 'focus', this._onFocus, this) - .on(container, 'blur', this._onBlur, this) - .on(container, 'mousedown', this._onMouseDown, this); - - this._map - .on('focus', this._addHooks, this) - .on('blur', this._removeHooks, this); - }, - - removeHooks: function () { - this._removeHooks(); - - var container = this._map._container; - - L.DomEvent - .off(container, 'focus', this._onFocus, this) - .off(container, 'blur', this._onBlur, this) - .off(container, 'mousedown', this._onMouseDown, this); - - this._map - .off('focus', this._addHooks, this) - .off('blur', this._removeHooks, this); - }, - - _onMouseDown: function () { - if (this._focused) { return; } - - var body = document.body, - docEl = document.documentElement, - top = body.scrollTop || docEl.scrollTop, - left = body.scrollLeft || docEl.scrollLeft; - - this._map._container.focus(); - - window.scrollTo(left, top); - }, - - _onFocus: function () { - this._focused = true; - this._map.fire('focus'); - }, - - _onBlur: function () { - this._focused = false; - this._map.fire('blur'); - }, - - _setPanOffset: function (pan) { - var keys = this._panKeys = {}, - codes = this.keyCodes, - i, len; - - for (i = 0, len = codes.left.length; i < len; i++) { - keys[codes.left[i]] = [-1 * pan, 0]; - } - for (i = 0, len = codes.right.length; i < len; i++) { - keys[codes.right[i]] = [pan, 0]; - } - for (i = 0, len = codes.down.length; i < len; i++) { - keys[codes.down[i]] = [0, pan]; - } - for (i = 0, len = codes.up.length; i < len; i++) { - keys[codes.up[i]] = [0, -1 * pan]; - } - }, - - _setZoomOffset: function (zoom) { - var keys = this._zoomKeys = {}, - codes = this.keyCodes, - i, len; - - for (i = 0, len = codes.zoomIn.length; i < len; i++) { - keys[codes.zoomIn[i]] = zoom; - } - for (i = 0, len = codes.zoomOut.length; i < len; i++) { - keys[codes.zoomOut[i]] = -zoom; - } - }, - - _addHooks: function () { - L.DomEvent.on(document, 'keydown', this._onKeyDown, this); - }, - - _removeHooks: function () { - L.DomEvent.off(document, 'keydown', this._onKeyDown, this); - }, - - _onKeyDown: function (e) { - var key = e.keyCode, - map = this._map; - - if (key in this._panKeys) { - - if (map._panAnim && map._panAnim._inProgress) { return; } - - map.panBy(this._panKeys[key]); - - if (map.options.maxBounds) { - map.panInsideBounds(map.options.maxBounds); - } - - } else if (key in this._zoomKeys) { - map.setZoom(map.getZoom() + this._zoomKeys[key]); - - } else { - return; - } - - L.DomEvent.stop(e); - } -}); - -L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard); - - -/* - * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. - */ - -L.Handler.MarkerDrag = L.Handler.extend({ - initialize: function (marker) { - this._marker = marker; - }, - - addHooks: function () { - var icon = this._marker._icon; - if (!this._draggable) { - this._draggable = new L.Draggable(icon, icon); - } - - this._draggable - .on('dragstart', this._onDragStart, this) - .on('drag', this._onDrag, this) - .on('dragend', this._onDragEnd, this); - this._draggable.enable(); - L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-draggable'); - }, - - removeHooks: function () { - this._draggable - .off('dragstart', this._onDragStart, this) - .off('drag', this._onDrag, this) - .off('dragend', this._onDragEnd, this); - - this._draggable.disable(); - L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); - }, - - moved: function () { - return this._draggable && this._draggable._moved; - }, - - _onDragStart: function () { - this._marker - .closePopup() - .fire('movestart') - .fire('dragstart'); - L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-dragging'); - }, - - _onDrag: function () { - var marker = this._marker, - shadow = marker._shadow, - iconPos = L.DomUtil.getPosition(marker._icon), - latlng = marker._map.layerPointToLatLng(iconPos); - - // update shadow position - if (shadow) { - L.DomUtil.setPosition(shadow, iconPos); - } - - marker._latlng = latlng; - - marker - .fire('move', {latlng: latlng}) - .fire('drag'); - }, - - _onDragEnd: function () { - this._marker - .fire('moveend') - .fire('dragend'); - L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-dragging'); - } -}); - - -/* - * L.Control is a base class for implementing map controls. Handles positioning. - * All other controls extend from this class. - */ - -L.Control = L.Class.extend({ - options: { - position: 'topright' - }, - - initialize: function (options) { - L.setOptions(this, options); - }, - - getPosition: function () { - return this.options.position; - }, - - setPosition: function (position) { - var map = this._map; - - if (map) { - map.removeControl(this); - } - - this.options.position = position; - - if (map) { - map.addControl(this); - } - - return this; - }, - - getContainer: function () { - return this._container; - }, - - addTo: function (map) { - this._map = map; - - var container = this._container = this.onAdd(map), - pos = this.getPosition(), - corner = map._controlCorners[pos]; - - L.DomUtil.addClass(container, 'leaflet-control'); - - if (pos.indexOf('bottom') !== -1) { - corner.insertBefore(container, corner.firstChild); - } else { - corner.appendChild(container); - } - - return this; - }, - - removeFrom: function (map) { - var pos = this.getPosition(), - corner = map._controlCorners[pos]; - - corner.removeChild(this._container); - this._map = null; - - if (this.onRemove) { - this.onRemove(map); - } - - return this; - }, - - _refocusOnMap: function () { - if (this._map) { - this._map.getContainer().focus(); - } - } -}); - -L.control = function (options) { - return new L.Control(options); -}; - - -// adds control-related methods to L.Map - -L.Map.include({ - addControl: function (control) { - control.addTo(this); - return this; - }, - - removeControl: function (control) { - control.removeFrom(this); - return this; - }, - - _initControlPos: function () { - var corners = this._controlCorners = {}, - l = 'leaflet-', - container = this._controlContainer = - L.DomUtil.create('div', l + 'control-container', this._container); - - function createCorner(vSide, hSide) { - var className = l + vSide + ' ' + l + hSide; - - corners[vSide + hSide] = L.DomUtil.create('div', className, container); - } - - createCorner('top', 'left'); - createCorner('top', 'right'); - createCorner('bottom', 'left'); - createCorner('bottom', 'right'); - }, - - _clearControlPos: function () { - this._container.removeChild(this._controlContainer); - } -}); - - -/* - * L.Control.Zoom is used for the default zoom buttons on the map. - */ - -L.Control.Zoom = L.Control.extend({ - options: { - position: 'topleft', - zoomInText: '+', - zoomInTitle: 'Zoom in', - zoomOutText: '-', - zoomOutTitle: 'Zoom out' - }, - - onAdd: function (map) { - var zoomName = 'leaflet-control-zoom', - container = L.DomUtil.create('div', zoomName + ' leaflet-bar'); - - this._map = map; - - this._zoomInButton = this._createButton( - this.options.zoomInText, this.options.zoomInTitle, - zoomName + '-in', container, this._zoomIn, this); - this._zoomOutButton = this._createButton( - this.options.zoomOutText, this.options.zoomOutTitle, - zoomName + '-out', container, this._zoomOut, this); - - this._updateDisabled(); - map.on('zoomend zoomlevelschange', this._updateDisabled, this); - - return container; - }, - - onRemove: function (map) { - map.off('zoomend zoomlevelschange', this._updateDisabled, this); - }, - - _zoomIn: function (e) { - this._map.zoomIn(e.shiftKey ? 3 : 1); - }, - - _zoomOut: function (e) { - this._map.zoomOut(e.shiftKey ? 3 : 1); - }, - - _createButton: function (html, title, className, container, fn, context) { - var link = L.DomUtil.create('a', className, container); - link.innerHTML = html; - link.href = '#'; - link.title = title; - - var stop = L.DomEvent.stopPropagation; - - L.DomEvent - .on(link, 'click', stop) - .on(link, 'mousedown', stop) - .on(link, 'dblclick', stop) - .on(link, 'click', L.DomEvent.preventDefault) - .on(link, 'click', fn, context) - .on(link, 'click', this._refocusOnMap, context); - - return link; - }, - - _updateDisabled: function () { - var map = this._map, - className = 'leaflet-disabled'; - - L.DomUtil.removeClass(this._zoomInButton, className); - L.DomUtil.removeClass(this._zoomOutButton, className); - - if (map._zoom === map.getMinZoom()) { - L.DomUtil.addClass(this._zoomOutButton, className); - } - if (map._zoom === map.getMaxZoom()) { - L.DomUtil.addClass(this._zoomInButton, className); - } - } -}); - -L.Map.mergeOptions({ - zoomControl: true -}); - -L.Map.addInitHook(function () { - if (this.options.zoomControl) { - this.zoomControl = new L.Control.Zoom(); - this.addControl(this.zoomControl); - } -}); - -L.control.zoom = function (options) { - return new L.Control.Zoom(options); -}; - - - -/* - * L.Control.Attribution is used for displaying attribution on the map (added by default). - */ - -L.Control.Attribution = L.Control.extend({ - options: { - position: 'bottomright', - prefix: 'Leaflet' - }, - - initialize: function (options) { - L.setOptions(this, options); - - this._attributions = {}; - }, - - onAdd: function (map) { - this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); - L.DomEvent.disableClickPropagation(this._container); - - map - .on('layeradd', this._onLayerAdd, this) - .on('layerremove', this._onLayerRemove, this); - - this._update(); - - return this._container; - }, - - onRemove: function (map) { - map - .off('layeradd', this._onLayerAdd) - .off('layerremove', this._onLayerRemove); - - }, - - setPrefix: function (prefix) { - this.options.prefix = prefix; - this._update(); - return this; - }, - - addAttribution: function (text) { - if (!text) { return; } - - if (!this._attributions[text]) { - this._attributions[text] = 0; - } - this._attributions[text]++; - - this._update(); - - return this; - }, - - removeAttribution: function (text) { - if (!text) { return; } - - if (this._attributions[text]) { - this._attributions[text]--; - this._update(); - } - - return this; - }, - - _update: function () { - if (!this._map) { return; } - - var attribs = []; - - for (var i in this._attributions) { - if (this._attributions[i]) { - attribs.push(i); - } - } - - var prefixAndAttribs = []; - - if (this.options.prefix) { - prefixAndAttribs.push(this.options.prefix); - } - if (attribs.length) { - prefixAndAttribs.push(attribs.join(', ')); - } - - this._container.innerHTML = prefixAndAttribs.join(' | '); - }, - - _onLayerAdd: function (e) { - if (e.layer.getAttribution) { - this.addAttribution(e.layer.getAttribution()); - } - }, - - _onLayerRemove: function (e) { - if (e.layer.getAttribution) { - this.removeAttribution(e.layer.getAttribution()); - } - } -}); - -L.Map.mergeOptions({ - attributionControl: true -}); - -L.Map.addInitHook(function () { - if (this.options.attributionControl) { - this.attributionControl = (new L.Control.Attribution()).addTo(this); - } -}); - -L.control.attribution = function (options) { - return new L.Control.Attribution(options); -}; - - -/* - * L.Control.Scale is used for displaying metric/imperial scale on the map. - */ - -L.Control.Scale = L.Control.extend({ - options: { - position: 'bottomleft', - maxWidth: 100, - metric: true, - imperial: true, - updateWhenIdle: false - }, - - onAdd: function (map) { - this._map = map; - - var className = 'leaflet-control-scale', - container = L.DomUtil.create('div', className), - options = this.options; - - this._addScales(options, className, container); - - map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); - map.whenReady(this._update, this); - - return container; - }, - - onRemove: function (map) { - map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); - }, - - _addScales: function (options, className, container) { - if (options.metric) { - this._mScale = L.DomUtil.create('div', className + '-line', container); - } - if (options.imperial) { - this._iScale = L.DomUtil.create('div', className + '-line', container); - } - }, - - _update: function () { - var bounds = this._map.getBounds(), - centerLat = bounds.getCenter().lat, - halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180), - dist = halfWorldMeters * (bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 180, - - size = this._map.getSize(), - options = this.options, - maxMeters = 0; - - if (size.x > 0) { - maxMeters = dist * (options.maxWidth / size.x); - } - - this._updateScales(options, maxMeters); - }, - - _updateScales: function (options, maxMeters) { - if (options.metric && maxMeters) { - this._updateMetric(maxMeters); - } - - if (options.imperial && maxMeters) { - this._updateImperial(maxMeters); - } - }, - - _updateMetric: function (maxMeters) { - var meters = this._getRoundNum(maxMeters); - - this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px'; - this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; - }, - - _updateImperial: function (maxMeters) { - var maxFeet = maxMeters * 3.2808399, - scale = this._iScale, - maxMiles, miles, feet; - - if (maxFeet > 5280) { - maxMiles = maxFeet / 5280; - miles = this._getRoundNum(maxMiles); - - scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px'; - scale.innerHTML = miles + ' mi'; - - } else { - feet = this._getRoundNum(maxFeet); - - scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px'; - scale.innerHTML = feet + ' ft'; - } - }, - - _getScaleWidth: function (ratio) { - return Math.round(this.options.maxWidth * ratio) - 10; - }, - - _getRoundNum: function (num) { - var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), - d = num / pow10; - - d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; - - return pow10 * d; - } -}); - -L.control.scale = function (options) { - return new L.Control.Scale(options); -}; - - -/* - * L.Control.Layers is a control to allow users to switch between different layers on the map. - */ - -L.Control.Layers = L.Control.extend({ - options: { - collapsed: true, - position: 'topright', - autoZIndex: true - }, - - initialize: function (baseLayers, overlays, options) { - L.setOptions(this, options); - - this._layers = {}; - this._lastZIndex = 0; - this._handlingClick = false; - - for (var i in baseLayers) { - this._addLayer(baseLayers[i], i); - } - - for (i in overlays) { - this._addLayer(overlays[i], i, true); - } - }, - - onAdd: function (map) { - this._initLayout(); - this._update(); - - map - .on('layeradd', this._onLayerChange, this) - .on('layerremove', this._onLayerChange, this); - - return this._container; - }, - - onRemove: function (map) { - map - .off('layeradd', this._onLayerChange) - .off('layerremove', this._onLayerChange); - }, - - addBaseLayer: function (layer, name) { - this._addLayer(layer, name); - this._update(); - return this; - }, - - addOverlay: function (layer, name) { - this._addLayer(layer, name, true); - this._update(); - return this; - }, - - removeLayer: function (layer) { - var id = L.stamp(layer); - delete this._layers[id]; - this._update(); - return this; - }, - - _initLayout: function () { - var className = 'leaflet-control-layers', - container = this._container = L.DomUtil.create('div', className); - - //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released - container.setAttribute('aria-haspopup', true); - - if (!L.Browser.touch) { - L.DomEvent - .disableClickPropagation(container) - .disableScrollPropagation(container); - } else { - L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); - } - - var form = this._form = L.DomUtil.create('form', className + '-list'); - - if (this.options.collapsed) { - if (!L.Browser.android) { - L.DomEvent - .on(container, 'mouseover', this._expand, this) - .on(container, 'mouseout', this._collapse, this); - } - var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container); - link.href = '#'; - link.title = 'Layers'; - - if (L.Browser.touch) { - L.DomEvent - .on(link, 'click', L.DomEvent.stop) - .on(link, 'click', this._expand, this); - } - else { - L.DomEvent.on(link, 'focus', this._expand, this); - } - - this._map.on('click', this._collapse, this); - // TODO keyboard accessibility - } else { - this._expand(); - } - - this._baseLayersList = L.DomUtil.create('div', className + '-base', form); - this._separator = L.DomUtil.create('div', className + '-separator', form); - this._overlaysList = L.DomUtil.create('div', className + '-overlays', form); - - container.appendChild(form); - }, - - _addLayer: function (layer, name, overlay) { - var id = L.stamp(layer); - - this._layers[id] = { - layer: layer, - name: name, - overlay: overlay - }; - - if (this.options.autoZIndex && layer.setZIndex) { - this._lastZIndex++; - layer.setZIndex(this._lastZIndex); - } - }, - - _update: function () { - if (!this._container) { - return; - } - - this._baseLayersList.innerHTML = ''; - this._overlaysList.innerHTML = ''; - - var baseLayersPresent = false, - overlaysPresent = false, - i, obj; - - for (i in this._layers) { - obj = this._layers[i]; - this._addItem(obj); - overlaysPresent = overlaysPresent || obj.overlay; - baseLayersPresent = baseLayersPresent || !obj.overlay; - } - - this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; - }, - - _onLayerChange: function (e) { - var obj = this._layers[L.stamp(e.layer)]; - - if (!obj) { return; } - - if (!this._handlingClick) { - this._update(); - } - - var type = obj.overlay ? - (e.type === 'layeradd' ? 'overlayadd' : 'overlayremove') : - (e.type === 'layeradd' ? 'baselayerchange' : null); - - if (type) { - this._map.fire(type, obj); - } - }, - - // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) - _createRadioElement: function (name, checked) { - - var radioHtml = ' this.options.zoomAnimationThreshold) { return false; } - - // offset is the pixel coords of the zoom origin relative to the current center - var scale = this.getZoomScale(zoom), - offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale), - origin = this._getCenterLayerPoint()._add(offset); - - // don't animate if the zoom origin isn't within one screen from the current center, unless forced - if (options.animate !== true && !this.getSize().contains(offset)) { return false; } - - this - .fire('movestart') - .fire('zoomstart'); - - this._animateZoom(center, zoom, origin, scale, null, true); - - return true; - }, - - _animateZoom: function (center, zoom, origin, scale, delta, backwards) { - - this._animatingZoom = true; - - // put transform transition on all layers with leaflet-zoom-animated class - L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim'); - - // remember what center/zoom to set after animation - this._animateToCenter = center; - this._animateToZoom = zoom; - - // disable any dragging during animation - if (L.Draggable) { - L.Draggable._disabled = true; - } - - this.fire('zoomanim', { - center: center, - zoom: zoom, - origin: origin, - scale: scale, - delta: delta, - backwards: backwards - }); - }, - - _onZoomTransitionEnd: function () { - - this._animatingZoom = false; - - L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim'); - - this._resetView(this._animateToCenter, this._animateToZoom, true, true); - - if (L.Draggable) { - L.Draggable._disabled = false; - } - } -}); - - -/* - Zoom animation logic for L.TileLayer. -*/ - -L.TileLayer.include({ - _animateZoom: function (e) { - if (!this._animating) { - this._animating = true; - this._prepareBgBuffer(); - } - - var bg = this._bgBuffer, - transform = L.DomUtil.TRANSFORM, - initialTransform = e.delta ? L.DomUtil.getTranslateString(e.delta) : bg.style[transform], - scaleStr = L.DomUtil.getScaleString(e.scale, e.origin); - - bg.style[transform] = e.backwards ? - scaleStr + ' ' + initialTransform : - initialTransform + ' ' + scaleStr; - }, - - _endZoomAnim: function () { - var front = this._tileContainer, - bg = this._bgBuffer; - - front.style.visibility = ''; - front.parentNode.appendChild(front); // Bring to fore - - // force reflow - L.Util.falseFn(bg.offsetWidth); - - this._animating = false; - }, - - _clearBgBuffer: function () { - var map = this._map; - - if (map && !map._animatingZoom && !map.touchZoom._zooming) { - this._bgBuffer.innerHTML = ''; - this._bgBuffer.style[L.DomUtil.TRANSFORM] = ''; - } - }, - - _prepareBgBuffer: function () { - - var front = this._tileContainer, - bg = this._bgBuffer; - - // if foreground layer doesn't have many tiles but bg layer does, - // keep the existing bg layer and just zoom it some more - - var bgLoaded = this._getLoadedTilesPercentage(bg), - frontLoaded = this._getLoadedTilesPercentage(front); - - if (bg && bgLoaded > 0.5 && frontLoaded < 0.5) { - - front.style.visibility = 'hidden'; - this._stopLoadingImages(front); - return; - } - - // prepare the buffer to become the front tile pane - bg.style.visibility = 'hidden'; - bg.style[L.DomUtil.TRANSFORM] = ''; - - // switch out the current layer to be the new bg layer (and vice-versa) - this._tileContainer = bg; - bg = this._bgBuffer = front; - - this._stopLoadingImages(bg); - - //prevent bg buffer from clearing right after zoom - clearTimeout(this._clearBgBufferTimer); - }, - - _getLoadedTilesPercentage: function (container) { - var tiles = container.getElementsByTagName('img'), - i, len, count = 0; - - for (i = 0, len = tiles.length; i < len; i++) { - if (tiles[i].complete) { - count++; - } - } - return count / len; - }, - - // stops loading all tiles in the background layer - _stopLoadingImages: function (container) { - var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')), - i, len, tile; - - for (i = 0, len = tiles.length; i < len; i++) { - tile = tiles[i]; - - if (!tile.complete) { - tile.onload = L.Util.falseFn; - tile.onerror = L.Util.falseFn; - tile.src = L.Util.emptyImageUrl; - - tile.parentNode.removeChild(tile); - } - } - } -}); - - -/* - * Provides L.Map with convenient shortcuts for using browser geolocation features. - */ - -L.Map.include({ - _defaultLocateOptions: { - watch: false, - setView: false, - maxZoom: Infinity, - timeout: 10000, - maximumAge: 0, - enableHighAccuracy: false - }, - - locate: function (/*Object*/ options) { - - options = this._locateOptions = L.extend(this._defaultLocateOptions, options); - - if (!navigator.geolocation) { - this._handleGeolocationError({ - code: 0, - message: 'Geolocation not supported.' - }); - return this; - } - - var onResponse = L.bind(this._handleGeolocationResponse, this), - onError = L.bind(this._handleGeolocationError, this); - - if (options.watch) { - this._locationWatchId = - navigator.geolocation.watchPosition(onResponse, onError, options); - } else { - navigator.geolocation.getCurrentPosition(onResponse, onError, options); - } - return this; - }, - - stopLocate: function () { - if (navigator.geolocation) { - navigator.geolocation.clearWatch(this._locationWatchId); - } - if (this._locateOptions) { - this._locateOptions.setView = false; - } - return this; - }, - - _handleGeolocationError: function (error) { - var c = error.code, - message = error.message || - (c === 1 ? 'permission denied' : - (c === 2 ? 'position unavailable' : 'timeout')); - - if (this._locateOptions.setView && !this._loaded) { - this.fitWorld(); - } - - this.fire('locationerror', { - code: c, - message: 'Geolocation error: ' + message + '.' - }); - }, - - _handleGeolocationResponse: function (pos) { - var lat = pos.coords.latitude, - lng = pos.coords.longitude, - latlng = new L.LatLng(lat, lng), - - latAccuracy = 180 * pos.coords.accuracy / 40075017, - lngAccuracy = latAccuracy / Math.cos(L.LatLng.DEG_TO_RAD * lat), - - bounds = L.latLngBounds( - [lat - latAccuracy, lng - lngAccuracy], - [lat + latAccuracy, lng + lngAccuracy]), - - options = this._locateOptions; - - if (options.setView) { - var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom); - this.setView(latlng, zoom); - } - - var data = { - latlng: latlng, - bounds: bounds, - timestamp: pos.timestamp - }; - - for (var i in pos.coords) { - if (typeof pos.coords[i] === 'number') { - data[i] = pos.coords[i]; - } - } - - this.fire('locationfound', data); - } -}); - - -}(window, document)); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet.css b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet.css deleted file mode 100755 index 1232550253..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet.css +++ /dev/null @@ -1,478 +0,0 @@ -/* required styles */ - -.leaflet-map-pane, -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow, -.leaflet-tile-pane, -.leaflet-tile-container, -.leaflet-overlay-pane, -.leaflet-shadow-pane, -.leaflet-marker-pane, -.leaflet-popup-pane, -.leaflet-overlay-pane svg, -.leaflet-zoom-box, -.leaflet-image-layer, -.leaflet-layer { - position: absolute; - left: 0; - top: 0; - } -.leaflet-container { - overflow: hidden; - -ms-touch-action: none; - } -.leaflet-tile, -.leaflet-marker-icon, -.leaflet-marker-shadow { - -webkit-user-select: none; - -moz-user-select: none; - user-select: none; - -webkit-user-drag: none; - } -.leaflet-marker-icon, -.leaflet-marker-shadow { - display: block; - } -/* map is broken in FF if you have max-width: 100% on tiles */ -.leaflet-container img { - max-width: none !important; - } -/* stupid Android 2 doesn't understand "max-width: none" properly */ -.leaflet-container img.leaflet-image-layer { - max-width: 15000px !important; - } -.leaflet-tile { - filter: inherit; - visibility: hidden; - } -.leaflet-tile-loaded { - visibility: inherit; - } -.leaflet-zoom-box { - width: 0; - height: 0; - } -/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ -.leaflet-overlay-pane svg { - -moz-user-select: none; - } - -.leaflet-tile-pane { z-index: 2; } -.leaflet-objects-pane { z-index: 3; } -.leaflet-overlay-pane { z-index: 4; } -.leaflet-shadow-pane { z-index: 5; } -.leaflet-marker-pane { z-index: 6; } -.leaflet-popup-pane { z-index: 7; } - -.leaflet-vml-shape { - width: 1px; - height: 1px; - } -.lvml { - behavior: url(#default#VML); - display: inline-block; - position: absolute; - } - - -/* control positioning */ - -.leaflet-control { - position: relative; - z-index: 7; - pointer-events: auto; - } -.leaflet-top, -.leaflet-bottom { - position: absolute; - z-index: 1000; - pointer-events: none; - } -.leaflet-top { - top: 0; - } -.leaflet-right { - right: 0; - } -.leaflet-bottom { - bottom: 0; - } -.leaflet-left { - left: 0; - } -.leaflet-control { - float: left; - clear: both; - } -.leaflet-right .leaflet-control { - float: right; - } -.leaflet-top .leaflet-control { - margin-top: 10px; - } -.leaflet-bottom .leaflet-control { - margin-bottom: 10px; - } -.leaflet-left .leaflet-control { - margin-left: 10px; - } -.leaflet-right .leaflet-control { - margin-right: 10px; - } - - -/* zoom and fade animations */ - -.leaflet-fade-anim .leaflet-tile, -.leaflet-fade-anim .leaflet-popup { - opacity: 0; - -webkit-transition: opacity 0.2s linear; - -moz-transition: opacity 0.2s linear; - -o-transition: opacity 0.2s linear; - transition: opacity 0.2s linear; - } -.leaflet-fade-anim .leaflet-tile-loaded, -.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { - opacity: 1; - } - -.leaflet-zoom-anim .leaflet-zoom-animated { - -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); - -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); - -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); - transition: transform 0.25s cubic-bezier(0,0,0.25,1); - } -.leaflet-zoom-anim .leaflet-tile, -.leaflet-pan-anim .leaflet-tile, -.leaflet-touching .leaflet-zoom-animated { - -webkit-transition: none; - -moz-transition: none; - -o-transition: none; - transition: none; - } - -.leaflet-zoom-anim .leaflet-zoom-hide { - visibility: hidden; - } - - -/* cursors */ - -.leaflet-clickable { - cursor: pointer; - } -.leaflet-container { - cursor: -webkit-grab; - cursor: -moz-grab; - } -.leaflet-popup-pane, -.leaflet-control { - cursor: auto; - } -.leaflet-dragging .leaflet-container, -.leaflet-dragging .leaflet-clickable { - cursor: move; - cursor: -webkit-grabbing; - cursor: -moz-grabbing; - } - - -/* visual tweaks */ - -.leaflet-container { - background: #ddd; - outline: 0; - } -.leaflet-container a { - color: #0078A8; - } -.leaflet-container a.leaflet-active { - outline: 2px solid orange; - } -.leaflet-zoom-box { - border: 2px dotted #38f; - background: rgba(255,255,255,0.5); - } - - -/* general typography */ -.leaflet-container { - font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; - } - - -/* general toolbar styles */ - -.leaflet-bar { - box-shadow: 0 1px 5px rgba(0,0,0,0.65); - border-radius: 4px; - } -.leaflet-bar a, -.leaflet-bar a:hover { - background-color: #fff; - border-bottom: 1px solid #ccc; - width: 26px; - height: 26px; - line-height: 26px; - display: block; - text-align: center; - text-decoration: none; - color: black; - } -.leaflet-bar a, -.leaflet-control-layers-toggle { - background-position: 50% 50%; - background-repeat: no-repeat; - display: block; - } -.leaflet-bar a:hover { - background-color: #f4f4f4; - } -.leaflet-bar a:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - } -.leaflet-bar a:last-child { - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-bottom: none; - } -.leaflet-bar a.leaflet-disabled { - cursor: default; - background-color: #f4f4f4; - color: #bbb; - } - -.leaflet-touch .leaflet-bar a { - width: 30px; - height: 30px; - line-height: 30px; - } - - -/* zoom control */ - -.leaflet-control-zoom-in, -.leaflet-control-zoom-out { - font: bold 18px 'Lucida Console', Monaco, monospace; - text-indent: 1px; - } -.leaflet-control-zoom-out { - font-size: 20px; - } - -.leaflet-touch .leaflet-control-zoom-in { - font-size: 22px; - } -.leaflet-touch .leaflet-control-zoom-out { - font-size: 24px; - } - - -/* layers control */ - -.leaflet-control-layers { - box-shadow: 0 1px 5px rgba(0,0,0,0.4); - background: #fff; - border-radius: 5px; - } -.leaflet-control-layers-toggle { - background-image: url(images/layers.png); - width: 36px; - height: 36px; - } -.leaflet-retina .leaflet-control-layers-toggle { - background-image: url(images/layers-2x.png); - background-size: 26px 26px; - } -.leaflet-touch .leaflet-control-layers-toggle { - width: 44px; - height: 44px; - } -.leaflet-control-layers .leaflet-control-layers-list, -.leaflet-control-layers-expanded .leaflet-control-layers-toggle { - display: none; - } -.leaflet-control-layers-expanded .leaflet-control-layers-list { - display: block; - position: relative; - } -.leaflet-control-layers-expanded { - padding: 6px 10px 6px 6px; - color: #333; - background: #fff; - } -.leaflet-control-layers-selector { - margin-top: 2px; - position: relative; - top: 1px; - } -.leaflet-control-layers label { - display: block; - } -.leaflet-control-layers-separator { - height: 0; - border-top: 1px solid #ddd; - margin: 5px -10px 5px -6px; - } - - -/* attribution and scale controls */ - -.leaflet-container .leaflet-control-attribution { - background: #fff; - background: rgba(255, 255, 255, 0.7); - margin: 0; - } -.leaflet-control-attribution, -.leaflet-control-scale-line { - padding: 0 5px; - color: #333; - } -.leaflet-control-attribution a { - text-decoration: none; - } -.leaflet-control-attribution a:hover { - text-decoration: underline; - } -.leaflet-container .leaflet-control-attribution, -.leaflet-container .leaflet-control-scale { - font-size: 11px; - } -.leaflet-left .leaflet-control-scale { - margin-left: 5px; - } -.leaflet-bottom .leaflet-control-scale { - margin-bottom: 5px; - } -.leaflet-control-scale-line { - border: 2px solid #777; - border-top: none; - line-height: 1.1; - padding: 2px 5px 1px; - font-size: 11px; - white-space: nowrap; - overflow: hidden; - -moz-box-sizing: content-box; - box-sizing: content-box; - - background: #fff; - background: rgba(255, 255, 255, 0.5); - } -.leaflet-control-scale-line:not(:first-child) { - border-top: 2px solid #777; - border-bottom: none; - margin-top: -2px; - } -.leaflet-control-scale-line:not(:first-child):not(:last-child) { - border-bottom: 2px solid #777; - } - -.leaflet-touch .leaflet-control-attribution, -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - box-shadow: none; - } -.leaflet-touch .leaflet-control-layers, -.leaflet-touch .leaflet-bar { - border: 2px solid rgba(0,0,0,0.2); - background-clip: padding-box; - } - - -/* popup */ - -.leaflet-popup { - position: absolute; - text-align: center; - } -.leaflet-popup-content-wrapper { - padding: 1px; - text-align: left; - border-radius: 12px; - } -.leaflet-popup-content { - margin: 13px 19px; - line-height: 1.4; - } -.leaflet-popup-content p { - margin: 18px 0; - } -.leaflet-popup-tip-container { - margin: 0 auto; - width: 40px; - height: 20px; - position: relative; - overflow: hidden; - } -.leaflet-popup-tip { - width: 17px; - height: 17px; - padding: 1px; - - margin: -10px auto 0; - - -webkit-transform: rotate(45deg); - -moz-transform: rotate(45deg); - -ms-transform: rotate(45deg); - -o-transform: rotate(45deg); - transform: rotate(45deg); - } -.leaflet-popup-content-wrapper, -.leaflet-popup-tip { - background: white; - - box-shadow: 0 3px 14px rgba(0,0,0,0.4); - } -.leaflet-container a.leaflet-popup-close-button { - position: absolute; - top: 0; - right: 0; - padding: 4px 4px 0 0; - text-align: center; - width: 18px; - height: 14px; - font: 16px/14px Tahoma, Verdana, sans-serif; - color: #c3c3c3; - text-decoration: none; - font-weight: bold; - background: transparent; - } -.leaflet-container a.leaflet-popup-close-button:hover { - color: #999; - } -.leaflet-popup-scrolled { - overflow: auto; - border-bottom: 1px solid #ddd; - border-top: 1px solid #ddd; - } - -.leaflet-oldie .leaflet-popup-content-wrapper { - zoom: 1; - } -.leaflet-oldie .leaflet-popup-tip { - width: 24px; - margin: 0 auto; - - -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)"; - filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678); - } -.leaflet-oldie .leaflet-popup-tip-container { - margin-top: -1px; - } - -.leaflet-oldie .leaflet-control-zoom, -.leaflet-oldie .leaflet-control-layers, -.leaflet-oldie .leaflet-popup-content-wrapper, -.leaflet-oldie .leaflet-popup-tip { - border: 1px solid #999; - } - - -/* div icon */ - -.leaflet-div-icon { - background: #fff; - border: 1px solid #666; - } diff --git a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet.js b/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet.js deleted file mode 100755 index 843a9713aa..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/libs/leaflet/leaflet.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com - (c) 2010-2013, Vladimir Agafonkin - (c) 2010-2011, CloudMade -*/ -!function(t,e,i){var n=t.L,o={};o.version="0.7-dev","object"==typeof module&&"object"==typeof module.exports?module.exports=o:"function"==typeof define&&define.amd&&define(o),o.noConflict=function(){return t.L=n,this},t.L=o,o.Util={extend:function(t){var e,i,n,o,s=Array.prototype.slice.call(arguments,1);for(i=0,n=s.length;n>i;i++){o=s[i]||{};for(e in o)o.hasOwnProperty(e)&&(t[e]=o[e])}return t},bind:function(t,e){var i=arguments.length>2?Array.prototype.slice.call(arguments,2):null;return function(){return t.apply(e,i||arguments)}},stamp:function(){var t=0,e="_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),invokeEach:function(t,e,i){var n,o;if("object"==typeof t){o=Array.prototype.slice.call(arguments,3);for(n in t)e.apply(i,[n,t[n]].concat(o));return!0}return!1},limitExecByInterval:function(t,e,i){var n,o;return function s(){var a=arguments;return n?(o=!0,void 0):(n=!0,setTimeout(function(){n=!1,o&&(s.apply(i,a),o=!1)},e),t.apply(i,a),void 0)}},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},trim:function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")},splitWords:function(t){return o.Util.trim(t).split(/\s+/)},setOptions:function(t,e){return t.options=o.extend({},t.options,e),t.options},getParamString:function(t,e,i){var n=[];for(var o in t)n.push(encodeURIComponent(i?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(e&&-1!==e.indexOf("?")?"&":"?")+n.join("&")},compileTemplate:function(t,e){return t=t.replace(/"/g,'\\"'),t=t.replace(/\{ *([\w_]+) *\}/g,function(t,i){return'" + o["'+i+'"]'+("function"==typeof e[i]?"(o)":"")+' + "'}),new Function("o",'return "'+t+'";')},template:function(t,e){var i=o.Util._templateCache=o.Util._templateCache||{};return i[t]=i[t]||o.Util.compileTemplate(t,e),i[t](e)},isArray:Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){var i,n,o=["webkit","moz","o","ms"];for(i=0;it;t++)n._initHooks[t].call(this)}},e},o.Class.include=function(t){o.extend(this.prototype,t)},o.Class.mergeOptions=function(t){o.extend(this.prototype.options,t)},o.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i)};var s="_leaflet_events";o.Mixin={},o.Mixin.Events={addEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addEventListener,this,e,i))return this;var n,a,r,h,l,u,c,p=this[s]=this[s]||{},d=i&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)r={action:e,context:i||this},h=t[n],i?(l=h+"_idx",u=l+"_len",c=p[l]=p[l]||{},c[d]||(c[d]=[],p[u]=(p[u]||0)+1),c[d].push(r)):(p[h]=p[h]||[],p[h].push(r));return this},hasEventListeners:function(t){var e=this[s];return!!e&&(t in e&&e[t].length>0||t+"_idx"in e&&e[t+"_idx_len"]>0)},removeEventListener:function(t,e,i){if(!this[s])return this;if(!t)return this.clearAllEventListeners();if(o.Util.invokeEach(t,this.removeEventListener,this,e,i))return this;var n,a,r,h,l,u,c,p,d,_=this[s],m=i&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)if(r=t[n],u=r+"_idx",c=u+"_len",p=_[u],e){if(h=i&&p?p[m]:_[r]){for(l=h.length-1;l>=0;l--)h[l].action!==e||i&&h[l].context!==i||(d=h.splice(l,1),d[0].action=o.Util.falseFn);i&&p&&0===h.length&&(delete p[m],_[c]--)}}else delete _[r],delete _[u];return this},clearAllEventListeners:function(){return delete this[s],this},fireEvent:function(t,e){if(!this.hasEventListeners(t))return this;var i,n,a,r,h,l=o.Util.extend({},e,{type:t,target:this}),u=this[s];if(u[t])for(i=u[t].slice(),n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context||this,l);r=u[t+"_idx"];for(h in r)if(i=r[h].slice())for(n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context||this,l);return this},addOneTimeEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addOneTimeEventListener,this,e,i))return this;var n=o.bind(function(){this.removeEventListener(t,e,i).removeEventListener(t,n,i)},this);return this.addEventListener(t,e,i).addEventListener(t,n,i)}},o.Mixin.Events.on=o.Mixin.Events.addEventListener,o.Mixin.Events.off=o.Mixin.Events.removeEventListener,o.Mixin.Events.once=o.Mixin.Events.addOneTimeEventListener,o.Mixin.Events.fire=o.Mixin.Events.fireEvent,function(){var n="ActiveXObject"in t,s=n&&!t.XMLHttpRequest,a=n&&!e.querySelector,r=n&&!e.addEventListener,h=navigator.userAgent.toLowerCase(),l=-1!==h.indexOf("webkit"),u=-1!==h.indexOf("chrome"),c=-1!==h.indexOf("phantom"),p=-1!==h.indexOf("android"),d=-1!==h.search("android [23]"),_=-1!==h.indexOf("gecko"),m=typeof orientation!=i+"",f=t.navigator&&t.navigator.msPointerEnabled&&t.navigator.msMaxTouchPoints&&!t.PointerEvent,g=t.PointerEvent&&t.navigator.pointerEnabled&&t.navigator.maxTouchPoints||f,v="devicePixelRatio"in t&&t.devicePixelRatio>1||"matchMedia"in t&&t.matchMedia("(min-resolution:144dpi)")&&t.matchMedia("(min-resolution:144dpi)").matches,y=e.documentElement,L=n&&"transition"in y.style,P="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix,x="MozPerspective"in y.style,w="OTransition"in y.style,T=!t.L_DISABLE_3D&&(L||P||x||w)&&!c,D=!t.L_NO_TOUCH&&!c&&function(){var t="ontouchstart";if(g||t in y)return!0;var i=e.createElement("div"),n=!1;return i.setAttribute?(i.setAttribute(t,"return;"),"function"==typeof i[t]&&(n=!0),i.removeAttribute(t),i=null,n):!1}();o.Browser={ie:n,ie6:s,ie7:a,ielt9:r,webkit:l,gecko:_&&!l&&!t.opera&&!n,android:p,android23:d,chrome:u,ie3d:L,webkit3d:P,gecko3d:x,opera3d:w,any3d:T,mobile:m,mobileWebkit:m&&l,mobileWebkit3d:m&&P,mobileOpera:m&&t.opera,touch:D,msPointer:f,pointer:g,retina:v}}(),o.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},o.Point.prototype={clone:function(){return new o.Point(this.x,this.y)},add:function(t){return this.clone()._add(o.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(o.point(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},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},distanceTo:function(t){t=o.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t=o.point(t),t.x===this.x&&t.y===this.y},contains:function(t){return t=o.point(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+o.Util.formatNum(this.x)+", "+o.Util.formatNum(this.y)+")"}},o.point=function(t,e,n){return t instanceof o.Point?t:o.Util.isArray(t)?new o.Point(t[0],t[1]):t===i||null===t?t:new o.Point(t,e,n)},o.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.Bounds.prototype={extend:function(t){return t=o.point(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 o.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new o.Point(this.min.x,this.max.y)},getTopRight:function(){return new o.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof o.Point?o.point(t):o.bounds(t),t instanceof o.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=o.bounds(t);var e=this.min,i=this.max,n=t.min,s=t.max,a=s.x>=e.x&&n.x<=i.x,r=s.y>=e.y&&n.y<=i.y;return a&&r},isValid:function(){return!(!this.min||!this.max)}},o.bounds=function(t,e){return!t||t instanceof o.Bounds?t:new o.Bounds(t,e)},o.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},o.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new o.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},o.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i];if(!n&&t.currentStyle&&(n=t.currentStyle[i]),(!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},getViewportOffset:function(t){var i,n=0,s=0,a=t,r=e.body,h=e.documentElement;do{if(n+=a.offsetTop||0,s+=a.offsetLeft||0,n+=parseInt(o.DomUtil.getStyle(a,"borderTopWidth"),10)||0,s+=parseInt(o.DomUtil.getStyle(a,"borderLeftWidth"),10)||0,i=o.DomUtil.getStyle(a,"position"),a.offsetParent===r&&"absolute"===i)break;if("fixed"===i){n+=r.scrollTop||h.scrollTop||0,s+=r.scrollLeft||h.scrollLeft||0;break}if("relative"===i&&!a.offsetLeft){var l=o.DomUtil.getStyle(a,"width"),u=o.DomUtil.getStyle(a,"max-width"),c=a.getBoundingClientRect();("none"!==l||"none"!==u)&&(s+=c.left+a.clientLeft),n+=c.top+(r.scrollTop||h.scrollTop||0);break}a=a.offsetParent}while(a);a=t;do{if(a===r)break;n-=a.scrollTop||0,s-=a.scrollLeft||0,a=a.parentNode}while(a);return new o.Point(s,n)},documentIsLtr:function(){return o.DomUtil._docIsLtrCached||(o.DomUtil._docIsLtrCached=!0,o.DomUtil._docIsLtr="ltr"===o.DomUtil.getStyle(e.body,"direction")),o.DomUtil._docIsLtr},create:function(t,i,n){var o=e.createElement(t);return o.className=i,n&&n.appendChild(o),o},hasClass:function(t,e){return t.className.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(t.className)},addClass:function(t,e){o.DomUtil.hasClass(t,e)||(t.className+=(t.className?" ":"")+e)},removeClass:function(t,e){t.className=o.Util.trim((" "+t.className+" ").replace(" "+e+" "," "))},setOpacity:function(t,e){if("opacity"in t.style)t.style.opacity=e;else if("filter"in t.style){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(o){if(1===e)return}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"}},testProp:function(t){for(var i=e.documentElement.style,n=0;ni||i===e?e:t),new o.LatLng(this.lat,i)}},o.latLng=function(t,e){return t instanceof o.LatLng?t:o.Util.isArray(t)?"number"==typeof t[0]||"string"==typeof t[0]?new o.LatLng(t[0],t[1]):null:t===i||null===t?t:"object"==typeof t&&"lat"in t?new o.LatLng(t.lat,"lng"in t?t.lng:t.lon):e===i?null:new o.LatLng(t,e)},o.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.LatLngBounds.prototype={extend:function(t){if(!t)return this;var e=o.latLng(t);return t=null!==e?e:o.latLngBounds(t),t instanceof o.LatLng?this._southWest||this._northEast?(this._southWest.lat=Math.min(t.lat,this._southWest.lat),this._southWest.lng=Math.min(t.lng,this._southWest.lng),this._northEast.lat=Math.max(t.lat,this._northEast.lat),this._northEast.lng=Math.max(t.lng,this._northEast.lng)):(this._southWest=new o.LatLng(t.lat,t.lng),this._northEast=new o.LatLng(t.lat,t.lng)):t instanceof o.LatLngBounds&&(this.extend(t._southWest),this.extend(t._northEast)),this},pad:function(t){var e=this._southWest,i=this._northEast,n=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new o.LatLngBounds(new o.LatLng(e.lat-n,e.lng-s),new o.LatLng(i.lat+n,i.lng+s))},getCenter:function(){return new o.LatLng((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 o.LatLng(this.getNorth(),this.getWest())},getSouthEast:function(){return new o.LatLng(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 o.LatLng?o.latLng(t):o.latLngBounds(t);var e,i,n=this._southWest,s=this._northEast;return t instanceof o.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=n.lat&&i.lat<=s.lat&&e.lng>=n.lng&&i.lng<=s.lng},intersects:function(t){t=o.latLngBounds(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),s=t.getNorthEast(),a=s.lat>=e.lat&&n.lat<=i.lat,r=s.lng>=e.lng&&n.lng<=i.lng;return a&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t){return t?(t=o.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast())):!1},isValid:function(){return!(!this._southWest||!this._northEast)}},o.latLngBounds=function(t,e){return!t||t instanceof o.LatLngBounds?t:new o.LatLngBounds(t,e)},o.Projection={},o.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=t.lng*e,a=n*e;return a=Math.log(Math.tan(Math.PI/4+a/2)),new o.Point(s,a)},unproject:function(t){var e=o.LatLng.RAD_TO_DEG,i=t.x*e,n=(2*Math.atan(Math.exp(t.y))-Math.PI/2)*e;return new o.LatLng(n,i)}},o.Projection.LonLat={project:function(t){return new o.Point(t.lng,t.lat)},unproject:function(t){return new o.LatLng(t.y,t.x)}},o.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},scale:function(t){return 256*Math.pow(2,t)}},o.CRS.Simple=o.extend({},o.CRS,{projection:o.Projection.LonLat,transformation:new o.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)}}),o.CRS.EPSG3857=o.extend({},o.CRS,{code:"EPSG:3857",projection:o.Projection.SphericalMercator,transformation:new o.Transformation(.5/Math.PI,.5,-.5/Math.PI,.5),project:function(t){var e=this.projection.project(t),i=6378137;return e.multiplyBy(i)}}),o.CRS.EPSG900913=o.extend({},o.CRS.EPSG3857,{code:"EPSG:900913"}),o.CRS.EPSG4326=o.extend({},o.CRS,{code:"EPSG:4326",projection:o.Projection.LonLat,transformation:new o.Transformation(1/360,.5,-1/360,.5)}),o.Map=o.Class.extend({includes:o.Mixin.Events,options:{crs:o.CRS.EPSG3857,fadeAnimation:o.DomUtil.TRANSITION&&!o.Browser.android23,trackResize:!0,markerZoomAnimation:o.DomUtil.TRANSITION&&o.Browser.any3d},initialize:function(t,e){e=o.setOptions(this,e),this._initContainer(t),this._initLayout(),this._onResize=o.bind(this._onResize,this),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.center&&e.zoom!==i&&this.setView(o.latLng(e.center),e.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0,this.callInitHooks(),this._addLayers(e.layers)},setView:function(t,e){return e=e===i?this.getZoom():e,this._resetView(o.latLng(t),this._limitZoom(e)),this},setZoom:function(t,e){return this._loaded?this.setView(this.getCenter(),t,{zoom:e}):(this._zoom=this._limitZoom(t),this)},zoomIn:function(t,e){return this.setZoom(this._zoom+(t||1),e)},zoomOut:function(t,e){return this.setZoom(this._zoom-(t||1),e)},setZoomAround:function(t,e,i){var n=this.getZoomScale(e),s=this.getSize().divideBy(2),a=t instanceof o.Point?t:this.latLngToContainerPoint(t),r=a.subtract(s).multiplyBy(1-1/n),h=this.containerPointToLatLng(s.add(r));return this.setView(h,e,{zoom:i})},fitBounds:function(t,e){e=e||{},t=t.getBounds?t.getBounds():o.latLngBounds(t);var i=o.point(e.paddingTopLeft||e.padding||[0,0]),n=o.point(e.paddingBottomRight||e.padding||[0,0]),s=this.getBoundsZoom(t,!1,i.add(n)),a=n.subtract(i).divideBy(2),r=this.project(t.getSouthWest(),s),h=this.project(t.getNorthEast(),s),l=this.unproject(r.add(h).divideBy(2).add(a),s);return s=e&&e.maxZoom?Math.min(e.maxZoom,s):s,this.setView(l,s,e)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,e){return this.setView(t,this._zoom,{pan:e})},panBy:function(t){return this.fire("movestart"),this._rawPanBy(o.point(t)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(t,e){if(t=o.latLngBounds(t),this.options.maxBounds=t,!t)return this._boundsMinZoom=null,this.off("moveend",this._panInsideMaxBounds,this),this;var i=this.getBoundsZoom(t,!0);return this._boundsMinZoom=i,this._loaded&&(this._zooma.x&&(r=Math.floor(a.x-n.x)),i.y>s.y&&(h=Math.floor(s.y-i.y)),i.x=s);return u&&e?null:e?s:s-1},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new o.Point(this._container.clientWidth,this._container.clientHeight),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(){var t=this._getTopLeftPoint();return new o.Bounds(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._initialTopLeftPoint},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t){var e=this.options.crs;return e.scale(t)/e.scale(this._zoom)},getScaleZoom:function(t){return this._zoom+Math.log(t)/Math.LN2},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(o.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(o.point(t),e)},layerPointToLatLng:function(t){var e=o.point(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(o.latLng(t))._round();return e._subtract(this.getPixelOrigin())},containerPointToLayerPoint:function(t){return o.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return o.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(o.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t)))},mouseEventToContainerPoint:function(t){return o.DomEvent.getMousePosition(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 e=this._container=o.DomUtil.get(t);if(!e)throw new Error("Map container not found.");if(e._leaflet)throw new Error("Map container is already initialized.");e._leaflet=!0},_initLayout:function(){var t=this._container;o.DomUtil.addClass(t,"leaflet-container"+(o.Browser.touch?" leaflet-touch":"")+(o.Browser.retina?" leaflet-retina":"")+(this.options.fadeAnimation?" leaflet-fade-anim":""));var e=o.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._mapPane=t.mapPane=this._createPane("leaflet-map-pane",this._container),this._tilePane=t.tilePane=this._createPane("leaflet-tile-pane",this._mapPane),t.objectsPane=this._createPane("leaflet-objects-pane",this._mapPane),t.shadowPane=this._createPane("leaflet-shadow-pane"),t.overlayPane=this._createPane("leaflet-overlay-pane"),t.markerPane=this._createPane("leaflet-marker-pane"),t.popupPane=this._createPane("leaflet-popup-pane");var e=" leaflet-zoom-hide";this.options.markerZoomAnimation||(o.DomUtil.addClass(t.markerPane,e),o.DomUtil.addClass(t.shadowPane,e),o.DomUtil.addClass(t.popupPane,e))},_createPane:function(t,e){return o.DomUtil.create("div",t,e||this._panes.objectsPane)},_clearPanes:function(){this._container.removeChild(this._mapPane)},_addLayers:function(t){t=t?o.Util.isArray(t)?t:[t]:[];for(var e=0,i=t.length;i>e;e++)this.addLayer(t[e])},_resetView:function(t,e,i,n){var s=this._zoom!==e;n||(this.fire("movestart"),s&&this.fire("zoomstart")),this._zoom=e,this._initialCenter=t,this._initialTopLeftPoint=this._getNewTopLeftPoint(t),i?this._initialTopLeftPoint._add(this._getMapPanePos()):o.DomUtil.setPosition(this._mapPane,new o.Point(0,0)),this._tileLayersToLoad=this._tileLayersNum;var a=!this._loaded;this._loaded=!0,a&&(this.fire("load"),this.eachLayer(this._layerAdd,this)),this.fire("viewreset",{hard:!i}),this.fire("move"),(s||n)&&this.fire("zoomend"),this.fire("moveend",{hard:!i})},_rawPanBy:function(t){o.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_updateZoomLevels:function(){var t,e=1/0,n=-1/0,o=this._getZoomSpan();for(t in this._zoomBoundLayers){var s=this._zoomBoundLayers[t];isNaN(s.options.minZoom)||(e=Math.min(e,s.options.minZoom)),isNaN(s.options.maxZoom)||(n=Math.max(n,s.options.maxZoom))}t===i?this._layersMaxZoom=this._layersMinZoom=i:(this._layersMaxZoom=n,this._layersMinZoom=e),o!==this._getZoomSpan()&&this.fire("zoomlevelschange")},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(e){if(o.DomEvent){e=e||"on",o.DomEvent[e](this._container,"click",this._onMouseClick,this);var i,n,s=["dblclick","mousedown","mouseup","mouseenter","mouseleave","mousemove","contextmenu"];for(i=0,n=s.length;n>i;i++)o.DomEvent[e](this._container,s[i],this._fireMouseEvent,this);this.options.trackResize&&o.DomEvent[e](t,"resize",this._onResize,this)}},_onResize:function(){o.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=o.Util.requestAnimFrame(this.invalidateSize,this,!1,this._container)},_onMouseClick:function(t){!this._loaded||!t._simulated&&(this.dragging&&this.dragging.moved()||this.boxZoom&&this.boxZoom.moved())||o.DomEvent._skipped(t)||(this.fire("preclick"),this._fireMouseEvent(t))},_fireMouseEvent:function(t){if(this._loaded&&!o.DomEvent._skipped(t)){var e=t.type;if(e="mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,this.hasEventListeners(e)){"contextmenu"===e&&o.DomEvent.preventDefault(t);var i=this.mouseEventToContainerPoint(t),n=this.containerPointToLayerPoint(i),s=this.layerPointToLatLng(n);this.fire(e,{latlng:s,layerPoint:n,containerPoint:i,originalEvent:t})}}},_onTileLayerLoad:function(){this._tileLayersToLoad--,this._tileLayersNum&&!this._tileLayersToLoad&&this.fire("tilelayersload")},_clearHandlers:function(){for(var t=0,e=this._handlers.length;e>t;t++)this._handlers[t].disable()},whenReady:function(t,e){return this._loaded?t.call(e||this,this):this.on("load",t,e),this},_layerAdd:function(t){t.onAdd(this),this.fire("layeradd",{layer:t})},_getMapPanePos:function(){return o.DomUtil.getPosition(this._mapPane)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(){return this.getPixelOrigin().subtract(this._getMapPanePos())},_getNewTopLeftPoint:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewTopLeftPoint(i,e).add(this._getMapPanePos());return this.project(t,e)._subtract(n)},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom();return Math.max(e,Math.min(i,t))}}),o.map=function(t,e){return new o.Map(t,e)},o.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.314245179,R_MAJOR:6378137,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=this.R_MAJOR,a=this.R_MINOR,r=t.lng*e*s,h=n*e,l=a/s,u=Math.sqrt(1-l*l),c=u*Math.sin(h);c=Math.pow((1-c)/(1+c),.5*u);var p=Math.tan(.5*(.5*Math.PI-h))/c;return h=-s*Math.log(p),new o.Point(r,h)},unproject:function(t){for(var e,i=o.LatLng.RAD_TO_DEG,n=this.R_MAJOR,s=this.R_MINOR,a=t.x*i/n,r=s/n,h=Math.sqrt(1-r*r),l=Math.exp(-t.y/n),u=Math.PI/2-2*Math.atan(l),c=15,p=1e-7,d=c,_=.1;Math.abs(_)>p&&--d>0;)e=h*Math.sin(u),_=Math.PI/2-2*Math.atan(l*Math.pow((1-e)/(1+e),.5*h))-u,u+=_;return new o.LatLng(u*i,a)}},o.CRS.EPSG3395=o.extend({},o.CRS,{code:"EPSG:3395",projection:o.Projection.Mercator,transformation:function(){var t=o.Projection.Mercator,e=t.R_MAJOR,i=.5/(Math.PI*e);return new o.Transformation(i,.5,-i,.5)}()}),o.TileLayer=o.Class.extend({includes:o.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:o.Browser.mobile,updateWhenIdle:o.Browser.mobile},initialize:function(t,e){e=o.setOptions(this,e),e.detectRetina&&o.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomOffset++,e.minZoom>0&&e.minZoom--,this.options.maxZoom--),e.bounds&&(e.bounds=o.latLngBounds(e.bounds)),this._url=t; -var i=this.options.subdomains;"string"==typeof i&&(this.options.subdomains=i.split(""))},onAdd:function(t){this._map=t,this._animated=t._zoomAnimated,this._initContainer(),t.on({viewreset:this._reset,moveend:this._update},this),this._animated&&t.on({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||(this._limitedUpdate=o.Util.limitExecByInterval(this._update,150,this),t.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._container.parentNode.removeChild(this._container),t.off({viewreset:this._reset,moveend:this._update},this),this._animated&&t.off({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||t.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var t=this._map._panes.tilePane;return this._container&&(t.appendChild(this._container),this._setAutoZIndex(t,Math.max)),this},bringToBack:function(){var t=this._map._panes.tilePane;return this._container&&(t.insertBefore(this._container,t.firstChild),this._setAutoZIndex(t,Math.min)),this},getAttribution:function(){return this.options.attribution},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},redraw:function(){return this._map&&(this._reset({hard:!0}),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t,e){var i,n,o,s=t.children,a=-e(1/0,-1/0);for(n=0,o=s.length;o>n;n++)s[n]!==this._container&&(i=parseInt(s[n].style.zIndex,10),isNaN(i)||(a=e(a,i)));this.options.zIndex=this._container.style.zIndex=(isFinite(a)?a:0)+e(1,-1)},_updateOpacity:function(){var t,e=this._tiles;if(o.Browser.ielt9)for(t in e)o.DomUtil.setOpacity(e[t],this.options.opacity);else o.DomUtil.setOpacity(this._container,this.options.opacity)},_initContainer:function(){var t=this._map._panes.tilePane;if(!this._container){if(this._container=o.DomUtil.create("div","leaflet-layer"),this._updateZIndex(),this._animated){var e="leaflet-tile-container";this._bgBuffer=o.DomUtil.create("div",e,this._container),this._tileContainer=o.DomUtil.create("div",e,this._container)}else this._tileContainer=this._container;t.appendChild(this._container),this.options.opacity<1&&this._updateOpacity()}},_reset:function(t){for(var e in this._tiles)this.fire("tileunload",{tile:this._tiles[e]});this._tiles={},this._tilesToLoad=0,this.options.reuseTiles&&(this._unusedTiles=[]),this._tileContainer.innerHTML="",this._animated&&t&&t.hard&&this._clearBgBuffer(),this._initContainer()},_getTileSize:function(){var t=this._map,e=t.getZoom(),i=this.options.maxNativeZoom,n=this.options.tileSize;return i&&e>i&&(n=Math.round(t.getZoomScale(e)/t.getZoomScale(i)*n)),n},_update:function(){if(this._map){var t=this._map,e=t.getPixelBounds(),i=t.getZoom(),n=this._getTileSize();if(!(i>this.options.maxZoom||in;n++)this._addTile(a[n],l);this._tileContainer.appendChild(l)}},_tileShouldBeLoaded:function(t){if(t.x+":"+t.y in this._tiles)return!1;var e=this.options;if(!e.continuousWorld){var i=this._getWrapTileNum();if(e.noWrap&&(t.x<0||t.x>=i)||t.y<0||t.y>=i)return!1}if(e.bounds){var n=e.tileSize,o=t.multiplyBy(n),s=o.add([n,n]),a=this._map.unproject(o),r=this._map.unproject(s);if(e.continuousWorld||e.noWrap||(a=a.wrap(),r=r.wrap()),!e.bounds.intersects([a,r]))return!1}return!0},_removeOtherTiles:function(t){var e,i,n,o;for(o in this._tiles)e=o.split(":"),i=parseInt(e[0],10),n=parseInt(e[1],10),(it.max.x||nt.max.y)&&this._removeTile(o)},_removeTile:function(t){var e=this._tiles[t];this.fire("tileunload",{tile:e,url:e.src}),this.options.reuseTiles?(o.DomUtil.removeClass(e,"leaflet-tile-loaded"),this._unusedTiles.push(e)):e.parentNode===this._tileContainer&&this._tileContainer.removeChild(e),o.Browser.android||(e.onload=null,e.src=o.Util.emptyImageUrl),delete this._tiles[t]},_addTile:function(t,e){var i=this._getTilePos(t),n=this._getTile();o.DomUtil.setPosition(n,i,o.Browser.chrome||o.Browser.android23),this._tiles[t.x+":"+t.y]=n,this._loadTile(n,t),n.parentNode!==this._tileContainer&&e.appendChild(n)},_getZoomForUrl:function(){var t=this.options,e=this._map.getZoom();return t.zoomReverse&&(e=t.maxZoom-e),e+=t.zoomOffset,t.maxNativeZoom?Math.min(e,t.maxNativeZoom):e},_getTilePos:function(t){var e=this._map.getPixelOrigin(),i=this._getTileSize();return t.multiplyBy(i).subtract(e)},getTileUrl:function(t){return o.Util.template(this._url,o.extend({s:this._getSubdomain(t),z:t.z,x:t.x,y:t.y},this.options))},_getWrapTileNum:function(){return Math.pow(2,this._getZoomForUrl())},_adjustTilePoint:function(t){var e=this._getWrapTileNum();this.options.continuousWorld||this.options.noWrap||(t.x=(t.x%e+e)%e),this.options.tms&&(t.y=e-t.y-1),t.z=this._getZoomForUrl()},_getSubdomain:function(t){var e=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var t=this._unusedTiles.pop();return this._resetTile(t),t}return this._createTile()},_resetTile:function(){},_createTile:function(){var t=o.DomUtil.create("img","leaflet-tile");return t.style.width=t.style.height=this._getTileSize()+"px",t.galleryimg="no",t.onselectstart=t.onmousemove=o.Util.falseFn,o.Browser.ielt9&&this.options.opacity!==i&&o.DomUtil.setOpacity(t,this.options.opacity),t},_loadTile:function(t,e){t._layer=this,t.onload=this._tileOnLoad,t.onerror=this._tileOnError,this._adjustTilePoint(e),t.src=this.getTileUrl(e),this.fire("tileloadstart",{tile:t,url:t.src})},_tileLoaded:function(){this._tilesToLoad--,this._animated&&o.DomUtil.addClass(this._tileContainer,"leaflet-zoom-animated"),this._tilesToLoad||(this.fire("load"),this._animated&&(clearTimeout(this._clearBgBufferTimer),this._clearBgBufferTimer=setTimeout(o.bind(this._clearBgBuffer,this),500)))},_tileOnLoad:function(){var t=this._layer;this.src!==o.Util.emptyImageUrl&&(o.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var e=t.options.errorTileUrl;e&&(this.src=e),t._tileLoaded()}}),o.tileLayer=function(t,e){return new o.TileLayer(t,e)},o.TileLayer.WMS=o.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(t,e){this._url=t;var i=o.extend({},this.defaultWmsParams),n=e.tileSize||this.options.tileSize;i.width=i.height=e.detectRetina&&o.Browser.retina?2*n:n;for(var s in e)this.options.hasOwnProperty(s)||"crs"===s||(i[s]=e[s]);this.wmsParams=i,o.setOptions(this,e)},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var e=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[e]=this._crs.code,o.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._map,i=this.options.tileSize,n=t.multiplyBy(i),s=n.add([i,i]),a=this._crs.project(e.unproject(n,t.z)),r=this._crs.project(e.unproject(s,t.z)),h=this._wmsVersion>=1.3&&this._crs===o.CRS.EPSG4326?[r.y,a.x,a.y,r.x].join(","):[a.x,r.y,r.x,a.y].join(","),l=o.Util.template(this._url,{s:this._getSubdomain(t)});return l+o.Util.getParamString(this.wmsParams,l,!0)+"&BBOX="+h},setParams:function(t,e){return o.extend(this.wmsParams,t),e||this.redraw(),this}}),o.tileLayer.wms=function(t,e){return new o.TileLayer.WMS(t,e)},o.TileLayer.Canvas=o.TileLayer.extend({options:{async:!1},initialize:function(t){o.setOptions(this,t)},redraw:function(){this._map&&(this._reset({hard:!0}),this._update());for(var t in this._tiles)this._redrawTile(this._tiles[t]);return this},_redrawTile:function(t){this.drawTile(t,t._tilePoint,this._map._zoom)},_createTile:function(){var t=o.DomUtil.create("canvas","leaflet-tile");return t.width=t.height=this.options.tileSize,t.onselectstart=t.onmousemove=o.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t._tilePoint=e,this._redrawTile(t),this.options.async||this.tileDrawn(t)},drawTile:function(){},tileDrawn:function(t){this._tileOnLoad.call(t)}}),o.tileLayer.canvas=function(t){return new o.TileLayer.Canvas(t)},o.ImageOverlay=o.Class.extend({includes:o.Mixin.Events,options:{opacity:1},initialize:function(t,e,i){this._url=t,this._bounds=o.latLngBounds(e),o.setOptions(this,i)},onAdd:function(t){this._map=t,this._image||this._initImage(),t._panes.overlayPane.appendChild(this._image),t.on("viewreset",this._reset,this),t.options.zoomAnimation&&o.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._image),t.off("viewreset",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var t=this._map._panes.overlayPane;return this._image&&t.insertBefore(this._image,t.firstChild),this},setUrl:function(t){this._url=t,this._image.src=this._url},getAttribution:function(){return this.options.attribution},_initImage:function(){this._image=o.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&o.Browser.any3d?o.DomUtil.addClass(this._image,"leaflet-zoom-animated"):o.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),o.extend(this._image,{galleryimg:"no",onselectstart:o.Util.falseFn,onmousemove:o.Util.falseFn,onload:o.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(t){var e=this._map,i=this._image,n=e.getZoomScale(t.zoom),s=this._bounds.getNorthWest(),a=this._bounds.getSouthEast(),r=e._latLngToNewLayerPoint(s,t.zoom,t.center),h=e._latLngToNewLayerPoint(a,t.zoom,t.center)._subtract(r),l=r._add(h._multiplyBy(.5*(1-1/n)));i.style[o.DomUtil.TRANSFORM]=o.DomUtil.getTranslateString(l)+" scale("+n+") "},_reset:function(){var t=this._image,e=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),i=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(e);o.DomUtil.setPosition(t,e),t.style.width=i.x+"px",t.style.height=i.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){o.DomUtil.setOpacity(this._image,this.options.opacity)}}),o.imageOverlay=function(t,e,i){return new o.ImageOverlay(t,e,i)},o.Icon=o.Class.extend({options:{className:""},initialize:function(t){o.setOptions(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,e){var i=this._getIconUrl(t);if(!i){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n;return n=e&&"IMG"===e.tagName?this._createImg(i,e):this._createImg(i),this._setIconStyles(n,t),n},_setIconStyles:function(t,e){var i,n=this.options,s=o.point(n[e+"Size"]);i="shadow"===e?o.point(n.shadowAnchor||n.iconAnchor):o.point(n.iconAnchor),!i&&s&&(i=s.divideBy(2,!0)),t.className="leaflet-marker-"+e+" "+n.className,i&&(t.style.marginLeft=-i.x+"px",t.style.marginTop=-i.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t,i){return o.Browser.ie6?(i||(i=e.createElement("div")),i.style.filter='progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+t+'")'):(i||(i=e.createElement("img")),i.src=t),i},_getIconUrl:function(t){return o.Browser.retina&&this.options[t+"RetinaUrl"]?this.options[t+"RetinaUrl"]:this.options[t+"Url"]}}),o.icon=function(t){return new o.Icon(t)},o.Icon.Default=o.Icon.extend({options:{iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]},_getIconUrl:function(t){var e=t+"Url";if(this.options[e])return this.options[e];o.Browser.retina&&"icon"===t&&(t+="-2x");var i=o.Icon.Default.imagePath;if(!i)throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return i+"/marker-"+t+".png"}}),o.Icon.Default.imagePath=function(){var t,i,n,o,s,a=e.getElementsByTagName("script"),r=/[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;for(t=0,i=a.length;i>t;t++)if(n=a[t].src,o=n.match(r))return s=n.split(r)[0],(s?s+"/":"")+"images"}(),o.Marker=o.Class.extend({includes:o.Mixin.Events,options:{icon:new o.Icon.Default,title:"",alt:"",clickable:!0,draggable:!1,keyboard:!0,zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250},initialize:function(t,e){o.setOptions(this,e),this._latlng=o.latLng(t)},onAdd:function(t){this._map=t,t.on("viewreset",this.update,this),this._initIcon(),this.update(),this.fire("add"),t.options.zoomAnimation&&t.options.markerZoomAnimation&&t.on("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this.dragging&&this.dragging.disable(),this._removeIcon(),this._removeShadow(),this.fire("remove"),t.off({viewreset:this.update,zoomanim:this._animateZoom},this),this._map=null},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=o.latLng(t),this.update(),this.fire("move",{latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update(),this},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup),this},update:function(){if(this._icon){var t=this._map.latLngToLayerPoint(this._latlng).round();this._setPos(t)}return this},_initIcon:function(){var t=this.options,e=this._map,i=e.options.zoomAnimation&&e.options.markerZoomAnimation,n=i?"leaflet-zoom-animated":"leaflet-zoom-hide",s=t.icon.createIcon(this._icon),a=!1;s!==this._icon&&(this._icon&&this._removeIcon(),a=!0,t.title&&(s.title=t.title),t.alt&&(s.alt=t.alt)),o.DomUtil.addClass(s,n),t.keyboard&&(s.tabIndex="0"),this._icon=s,this._initInteraction(),t.riseOnHover&&o.DomEvent.on(s,"mouseover",this._bringToFront,this).on(s,"mouseout",this._resetZIndex,this);var r=t.icon.createShadow(this._shadow),h=!1;r!==this._shadow&&(this._removeShadow(),h=!0),r&&o.DomUtil.addClass(r,n),this._shadow=r,t.opacity<1&&this._updateOpacity();var l=this._map._panes;a&&l.markerPane.appendChild(this._icon),r&&h&&l.shadowPane.appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&o.DomEvent.off(this._icon,"mouseover",this._bringToFront).off(this._icon,"mouseout",this._resetZIndex),this._map._panes.markerPane.removeChild(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&this._map._panes.shadowPane.removeChild(this._shadow),this._shadow=null},_setPos:function(t){o.DomUtil.setPosition(this._icon,t),this._shadow&&o.DomUtil.setPosition(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 e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(e)},_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];o.DomUtil.addClass(t,"leaflet-clickable"),o.DomEvent.on(t,"click",this._onMouseClick,this),o.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;is?(e.height=s+"px",o.DomUtil.addClass(t,a)):o.DomUtil.removeClass(t,a),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=this._animated,i=o.point(this.options.offset);e&&o.DomUtil.setPosition(this._container,t),this._containerBottom=-i.y-(e?0:t.y),this._containerLeft=-Math.round(this._containerWidth/2)+i.x+(e?0:t.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"}},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);o.DomUtil.setPosition(this._container,e)},_adjustPan:function(){if(this.options.autoPan){var t=this._map,e=this._container.offsetHeight,i=this._containerWidth,n=new o.Point(this._containerLeft,-e-this._containerBottom);this._animated&&n._add(o.DomUtil.getPosition(this._container));var s=t.layerPointToContainerPoint(n),a=o.point(this.options.autoPanPadding),r=o.point(this.options.autoPanPaddingTopLeft||a),h=o.point(this.options.autoPanPaddingBottomRight||a),l=t.getSize(),u=0,c=0;s.x+i+h.x>l.x&&(u=s.x+i-l.x+h.x),s.x-u-r.x<0&&(u=s.x-r.x),s.y+e+h.y>l.y&&(c=s.y+e-l.y+h.y),s.y-c-r.y<0&&(c=s.y-r.y),(u||c)&&t.fire("autopanstart").panBy([u,c])}},_onCloseButtonClick:function(t){this._close(),o.DomEvent.stop(t)}}),o.popup=function(t,e){return new o.Popup(t,e)},o.Map.include({openPopup:function(t,e,i){if(this.closePopup(),!(t instanceof o.Popup)){var n=t;t=new o.Popup(i).setLatLng(e).setContent(n)}return t._isOpen=!0,this._popup=t,this.addLayer(t)},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&(this.removeLayer(t),t._isOpen=!1),this}}),o.Marker.include({openPopup:function(){return this._popup&&this._map&&!this._map.hasLayer(this._popup)&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(){return this._popup&&(this._popup._isOpen?this.closePopup():this.openPopup()),this},bindPopup:function(t,e){var i=o.point(this.options.icon.options.popupAnchor||[0,0]);return i=i.add(o.Popup.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=o.extend({offset:i},e),this._popupHandlersAdded||(this.on("click",this.togglePopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popupHandlersAdded=!0),t instanceof o.Popup?(o.setOptions(t,e),this._popup=t):this._popup=new o.Popup(e,this).setContent(t),this},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.togglePopup,this).off("remove",this.closePopup,this).off("move",this._movePopup,this),this._popupHandlersAdded=!1),this},getPopup:function(){return this._popup},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),o.LayerGroup=o.Class.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},addLayer:function(t){var e=this.getLayerId(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[e]&&this._map.removeLayer(this._layers[e]),delete this._layers[e],this},hasLayer:function(t){return t?t in this._layers||this.getLayerId(t)in this._layers:!1},clearLayers:function(){return this.eachLayer(this.removeLayer,this),this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)i=this._layers[e],i[t]&&i[t].apply(i,n);return this},onAdd:function(t){this._map=t,this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t),this._map=null},addTo:function(t){return t.addLayer(this),this},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];for(var e in this._layers)t.push(this._layers[e]);return t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return o.stamp(t)}}),o.layerGroup=function(t){return new o.LayerGroup(t)},o.FeatureGroup=o.LayerGroup.extend({includes:o.Mixin.Events,statics:{EVENTS:"click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose"},addLayer:function(t){return this.hasLayer(t)?this:("on"in t&&t.on(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.addLayer.call(this,t),this._popupContent&&t.bindPopup&&t.bindPopup(this._popupContent,this._popupOptions),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),t.off(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.removeLayer.call(this,t),this._popupContent&&this.invoke("unbindPopup"),this.fire("layerremove",{layer:t})):this},bindPopup:function(t,e){return this._popupContent=t,this._popupOptions=e,this.invoke("bindPopup",t,e)},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 o.LatLngBounds;return this.eachLayer(function(e){t.extend(e instanceof o.Marker?e.getLatLng():e.getBounds())}),t},_propagateEvent:function(t){t.layer||(t.layer=t.target),t.target=this,this.fire(t.type,t)}}),o.featureGroup=function(t){return new o.FeatureGroup(t)},o.Path=o.Class.extend({includes:[o.Mixin.Events],statics:{CLIP_PADDING:function(){var e=o.Browser.mobile?1280:2e3,i=(e/Math.max(t.outerWidth,t.outerHeight)-1)/2;return Math.max(0,Math.min(.5,i))}()},options:{stroke:!0,color:"#0033ff",dashArray:null,lineCap:null,lineJoin:null,weight:5,opacity:.5,fill:!1,fillColor:null,fillOpacity:.2,clickable:!0},initialize:function(t){o.setOptions(this,t)},onAdd:function(t){this._map=t,this._container||(this._initElements(),this._initEvents()),this.projectLatlngs(),this._updatePath(),this._container&&this._map._pathRoot.appendChild(this._container),this.fire("add"),t.on({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){t._pathRoot.removeChild(this._container),this.fire("remove"),this._map=null,o.Browser.vml&&(this._container=null,this._stroke=null,this._fill=null),t.off({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},projectLatlngs:function(){},setStyle:function(t){return o.setOptions(this,t),this._container&&this._updateStyle(),this},redraw:function(){return this._map&&(this.projectLatlngs(),this._updatePath()),this}}),o.Map.include({_updatePathViewport:function(){var t=o.Path.CLIP_PADDING,e=this.getSize(),i=o.DomUtil.getPosition(this._mapPane),n=i.multiplyBy(-1)._subtract(e.multiplyBy(t)._round()),s=n.add(e.multiplyBy(1+2*t)._round());this._pathViewport=new o.Bounds(n,s)}}),o.Path.SVG_NS="http://www.w3.org/2000/svg",o.Browser.svg=!(!e.createElementNS||!e.createElementNS(o.Path.SVG_NS,"svg").createSVGRect),o.Path=o.Path.extend({statics:{SVG:o.Browser.svg},bringToFront:function(){var t=this._map._pathRoot,e=this._container;return e&&t.lastChild!==e&&t.appendChild(e),this},bringToBack:function(){var t=this._map._pathRoot,e=this._container,i=t.firstChild;return e&&i!==e&&t.insertBefore(e,i),this},getPathString:function(){},_createElement:function(t){return e.createElementNS(o.Path.SVG_NS,t)},_initElements:function(){this._map._initPathRoot(),this._initPath(),this._initStyle()},_initPath:function(){this._container=this._createElement("g"),this._path=this._createElement("path"),this._container.appendChild(this._path)},_initStyle:function(){this.options.stroke&&(this._path.setAttribute("stroke-linejoin","round"),this._path.setAttribute("stroke-linecap","round")),this.options.fill&&this._path.setAttribute("fill-rule","evenodd"),this.options.pointerEvents&&this._path.setAttribute("pointer-events",this.options.pointerEvents),this.options.clickable||this.options.pointerEvents||this._path.setAttribute("pointer-events","none"),this._updateStyle()},_updateStyle:function(){this.options.stroke?(this._path.setAttribute("stroke",this.options.color),this._path.setAttribute("stroke-opacity",this.options.opacity),this._path.setAttribute("stroke-width",this.options.weight),this.options.dashArray?this._path.setAttribute("stroke-dasharray",this.options.dashArray):this._path.removeAttribute("stroke-dasharray"),this.options.lineCap&&this._path.setAttribute("stroke-linecap",this.options.lineCap),this.options.lineJoin&&this._path.setAttribute("stroke-linejoin",this.options.lineJoin)):this._path.setAttribute("stroke","none"),this.options.fill?(this._path.setAttribute("fill",this.options.fillColor||this.options.color),this._path.setAttribute("fill-opacity",this.options.fillOpacity)):this._path.setAttribute("fill","none")},_updatePath:function(){var t=this.getPathString();t||(t="M0 0"),this._path.setAttribute("d",t)},_initEvents:function(){if(this.options.clickable){(o.Browser.svg||!o.Browser.vml)&&this._path.setAttribute("class","leaflet-clickable"),o.DomEvent.on(this._container,"click",this._onMouseClick,this);for(var t=["dblclick","mousedown","mouseover","mouseout","mousemove","contextmenu"],e=0;e';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(n){return!1}}(),o.Path=o.Browser.svg||!o.Browser.vml?o.Path:o.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("')}}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var t=this._container=this._createElement("shape");o.DomUtil.addClass(t,"leaflet-vml-shape"),this.options.clickable&&o.DomUtil.addClass(t,"leaflet-clickable"),t.coordsize="1 1",this._path=this._createElement("path"),t.appendChild(this._path),this._map._pathRoot.appendChild(t)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var t=this._stroke,e=this._fill,i=this.options,n=this._container;n.stroked=i.stroke,n.filled=i.fill,i.stroke?(t||(t=this._stroke=this._createElement("stroke"),t.endcap="round",n.appendChild(t)),t.weight=i.weight+"px",t.color=i.color,t.opacity=i.opacity,t.dashStyle=i.dashArray?o.Util.isArray(i.dashArray)?i.dashArray.join(" "):i.dashArray.replace(/( *, *)/g," "):"",i.lineCap&&(t.endcap=i.lineCap.replace("butt","flat")),i.lineJoin&&(t.joinstyle=i.lineJoin)):t&&(n.removeChild(t),this._stroke=null),i.fill?(e||(e=this._fill=this._createElement("fill"),n.appendChild(e)),e.color=i.fillColor||i.color,e.opacity=i.fillOpacity):e&&(n.removeChild(e),this._fill=null)},_updatePath:function(){var t=this._container.style;t.display="none",this._path.v=this.getPathString()+" ",t.display=""}}),o.Map.include(o.Browser.svg||!o.Browser.vml?{}:{_initPathRoot:function(){if(!this._pathRoot){var t=this._pathRoot=e.createElement("div");t.className="leaflet-vml-container",this._panes.overlayPane.appendChild(t),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}}),o.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),o.Path=o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?o.Path:o.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(t){return o.setOptions(this,t),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(t){t.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&(this._map.off("click",this._onClick,this),this._map.off("mousemove",this._onMouseMove,this)),this._requestUpdate(),this._map=null},_requestUpdate:function(){this._map&&!o.Path._updateRequest&&(o.Path._updateRequest=o.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){o.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var t=this.options;t.stroke&&(this._ctx.lineWidth=t.weight,this._ctx.strokeStyle=t.color),t.fill&&(this._ctx.fillStyle=t.fillColor||t.color)},_drawPath:function(){var t,e,i,n,s,a;for(this._ctx.beginPath(),t=0,i=this._parts.length;i>t;t++){for(e=0,n=this._parts[t].length;n>e;e++)s=this._parts[t][e],a=(0===e?"move":"line")+"To",this._ctx[a](s.x,s.y);this instanceof o.Polygon&&this._ctx.closePath()}},_checkIfEmpty:function(){return!this._parts.length},_updatePath:function(){if(!this._checkIfEmpty()){var t=this._ctx,e=this.options;this._drawPath(),t.save(),this._updateStyle(),e.fill&&(t.globalAlpha=e.fillOpacity,t.fill()),e.stroke&&(t.globalAlpha=e.opacity,t.stroke()),t.restore()}},_initEvents:function(){this.options.clickable&&(this._map.on("mousemove",this._onMouseMove,this),this._map.on("click",this._onClick,this))},_onClick:function(t){this._containsPoint(t.layerPoint)&&this.fire("click",t)},_onMouseMove:function(t){this._map&&!this._map._animatingZoom&&(this._containsPoint(t.layerPoint)?(this._ctx.canvas.style.cursor="pointer",this._mouseInside=!0,this.fire("mouseover",t)):this._mouseInside&&(this._ctx.canvas.style.cursor="",this._mouseInside=!1,this.fire("mouseout",t)))}}),o.Map.include(o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?{}:{_initPathRoot:function(){var t,i=this._pathRoot;i||(i=this._pathRoot=e.createElement("canvas"),i.style.position="absolute",t=this._canvasCtx=i.getContext("2d"),t.lineCap="round",t.lineJoin="round",this._panes.overlayPane.appendChild(i),this.options.zoomAnimation&&(this._pathRoot.className="leaflet-zoom-animated",this.on("zoomanim",this._animatePathZoom),this.on("zoomend",this._endPathZoom)),this.on("moveend",this._updateCanvasViewport),this._updateCanvasViewport())},_updateCanvasViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max.subtract(e),n=this._pathRoot;o.DomUtil.setPosition(n,e),n.width=i.x,n.height=i.y,n.getContext("2d").translate(-e.x,-e.y)}}}),o.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var a,r=[];for(a=0;n>a;a++)s[a]&&r.push(t[a]);return r},_simplifyDPStep:function(t,e,i,n,o){var s,a,r,h=0;for(a=n+1;o-1>=a;a++)r=this._sqClosestPointOnSegment(t[a],t[n],t[o],!0),r>h&&(s=a,h=r);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;s>n;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return s-1>o&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n){var o,s,a,r=n?this._lastCode:this._getBitCode(t,i),h=this._getBitCode(e,i);for(this._lastCode=h;;){if(!(r|h))return[t,e];if(r&h)return!1;o=r||h,s=this._getEdgeIntersection(t,e,o,i),a=this._getBitCode(s,i),o===r?(t=s,r=a):(e=s,h=a)}},_getEdgeIntersection:function(t,e,i,n){var s=e.x-t.x,a=e.y-t.y,r=n.min,h=n.max;return 8&i?new o.Point(t.x+s*(h.y-t.y)/a,h.y):4&i?new o.Point(t.x+s*(r.y-t.y)/a,r.y):2&i?new o.Point(h.x,t.y+a*(h.x-t.x)/s):1&i?new o.Point(r.x,t.y+a*(r.x-t.x)/s):void 0},_getBitCode:function(t,e){var i=0;return t.xe.max.x&&(i|=2),t.ye.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,n){var s,a=e.x,r=e.y,h=i.x-a,l=i.y-r,u=h*h+l*l;return u>0&&(s=((t.x-a)*h+(t.y-r)*l)/u,s>1?(a=i.x,r=i.y):s>0&&(a+=h*s,r+=l*s)),h=t.x-a,l=t.y-r,n?h*h+l*l:new o.Point(a,r)}},o.Polyline=o.Path.extend({initialize:function(t,e){o.Path.prototype.initialize.call(this,e),this._latlngs=this._convertLatLngs(t)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var t=0,e=this._latlngs.length;e>t;t++)this._originalPoints[t]=this._map.latLngToLayerPoint(this._latlngs[t])},getPathString:function(){for(var t=0,e=this._parts.length,i="";e>t;t++)i+=this._getPathPartStr(this._parts[t]);return i},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._latlngs=this._convertLatLngs(t),this.redraw()},addLatLng:function(t){return this._latlngs.push(o.latLng(t)),this.redraw()},spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._convertLatLngs(this._latlngs,!0),this.redraw(),t},closestLayerPoint:function(t){for(var e,i,n=1/0,s=this._parts,a=null,r=0,h=s.length;h>r;r++)for(var l=s[r],u=1,c=l.length;c>u;u++){e=l[u-1],i=l[u];var p=o.LineUtil._sqClosestPointOnSegment(t,e,i,!0);n>p&&(n=p,a=o.LineUtil._sqClosestPointOnSegment(t,e,i))}return a&&(a.distance=Math.sqrt(n)),a},getBounds:function(){return new o.LatLngBounds(this.getLatLngs())},_convertLatLngs:function(t,e){var i,n,s=e?t:[];for(i=0,n=t.length;n>i;i++){if(o.Util.isArray(t[i])&&"number"!=typeof t[i][0])return;s[i]=o.latLng(t[i])}return s},_initEvents:function(){o.Path.prototype._initEvents.call(this)},_getPathPartStr:function(t){for(var e,i=o.Path.VML,n=0,s=t.length,a="";s>n;n++)e=t[n],i&&e._round(),a+=(n?"L":"M")+e.x+" "+e.y;return a},_clipPoints:function(){var t,e,i,n=this._originalPoints,s=n.length;if(this.options.noClip)return this._parts=[n],void 0;this._parts=[];var a=this._parts,r=this._map._pathViewport,h=o.LineUtil;for(t=0,e=0;s-1>t;t++)i=h.clipSegment(n[t],n[t+1],r,t),i&&(a[e]=a[e]||[],a[e].push(i[0]),(i[1]!==n[t+1]||t===s-2)&&(a[e].push(i[1]),e++))},_simplifyPoints:function(){for(var t=this._parts,e=o.LineUtil,i=0,n=t.length;n>i;i++)t[i]=e.simplify(t[i],this.options.smoothFactor)},_updatePath:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),o.Path.prototype._updatePath.call(this))}}),o.polyline=function(t,e){return new o.Polyline(t,e)},o.PolyUtil={},o.PolyUtil.clipPolygon=function(t,e){var i,n,s,a,r,h,l,u,c,p=[1,4,2,8],d=o.LineUtil;for(n=0,l=t.length;l>n;n++)t[n]._code=d._getBitCode(t[n],e);for(a=0;4>a;a++){for(u=p[a],i=[],n=0,l=t.length,s=l-1;l>n;s=n++)r=t[n],h=t[s],r._code&u?h._code&u||(c=d._getEdgeIntersection(h,r,u,e),c._code=d._getBitCode(c,e),i.push(c)):(h._code&u&&(c=d._getEdgeIntersection(h,r,u,e),c._code=d._getBitCode(c,e),i.push(c)),i.push(r));t=i}return t},o.Polygon=o.Polyline.extend({options:{fill:!0},initialize:function(t,e){o.Polyline.prototype.initialize.call(this,t,e),this._initWithHoles(t)},_initWithHoles:function(t){var e,i,n;if(t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0])for(this._latlngs=this._convertLatLngs(t[0]),this._holes=t.slice(1),e=0,i=this._holes.length;i>e;e++)n=this._holes[e]=this._convertLatLngs(this._holes[e]),n[0].equals(n[n.length-1])&&n.pop();t=this._latlngs,t.length>=2&&t[0].equals(t[t.length-1])&&t.pop()},projectLatlngs:function(){if(o.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[],this._holes){var t,e,i,n;for(t=0,i=this._holes.length;i>t;t++)for(this._holePoints[t]=[],e=0,n=this._holes[t].length;n>e;e++)this._holePoints[t][e]=this._map.latLngToLayerPoint(this._holes[t][e])}},setLatLngs:function(t){return t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0]?(this._initWithHoles(t),this.redraw()):o.Polyline.prototype.setLatLngs.call(this,t)},_clipPoints:function(){var t=this._originalPoints,e=[];if(this._parts=[t].concat(this._holePoints),!this.options.noClip){for(var i=0,n=this._parts.length;n>i;i++){var s=o.PolyUtil.clipPolygon(this._parts[i],this._map._pathViewport);s.length&&e.push(s)}this._parts=e}},_getPathPartStr:function(t){var e=o.Polyline.prototype._getPathPartStr.call(this,t);return e+(o.Browser.svg?"z":"x")}}),o.polygon=function(t,e){return new o.Polygon(t,e)},function(){function t(t){return o.FeatureGroup.extend({initialize:function(t,e){this._layers={},this._options=e,this.setLatLngs(t)},setLatLngs:function(e){var i=0,n=e.length;for(this.eachLayer(function(t){n>i?t.setLatLngs(e[i++]):this.removeLayer(t)},this);n>i;)this.addLayer(new t(e[i++],this._options));return this},getLatLngs:function(){var t=[];return this.eachLayer(function(e){t.push(e.getLatLngs())}),t}})}o.MultiPolyline=t(o.Polyline),o.MultiPolygon=t(o.Polygon),o.multiPolyline=function(t,e){return new o.MultiPolyline(t,e)},o.multiPolygon=function(t,e){return new o.MultiPolygon(t,e)}}(),o.Rectangle=o.Polygon.extend({initialize:function(t,e){o.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=o.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),o.rectangle=function(t,e){return new o.Rectangle(t,e)},o.Circle=o.Path.extend({initialize:function(t,e,i){o.Path.prototype.initialize.call(this,i),this._latlng=o.latLng(t),this._mRadius=e},options:{fill:!0},setLatLng:function(t){return this._latlng=o.latLng(t),this.redraw()},setRadius:function(t){return this._mRadius=t,this.redraw()},projectLatlngs:function(){var t=this._getLngRadius(),e=this._latlng,i=this._map.latLngToLayerPoint([e.lat,e.lng-t]);this._point=this._map.latLngToLayerPoint(e),this._radius=Math.max(this._point.x-i.x,1)},getBounds:function(){var t=this._getLngRadius(),e=360*(this._mRadius/40075017),i=this._latlng;return new o.LatLngBounds([i.lat-e,i.lng-t],[i.lat+e,i.lng+t])},getLatLng:function(){return this._latlng},getPathString:function(){var t=this._point,e=this._radius;return this._checkIfEmpty()?"":o.Browser.svg?"M"+t.x+","+(t.y-e)+"A"+e+","+e+",0,1,1,"+(t.x-.1)+","+(t.y-e)+" z":(t._round(),e=Math.round(e),"AL "+t.x+","+t.y+" "+e+","+e+" 0,"+23592600)},getRadius:function(){return this._mRadius},_getLatRadius:function(){return 360*(this._mRadius/40075017)},_getLngRadius:function(){return this._getLatRadius()/Math.cos(o.LatLng.DEG_TO_RAD*this._latlng.lat)},_checkIfEmpty:function(){if(!this._map)return!1;var t=this._map._pathViewport,e=this._radius,i=this._point;return i.x-e>t.max.x||i.y-e>t.max.y||i.x+ei;i++)for(l=this._parts[i],n=0,r=l.length,s=r-1;r>n;s=n++)if((e||0!==n)&&(h=o.LineUtil.pointToSegmentDistance(t,l[s],l[n]),u>=h))return!0;return!1}}:{}),o.Polygon.include(o.Path.CANVAS?{_containsPoint:function(t){var e,i,n,s,a,r,h,l,u=!1;if(o.Polyline.prototype._containsPoint.call(this,t,!0))return!0;for(s=0,h=this._parts.length;h>s;s++)for(e=this._parts[s],a=0,l=e.length,r=l-1;l>a;r=a++)i=e[a],n=e[r],i.y>t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(u=!u);return u}}:{}),o.Circle.include(o.Path.CANVAS?{_drawPath:function(){var t=this._point;this._ctx.beginPath(),this._ctx.arc(t.x,t.y,this._radius,0,2*Math.PI,!1)},_containsPoint:function(t){var e=this._point,i=this.options.stroke?this.options.weight/2:0;return t.distanceTo(e)<=this._radius+i}}:{}),o.CircleMarker.include(o.Path.CANVAS?{_updateStyle:function(){o.Path.prototype._updateStyle.call(this)}}:{}),o.GeoJSON=o.FeatureGroup.extend({initialize:function(t,e){o.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,s=o.Util.isArray(t)?t:t.features;if(s){for(e=0,i=s.length;i>e;e++)n=s[e],(n.geometries||n.geometry||n.features||n.coordinates)&&this.addData(s[e]);return this}var a=this.options;if(!a.filter||a.filter(t)){var r=o.GeoJSON.geometryToLayer(t,a.pointToLayer,a.coordsToLatLng,a);return r.feature=o.GeoJSON.asFeature(t),r.defaultOptions=r.options,this.resetStyle(r),a.onEachFeature&&a.onEachFeature(t,r),this.addLayer(r)}},resetStyle:function(t){var e=this.options.style;e&&(o.Util.extend(t.options,t.defaultOptions),this._setLayerStyle(t,e))},setStyle:function(t){this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),o.extend(o.GeoJSON,{geometryToLayer:function(t,e,i,n){var s,a,r,h,l="Feature"===t.type?t.geometry:t,u=l.coordinates,c=[];switch(i=i||this.coordsToLatLng,l.type){case"Point":return s=i(u),e?e(t,s):new o.Marker(s);case"MultiPoint":for(r=0,h=u.length;h>r;r++)s=i(u[r]),c.push(e?e(t,s):new o.Marker(s));return new o.FeatureGroup(c);case"LineString":return a=this.coordsToLatLngs(u,0,i),new o.Polyline(a,n);case"Polygon":if(2===u.length&&!u[1].length)throw new Error("Invalid GeoJSON object.");return a=this.coordsToLatLngs(u,1,i),new o.Polygon(a,n);case"MultiLineString":return a=this.coordsToLatLngs(u,1,i),new o.MultiPolyline(a,n);case"MultiPolygon":return a=this.coordsToLatLngs(u,2,i),new o.MultiPolygon(a,n);case"GeometryCollection":for(r=0,h=l.geometries.length;h>r;r++)c.push(this.geometryToLayer({geometry:l.geometries[r],type:"Feature",properties:t.properties},e,i,n));return new o.FeatureGroup(c);default:throw new Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t){return new o.LatLng(t[1],t[0])},coordsToLatLngs:function(t,e,i){var n,o,s,a=[];for(o=0,s=t.length;s>o;o++)n=e?this.coordsToLatLngs(t[o],e-1,i):(i||this.coordsToLatLng)(t[o]),a.push(n);return a},latLngToCoords:function(t){return[t.lng,t.lat]},latLngsToCoords:function(t){for(var e=[],i=0,n=t.length;n>i;i++)e.push(o.GeoJSON.latLngToCoords(t[i]));return e},getFeature:function(t,e){return t.feature?o.extend({},t.feature,{geometry:e}):o.GeoJSON.asFeature(e)},asFeature:function(t){return"Feature"===t.type?t:{type:"Feature",properties:{},geometry:t}}});var a={toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"Point",coordinates:o.GeoJSON.latLngToCoords(this.getLatLng())})}};o.Marker.include(a),o.Circle.include(a),o.CircleMarker.include(a),o.Polyline.include({toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"LineString",coordinates:o.GeoJSON.latLngsToCoords(this.getLatLngs())})}}),o.Polygon.include({toGeoJSON:function(){var t,e,i,n=[o.GeoJSON.latLngsToCoords(this.getLatLngs())];if(n[0].push(n[0][0]),this._holes)for(t=0,e=this._holes.length;e>t;t++)i=o.GeoJSON.latLngsToCoords(this._holes[t]),i.push(i[0]),n.push(i);return o.GeoJSON.getFeature(this,{type:"Polygon",coordinates:n})}}),function(){function t(t){return function(){var e=[];return this.eachLayer(function(t){e.push(t.toGeoJSON().geometry.coordinates)}),o.GeoJSON.getFeature(this,{type:t,coordinates:e})}}o.MultiPolyline.include({toGeoJSON:t("MultiLineString")}),o.MultiPolygon.include({toGeoJSON:t("MultiPolygon")}),o.LayerGroup.include({toGeoJSON:function(){var e,i=this.feature&&this.feature.geometry,n=[];if(i&&"MultiPoint"===i.type)return t("MultiPoint").call(this);var s=i&&"GeometryCollection"===i.type;return this.eachLayer(function(t){t.toGeoJSON&&(e=t.toGeoJSON(),n.push(s?e.geometry:o.GeoJSON.asFeature(e)))}),s?o.GeoJSON.getFeature(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}})}(),o.geoJson=function(t,e){return new o.GeoJSON(t,e)},o.DomEvent={addListener:function(t,e,i,n){var s,a,r,h=o.stamp(i),l="_leaflet_"+e+h;return t[l]?this:(s=function(e){return i.call(n||t,e||o.DomEvent._getEvent())},o.Browser.pointer&&0===e.indexOf("touch")?this.addPointerListener(t,e,s,h):(o.Browser.touch&&"dblclick"===e&&this.addDoubleTapListener&&this.addDoubleTapListener(t,s,h),"addEventListener"in t?"mousewheel"===e?(t.addEventListener("DOMMouseScroll",s,!1),t.addEventListener(e,s,!1)):"mouseenter"===e||"mouseleave"===e?(a=s,r="mouseenter"===e?"mouseover":"mouseout",s=function(e){return o.DomEvent._checkMouse(t,e)?a(e):void 0},t.addEventListener(r,s,!1)):"click"===e&&o.Browser.android?(a=s,s=function(t){return o.DomEvent._filterClick(t,a)},t.addEventListener(e,s,!1)):t.addEventListener(e,s,!1):"attachEvent"in t&&t.attachEvent("on"+e,s),t[l]=s,this))},removeListener:function(t,e,i){var n=o.stamp(i),s="_leaflet_"+e+n,a=t[s];return a?(o.Browser.pointer&&0===e.indexOf("touch")?this.removePointerListener(t,e,n):o.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,n):"removeEventListener"in t?"mousewheel"===e?(t.removeEventListener("DOMMouseScroll",a,!1),t.removeEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?t.removeEventListener("mouseenter"===e?"mouseover":"mouseout",a,!1):t.removeEventListener(e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[s]=null,this):this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,o.DomEvent._skipped(t),this},disableScrollPropagation:function(t){var e=o.DomEvent.stopPropagation;return o.DomEvent.on(t,"mousewheel",e).on(t,"MozMousePixelScroll",e)},disableClickPropagation:function(t){for(var e=o.DomEvent.stopPropagation,i=o.Draggable.START.length-1;i>=0;i--)o.DomEvent.on(t,o.Draggable.START[i],e);return o.DomEvent.on(t,"click",o.DomEvent._fakeStop).on(t,"dblclick",e)},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return o.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,i){var n=e.body,s=e.documentElement,a=o.DomUtil.documentIsLtr()?t.pageX?t.pageX-n.scrollLeft-s.scrollLeft:t.clientX:o.Browser.gecko?t.pageX-n.scrollLeft-s.scrollLeft:t.pageX?t.pageX-n.scrollLeft+s.scrollLeft:t.clientX,r=t.pageY?t.pageY-n.scrollTop-s.scrollTop:t.clientY,h=new o.Point(a,r);if(!i)return h;var l=i.getBoundingClientRect(),u=l.left-i.clientLeft,c=l.top-i.clientTop;return h._subtract(new o.Point(u,c))},getWheelDelta:function(t){var e=0;return t.wheelDelta&&(e=t.wheelDelta/120),t.detail&&(e=-t.detail/3),e},_skipEvents:{},_fakeStop:function(t){o.DomEvent._skipEvents[t.type]=!0},_skipped:function(t){var e=this._skipEvents[t.type];return this._skipEvents[t.type]=!1,e},_checkMouse:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(n){return!1}return i!==t},_getEvent:function(){var e=t.event;if(!e)for(var i=arguments.callee.caller;i&&(e=i.arguments[0],!e||t.Event!==e.constructor);)i=i.caller;return e},_filterClick:function(t,e){var i=t.timeStamp||t.originalEvent.timeStamp,n=o.DomEvent._lastClick&&i-o.DomEvent._lastClick;return n&&n>100&&1e3>n||t.target._simulatedClick&&!t._simulated?(o.DomEvent.stop(t),void 0):(o.DomEvent._lastClick=i,e(t))}},o.DomEvent.on=o.DomEvent.addListener,o.DomEvent.off=o.DomEvent.removeListener,o.Draggable=o.Class.extend({includes:o.Mixin.Events,statics:{START:o.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"}},initialize:function(t,e){this._element=t,this._dragStartTarget=e||t},enable:function(){if(!this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.on(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!0}},disable:function(){if(this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.off(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!1,this._moved=!1}},_onDown:function(t){if(this._moved=!1,!(t.shiftKey||1!==t.which&&1!==t.button&&!t.touches||(o.DomEvent.stopPropagation(t),o.Draggable._disabled||(o.DomUtil.disableImageDrag(),o.DomUtil.disableTextSelection(),this._moving)))){var i=t.touches?t.touches[0]:t;this._startPoint=new o.Point(i.clientX,i.clientY),this._startPos=this._newPos=o.DomUtil.getPosition(this._element),o.DomEvent.on(e,o.Draggable.MOVE[t.type],this._onMove,this).on(e,o.Draggable.END[t.type],this._onUp,this)}},_onMove:function(t){if(t.touches&&t.touches.length>1)return this._moved=!0,void 0;var i=t.touches&&1===t.touches.length?t.touches[0]:t,n=new o.Point(i.clientX,i.clientY),s=n.subtract(this._startPoint);(s.x||s.y)&&(o.DomEvent.preventDefault(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=o.DomUtil.getPosition(this._element).subtract(s),o.Browser.touch||o.DomUtil.addClass(e.body,"leaflet-dragging")),this._newPos=this._startPos.add(s),this._moving=!0,o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget))},_updatePosition:function(){this.fire("predrag"),o.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(){o.Browser.touch||o.DomUtil.removeClass(e.body,"leaflet-dragging");for(var t in o.Draggable.MOVE)o.DomEvent.off(e,o.Draggable.MOVE[t],this._onMove).off(e,o.Draggable.END[t],this._onUp);o.DomUtil.enableImageDrag(),o.DomUtil.enableTextSelection(),this._moved&&(o.Util.cancelAnimFrame(this._animRequest),this.fire("dragend")),this._moving=!1}}),o.Handler=o.Class.extend({initialize:function(t){this._map=t},enable:function(){this._enabled||(this._enabled=!0,this.addHooks())},disable:function(){this._enabled&&(this._enabled=!1,this.removeHooks())},enabled:function(){return!!this._enabled}}),o.Map.mergeOptions({dragging:!0,inertia:!o.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,inertiaThreshold:o.Browser.touch?32:18,easeLinearity:.25,worldCopyJump:!1}),o.Map.Drag=o.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new o.Draggable(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),t.on("viewreset",this._onViewReset,this),t.whenReady(this._onViewReset,this))}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var t=this._map;t._panAnim&&t._panAnim.stop(),t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var t=this._lastTime=+new Date,e=this._lastPos=this._draggable._newPos;this._positions.push(e),this._times.push(t),t-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var t=this._map.getSize()._divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.project([0,180]).x},_onPreDrag:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,a=Math.abs(o+i)e.inertiaThreshold||!this._positions[0];if(t.fire("dragend"),n)t.fire("moveend");else{var s=this._lastPos.subtract(this._positions[0]),a=(this._lastTime+i-this._times[0])/1e3,r=e.easeLinearity,h=s.multiplyBy(r/a),l=h.distanceTo([0,0]),u=Math.min(e.inertiaMaxSpeed,l),c=h.multiplyBy(u/l),p=u/(e.inertiaDeceleration*r),d=c.multiplyBy(-p/2).round();d.x&&d.y?o.Util.requestAnimFrame(function(){t.panBy(d,{duration:p,easeLinearity:r,noMoveStart:!0})}):t.fire("moveend")}}}),o.Map.addInitHook("addHandler","dragging",o.Map.Drag),o.Map.mergeOptions({doubleClickZoom:!0}),o.Map.DoubleClickZoom=o.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom()+1;"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}}),o.Map.addInitHook("addHandler","doubleClickZoom",o.Map.DoubleClickZoom),o.Map.mergeOptions({scrollWheelZoom:!0}),o.Map.ScrollWheelZoom=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),o.DomEvent.on(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault),this._delta=0},removeHooks:function(){o.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll),o.DomEvent.off(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault)},_onWheelScroll:function(t){var e=o.DomEvent.getWheelDelta(t);this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var i=Math.max(40-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(o.bind(this._performZoom,this),i),o.DomEvent.preventDefault(t),o.DomEvent.stopPropagation(t)},_performZoom:function(){var t=this._map,e=this._delta,i=t.getZoom();e=e>0?Math.ceil(e):Math.floor(e),e=Math.max(Math.min(e,4),-4),e=t._limitZoom(i+e)-i,this._delta=0,this._startTime=null,e&&("center"===t.options.scrollWheelZoom?t.setZoom(i+e):t.setZoomAround(this._lastMousePos,i+e))}}),o.Map.addInitHook("addHandler","scrollWheelZoom",o.Map.ScrollWheelZoom),o.extend(o.DomEvent,{_touchstart:o.Browser.msPointer?"MSPointerDown":o.Browser.pointer?"pointerdown":"touchstart",_touchend:o.Browser.msPointer?"MSPointerUp":o.Browser.pointer?"pointerup":"touchend",addDoubleTapListener:function(t,i,n){function s(t){var e;if(o.Browser.pointer?(_.push(t.pointerId),e=_.length):e=t.touches.length,!(e>1)){var i=Date.now(),n=i-(r||i);h=t.touches?t.touches[0]:t,l=n>0&&u>=n,r=i}}function a(t){if(o.Browser.pointer){var e=_.indexOf(t.pointerId);if(-1===e)return;_.splice(e,1)}if(l){if(o.Browser.pointer){var n,s={};for(var a in h)n=h[a],s[a]="function"==typeof n?n.bind(h):n;h=s}h.type="dblclick",i(h),r=null}}var r,h,l=!1,u=250,c="_leaflet_",p=this._touchstart,d=this._touchend,_=[];t[c+p+n]=s,t[c+d+n]=a;var m=o.Browser.pointer?e.documentElement:t;return t.addEventListener(p,s,!1),m.addEventListener(d,a,!1),o.Browser.pointer&&m.addEventListener(o.DomEvent.POINTER_CANCEL,a,!1),this},removeDoubleTapListener:function(t,i){var n="_leaflet_";return t.removeEventListener(this._touchstart,t[n+this._touchstart+i],!1),(o.Browser.pointer?e.documentElement:t).removeEventListener(this._touchend,t[n+this._touchend+i],!1),o.Browser.pointer&&e.documentElement.removeEventListener(o.DomEvent.POINTER_CANCEL,t[n+this._touchend+i],!1),this}}),o.extend(o.DomEvent,{POINTER_DOWN:o.Browser.msPointer?"MSPointerDown":"pointerdown",POINTER_MOVE:o.Browser.msPointer?"MSPointerMove":"pointermove",POINTER_UP:o.Browser.msPointer?"MSPointerUp":"pointerup",POINTER_CANCEL:o.Browser.msPointer?"MSPointerCancel":"pointercancel",_pointers:[],_pointerDocumentListener:!1,addPointerListener:function(t,e,i,n){switch(e){case"touchstart":return this.addPointerListenerStart(t,e,i,n);case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return this.addPointerListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addPointerListenerStart:function(t,i,n,s){var a="_leaflet_",r=this._pointers,h=function(t){o.DomEvent.preventDefault(t);for(var e=!1,i=0;i1))&&(this._moved||(o.DomUtil.addClass(e._mapPane,"leaflet-touching"),e.fire("movestart").fire("zoomstart"),this._moved=!0),o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),o.DomEvent.preventDefault(t))}},_updateOnMove:function(){var t=this._map,e=this._getScaleOrigin(),i=t.layerPointToLatLng(e),n=t.getScaleZoom(this._scale);t._animateZoom(i,n,this._startCenter,this._scale,this._delta)},_onTouchEnd:function(){if(!this._moved||!this._zooming)return this._zooming=!1,void 0;var t=this._map;this._zooming=!1,o.DomUtil.removeClass(t._mapPane,"leaflet-touching"),o.Util.cancelAnimFrame(this._animRequest),o.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd);var i=this._getScaleOrigin(),n=t.layerPointToLatLng(i),s=t.getZoom(),a=t.getScaleZoom(this._scale)-s,r=a>0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r),l=t.getZoomScale(h)/this._scale;t._animateZoom(n,h,i,l)},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),o.Map.addInitHook("addHandler","touchZoom",o.Map.TouchZoom),o.Map.mergeOptions({tap:!0,tapTolerance:15}),o.Map.Tap=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(o.DomEvent.preventDefault(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,clearTimeout(this._holdTimeout),void 0;var i=t.touches[0],n=i.target;this._startPos=this._newPos=new o.Point(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._holdTimeout=setTimeout(o.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),o.DomEvent.on(e,"touchmove",this._onMove,this).on(e,"touchend",this._onUp,this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),o.DomEvent.off(e,"touchmove",this._onMove,this).off(e,"touchend",this._onUp,this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],n=i.target;n&&n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.removeClass(n,"leaflet-active"),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var e=t.touches[0];this._newPos=new o.Point(e.clientX,e.clientY)},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o._simulated=!0,n.target._simulatedClick=!0,o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),o.Browser.touch&&!o.Browser.pointer&&o.Map.addInitHook("addHandler","tap",o.Map.Tap),o.Map.mergeOptions({boxZoom:!0}),o.Map.BoxZoom=o.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._moved=!1},addHooks:function(){o.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){o.DomEvent.off(this._container,"mousedown",this._onMouseDown),this._moved=!1},moved:function(){return this._moved},_onMouseDown:function(t){return this._moved=!1,!t.shiftKey||1!==t.which&&1!==t.button?!1:(o.DomUtil.disableTextSelection(),o.DomUtil.disableImageDrag(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),this._box=o.DomUtil.create("div","leaflet-zoom-box",this._pane),o.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",o.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).on(e,"keydown",this._onKeyDown,this),this._map.fire("boxzoomstart"),void 0)},_onMouseMove:function(t){var e=this._startLayerPoint,i=this._box,n=this._map.mouseEventToLayerPoint(t),s=n.subtract(e),a=new o.Point(Math.min(n.x,e.x),Math.min(n.y,e.y));o.DomUtil.setPosition(i,a),this._moved=!0,i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_finish:function(){this._pane.removeChild(this._box),this._container.style.cursor="",o.DomUtil.enableTextSelection(),o.DomUtil.enableImageDrag(),o.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp).off(e,"keydown",this._onKeyDown)},_onMouseUp:function(t){this._finish();var e=this._map,i=e.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(i)){var n=new o.LatLngBounds(e.layerPointToLatLng(this._startLayerPoint),e.layerPointToLatLng(i));e.fitBounds(n),e.fire("boxzoomend",{boxZoomBounds:n})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}}),o.Map.addInitHook("addHandler","boxZoom",o.Map.BoxZoom),o.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),o.Map.Keyboard=o.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,173]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),o.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;o.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){if(!this._focused){var i=e.body,n=e.documentElement,o=i.scrollTop||n.scrollTop,s=i.scrollLeft||n.scrollLeft;this._map._container.focus(),t.scrollTo(s,o)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){o.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){o.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(e in this._panKeys){if(i._panAnim&&i._panAnim._inProgress)return;i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds)}else{if(!(e in this._zoomKeys))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}o.DomEvent.stop(t)}}),o.Map.addInitHook("addHandler","keyboard",o.Map.Keyboard),o.Handler.MarkerDrag=o.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new o.Draggable(t,t)),this._draggable.on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this),this._draggable.enable(),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off("dragstart",this._onDragStart,this).off("drag",this._onDrag,this).off("dragend",this._onDragEnd,this),this._draggable.disable(),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart"),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-dragging")},_onDrag:function(){var t=this._marker,e=t._shadow,i=o.DomUtil.getPosition(t._icon),n=t._map.layerPointToLatLng(i);e&&o.DomUtil.setPosition(e,i),t._latlng=n,t.fire("move",{latlng:n}).fire("drag")},_onDragEnd:function(){this._marker.fire("moveend").fire("dragend"),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-dragging")}}),o.Control=o.Class.extend({options:{position:"topright"},initialize:function(t){o.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),n=t._controlCorners[i];return o.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?n.insertBefore(e,n.firstChild):n.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this},_refocusOnMap:function(){this._map&&this._map.getContainer().focus()}}),o.control=function(t){return new o.Control(t)},o.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=o.DomUtil.create("div",a,n)}var e=this._controlCorners={},i="leaflet-",n=this._controlContainer=o.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){this._container.removeChild(this._controlContainer)}}),o.Control.Zoom=o.Control.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"-",zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=o.DomUtil.create("div",e+" leaflet-bar");return this._map=t,this._zoomInButton=this._createButton(this.options.zoomInText,this.options.zoomInTitle,e+"-in",i,this._zoomIn,this),this._zoomOutButton=this._createButton(this.options.zoomOutText,this.options.zoomOutTitle,e+"-out",i,this._zoomOut,this),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,n,s,a){var r=o.DomUtil.create("a",i,n);r.innerHTML=t,r.href="#",r.title=e;var h=o.DomEvent.stopPropagation;return o.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",o.DomEvent.preventDefault).on(r,"click",s,a).on(r,"click",this._refocusOnMap,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";o.DomUtil.removeClass(this._zoomInButton,e),o.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&o.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&o.DomUtil.addClass(this._zoomInButton,e)}}),o.Map.mergeOptions({zoomControl:!0}),o.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new o.Control.Zoom,this.addControl(this.zoomControl))}),o.control.zoom=function(t){return new o.Control.Zoom(t)},o.Control.Attribution=o.Control.extend({options:{position:"bottomright",prefix:'Leaflet'},initialize:function(t){o.setOptions(this,t),this._attributions={}},onAdd:function(t){return this._container=o.DomUtil.create("div","leaflet-control-attribution"),o.DomEvent.disableClickPropagation(this._container),t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},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):void 0},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):void 0},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" | ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),o.Map.mergeOptions({attributionControl:!0}),o.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new o.Control.Attribution).addTo(this))}),o.control.attribution=function(t){return new o.Control.Attribution(t)},o.Control.Scale=o.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=o.DomUtil.create("div",e),n=this.options;return this._addScales(n,e,i),t.on(n.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,e,i){t.metric&&(this._mScale=o.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=o.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),o.control.scale=function(t){return new o.Control.Scale(t)},o.Control.Layers=o.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){o.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange).off("layerremove",this._onLayerChange)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=o.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=o.DomUtil.create("div",t);e.setAttribute("aria-haspopup",!0),o.Browser.touch?o.DomEvent.on(e,"click",o.DomEvent.stopPropagation):o.DomEvent.disableClickPropagation(e).disableScrollPropagation(e);var i=this._form=o.DomUtil.create("form",t+"-list");if(this.options.collapsed){o.Browser.android||o.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var n=this._layersLink=o.DomUtil.create("a",t+"-toggle",e);n.href="#",n.title="Layers",o.Browser.touch?o.DomEvent.on(n,"click",o.DomEvent.stop).on(n,"click",this._expand,this):o.DomEvent.on(n,"focus",this._expand,this),this._map.on("click",this._collapse,this)}else this._expand();this._baseLayersList=o.DomUtil.create("div",t+"-base",i),this._separator=o.DomUtil.create("div",t+"-separator",i),this._overlaysList=o.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var n=o.stamp(t);this._layers[n]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t,e,i=!1,n=!1;for(t in this._layers)e=this._layers[t],this._addItem(e),n=n||e.overlay,i=i||!e.overlay;this._separator.style.display=n&&i?"":"none"}},_onLayerChange:function(t){var e=this._layers[o.stamp(t.layer)];if(e){this._handlingClick||this._update();var i=e.overlay?"layeradd"===t.type?"overlayadd":"overlayremove":"layeradd"===t.type?"baselayerchange":null;i&&this._map.fire(i,e)}},_createRadioElement:function(t,i){var n='t;t++)e=n[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?this._map.addLayer(i.layer):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);this._handlingClick=!1,this._refocusOnMap()},_expand:function(){o.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),o.control.layers=function(t,e,i){return new o.Control.Layers(t,e,i)},o.PosAnimation=o.Class.extend({includes:o.Mixin.Events,run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._newPos=e,this.fire("start"),t.style[o.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(n||.5)+",1)",o.DomEvent.on(t,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),o.DomUtil.setPosition(t,e),o.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(o.bind(this._onStep,this),50)},stop:function(){this._inProgress&&(o.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),o.Util.falseFn(this._el.offsetWidth))},_onStep:function(){var t=this._getPos();return t?(this._el._leaflet_pos=t,this.fire("step"),void 0):(this._onTransitionEnd(),void 0)},_transformRe:/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,_getPos:function(){var e,i,n,s=this._el,a=t.getComputedStyle(s);if(o.Browser.any3d){if(n=a[o.DomUtil.TRANSFORM].match(this._transformRe),!n)return;e=parseFloat(n[1]),i=parseFloat(n[2])}else e=parseFloat(a.left),i=parseFloat(a.top);return new o.Point(e,i,!0)},_onTransitionEnd:function(){o.DomEvent.off(this._el,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[o.DomUtil.TRANSITION]="",this._el._leaflet_pos=this._newPos,clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),o.Map.include({setView:function(t,e,n){if(e=e===i?this._zoom:this._limitZoom(e),t=o.latLng(t),n=n||{},this._panAnim&&this._panAnim.stop(),this._loaded&&!n.reset&&n!==!0){n.animate!==i&&(n.zoom=o.extend({animate:n.animate},n.zoom),n.pan=o.extend({animate:n.animate},n.pan));var s=this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan);if(s)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e){if(t=o.point(t).round(),e=e||{},!t.x&&!t.y)return this;if(this._panAnim||(this._panAnim=new o.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire("movestart"),e.animate!==!1){o.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var i=this._getMapPanePos().subtract(t);this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){o.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._floor();return(e&&e.animate)===!0||this.getSize().contains(i)?(this.panBy(i,e),!0):!1}}),o.PosAnimation=o.DomUtil.TRANSITION?o.PosAnimation:o.PosAnimation.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=o.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=o.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));o.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){o.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),o.Map.mergeOptions({zoomAnimation:!0,zoomAnimationThreshold:4}),o.DomUtil.TRANSITION&&o.Map.addInitHook(function(){this._zoomAnimated=this.options.zoomAnimation&&o.DomUtil.TRANSITION&&o.Browser.any3d&&!o.Browser.android23&&!o.Browser.mobileOpera,this._zoomAnimated&&o.DomEvent.on(this._mapPane,o.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),o.Map.include(o.DomUtil.TRANSITION?{_catchTransitionEnd:function(){this._animatingZoom&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,i){if(this._animatingZoom)return!0;if(i=i||{},!this._zoomAnimated||i.animate===!1||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/n),s=this._getCenterLayerPoint()._add(o);return i.animate===!0||this.getSize().contains(o)?(this.fire("movestart").fire("zoomstart"),this._animateZoom(t,e,s,n,null,!0),!0):!1},_animateZoom:function(t,e,i,n,s,a){this._animatingZoom=!0,o.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this._animateToCenter=t,this._animateToZoom=e,o.Draggable&&(o.Draggable._disabled=!0),this.fire("zoomanim",{center:t,zoom:e,origin:i,scale:n,delta:s,backwards:a})},_onZoomTransitionEnd:function(){this._animatingZoom=!1,o.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),o.Draggable&&(o.Draggable._disabled=!1)}}:{}),o.TileLayer.include({_animateZoom:function(t){this._animating||(this._animating=!0,this._prepareBgBuffer());var e=this._bgBuffer,i=o.DomUtil.TRANSFORM,n=t.delta?o.DomUtil.getTranslateString(t.delta):e.style[i],s=o.DomUtil.getScaleString(t.scale,t.origin);e.style[i]=t.backwards?s+" "+n:n+" "+s},_endZoomAnim:function(){var t=this._tileContainer,e=this._bgBuffer;t.style.visibility="",t.parentNode.appendChild(t),o.Util.falseFn(e.offsetWidth),this._animating=!1},_clearBgBuffer:function(){var t=this._map;!t||t._animatingZoom||t.touchZoom._zooming||(this._bgBuffer.innerHTML="",this._bgBuffer.style[o.DomUtil.TRANSFORM]="")},_prepareBgBuffer:function(){var t=this._tileContainer,e=this._bgBuffer,i=this._getLoadedTilesPercentage(e),n=this._getLoadedTilesPercentage(t);return e&&i>.5&&.5>n?(t.style.visibility="hidden",this._stopLoadingImages(t),void 0):(e.style.visibility="hidden",e.style[o.DomUtil.TRANSFORM]="",this._tileContainer=e,e=this._bgBuffer=t,this._stopLoadingImages(e),clearTimeout(this._clearBgBufferTimer),void 0)},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,n,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)n=s[e],n.complete||(n.onload=o.Util.falseFn,n.onerror=o.Util.falseFn,n.src=o.Util.emptyImageUrl,n.parentNode.removeChild(n))}}),o.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locateOptions=o.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=o.bind(this._handleGeolocationResponse,this),i=o.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=t.coords.latitude,i=t.coords.longitude,n=new o.LatLng(e,i),s=180*t.coords.accuracy/40075017,a=s/Math.cos(o.LatLng.DEG_TO_RAD*e),r=o.latLngBounds([e-s,i-a],[e+s,i+a]),h=this._locateOptions;if(h.setView){var l=Math.min(this.getBoundsZoom(r),h.maxZoom);this.setView(n,l)}var u={latlng:n,bounds:r,timestamp:t.timestamp};for(var c in t.coords)"number"==typeof t.coords[c]&&(u[c]=t.coords[c]);this.fire("locationfound",u)}})}(window,document); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/package.json b/mapentity/static/mapentity/Leaflet.label/package.json deleted file mode 100644 index a74c9e0d92..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "leaflet.label", - "version": "0.2.1", - "description": "Labels for leaflet maps", - "devDependencies": { - "jshint": "~2.1.4", - "uglify-js": "~2.3.6" - }, - "main": "dist/leaflet.label.js", - "scripts": { - "test": "jake test", - "prepublish": "jake" - }, - "repository": { - "type": "git", - "url": "git://github.com/Leaflet/Leaflet.label.git" - }, - "keywords": ["gis", "map"] -} diff --git a/mapentity/static/mapentity/Leaflet.label/src/BaseMarkerMethods.js b/mapentity/static/mapentity/Leaflet.label/src/BaseMarkerMethods.js deleted file mode 100644 index 6b66d355a6..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/BaseMarkerMethods.js +++ /dev/null @@ -1,129 +0,0 @@ -// This object is a mixin for L.Marker and L.CircleMarker. We declare it here as both need to include the contents. -L.BaseMarkerMethods = { - showLabel: function () { - if (this.label && this._map) { - this.label.setLatLng(this._latlng); - this._map.showLabel(this.label); - } - - return this; - }, - - hideLabel: function () { - if (this.label) { - this.label.close(); - } - return this; - }, - - setLabelNoHide: function (noHide) { - if (this._labelNoHide === noHide) { - return; - } - - this._labelNoHide = noHide; - - if (noHide) { - this._removeLabelRevealHandlers(); - this.showLabel(); - } else { - this._addLabelRevealHandlers(); - this.hideLabel(); - } - }, - - bindLabel: function (content, options) { - var labelAnchor = this.options.icon ? this.options.icon.options.labelAnchor : this.options.labelAnchor, - anchor = L.point(labelAnchor) || L.point(0, 0); - - anchor = anchor.add(L.Label.prototype.options.offset); - - if (options && options.offset) { - anchor = anchor.add(options.offset); - } - - options = L.Util.extend({offset: anchor}, options); - - this._labelNoHide = options.noHide; - - if (!this.label) { - if (!this._labelNoHide) { - this._addLabelRevealHandlers(); - } - - this - .on('remove', this.hideLabel, this) - .on('move', this._moveLabel, this) - .on('add', this._onMarkerAdd, this); - - this._hasLabelHandlers = true; - } - - this.label = new L.Label(options, this) - .setContent(content); - - return this; - }, - - unbindLabel: function () { - if (this.label) { - this.hideLabel(); - - this.label = null; - - if (this._hasLabelHandlers) { - if (!this._labelNoHide) { - this._removeLabelRevealHandlers(); - } - - this - .off('remove', this.hideLabel, this) - .off('move', this._moveLabel, this) - .off('add', this._onMarkerAdd, this); - } - - this._hasLabelHandlers = false; - } - return this; - }, - - updateLabelContent: function (content) { - if (this.label) { - this.label.setContent(content); - } - }, - - getLabel: function () { - return this.label; - }, - - _onMarkerAdd: function () { - if (this._labelNoHide) { - this.showLabel(); - } - }, - - _addLabelRevealHandlers: function () { - this - .on('mouseover', this.showLabel, this) - .on('mouseout', this.hideLabel, this); - - if (L.Browser.touch) { - this.on('click', this.showLabel, this); - } - }, - - _removeLabelRevealHandlers: function () { - this - .off('mouseover', this.showLabel, this) - .off('mouseout', this.hideLabel, this); - - if (L.Browser.touch) { - this.off('click', this.showLabel, this); - } - }, - - _moveLabel: function (e) { - this.label.setLatLng(e.latlng); - } -}; \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/src/CircleMarker.Label.js b/mapentity/static/mapentity/Leaflet.label/src/CircleMarker.Label.js deleted file mode 100644 index 1833642b5b..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/CircleMarker.Label.js +++ /dev/null @@ -1,7 +0,0 @@ -// Add in an option to icon that is used to set where the label anchor is -L.CircleMarker.mergeOptions({ - labelAnchor: new L.Point(0, 0) -}); - - -L.CircleMarker.include(L.BaseMarkerMethods); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/src/FeatureGroup.Label.js b/mapentity/static/mapentity/Leaflet.label/src/FeatureGroup.Label.js deleted file mode 100644 index 4f342a1707..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/FeatureGroup.Label.js +++ /dev/null @@ -1,20 +0,0 @@ -L.FeatureGroup.include({ - // TODO: remove this when AOP is supported in Leaflet, need this as we cannot put code in removeLayer() - clearLayers: function () { - this.unbindLabel(); - this.eachLayer(this.removeLayer, this); - return this; - }, - - bindLabel: function (content, options) { - return this.invoke('bindLabel', content, options); - }, - - unbindLabel: function () { - return this.invoke('unbindLabel'); - }, - - updateLabelContent: function (content) { - this.invoke('updateLabelContent', content); - } -}); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/src/Label.js b/mapentity/static/mapentity/Leaflet.label/src/Label.js deleted file mode 100644 index 0b0436d210..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/Label.js +++ /dev/null @@ -1,247 +0,0 @@ -L.Label = L.Class.extend({ - - includes: L.Mixin.Events, - - options: { - className: '', - clickable: false, - direction: 'right', - noHide: false, - offset: [12, -15], // 6 (width of the label triangle) + 6 (padding) - opacity: 1, - zoomAnimation: true - }, - - initialize: function (options, source) { - L.setOptions(this, options); - - this._source = source; - this._animated = L.Browser.any3d && this.options.zoomAnimation; - this._isOpen = false; - }, - - onAdd: function (map) { - this._map = map; - - this._pane = this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane; - - if (!this._container) { - this._initLayout(); - } - - this._pane.appendChild(this._container); - - this._initInteraction(); - - this._update(); - - this.setOpacity(this.options.opacity); - - map - .on('moveend', this._onMoveEnd, this) - .on('viewreset', this._onViewReset, this); - - if (this._animated) { - map.on('zoomanim', this._zoomAnimation, this); - } - - if (L.Browser.touch && !this.options.noHide) { - L.DomEvent.on(this._container, 'click', this.close, this); - } - }, - - onRemove: function (map) { - this._pane.removeChild(this._container); - - map.off({ - zoomanim: this._zoomAnimation, - moveend: this._onMoveEnd, - viewreset: this._onViewReset - }, this); - - this._removeInteraction(); - - this._map = null; - }, - - setLatLng: function (latlng) { - this._latlng = L.latLng(latlng); - if (this._map) { - this._updatePosition(); - } - return this; - }, - - setContent: function (content) { - // Backup previous content and store new content - this._previousContent = this._content; - this._content = content; - - this._updateContent(); - - return this; - }, - - close: function () { - var map = this._map; - - if (map) { - if (L.Browser.touch && !this.options.noHide) { - L.DomEvent.off(this._container, 'click', this.close); - } - - map.removeLayer(this); - } - }, - - updateZIndex: function (zIndex) { - this._zIndex = zIndex; - - if (this._container && this._zIndex) { - this._container.style.zIndex = zIndex; - } - }, - - setOpacity: function (opacity) { - this.options.opacity = opacity; - - if (this._container) { - L.DomUtil.setOpacity(this._container, opacity); - } - }, - - _initLayout: function () { - this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated'); - this.updateZIndex(this._zIndex); - }, - - _update: function () { - if (!this._map) { return; } - - this._container.style.visibility = 'hidden'; - - this._updateContent(); - this._updatePosition(); - - this._container.style.visibility = ''; - }, - - _updateContent: function () { - if (!this._content || !this._map || this._prevContent === this._content) { - return; - } - - if (typeof this._content === 'string') { - this._container.innerHTML = this._content; - - this._prevContent = this._content; - - this._labelWidth = this._container.offsetWidth; - } - }, - - _updatePosition: function () { - var pos = this._map.latLngToLayerPoint(this._latlng); - - this._setPosition(pos); - }, - - _setPosition: function (pos) { - var map = this._map, - container = this._container, - centerPoint = map.latLngToContainerPoint(map.getCenter()), - labelPoint = map.layerPointToContainerPoint(pos), - direction = this.options.direction, - labelWidth = this._labelWidth, - offset = L.point(this.options.offset); - - // position to the right (right or auto & needs to) - if (direction === 'right' || direction === 'auto' && labelPoint.x < centerPoint.x) { - L.DomUtil.addClass(container, 'leaflet-label-right'); - L.DomUtil.removeClass(container, 'leaflet-label-left'); - - pos = pos.add(offset); - } else { // position to the left - L.DomUtil.addClass(container, 'leaflet-label-left'); - L.DomUtil.removeClass(container, 'leaflet-label-right'); - - pos = pos.add(L.point(-offset.x - labelWidth, offset.y)); - } - - L.DomUtil.setPosition(container, pos); - }, - - _zoomAnimation: function (opt) { - var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); - - this._setPosition(pos); - }, - - _onMoveEnd: function () { - if (!this._animated || this.options.direction === 'auto') { - this._updatePosition(); - } - }, - - _onViewReset: function (e) { - /* if map resets hard, we must update the label */ - if (e && e.hard) { - this._update(); - } - }, - - _initInteraction: function () { - if (!this.options.clickable) { return; } - - var container = this._container, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; - - L.DomUtil.addClass(container, 'leaflet-clickable'); - L.DomEvent.on(container, 'click', this._onMouseClick, this); - - for (var i = 0; i < events.length; i++) { - L.DomEvent.on(container, events[i], this._fireMouseEvent, this); - } - }, - - _removeInteraction: function () { - if (!this.options.clickable) { return; } - - var container = this._container, - events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; - - L.DomUtil.removeClass(container, 'leaflet-clickable'); - L.DomEvent.off(container, 'click', this._onMouseClick, this); - - for (var i = 0; i < events.length; i++) { - L.DomEvent.off(container, events[i], this._fireMouseEvent, this); - } - }, - - _onMouseClick: function (e) { - if (this.hasEventListeners(e.type)) { - L.DomEvent.stopPropagation(e); - } - - this.fire(e.type, { - originalEvent: e - }); - }, - - _fireMouseEvent: function (e) { - this.fire(e.type, { - originalEvent: e - }); - - // TODO proper custom event propagation - // this line will always be called if marker is in a FeatureGroup - if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) { - L.DomEvent.preventDefault(e); - } - if (e.type !== 'mousedown') { - L.DomEvent.stopPropagation(e); - } else { - L.DomEvent.preventDefault(e); - } - } -}); diff --git a/mapentity/static/mapentity/Leaflet.label/src/Leaflet.label.js b/mapentity/static/mapentity/Leaflet.label/src/Leaflet.label.js deleted file mode 100644 index 5520696f22..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/Leaflet.label.js +++ /dev/null @@ -1,5 +0,0 @@ -/* - * Leaflet.label assumes that you have already included the Leaflet library. - */ - -L.labelVersion = '0.2.2-dev'; \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/src/Map.Label.js b/mapentity/static/mapentity/Leaflet.label/src/Map.Label.js deleted file mode 100644 index 7a892cd5a0..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/Map.Label.js +++ /dev/null @@ -1,5 +0,0 @@ -L.Map.include({ - showLabel: function (label) { - return this.addLayer(label); - } -}); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/src/Marker.Label.js b/mapentity/static/mapentity/Leaflet.label/src/Marker.Label.js deleted file mode 100644 index b8d91c2725..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/Marker.Label.js +++ /dev/null @@ -1,55 +0,0 @@ -// Add in an option to icon that is used to set where the label anchor is -L.Icon.Default.mergeOptions({ - labelAnchor: new L.Point(9, -20) -}); - -// Have to do this since Leaflet is loaded before this plugin and initializes -// L.Marker.options.icon therefore missing our mixin above. -L.Marker.mergeOptions({ - icon: new L.Icon.Default() -}); - -L.Marker.include(L.BaseMarkerMethods); -L.Marker.include({ - _originalUpdateZIndex: L.Marker.prototype._updateZIndex, - - _updateZIndex: function (offset) { - var zIndex = this._zIndex + offset; - - this._originalUpdateZIndex(offset); - - if (this.label) { - this.label.updateZIndex(zIndex); - } - }, - - _originalSetOpacity: L.Marker.prototype.setOpacity, - - setOpacity: function (opacity, labelHasSemiTransparency) { - this.options.labelHasSemiTransparency = labelHasSemiTransparency; - - this._originalSetOpacity(opacity); - }, - - _originalUpdateOpacity: L.Marker.prototype._updateOpacity, - - _updateOpacity: function () { - var absoluteOpacity = this.options.opacity === 0 ? 0 : 1; - - this._originalUpdateOpacity(); - - if (this.label) { - this.label.setOpacity(this.options.labelHasSemiTransparency ? this.options.opacity : absoluteOpacity); - } - }, - - _originalSetLatLng: L.Marker.prototype.setLatLng, - - setLatLng: function (latlng) { - if (this.label && !this._labelNoHide) { - this.hideLabel(); - } - - return this._originalSetLatLng(latlng); - } -}); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/src/Path.Label.js b/mapentity/static/mapentity/Leaflet.label/src/Path.Label.js deleted file mode 100644 index 02a50feeea..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/Path.Label.js +++ /dev/null @@ -1,55 +0,0 @@ -L.Path.include({ - bindLabel: function (content, options) { - if (!this.label || this.label.options !== options) { - this.label = new L.Label(options, this); - } - - this.label.setContent(content); - - if (!this._showLabelAdded) { - this - .on('mouseover', this._showLabel, this) - .on('mousemove', this._moveLabel, this) - .on('mouseout remove', this._hideLabel, this); - - if (L.Browser.touch) { - this.on('click', this._showLabel, this); - } - this._showLabelAdded = true; - } - - return this; - }, - - unbindLabel: function () { - if (this.label) { - this._hideLabel(); - this.label = null; - this._showLabelAdded = false; - this - .off('mouseover', this._showLabel, this) - .off('mousemove', this._moveLabel, this) - .off('mouseout remove', this._hideLabel, this); - } - return this; - }, - - updateLabelContent: function (content) { - if (this.label) { - this.label.setContent(content); - } - }, - - _showLabel: function (e) { - this.label.setLatLng(e.latlng); - this._map.showLabel(this.label); - }, - - _moveLabel: function (e) { - this.label.setLatLng(e.latlng); - }, - - _hideLabel: function () { - this.label.close(); - } -}); \ No newline at end of file diff --git a/mapentity/static/mapentity/Leaflet.label/src/copyright.js b/mapentity/static/mapentity/Leaflet.label/src/copyright.js deleted file mode 100644 index 96c59642ac..0000000000 --- a/mapentity/static/mapentity/Leaflet.label/src/copyright.js +++ /dev/null @@ -1,8 +0,0 @@ -/* - Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps. - (c) 2012-2013, Jacob Toye, Smartrak - - https://github.com/Leaflet/Leaflet.label - http://leafletjs.com - https://github.com/jacobtoye -*/ diff --git a/mapentity/static/mapentity/dataTables/js/dataTables.ajax_list.js b/mapentity/static/mapentity/dataTables/js/dataTables.ajax_list.js index effafff6c9..4ebacec9ee 100644 --- a/mapentity/static/mapentity/dataTables/js/dataTables.ajax_list.js +++ b/mapentity/static/mapentity/dataTables/js/dataTables.ajax_list.js @@ -10,8 +10,8 @@ JQDataTable.init = function($elem, url, options) { "oLanguage": { "sLengthMenu": "_MENU_ records per page" }, - "bProcessing": false, - "bServerSide": false, + "bProcessing": true, + "bServerSide": true, "sAjaxSource": url }; diff --git a/mapentity/static/mapentity/dataTables/js/dataTables.plugins.js b/mapentity/static/mapentity/dataTables/js/dataTables.plugins.js index cb1f08aa95..e5e5519851 100644 --- a/mapentity/static/mapentity/dataTables/js/dataTables.plugins.js +++ b/mapentity/static/mapentity/dataTables/js/dataTables.plugins.js @@ -70,49 +70,38 @@ $.fn.dataTableExt.oApi.fnReloadAjax = function ( oSettings, sNewSource, sAjaxDat } this.oApi._fnProcessingDisplay( oSettings, true ); var that = this; - var iStart = oSettings._iDisplayStart; var aData = []; this.oApi._fnServerParams( oSettings, aData ); + that.oApi._fnClearTable( oSettings ); + var callback_args = {}; + var getObjectDataFn = null; - oSettings.fnServerData( oSettings.sAjaxSource, aData, function(json) { - /* Clear the old information from the table */ - that.oApi._fnClearTable( oSettings ); - - /* Got the data - add it to the table */ - var callback_args = {}; - var getObjectDataFn = null; - - if (sAjaxDataPropWithCbArg !== undefined) { - getObjectDataFn = function(data, type) { - return sAjaxDataPropWithCbArg(data, type, callback_args); - }; - } else if (oSettings.sAjaxDataProp !== "") { - getObjectDataFn = oSettings.sAjaxDataProp; - } - - var aData = getObjectDataFn ? that.oApi._fnGetObjectDataFn(getObjectDataFn)( json ) : json; - - for ( var i=0 ; i Leaflet.Control.FullScreen Demo - + - + @@ -21,18 +23,20 @@