diff --git a/dist/angular-leaflet-directive.js b/dist/angular-leaflet-directive.js
index 5f94e7a2..05e83b62 100644
--- a/dist/angular-leaflet-directive.js
+++ b/dist/angular-leaflet-directive.js
@@ -1,5 +1,34 @@
+/**!
+ * The MIT License
+ *
+ * Copyright (c) 2013 the angular-leaflet-directive Team, http://tombatossals.github.io/angular-leaflet-directive
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 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.
+ *
+ * angular-leaflet-directive
+ * https://github.com/tombatossals/angular-leaflet-directive
+ *
+ * @authors https://github.com/tombatossals/angular-leaflet-directive/graphs/contributors
+ */
+
/*!
-* angular-leaflet-directive 0.9.1 2015-11-04
+* angular-leaflet-directive 2015-11-06
* angular-leaflet-directive - An AngularJS directive to easily interact with Leaflet maps
* git: https://github.com/tombatossals/angular-leaflet-directive
*/
diff --git a/dist/angular-leaflet-directive.min.js b/dist/angular-leaflet-directive.min.js
index 2d491c57..aab6e085 100644
--- a/dist/angular-leaflet-directive.min.js
+++ b/dist/angular-leaflet-directive.min.js
@@ -28,7 +28,7 @@
*/
/*!
-* angular-leaflet-directive 0.9.1 2015-11-04
+* angular-leaflet-directive 2015-11-06
* angular-leaflet-directive - An AngularJS directive to easily interact with Leaflet maps
* git: https://github.com/tombatossals/angular-leaflet-directive
*/
@@ -36,4 +36,5 @@
'use strict';
!function(angular){"use strict";angular.module("leaflet-directive",[]).directive("leaflet",["$q","leafletData","leafletMapDefaults","leafletHelpers","leafletMapEvents",function(a,b,c,d,e){return{restrict:"EA",replace:!0,scope:{center:"=",lfCenter:"=",defaults:"=",maxbounds:"=",bounds:"=",markers:"=",legend:"=",geojson:"=",paths:"=",tiles:"=",layers:"=",controls:"=",decorations:"=",eventBroadcast:"=",markersWatchOptions:"=",geojsonWatchOptions:"="},transclude:!0,template:'
',controller:["$scope",function(b){this._leafletMap=a.defer(),this.getMap=function(){return this._leafletMap.promise},this.getLeafletScope=function(){return b}}],link:function(a,f,g,h){function i(){isNaN(g.width)?f.css("width",g.width):f.css("width",g.width+"px")}function j(){isNaN(g.height)?f.css("height",g.height):f.css("height",g.height+"px")}var k=d.isDefined,l=c.setDefaults(a.defaults,g.id),m=e.getAvailableMapEvents(),n=e.addEvents;a.mapId=g.id,b.setDirectiveControls({},g.id),k(g.width)&&(i(),a.$watch(function(){return f[0].getAttribute("width")},function(){i(),o.invalidateSize()})),k(g.height)&&(j(),a.$watch(function(){return f[0].getAttribute("height")},function(){j(),o.invalidateSize()}));var o=new L.Map(f[0],c.getMapCreationDefaults(g.id));if(h._leafletMap.resolve(o),k(g.center)||k(g.lfCenter)||o.setView([l.center.lat,l.center.lng],l.center.zoom),!k(g.tiles)&&!k(g.layers)){var p=L.tileLayer(l.tileLayer,l.tileLayerOptions);p.addTo(o),b.setTiles(p,g.id)}if(k(o.zoomControl)&&k(l.zoomControlPosition)&&o.zoomControl.setPosition(l.zoomControlPosition),k(o.zoomControl)&&l.zoomControl===!1&&o.zoomControl.removeFrom(o),k(o.zoomsliderControl)&&k(l.zoomsliderControl)&&l.zoomsliderControl===!1&&o.zoomsliderControl.removeFrom(o),!k(g.eventBroadcast)){var q="broadcast";n(o,m,"eventName",a,q)}o.whenReady(function(){b.setMap(o,g.id)}),a.$on("$destroy",function(){c.reset(),o.remove(),b.unresolveMap(g.id)}),a.$on("invalidateSize",function(){o.invalidateSize()})}}}]),angular.module("leaflet-directive").factory("leafletBoundsHelpers",["$log","leafletHelpers",function(a,b){function c(a){return angular.isDefined(a)&&angular.isDefined(a.southWest)&&angular.isDefined(a.northEast)&&angular.isNumber(a.southWest.lat)&&angular.isNumber(a.southWest.lng)&&angular.isNumber(a.northEast.lat)&&angular.isNumber(a.northEast.lng)}var d=b.isArray,e=b.isNumber,f=b.isFunction,g=b.isDefined;return{createLeafletBounds:function(a){return c(a)?L.latLngBounds([a.southWest.lat,a.southWest.lng],[a.northEast.lat,a.northEast.lng]):void 0},isValidBounds:c,createBoundsFromArray:function(b){return d(b)&&2===b.length&&d(b[0])&&d(b[1])&&2===b[0].length&&2===b[1].length&&e(b[0][0])&&e(b[0][1])&&e(b[1][0])&&e(b[1][1])?{northEast:{lat:b[0][0],lng:b[0][1]},southWest:{lat:b[1][0],lng:b[1][1]}}:void a.error("[AngularJS - Leaflet] The bounds array is not valid.")},createBoundsFromLeaflet:function(b){if(!(g(b)&&f(b.getNorthEast)&&f(b.getSouthWest)))return void a.error("[AngularJS - Leaflet] The leaflet bounds is not valid object.");var c=b.getNorthEast(),d=b.getSouthWest();return{northEast:{lat:c.lat,lng:c.lng},southWest:{lat:d.lat,lng:d.lng}}}}}]),angular.module("leaflet-directive").factory("leafletControlHelpers",["$rootScope","$log","leafletHelpers","leafletLayerHelpers","leafletMapDefaults",function(a,b,c,d,e){var f=c.isDefined,g=c.isObject,h=d.createLayer,i={},j=c.errorHeader+" [Controls] ",k=function(a,b,c){var d=e.getDefaults(c);if(!d.controls.layers.visible)return!1;var h=!1;return g(a)&&Object.keys(a).forEach(function(b){var c=a[b];f(c.layerOptions)&&c.layerOptions.showOnSelector===!1||(h=!0)}),g(b)&&Object.keys(b).forEach(function(a){var c=b[a];f(c.layerParams)&&c.layerParams.showOnSelector===!1||(h=!0)}),h},l=function(a){var b=e.getDefaults(a),c={collapsed:b.controls.layers.collapsed,position:b.controls.layers.position,autoZIndex:!1};angular.extend(c,b.controls.layers.options);var d;return d=b.controls.layers&&f(b.controls.layers.control)?b.controls.layers.control.apply(this,[[],[],c]):new L.control.layers([],[],c)},m={draw:{isPluginLoaded:function(){return angular.isDefined(L.Control.Draw)?!0:(b.error(j+" Draw plugin is not loaded."),!1)},checkValidParams:function(){return!0},createControl:function(a){return new L.Control.Draw(a)}},scale:{isPluginLoaded:function(){return!0},checkValidParams:function(){return!0},createControl:function(a){return new L.control.scale(a)}},fullscreen:{isPluginLoaded:function(){return angular.isDefined(L.Control.Fullscreen)?!0:(b.error(j+" Fullscreen plugin is not loaded."),!1)},checkValidParams:function(){return!0},createControl:function(a){return new L.Control.Fullscreen(a)}},search:{isPluginLoaded:function(){return angular.isDefined(L.Control.Search)?!0:(b.error(j+" Search plugin is not loaded."),!1)},checkValidParams:function(){return!0},createControl:function(a){return new L.Control.Search(a)}},custom:{},minimap:{isPluginLoaded:function(){return angular.isDefined(L.Control.MiniMap)?!0:(b.error(j+" Minimap plugin is not loaded."),!1)},checkValidParams:function(a){return f(a.layer)?!0:(b.warn(j+' minimap "layer" option should be defined.'),!1)},createControl:function(a){var c=h(a.layer);return f(c)?new L.Control.MiniMap(c,a):void b.warn(j+' minimap control "layer" could not be created.')}}};return{layersControlMustBeVisible:k,isValidControlType:function(a){return-1!==Object.keys(m).indexOf(a)},createControl:function(a,b){return m[a].checkValidParams(b)?m[a].createControl(b):void 0},updateLayersControl:function(a,b,c,d,e,g){var h,j=i[b],m=k(d,e,b);if(f(j)&&c){for(h in g.baselayers)j.removeLayer(g.baselayers[h]);for(h in g.overlays)j.removeLayer(g.overlays[h]);a.removeControl(j),delete i[b]}if(m){j=l(b),i[b]=j;for(h in d){var n=f(d[h].layerOptions)&&d[h].layerOptions.showOnSelector===!1;!n&&f(g.baselayers[h])&&j.addBaseLayer(g.baselayers[h],d[h].name)}for(h in e){var o=f(e[h].layerParams)&&e[h].layerParams.showOnSelector===!1;!o&&f(g.overlays[h])&&j.addOverlay(g.overlays[h],e[h].name)}a.addControl(j)}return m}}}]),angular.module("leaflet-directive").service("leafletData",["$log","$q","leafletHelpers",function(a,b,c){var d=c.getDefer,e=c.getUnresolvedDefer,f=c.setResolvedDefer,g={},h=this,i=function(a){return a.charAt(0).toUpperCase()+a.slice(1)},j=["map","tiles","layers","paths","markers","geoJSON","UTFGrid","decorations","directiveControls"];j.forEach(function(a){g[a]={}}),this.unresolveMap=function(a){var b=c.obtainEffectiveMapId(g.map,a);j.forEach(function(a){g[a][b]=void 0})},j.forEach(function(a){var b=i(a);h["set"+b]=function(b,c){var d=e(g[a],c);d.resolve(b),f(g[a],c)},h["get"+b]=function(b){var c=d(g[a],b);return c.promise}})}]),angular.module("leaflet-directive").service("leafletDirectiveControlsHelpers",["$log","leafletData","leafletHelpers",function(a,b,c){var d=c.isDefined,e=c.isString,f=c.isObject,g=c.errorHeader,h=g+"[leafletDirectiveControlsHelpers",i=function(c,g,i,j){var k=h+".extend] ",l={};if(!d(g))return void a.error(k+"thingToAddName cannot be undefined");if(e(g)&&d(i)&&d(j))l[g]={create:i,clean:j};else{if(!f(g)||d(i)||d(j))return void a.error(k+"incorrect arguments");l=g}b.getDirectiveControls().then(function(a){angular.extend(a,l),b.setDirectiveControls(a,c)})};return{extend:i}}]),angular.module("leaflet-directive").service("leafletGeoJsonHelpers",["leafletHelpers","leafletIterators",function(a,b){var c=a,d=b,e=function(a,b){return this.lat=a,this.lng=b,this},f=function(a){return Array.isArray(a)&&2===a.length?a[1]:c.isDefined(a.type)&&"Point"===a.type?+a.coordinates[1]:+a.lat},g=function(a){return Array.isArray(a)&&2===a.length?a[0]:c.isDefined(a.type)&&"Point"===a.type?+a.coordinates[0]:+a.lng},h=function(a){if(c.isUndefined(a))return!1;if(c.isArray(a)){if(2===a.length&&c.isNumber(a[0])&&c.isNumber(a[1]))return!0}else if(c.isDefined(a.type)&&"Point"===a.type&&c.isArray(a.coordinates)&&2===a.coordinates.length&&c.isNumber(a.coordinates[0])&&c.isNumber(a.coordinates[1]))return!0;var b=d.all(["lat","lng"],function(b){return c.isDefined(a[b])&&c.isNumber(a[b])});return b},i=function(a){if(a&&h(a)){var b=null;if(Array.isArray(a)&&2===a.length)b=new e(a[1],a[0]);else{if(!c.isDefined(a.type)||"Point"!==a.type)return a;b=new e(a.coordinates[1],a.coordinates[0])}return angular.extend(a,b)}};return{getLat:f,getLng:g,validateCoords:h,getCoords:i}}]),angular.module("leaflet-directive").service("leafletHelpers",["$q","$log",function(a,b){function c(a,c){var d,f;if(angular.isDefined(c))d=c;else if(0===Object.keys(a).length)d="main";else if(Object.keys(a).length>=1)for(f in a)a.hasOwnProperty(f)&&(d=f);else b.error(e+"- You have more than 1 map on the DOM, you must provide the map ID to the leafletData.getXXX call");return d}function d(b,d){var e,f=c(b,d);return angular.isDefined(b[f])&&b[f].resolvedDefer!==!0?e=b[f].defer:(e=a.defer(),b[f]={defer:e,resolvedDefer:!1}),e}var e="[AngularJS - Leaflet] ",f=angular.copy,g=f,h=function(a,b){var c;if(a&&angular.isObject(a))return null!==b&&angular.isString(b)?(c=a,b.split(".").forEach(function(a){c&&(c=c[a])}),c):b},i=function(a){return a.split(".").reduce(function(a,b){return a+'["'+b+'"]'})},j=function(a){return a.reduce(function(a,b){return a+"."+b})},k=function(a){return angular.isDefined(a)&&null!==a},l=function(a){return!k(a)},m=/([\:\-\_]+(.))/g,n=/^moz([A-Z])/,o=/^((?:x|data)[\:\-_])/i,p=function(a){return a.replace(m,function(a,b,c,d){return d?c.toUpperCase():c}).replace(n,"Moz$1")},q=function(a){return p(a.replace(o,""))};return{camelCase:p,directiveNormalize:q,copy:f,clone:g,errorHeader:e,getObjectValue:h,getObjectArrayPath:i,getObjectDotPath:j,defaultTo:function(a,b){return k(a)?a:b},isTruthy:function(a){return"true"===a||a===!0},isEmpty:function(a){return 0===Object.keys(a).length},isUndefinedOrEmpty:function(a){return angular.isUndefined(a)||null===a||0===Object.keys(a).length},isDefined:k,isUndefined:l,isNumber:angular.isNumber,isString:angular.isString,isArray:angular.isArray,isObject:angular.isObject,isFunction:angular.isFunction,equals:angular.equals,isValidCenter:function(a){return angular.isDefined(a)&&angular.isNumber(a.lat)&&angular.isNumber(a.lng)&&angular.isNumber(a.zoom)},isValidPoint:function(a){return angular.isDefined(a)?angular.isArray(a)?2===a.length&&angular.isNumber(a[0])&&angular.isNumber(a[1]):angular.isNumber(a.lat)&&angular.isNumber(a.lng):!1},isSameCenterOnMap:function(a,b){var c=b.getCenter(),d=b.getZoom();return a.lat&&a.lng&&c.lat.toFixed(4)===a.lat.toFixed(4)&&c.lng.toFixed(4)===a.lng.toFixed(4)&&d===a.zoom?!0:!1},safeApply:function(a,b){var c=a.$root.$$phase;"$apply"===c||"$digest"===c?a.$eval(b):a.$evalAsync(b)},obtainEffectiveMapId:c,getDefer:function(a,b){var e,f=c(a,b);return e=angular.isDefined(a[f])&&a[f].resolvedDefer!==!1?a[f].defer:d(a,b)},getUnresolvedDefer:d,setResolvedDefer:function(a,b){var d=c(a,b);a[d].resolvedDefer=!0},rangeIsSupported:function(){var a=document.createElement("input");return a.setAttribute("type","range"),"range"===a.type},FullScreenControlPlugin:{isLoaded:function(){return angular.isDefined(L.Control.Fullscreen)}},MiniMapControlPlugin:{isLoaded:function(){return angular.isDefined(L.Control.MiniMap)}},AwesomeMarkersPlugin:{isLoaded:function(){return angular.isDefined(L.AwesomeMarkers)&&angular.isDefined(L.AwesomeMarkers.Icon)},is:function(a){return this.isLoaded()?a instanceof L.AwesomeMarkers.Icon:!1},equal:function(a,b){return this.isLoaded()&&this.is(a)?angular.equals(a,b):!1}},VectorMarkersPlugin:{isLoaded:function(){return angular.isDefined(L.VectorMarkers)&&angular.isDefined(L.VectorMarkers.Icon)},is:function(a){return this.isLoaded()?a instanceof L.VectorMarkers.Icon:!1},equal:function(a,b){return this.isLoaded()&&this.is(a)?angular.equals(a,b):!1}},DomMarkersPlugin:{isLoaded:function(){return angular.isDefined(L.DomMarkers)&&angular.isDefined(L.DomMarkers.Icon)?!0:!1},is:function(a){return this.isLoaded()?a instanceof L.DomMarkers.Icon:!1},equal:function(a,b){return this.isLoaded()&&this.is(a)?angular.equals(a,b):!1}},PolylineDecoratorPlugin:{isLoaded:function(){return angular.isDefined(L.PolylineDecorator)?!0:!1},is:function(a){return this.isLoaded()?a instanceof L.PolylineDecorator:!1},equal:function(a,b){return this.isLoaded()&&this.is(a)?angular.equals(a,b):!1}},MakiMarkersPlugin:{isLoaded:function(){return angular.isDefined(L.MakiMarkers)&&angular.isDefined(L.MakiMarkers.Icon)?!0:!1},is:function(a){return this.isLoaded()?a instanceof L.MakiMarkers.Icon:!1},equal:function(a,b){return this.isLoaded()&&this.is(a)?angular.equals(a,b):!1}},ExtraMarkersPlugin:{isLoaded:function(){return angular.isDefined(L.ExtraMarkers)&&angular.isDefined(L.ExtraMarkers.Icon)?!0:!1},is:function(a){return this.isLoaded()?a instanceof L.ExtraMarkers.Icon:!1},equal:function(a,b){return this.isLoaded()&&this.is(a)?angular.equals(a,b):!1}},LabelPlugin:{isLoaded:function(){return angular.isDefined(L.Label)},is:function(a){return this.isLoaded()?a instanceof L.MarkerClusterGroup:!1}},MarkerClusterPlugin:{isLoaded:function(){return angular.isDefined(L.MarkerClusterGroup)},is:function(a){return this.isLoaded()?a instanceof L.MarkerClusterGroup:!1}},GoogleLayerPlugin:{isLoaded:function(){return angular.isDefined(L.Google)},is:function(a){return this.isLoaded()?a instanceof L.Google:!1}},LeafletProviderPlugin:{isLoaded:function(){return angular.isDefined(L.TileLayer.Provider)},is:function(a){return this.isLoaded()?a instanceof L.TileLayer.Provider:!1}},ChinaLayerPlugin:{isLoaded:function(){return angular.isDefined(L.tileLayer.chinaProvider)}},HeatLayerPlugin:{isLoaded:function(){return angular.isDefined(L.heatLayer)}},WebGLHeatMapLayerPlugin:{isLoaded:function(){return angular.isDefined(L.TileLayer.WebGLHeatMap)}},BingLayerPlugin:{isLoaded:function(){return angular.isDefined(L.BingLayer)},is:function(a){return this.isLoaded()?a instanceof L.BingLayer:!1}},WFSLayerPlugin:{isLoaded:function(){return void 0!==L.GeoJSON.WFS},is:function(a){return this.isLoaded()?a instanceof L.GeoJSON.WFS:!1}},AGSBaseLayerPlugin:{isLoaded:function(){return void 0!==L.esri&&void 0!==L.esri.basemapLayer},is:function(a){return this.isLoaded()?a instanceof L.esri.basemapLayer:!1}},AGSLayerPlugin:{isLoaded:function(){return void 0!==lvector&&void 0!==lvector.AGS},is:function(a){return this.isLoaded()?a instanceof lvector.AGS:!1}},AGSFeatureLayerPlugin:{isLoaded:function(){return void 0!==L.esri&&void 0!==L.esri.featureLayer},is:function(a){return this.isLoaded()?a instanceof L.esri.featureLayer:!1}},AGSTiledMapLayerPlugin:{isLoaded:function(){return void 0!==L.esri&&void 0!==L.esri.tiledMapLayer},is:function(a){return this.isLoaded()?a instanceof L.esri.tiledMapLayer:!1}},AGSDynamicMapLayerPlugin:{isLoaded:function(){return void 0!==L.esri&&void 0!==L.esri.dynamicMapLayer},is:function(a){return this.isLoaded()?a instanceof L.esri.dynamicMapLayer:!1}},AGSImageMapLayerPlugin:{isLoaded:function(){return void 0!==L.esri&&void 0!==L.esri.imageMapLayer},is:function(a){return this.isLoaded()?a instanceof L.esri.imageMapLayer:!1}},AGSClusteredLayerPlugin:{isLoaded:function(){return void 0!==L.esri&&void 0!==L.esri.clusteredFeatureLayer},is:function(a){return this.isLoaded()?a instanceof L.esri.clusteredFeatureLayer:!1}},AGSHeatmapLayerPlugin:{isLoaded:function(){return void 0!==L.esri&&void 0!==L.esri.heatmapFeatureLayer},is:function(a){return this.isLoaded()?a instanceof L.esri.heatmapFeatureLayer:!1}},YandexLayerPlugin:{isLoaded:function(){return angular.isDefined(L.Yandex)},is:function(a){return this.isLoaded()?a instanceof L.Yandex:!1}},GeoJSONPlugin:{isLoaded:function(){return angular.isDefined(L.TileLayer.GeoJSON)},is:function(a){return this.isLoaded()?a instanceof L.TileLayer.GeoJSON:!1}},UTFGridPlugin:{isLoaded:function(){return angular.isDefined(L.UtfGrid)},is:function(a){return this.isLoaded()?a instanceof L.UtfGrid:(b.error("[AngularJS - Leaflet] No UtfGrid plugin found."),!1)}},CartoDB:{isLoaded:function(){return cartodb},is:function(){return!0}},Leaflet:{DivIcon:{is:function(a){return a instanceof L.DivIcon},equal:function(a,b){return this.is(a)?angular.equals(a,b):!1}},Icon:{is:function(a){return a instanceof L.Icon},equal:function(a,b){return this.is(a)?angular.equals(a,b):!1}}},watchOptions:{doWatch:!0,isDeep:!0,individual:{doWatch:!0,isDeep:!0}}}}]),angular.module("leaflet-directive").service("leafletIterators",["$log","leafletHelpers",function(a,b){var c,d=b,e=b.errorHeader+"leafletIterators: ",f=Object.keys,g=d.isFunction,h=d.isObject,i=Math.pow(2,53)-1,j=function(a){var b=null!==a&&a.length;return d.isNumber(b)&&b>=0&&i>=b},k=function(a){return a},l=function(a){return function(b){return null===b?void 0:b[a]}},m=function(a,b,c){if(void 0===b)return a;switch(null===c?3:c){case 1:return function(c){return a.call(b,c)};case 2:return function(c,d){return a.call(b,c,d)};case 3:return function(c,d,e){return a.call(b,c,d,e)};case 4:return function(c,d,e,f){return a.call(b,c,d,e,f)}}return function(){return a.apply(b,arguments)}},n=function(a,b){return function(c){var d=arguments.length;if(2>d||null===c)return c;for(var e=1;d>e;e++)for(var f=arguments[e],g=a(f),h=g.length,i=0;h>i;i++){var j=g[i];b&&void 0!==c[j]||(c[j]=f[j])}return c}},o=null;c=o=n(f);var p,q=function(a,b){var c=f(b),d=c.length;if(null===a)return!d;for(var e=Object(a),g=0;d>g;g++){var h=c[g];if(b[h]!==e[h]||!(h in e))return!1}return!0},r=null;p=r=function(a){return a=c({},a),function(b){return q(b,a)}};var s,t=function(a,b,c){return null===a?k:g(a)?m(a,b,c):h(a)?p(a):l(a)},u=null;s=u=function(a,b,c){b=t(b,c);for(var d=!j(a)&&f(a),e=(d||a).length,g=0;e>g;g++){var h=d?d[g]:g;if(!b(a[h],h,a))return!1}return!0};var v=function(b,c,f,g){return f||d.isDefined(b)&&d.isDefined(c)?d.isFunction(c)?!1:(g=d.defaultTo(c,"cb"),a.error(e+g+" is not a function"),!0):!0},w=function(a,b,c){if(!v(void 0,c,!0,"internalCb")&&!v(a,b))for(var d in a)a.hasOwnProperty(d)&&c(a[d],d)},x=function(a,b){w(a,b,function(a,c){b(a,c)})};return{each:x,forEach:x,every:s,all:u}}]),angular.module("leaflet-directive").factory("leafletLayerHelpers",["$rootScope","$log","$q","leafletHelpers","leafletIterators",function($rootScope,$log,$q,leafletHelpers,leafletIterators){function isValidLayerType(a){return isString(a.type)?-1===Object.keys(layerTypes).indexOf(a.type)?($log.error("[AngularJS - Leaflet] A layer must have a valid type: "+Object.keys(layerTypes)),!1):layerTypes[a.type].mustHaveUrl&&!isString(a.url)?($log.error("[AngularJS - Leaflet] A base layer must have an url"),!1):layerTypes[a.type].mustHaveData&&!isDefined(a.data)?($log.error('[AngularJS - Leaflet] The base layer must have a "data" array attribute'),!1):layerTypes[a.type].mustHaveLayer&&!isDefined(a.layer)?($log.error("[AngularJS - Leaflet] The type of layer "+a.type+" must have an layer defined"),!1):layerTypes[a.type].mustHaveBounds&&!isDefined(a.bounds)?($log.error("[AngularJS - Leaflet] The type of layer "+a.type+" must have bounds defined"),!1):layerTypes[a.type].mustHaveKey&&!isDefined(a.key)?($log.error("[AngularJS - Leaflet] The type of layer "+a.type+" must have key defined"),!1):!0:($log.error("[AngularJS - Leaflet] A layer must have a valid type defined."),!1)}function createLayer(a){if(isValidLayerType(a)){if(!isString(a.name))return void $log.error("[AngularJS - Leaflet] A base layer must have a name");isObject(a.layerParams)||(a.layerParams={}),isObject(a.layerOptions)||(a.layerOptions={});for(var b in a.layerParams)a.layerOptions[b]=a.layerParams[b];var c={url:a.url,data:a.data,options:a.layerOptions,layer:a.layer,icon:a.icon,type:a.layerType,bounds:a.bounds,key:a.key,apiKey:a.apiKey,pluginOptions:a.pluginOptions,user:a.user};return layerTypes[a.type].createLayer(c)}}function safeAddLayer(a,b){b&&"function"==typeof b.addTo?b.addTo(a):a.addLayer(b)}function safeRemoveLayer(a,b,c){if(isDefined(c)&&isDefined(c.loadedDefer))if(angular.isFunction(c.loadedDefer)){var d=c.loadedDefer();$log.debug("Loaded Deferred",d);var e=d.length;if(e>0)for(var f=function(){e--,0===e&&a.removeLayer(b)},g=0;g'+b.error.message+"";else if("arcgis"===c)for(var e=0;e'+f.layerName+"";for(var g=0;g'+h.label+"
"}}else"image"===c&&(a.innerHTML=' ')},b=function(b,c,d,e){return function(){var f=L.DomUtil.create("div",c);return L.Browser.touch?L.DomEvent.on(f,"click",L.DomEvent.stopPropagation):(L.DomEvent.disableClickPropagation(f),L.DomEvent.on(f,"mousewheel",L.DomEvent.stopPropagation)),a(f,b,d,e),f}},c=function(a,b){return function(){for(var c=L.DomUtil.create("div",b),d=0;d'+a.labels[d]+"
";return L.Browser.touch?L.DomEvent.on(c,"click",L.DomEvent.stopPropagation):(L.DomEvent.disableClickPropagation(c),L.DomEvent.on(c,"mousewheel",L.DomEvent.stopPropagation)),c}};return{getOnAddLegend:b,getOnAddArrayLegend:c,updateLegend:a}}),angular.module("leaflet-directive").factory("leafletMapDefaults",["$q","leafletHelpers",function(a,b){function c(){return{keyboard:!0,dragging:!0,worldCopyJump:!1,doubleClickZoom:!0,scrollWheelZoom:!0,tap:!0,touchZoom:!0,zoomControl:!0,zoomsliderControl:!1,zoomControlPosition:"topleft",attributionControl:!0,controls:{layers:{visible:!0,position:"topright",collapsed:!0}},nominatim:{server:" http://nominatim.openstreetmap.org/search"},crs:L.CRS.EPSG3857,tileLayer:"//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",tileLayerOptions:{attribution:'© OpenStreetMap contributors'},path:{weight:10,opacity:1,color:"#0000ff"},center:{lat:0,lng:0,zoom:1}}}var d=b.isDefined,e=b.isObject,f=b.obtainEffectiveMapId,g={};return{reset:function(){g={}},getDefaults:function(a){var b=f(g,a);return g[b]},getMapCreationDefaults:function(a){var b=f(g,a),c=g[b],e={maxZoom:c.maxZoom,keyboard:c.keyboard,dragging:c.dragging,zoomControl:c.zoomControl,doubleClickZoom:c.doubleClickZoom,scrollWheelZoom:c.scrollWheelZoom,tap:c.tap,touchZoom:c.touchZoom,attributionControl:c.attributionControl,worldCopyJump:c.worldCopyJump,crs:c.crs};if(d(c.minZoom)&&(e.minZoom=c.minZoom),d(c.zoomAnimation)&&(e.zoomAnimation=c.zoomAnimation),d(c.fadeAnimation)&&(e.fadeAnimation=c.fadeAnimation),d(c.markerZoomAnimation)&&(e.markerZoomAnimation=c.markerZoomAnimation),c.map)for(var h in c.map)e[h]=c.map[h];return e},setDefaults:function(a,b){var h=c();d(a)&&(h.doubleClickZoom=d(a.doubleClickZoom)?a.doubleClickZoom:h.doubleClickZoom,h.scrollWheelZoom=d(a.scrollWheelZoom)?a.scrollWheelZoom:h.doubleClickZoom,h.tap=d(a.tap)?a.tap:h.tap,h.touchZoom=d(a.touchZoom)?a.touchZoom:h.doubleClickZoom,h.zoomControl=d(a.zoomControl)?a.zoomControl:h.zoomControl,h.zoomsliderControl=d(a.zoomsliderControl)?a.zoomsliderControl:h.zoomsliderControl,h.attributionControl=d(a.attributionControl)?a.attributionControl:h.attributionControl,h.tileLayer=d(a.tileLayer)?a.tileLayer:h.tileLayer,h.zoomControlPosition=d(a.zoomControlPosition)?a.zoomControlPosition:h.zoomControlPosition,h.keyboard=d(a.keyboard)?a.keyboard:h.keyboard,h.dragging=d(a.dragging)?a.dragging:h.dragging,d(a.controls)&&angular.extend(h.controls,a.controls),e(a.crs)?h.crs=a.crs:d(L.CRS[a.crs])&&(h.crs=L.CRS[a.crs]),d(a.center)&&angular.copy(a.center,h.center),d(a.tileLayerOptions)&&angular.copy(a.tileLayerOptions,h.tileLayerOptions),d(a.maxZoom)&&(h.maxZoom=a.maxZoom),d(a.minZoom)&&(h.minZoom=a.minZoom),d(a.zoomAnimation)&&(h.zoomAnimation=a.zoomAnimation),d(a.fadeAnimation)&&(h.fadeAnimation=a.fadeAnimation),d(a.markerZoomAnimation)&&(h.markerZoomAnimation=a.markerZoomAnimation),d(a.worldCopyJump)&&(h.worldCopyJump=a.worldCopyJump),d(a.map)&&(h.map=a.map),d(a.path)&&(h.path=a.path));var i=f(g,b);return g[i]=h,h}}}]),angular.module("leaflet-directive").service("leafletMarkersHelpers",["$rootScope","$timeout","leafletHelpers","$log","$compile","leafletGeoJsonHelpers",function(a,b,c,d,e,f){var g=c.isDefined,h=c.defaultTo,i=c.MarkerClusterPlugin,j=c.AwesomeMarkersPlugin,k=c.VectorMarkersPlugin,l=c.MakiMarkersPlugin,m=c.ExtraMarkersPlugin,n=c.DomMarkersPlugin,o=c.safeApply,p=c,q=c.isString,r=c.isNumber,s=c.isObject,t={},u=f,v=c.errorHeader,w=function(a){
var b="";return["_icon","_latlng","_leaflet_id","_map","_shadow"].forEach(function(c){b+=c+": "+h(a[c],"undefined")+" \n"}),"[leafletMarker] : \n"+b},x=function(a,b){var c=b?console:d;c.debug(w(a))},y=function(b){if(g(b)&&g(b.type)&&"awesomeMarker"===b.type)return j.isLoaded()||d.error(v+" The AwesomeMarkers Plugin is not loaded."),new L.AwesomeMarkers.icon(b);if(g(b)&&g(b.type)&&"vectorMarker"===b.type)return k.isLoaded()||d.error(v+" The VectorMarkers Plugin is not loaded."),new L.VectorMarkers.icon(b);if(g(b)&&g(b.type)&&"makiMarker"===b.type)return l.isLoaded()||d.error(v+"The MakiMarkers Plugin is not loaded."),new L.MakiMarkers.icon(b);if(g(b)&&g(b.type)&&"extraMarker"===b.type)return m.isLoaded()||d.error(v+"The ExtraMarkers Plugin is not loaded."),new L.ExtraMarkers.icon(b);if(g(b)&&g(b.type)&&"div"===b.type)return new L.divIcon(b);if(g(b)&&g(b.type)&&"dom"===b.type){n.isLoaded()||d.error(v+"The DomMarkers Plugin is not loaded.");var c=angular.isFunction(b.getMarkerScope)?b.getMarkerScope():a,f=e(b.template)(c),h=angular.copy(b);return h.element=f[0],new L.DomMarkers.icon(h)}if(g(b)&&g(b.type)&&"icon"===b.type)return b.icon;var i="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAGmklEQVRYw7VXeUyTZxjvNnfELFuyIzOabermMZEeQC/OclkO49CpOHXOLJl/CAURuYbQi3KLgEhbrhZ1aDwmaoGqKII6odATmH/scDFbdC7LvFqOCc+e95s2VG50X/LLm/f4/Z7neY/ne18aANCmAr5E/xZf1uDOkTcGcWR6hl9247tT5U7Y6SNvWsKT63P58qbfeLJG8M5qcgTknrvvrdDbsT7Ml+tv82X6vVxJE33aRmgSyYtcWVMqX97Yv2JvW39UhRE2HuyBL+t+gK1116ly06EeWFNlAmHxlQE0OMiV6mQCScusKRlhS3QLeVJdl1+23h5dY4FNB3thrbYboqptEFlphTC1hSpJnbRvxP4NWgsE5Jyz86QNNi/5qSUTGuFk1gu54tN9wuK2wc3o+Wc13RCmsoBwEqzGcZsxsvCSy/9wJKf7UWf1mEY8JWfewc67UUoDbDjQC+FqK4QqLVMGGR9d2wurKzqBk3nqIT/9zLxRRjgZ9bqQgub+DdoeCC03Q8j+0QhFhBHR/eP3U/zCln7Uu+hihJ1+bBNffLIvmkyP0gpBZWYXhKussK6mBz5HT6M1Nqpcp+mBCPXosYQfrekGvrjewd59/GvKCE7TbK/04/ZV5QZYVWmDwH1mF3xa2Q3ra3DBC5vBT1oP7PTj4C0+CcL8c7C2CtejqhuCnuIQHaKHzvcRfZpnylFfXsYJx3pNLwhKzRAwAhEqG0SpusBHfAKkxw3w4627MPhoCH798z7s0ZnBJ/MEJbZSbXPhER2ih7p2ok/zSj2cEJDd4CAe+5WYnBCgR2uruyEw6zRoW6/DWJ/OeAP8pd/BGtzOZKpG8oke0SX6GMmRk6GFlyAc59K32OTEinILRJRchah8HQwND8N435Z9Z0FY1EqtxUg+0SO6RJ/mmXz4VuS+DpxXC3gXmZwIL7dBSH4zKE50wESf8qwVgrP1EIlTO5JP9Igu0aexdh28F1lmAEGJGfh7jE6ElyM5Rw/FDcYJjWhbeiBYoYNIpc2FT/SILivp0F1ipDWk4BIEo2VuodEJUifhbiltnNBIXPUFCMpthtAyqws/BPlEF/VbaIxErdxPphsU7rcCp8DohC+GvBIPJS/tW2jtvTmmAeuNO8BNOYQeG8G/2OzCJ3q+soYB5i6NhMaKr17FSal7GIHheuV3uSCY8qYVuEm1cOzqdWr7ku/R0BDoTT+DT+ohCM6/CCvKLKO4RI+dXPeAuaMqksaKrZ7L3FE5FIFbkIceeOZ2OcHO6wIhTkNo0ffgjRGxEqogXHYUPHfWAC/lADpwGcLRY3aeK4/oRGCKYcZXPVoeX/kelVYY8dUGf8V5EBRbgJXT5QIPhP9ePJi428JKOiEYhYXFBqou2Guh+p/mEB1/RfMw6rY7cxcjTrneI1FrDyuzUSRm9miwEJx8E/gUmqlyvHGkneiwErR21F3tNOK5Tf0yXaT+O7DgCvALTUBXdM4YhC/IawPU+2PduqMvuaR6eoxSwUk75ggqsYJ7VicsnwGIkZBSXKOUww73WGXyqP+J2/b9c+gi1YAg/xpwck3gJuucNrh5JvDPvQr0WFXf0piyt8f8/WI0hV4pRxxkQZdJDfDJNOAmM0Ag8jyT6hz0WGXWuP94Yh2jcfjmXAGvHCMslRimDHYuHuDsy2QtHuIavznhbYURq5R57KpzBBRZKPJi8eQg48h4j8SDdowifdIrEVdU+gbO6QNvRRt4ZBthUaZhUnjlYObNagV3keoeru3rU7rcuceqU1mJBxy+BWZYlNEBH+0eH4vRiB+OYybU2hnblYlTvkHinM4m54YnxSyaZYSF6R3jwgP7udKLGIX6r/lbNa9N6y5MFynjWDtrHd75ZvTYAPO/6RgF0k76mQla3FGq7dO+cH8sKn0Vo7nDllwAhqwLPkxrHwWmHJOo+AKJ4rab5OgrM7rVu8eWb2Pu0Dh4eDgXoOfvp7Y7QeqknRmvcTBEyq9m/HQQSCSz6LHq3z0yzsNySRfMS253wl2KyRDbcZPcfJKjZmSEOjcxyi+Y8dUOtsIEH6R2wNykdqrkYJ0RV92H0W58pkfQk7cKevsLK10Py8SdMGfXNXATY+pPbyJR/ET6n9nIfztNtZYRV9XniQu9IA2vOVgy4ir7GCLVmmd+zjkH0eAF9Po6K61pmCXHxU5rHMYd1ftc3owjwRSVRzLjKvqZEty6cRUD7jGqiOdu5HG6MdHjNcNYGqfDm5YRzLBBCCDl/2bk8a8gdbqcfwECu62Fg/HrggAAAABJRU5ErkJggg==",o="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAYAAACoYAD2AAAC5ElEQVRYw+2YW4/TMBCF45S0S1luXZCABy5CgLQgwf//S4BYBLTdJLax0fFqmB07nnQfEGqkIydpVH85M+NLjPe++dcPc4Q8Qh4hj5D/AaQJx6H/4TMwB0PeBNwU7EGQAmAtsNfAzoZkgIa0ZgLMa4Aj6CxIAsjhjOCoL5z7Glg1JAOkaicgvQBXuncwJAWjksLtBTWZe04CnYRktUGdilALppZBOgHGZcBzL6OClABvMSVIzyBjazOgrvACf1ydC5mguqAVg6RhdkSWQFj2uxfaq/BrIZOLEWgZdALIDvcMcZLD8ZbLC9de4yR1sYMi4G20S4Q/PWeJYxTOZn5zJXANZHIxAd4JWhPIloTJZhzMQduM89WQ3MUVAE/RnhAXpTycqys3NZALOBbB7kFrgLesQl2h45Fcj8L1tTSohUwuxhy8H/Qg6K7gIs+3kkaigQCOcyEXCHN07wyQazhrmIulvKMQAwMcmLNqyCVyMAI+BuxSMeTk3OPikLY2J1uE+VHQk6ANrhds+tNARqBeaGc72cK550FP4WhXmFmcMGhTwAR1ifOe3EvPqIegFmF+C8gVy0OfAaWQPMR7gF1OQKqGoBjq90HPMP01BUjPOqGFksC4emE48tWQAH0YmvOgF3DST6xieJgHAWxPAHMuNhrImIdvoNOKNWIOcE+UXE0pYAnkX6uhWsgVXDxHdTfCmrEEmMB2zMFimLVOtiiajxiGWrbU52EeCdyOwPEQD8LqyPH9Ti2kgYMf4OhSKB7qYILbBv3CuVTJ11Y80oaseiMWOONc/Y7kJYe0xL2f0BaiFTxknHO5HaMGMublKwxFGzYdWsBF174H/QDknhTHmHHN39iWFnkZx8lPyM8WHfYELmlLKtgWNmFNzQcC1b47gJ4hL19i7o65dhH0Negbca8vONZoP7doIeOC9zXm8RjuL0Gf4d4OYaU5ljo3GYiqzrWQHfJxA6ALhDpVKv9qYeZA8eM3EhfPSCmpuD0AAAAASUVORK5CYII=";return g(b)&&g(b.iconUrl)?new L.Icon(b):new L.Icon.Default({iconUrl:i,shadowUrl:o,iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]})},z=function(a){g(t[a])&&t.splice(a,1)},A=function(){t={}},B=function(a,b,c){if(a.closePopup(),g(c)&&g(c.overlays))for(var d in c.overlays)if((c.overlays[d]instanceof L.LayerGroup||c.overlays[d]instanceof L.FeatureGroup)&&c.overlays[d].hasLayer(a))return void c.overlays[d].removeLayer(a);if(g(t))for(var e in t)t[e].hasLayer(a)&&t[e].removeLayer(a);b.hasLayer(a)&&b.removeLayer(a)},C=function(a,b){var c=a._popup._container.offsetHeight,d=new L.Point(a._popup._containerLeft,-c-a._popup._containerBottom),e=b.layerPointToContainerPoint(d);null!==e&&a._popup._adjustPan()},D=function(a,b){e(a._popup._contentNode)(b)},E=function(a,c,d){var e=a._popup._contentNode.innerText||a._popup._contentNode.textContent;e.length<1&&b(function(){E(a,c,d)});var f=a._popup._contentNode.offsetWidth;return a._popup._updateLayout(),a._popup._updatePosition(),a._popup.options.autoPan&&C(a,d),f},F=function(b,c,e){var f=angular.isFunction(c.getMessageScope)?c.getMessageScope():a,h=g(c.compileMessage)?c.compileMessage:!0;if(h){if(!g(b._popup)||!g(b._popup._contentNode))return d.error(v+"Popup is invalid or does not have any content."),!1;D(b,f),E(b,c,e)}},G=function(b,c){var d=angular.isFunction(c.getMessageScope)?c.getMessageScope():a,f=angular.isFunction(c.getLabelScope)?c.getLabelScope():d,h=g(c.compileMessage)?c.compileMessage:!0;p.LabelPlugin.isLoaded()&&g(c.label)&&(g(c.label.options)&&c.label.options.noHide===!0&&b.showLabel(),h&&g(b.label)&&e(b.label._container)(f))},H=function(a,b,c,e,f,h,i){if(g(b)){if(!u.validateCoords(a))return d.warn("There are problems with lat-lng data, please verify your marker model"),void B(c,i,h);var j=a===b;if(g(a.iconAngle)&&b.iconAngle!==a.iconAngle&&c.setIconAngle(a.iconAngle),q(a.layer)||q(b.layer)&&(g(h.overlays[b.layer])&&h.overlays[b.layer].hasLayer(c)&&(h.overlays[b.layer].removeLayer(c),c.closePopup()),i.hasLayer(c)||i.addLayer(c)),(r(a.opacity)||r(parseFloat(a.opacity)))&&a.opacity!==b.opacity&&c.setOpacity(a.opacity),q(a.layer)&&b.layer!==a.layer){if(q(b.layer)&&g(h.overlays[b.layer])&&h.overlays[b.layer].hasLayer(c)&&h.overlays[b.layer].removeLayer(c),c.closePopup(),i.hasLayer(c)&&i.removeLayer(c),!g(h.overlays[a.layer]))return void d.error(v+"You must use a name of an existing layer");var k=h.overlays[a.layer];if(!(k instanceof L.LayerGroup||k instanceof L.FeatureGroup))return void d.error(v+'A marker can only be added to a layer of type "group" or "featureGroup"');k.addLayer(c),i.hasLayer(c)&&a.focus===!0&&c.openPopup()}if(a.draggable!==!0&&b.draggable===!0&&g(c.dragging)&&c.dragging.disable(),a.draggable===!0&&b.draggable!==!0&&(c.dragging?c.dragging.enable():L.Handler.MarkerDrag&&(c.dragging=new L.Handler.MarkerDrag(c),c.options.draggable=!0,c.dragging.enable())),s(a.icon)||s(b.icon)&&(c.setIcon(y()),c.closePopup(),c.unbindPopup(),q(a.message)&&c.bindPopup(a.message,a.popupOptions)),s(a.icon)&&s(b.icon)&&!angular.equals(a.icon,b.icon)){var l=!1;c.dragging&&(l=c.dragging.enabled()),c.setIcon(y(a.icon)),l&&c.dragging.enable(),c.closePopup(),c.unbindPopup(),q(a.message)&&(c.bindPopup(a.message,a.popupOptions),i.hasLayer(c)&&a.focus===!0&&c.openPopup())}!q(a.message)&&q(b.message)&&(c.closePopup(),c.unbindPopup()),p.LabelPlugin.isLoaded()&&(g(a.label)&&g(a.label.message)?"label"in b&&"message"in b.label&&!angular.equals(a.label.message,b.label.message)?c.updateLabelContent(a.label.message):!angular.isFunction(c.getLabel)||angular.isFunction(c.getLabel)&&!g(c.getLabel())?(c.bindLabel(a.label.message,a.label.options),G(c,a)):G(c,a):(!("label"in a)||"message"in a.label)&&angular.isFunction(c.unbindLabel)&&c.unbindLabel()),q(a.message)&&!q(b.message)&&c.bindPopup(a.message,a.popupOptions),q(a.message)&&q(b.message)&&a.message!==b.message&&c.setPopupContent(a.message);var m=!1;a.focus!==!0&&b.focus===!0&&(c.closePopup(),m=!0),(a.focus===!0&&(!g(b.focus)||b.focus===!1)||j&&a.focus===!0)&&(c.openPopup(),m=!0),b.zIndexOffset!==a.zIndexOffset&&c.setZIndexOffset(a.zIndexOffset);var n=c.getLatLng(),o=q(a.layer)&&p.MarkerClusterPlugin.is(h.overlays[a.layer]);o?m?(a.lat!==b.lat||a.lng!==b.lng)&&(h.overlays[a.layer].removeLayer(c),c.setLatLng([a.lat,a.lng]),h.overlays[a.layer].addLayer(c)):n.lat!==a.lat||n.lng!==a.lng?(h.overlays[a.layer].removeLayer(c),c.setLatLng([a.lat,a.lng]),h.overlays[a.layer].addLayer(c)):a.lat!==b.lat||a.lng!==b.lng?(h.overlays[a.layer].removeLayer(c),c.setLatLng([a.lat,a.lng]),h.overlays[a.layer].addLayer(c)):s(a.icon)&&s(b.icon)&&!angular.equals(a.icon,b.icon)&&(h.overlays[a.layer].removeLayer(c),h.overlays[a.layer].addLayer(c)):(n.lat!==a.lat||n.lng!==a.lng)&&c.setLatLng([a.lat,a.lng])}};return{resetMarkerGroup:z,resetMarkerGroups:A,deleteMarker:B,manageOpenPopup:F,manageOpenLabel:G,createMarker:function(a){if(!g(a)||!u.validateCoords(a))return void d.error(v+"The marker definition is not valid.");var b=u.getCoords(a);if(!g(b))return void d.error(v+"Unable to get coordinates from markerData.");var c={icon:y(a.icon),title:g(a.title)?a.title:"",draggable:g(a.draggable)?a.draggable:!1,clickable:g(a.clickable)?a.clickable:!0,riseOnHover:g(a.riseOnHover)?a.riseOnHover:!1,zIndexOffset:g(a.zIndexOffset)?a.zIndexOffset:0,iconAngle:g(a.iconAngle)?a.iconAngle:0};for(var e in a)a.hasOwnProperty(e)&&!c.hasOwnProperty(e)&&(c[e]=a[e]);var f=new L.marker(b,c);return q(a.message)||f.unbindPopup(),f},addMarkerToGroup:function(a,b,c,e){return q(b)?i.isLoaded()?(g(t[b])||(t[b]=new L.MarkerClusterGroup(c),e.addLayer(t[b])),void t[b].addLayer(a)):void d.error(v+"The MarkerCluster plugin is not loaded."):void d.error(v+"The marker group you have specified is invalid.")},listenMarkerEvents:function(a,b,c,d,e){a.on("popupopen",function(){o(c,function(){(g(a._popup)||g(a._popup._contentNode))&&(b.focus=!0,F(a,b,e))})}),a.on("popupclose",function(){o(c,function(){b.focus=!1})}),a.on("add",function(){o(c,function(){"label"in b&&G(a,b)})})},updateMarker:H,addMarkerWatcher:function(a,b,c,d,e,f){var i=p.getObjectArrayPath("markers."+b);f=h(f,!0);var j=c.$watch(i,function(f,h){return g(f)?void H(f,h,a,b,c,d,e):(B(a,e,d),void j())},f)},string:w,log:x}}]),angular.module("leaflet-directive").factory("leafletPathsHelpers",["$rootScope","$log","leafletHelpers",function(a,b,c){function d(a){return a.filter(function(a){return k(a)}).map(function(a){return e(a)})}function e(a){return i(a)?new L.LatLng(a[0],a[1]):new L.LatLng(a.lat,a.lng)}function f(a){return a.map(function(a){return d(a)})}function g(a,b){for(var c={},d=0;d0&&e(a[0].boundingbox)?i.resolve(a[0]):i.reject("[Nominatim] Invalid address")}),i.promise}}}]),angular.module("leaflet-directive").directive("bounds",["$log","$timeout","$http","leafletHelpers","nominatimService","leafletBoundsHelpers",function(a,b,c,d,e,f){return{restrict:"A",scope:!1,replace:!1,require:["leaflet"],link:function(c,g,h,i){var j=d.isDefined,k=f.createLeafletBounds,l=i[0].getLeafletScope(),m=i[0],n=d.errorHeader+" [Bounds] ",o=function(a){return 0===a._southWest.lat&&0===a._southWest.lng&&0===a._northEast.lat&&0===a._northEast.lng};m.getMap().then(function(d){l.$on("boundsChanged",function(a){var c=a.currentScope,e=d.getBounds();if(!o(e)&&!c.settingBoundsFromScope){c.settingBoundsFromLeaflet=!0;var f={northEast:{lat:e._northEast.lat,lng:e._northEast.lng},southWest:{lat:e._southWest.lat,lng:e._southWest.lng},options:e.options};angular.equals(c.bounds,f)||(c.bounds=f),b(function(){c.settingBoundsFromLeaflet=!1})}});var f;l.$watch("bounds",function(g){if(!c.settingBoundsFromLeaflet){if(j(g.address)&&g.address!==f)return c.settingBoundsFromScope=!0,e.query(g.address,h.id).then(function(a){var b=a.boundingbox,c=[[b[0],b[2]],[b[1],b[3]]];d.fitBounds(c)},function(b){a.error(n+" "+b+".")}),f=g.address,void b(function(){c.settingBoundsFromScope=!1});var i=k(g);i&&!d.getBounds().equals(i)&&(c.settingBoundsFromScope=!0,d.fitBounds(i,g.options),b(function(){c.settingBoundsFromScope=!1}))}},!0)})}}}]);var centerDirectiveTypes=["center","lfCenter"],centerDirectives={};centerDirectiveTypes.forEach(function(a){centerDirectives[a]=["$log","$q","$location","$timeout","leafletMapDefaults","leafletHelpers","leafletBoundsHelpers","leafletMapEvents",function(b,c,d,e,f,g,h,i){var j,k=g.isDefined,l=g.isNumber,m=g.isSameCenterOnMap,n=g.safeApply,o=g.isValidCenter,p=h.isValidBounds,q=g.isUndefinedOrEmpty,r=g.errorHeader,s=function(a,b){return k(a)&&p(a)&&q(b)};return{restrict:"A",scope:!1,replace:!1,require:"leaflet",controller:function(){j=c.defer(),this.getCenter=function(){return j.promise}},link:function(c,g,p,q){var t=q.getLeafletScope(),u=t[a];q.getMap().then(function(c){var g=f.getDefaults(p.id);if(-1!==p[a].search("-"))return b.error(r+' The "center" variable can\'t use a "-" on its key name: "'+p[a]+'".'),void c.setView([g.center.lat,g.center.lng],g.center.zoom);if(s(t.bounds,u))c.fitBounds(h.createLeafletBounds(t.bounds),t.bounds.options),u=c.getCenter(),n(t,function(b){angular.extend(b[a],{lat:c.getCenter().lat,lng:c.getCenter().lng,zoom:c.getZoom(),autoDiscover:!1})}),n(t,function(a){var b=c.getBounds();a.bounds={northEast:{lat:b._northEast.lat,lng:b._northEast.lng},southWest:{lat:b._southWest.lat,lng:b._southWest.lng}}});else{if(!k(u))return b.error(r+' The "center" property is not defined in the main scope'),void c.setView([g.center.lat,g.center.lng],g.center.zoom);k(u.lat)&&k(u.lng)||k(u.autoDiscover)||angular.copy(g.center,u)}var q,v;if("yes"===p.urlHashCenter){var w=function(){var a,b=d.search();if(k(b.c)){var c=b.c.split(":");3===c.length&&(a={lat:parseFloat(c[0]),lng:parseFloat(c[1]),zoom:parseInt(c[2],10)})}return a};q=w(),t.$on("$locationChangeSuccess",function(b){var d=b.currentScope,e=w();k(e)&&!m(e,c)&&angular.extend(d[a],{lat:e.lat,lng:e.lng,zoom:e.zoom})})}t.$watch(a,function(a){return t.settingCenterFromLeaflet?void 0:(k(q)&&(angular.copy(q,a),q=void 0),o(a)||a.autoDiscover===!0?a.autoDiscover===!0?(l(a.zoom)||c.setView([g.center.lat,g.center.lng],g.center.zoom),void(l(a.zoom)&&a.zoom>g.center.zoom?c.locate({setView:!0,maxZoom:a.zoom}):k(g.maxZoom)?c.locate({setView:!0,maxZoom:g.maxZoom}):c.locate({setView:!0}))):void(v&&m(a,c)||(t.settingCenterFromScope=!0,c.setView([a.lat,a.lng],a.zoom),i.notifyCenterChangedToBounds(t,c),e(function(){t.settingCenterFromScope=!1}))):void b.warn(r+" invalid 'center'"))},!0),c.whenReady(function(){v=!0}),c.on("moveend",function(){j.resolve(),i.notifyCenterUrlHashChanged(t,c,p,d.search()),m(u,c)||t.settingCenterFromScope||(t.settingCenterFromLeaflet=!0,n(t,function(b){t.settingCenterFromScope||angular.extend(b[a],{lat:c.getCenter().lat,lng:c.getCenter().lng,zoom:c.getZoom(),autoDiscover:!1}),i.notifyCenterChangedToBounds(t,c),e(function(){t.settingCenterFromLeaflet=!1})}))}),u.autoDiscover===!0&&c.on("locationerror",function(){b.warn(r+" The Geolocation API is unauthorized on this page."),o(u)?(c.setView([u.lat,u.lng],u.zoom),i.notifyCenterChangedToBounds(t,c)):(c.setView([g.center.lat,g.center.lng],g.center.zoom),i.notifyCenterChangedToBounds(t,c))})})}}}]}),centerDirectiveTypes.forEach(function(a){angular.module("leaflet-directive").directive(a,centerDirectives[a])}),angular.module("leaflet-directive").directive("controls",["$log","leafletHelpers","leafletControlHelpers",function(a,b,c){return{restrict:"A",scope:!1,replace:!1,require:"?^leaflet",link:function(d,e,f,g){if(g){var h=c.createControl,i=c.isValidControlType,j=g.getLeafletScope(),k=b.isDefined,l=b.isArray,m={},n=b.errorHeader+" [Controls] ";g.getMap().then(function(b){j.$watchCollection("controls",function(c){for(var d in m)k(c[d])||(b.hasControl(m[d])&&b.removeControl(m[d]),delete m[d]);for(var e in c){var f,g=k(c[e].type)?c[e].type:e;if(!i(g))return void a.error(n+" Invalid control type: "+g+".");if("custom"!==g)f=h(g,c[e]),b.addControl(f),m[e]=f;else{var j=c[e];if(l(j))for(var o in j){var p=j[o];b.addControl(p),m[e]=k(m[e])?m[e].concat([p]):[p]}else b.addControl(j),m[e]=j}}})})}}}}]),angular.module("leaflet-directive").directive("decorations",["$log","leafletHelpers",function(a,b){return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(c,d,e,f){function g(b){return k(b)&&k(b.coordinates)&&(j.isLoaded()||a.error("[AngularJS - Leaflet] The PolylineDecorator Plugin is not loaded.")),L.polylineDecorator(b.coordinates)}function h(a,b){return k(a)&&k(b)&&k(b.coordinates)&&k(b.patterns)?(a.setPaths(b.coordinates),a.setPatterns(b.patterns),a):void 0}var i=f.getLeafletScope(),j=b.PolylineDecoratorPlugin,k=b.isDefined,l={};f.getMap().then(function(a){i.$watch("decorations",function(b){for(var c in l)k(b[c])&&angular.equals(b[c],l)||(a.removeLayer(l[c]),delete l[c]);for(var d in b){var e=b[d],f=g(e);k(f)&&(l[d]=f,a.addLayer(f),h(f,e))}},!0)})}}}]),angular.module("leaflet-directive").directive("eventBroadcast",["$log","$rootScope","leafletHelpers","leafletMapEvents","leafletIterators",function(a,b,c,d,e){return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(b,f,g,h){var i=c.isObject,j=c.isDefined,k=h.getLeafletScope(),l=k.eventBroadcast,m=d.getAvailableMapEvents(),n=d.addEvents;h.getMap().then(function(b){var c=[],d="broadcast";j(l.map)?i(l.map)?("emit"!==l.map.logic&&"broadcast"!==l.map.logic?a.warn("[AngularJS - Leaflet] Available event propagation logic are: 'emit' or 'broadcast'."):d=l.map.logic,i(l.map.enable)&&l.map.enable.length>=0?e.each(l.map.enable,function(a){-1===c.indexOf(a)&&-1!==m.indexOf(a)&&c.push(a)}):a.warn("[AngularJS - Leaflet] event-broadcast.map.enable must be an object check your model.")):a.warn("[AngularJS - Leaflet] event-broadcast.map must be an object check your model."):c=m,n(b,c,"eventName",k,d)})}}}]),angular.module("leaflet-directive").directive("geojson",["$log","$rootScope","leafletData","leafletHelpers","leafletWatchHelpers","leafletDirectiveControlsHelpers","leafletIterators","leafletGeoJsonEvents",function(a,b,c,d,e,f,g,h){var i=e.maybeWatch,j=d.watchOptions,k=f.extend,l=d,m=g;return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(a,b,e,f){var g=d.isDefined,n=f.getLeafletScope(),o={},p=!1;f.getMap().then(function(a){var b=n.geojsonWatchOptions||j,f=function(a,b){var c;return c=angular.isFunction(a.onEachFeature)?a.onEachFeature:function(c,f){d.LabelPlugin.isLoaded()&&g(c.properties.description)&&f.bindLabel(c.properties.description),h.bindEvents(e.id,f,null,c,n,b,{resetStyleOnMouseout:a.resetStyleOnMouseout,mapId:e.id})}},q=l.isDefined(e.geojsonNested)&&l.isTruthy(e.geojsonNested),r=function(){if(o){var b=function(b){g(b)&&a.hasLayer(b)&&a.removeLayer(b)};return q?void m.each(o,function(a){b(a)}):void b(o)}},s=function(b,d){var h=angular.copy(b);if(g(h)&&g(h.data)){var i=f(h,d);g(h.options)||(h.options={style:h.style,filter:h.filter,onEachFeature:i,pointToLayer:h.pointToLayer});var j=L.geoJson(h.data,h.options);d&&l.isString(d)?o[d]=j:o=j,j.addTo(a),p||(p=!0,c.setGeoJSON(o,e.id))}},t=function(a){if(r(),q){if(!a||!Object.keys(a).length)return;return void m.each(a,function(a,b){s(a,b)})}s(a)};k(e.id,"geojson",t,r),i(n,"geojson",b,function(a){t(a)})})}}}]),angular.module("leaflet-directive").directive("layercontrol",["$filter","$log","leafletData","leafletHelpers",function(a,b,c,d){return{restrict:"E",scope:{icons:"=?",autoHideOpacity:"=?",showGroups:"=?",title:"@",baseTitle:"@",overlaysTitle:"@"},replace:!0,transclude:!1,require:"^leaflet",controller:["$scope","$element","$sce",function(a,e,f){b.debug("[Angular Directive - Layers] layers",a,e);var g=d.safeApply,h=d.isDefined;angular.extend(a,{baselayer:"",oldGroup:"",layerProperties:{},groupProperties:{},rangeIsSupported:d.rangeIsSupported(),changeBaseLayer:function(b,e){d.safeApply(a,function(d){d.baselayer=b,c.getMap().then(function(e){c.getLayers().then(function(c){if(!e.hasLayer(c.baselayers[b])){for(var f in d.layers.baselayers)d.layers.baselayers[f].icon=d.icons.unradio,e.hasLayer(c.baselayers[f])&&e.removeLayer(c.baselayers[f]);e.addLayer(c.baselayers[b]),d.layers.baselayers[b].icon=a.icons.radio}})})}),e.preventDefault()},moveLayer:function(b,c,d){var e=Object.keys(a.layers.baselayers).length;if(c>=1+e&&c<=a.overlaysArray.length+e){var f;for(var h in a.layers.overlays)if(a.layers.overlays[h].index===c){f=a.layers.overlays[h];break}f&&g(a,function(){f.index=b.index,b.index=c})}d.stopPropagation(),d.preventDefault()},initIndex:function(b,c){var d=Object.keys(a.layers.baselayers).length;b.index=h(b.index)?b.index:c+d+1},initGroup:function(b){a.groupProperties[b]=a.groupProperties[b]?a.groupProperties[b]:{}},toggleOpacity:function(b,c){if(c.visible){if(a.autoHideOpacity&&!a.layerProperties[c.name].opacityControl)for(var d in a.layerProperties)a.layerProperties[d].opacityControl=!1;a.layerProperties[c.name].opacityControl=!a.layerProperties[c.name].opacityControl}b.stopPropagation(),b.preventDefault()},toggleLegend:function(b){a.layerProperties[b.name].showLegend=!a.layerProperties[b.name].showLegend},showLegend:function(b){return b.legend&&a.layerProperties[b.name].showLegend},unsafeHTML:function(a){return f.trustAsHtml(a)},getOpacityIcon:function(b){return b.visible&&a.layerProperties[b.name].opacityControl?a.icons.close:a.icons.open},getGroupIcon:function(b){return b.visible?a.icons.check:a.icons.uncheck},changeOpacity:function(b){var d=a.layerProperties[b.name].opacity;c.getMap().then(function(e){c.getLayers().then(function(c){var f;for(var g in a.layers.overlays)if(a.layers.overlays[g]===b){f=c.overlays[g];break}e.hasLayer(f)&&(f.setOpacity&&f.setOpacity(d/100),f.getLayers&&f.eachLayer&&f.eachLayer(function(a){a.setOpacity&&a.setOpacity(d/100)}))})})},changeGroupVisibility:function(b){if(h(a.groupProperties[b])){var c=a.groupProperties[b].visible;for(var d in a.layers.overlays){var e=a.layers.overlays[d];e.group===b&&(e.visible=c)}}}});var i=e.get(0);L.Browser.touch?L.DomEvent.on(i,"click",L.DomEvent.stopPropagation):(L.DomEvent.disableClickPropagation(i),L.DomEvent.on(i,"mousewheel",L.DomEvent.stopPropagation))}],template:'',link:function(a,b,e,f){var g=d.isDefined,h=f.getLeafletScope(),i=h.layers;a.$watch("icons",function(){var b={uncheck:"fa fa-square-o",check:"fa fa-check-square-o",radio:"fa fa-dot-circle-o",unradio:"fa fa-circle-o",up:"fa fa-angle-up",down:"fa fa-angle-down",open:"fa fa-angle-double-down",close:"fa fa-angle-double-up",toggleLegend:"fa fa-pencil-square-o"};g(a.icons)?(angular.extend(b,a.icons),angular.extend(a.icons,b)):a.icons=b}),e.order=!g(e.order)||"normal"!==e.order&&"reverse"!==e.order?"normal":e.order,a.order="normal"===e.order,a.orderNumber="normal"===e.order?-1:1,a.layers=i,f.getMap().then(function(b){h.$watch("layers.baselayers",function(d){var e={};c.getLayers().then(function(c){var f;for(f in d){var g=d[f];g.icon=a.icons[b.hasLayer(c.baselayers[f])?"radio":"unradio"],e[f]=g}a.baselayersArray=e})}),h.$watch("layers.overlays",function(b){var d=[],e={};c.getLayers().then(function(c){var f;for(f in b){var h=b[f];h.icon=a.icons[h.visible?"check":"uncheck"],d.push(h),g(a.layerProperties[h.name])||(a.layerProperties[h.name]={opacity:g(h.layerOptions.opacity)?100*h.layerOptions.opacity:100,opacityControl:!1,showLegend:!0}),g(h.group)&&(g(a.groupProperties[h.group])||(a.groupProperties[h.group]={visible:!1}),e[h.group]=g(e[h.group])?e[h.group]:{count:0,visibles:0},e[h.group].count++,h.visible&&e[h.group].visibles++),g(h.index)&&c.overlays[f].setZIndex&&c.overlays[f].setZIndex(b[f].index)}for(f in e)a.groupProperties[f].visible=e[f].visibles===e[f].count;a.overlaysArray=d})},!0)})}}}]),angular.module("leaflet-directive").directive("layers",["$log","$q","leafletData","leafletHelpers","leafletLayerHelpers","leafletControlHelpers",function(a,b,c,d,e,f){return{restrict:"A",scope:!1,replace:!1,require:"leaflet",controller:["$scope",function(a){a._leafletLayers=b.defer(),this.getLayers=function(){return a._leafletLayers.promise}}],link:function(a,b,g,h){var i=d.isDefined,j={},k=h.getLeafletScope(),l=k.layers,m=e.createLayer,n=e.safeAddLayer,o=e.safeRemoveLayer,p=f.updateLayersControl,q=!1;h.getMap().then(function(b){a._leafletLayers.resolve(j),c.setLayers(j,g.id),j.baselayers={},j.overlays={};var d=g.id,e=!1;for(var f in l.baselayers){var h=m(l.baselayers[f]);i(h)?(j.baselayers[f]=h,l.baselayers[f].top===!0&&(n(b,j.baselayers[f]),e=!0)):delete l.baselayers[f]}!e&&Object.keys(j.baselayers).length>0&&n(b,j.baselayers[Object.keys(l.baselayers)[0]]);for(f in l.overlays){"cartodb"===l.overlays[f].type;var r=m(l.overlays[f]);i(r)?(j.overlays[f]=r,l.overlays[f].visible===!0&&n(b,j.overlays[f])):delete l.overlays[f]}k.$watch("layers.baselayers",function(a,c){if(angular.equals(a,c))return q=p(b,d,q,a,l.overlays,j),!0;for(var e in j.baselayers)(!i(a[e])||a[e].doRefresh)&&(b.hasLayer(j.baselayers[e])&&b.removeLayer(j.baselayers[e]),delete j.baselayers[e],a[e]&&a[e].doRefresh&&(a[e].doRefresh=!1));for(var f in a)if(i(j.baselayers[f]))a[f].top!==!0||b.hasLayer(j.baselayers[f])?a[f].top===!1&&b.hasLayer(j.baselayers[f])&&b.removeLayer(j.baselayers[f]):n(b,j.baselayers[f]);else{var g=m(a[f]);i(g)&&(j.baselayers[f]=g,a[f].top===!0&&n(b,j.baselayers[f]))}var h=!1;for(var k in j.baselayers)if(b.hasLayer(j.baselayers[k])){h=!0;break}!h&&Object.keys(j.baselayers).length>0&&n(b,j.baselayers[Object.keys(j.baselayers)[0]]),q=p(b,d,q,a,l.overlays,j)},!0),k.$watch("layers.overlays",function(a,c){if(angular.equals(a,c))return q=p(b,d,q,l.baselayers,a,j),!0;for(var e in j.overlays)if(!i(a[e])||a[e].doRefresh){if(b.hasLayer(j.overlays[e])){var f=i(a[e])?a[e].layerOptions:null;o(b,j.overlays[e],f)}delete j.overlays[e],a[e]&&a[e].doRefresh&&(a[e].doRefresh=!1)}for(var g in a){if(i(j.overlays[g]))a[g].visible&&!b.hasLayer(j.overlays[g])?n(b,j.overlays[g]):a[g].visible===!1&&b.hasLayer(j.overlays[g])&&o(b,j.overlays[g],a[g].layerOptions);else{
-var h=m(a[g]);if(!i(h))continue;j.overlays[g]=h,a[g].visible===!0&&n(b,j.overlays[g])}a[g].visible&&b._loaded&&a[g].data&&"heatmap"===a[g].type&&(j.overlays[g].setData(a[g].data),j.overlays[g].update())}q=p(b,d,q,l.baselayers,a,j)},!0)})}}}]),angular.module("leaflet-directive").directive("legend",["$log","$http","leafletHelpers","leafletLegendHelpers",function(a,b,c,d){return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(e,f,g,h){var i,j,k,l,m=c.isArray,n=c.isDefined,o=c.isFunction,p=h.getLeafletScope(),q=p.legend;p.$watch("legend",function(a){n(a)&&(i=a.legendClass?a.legendClass:"legend",j=a.position||"bottomright",l=a.type||"arcgis")},!0),h.getMap().then(function(c){p.$watch("legend",function(b){return n(b)?n(b.url)||"arcgis"!==l||m(b.colors)&&m(b.labels)&&b.colors.length===b.labels.length?n(b.url)?void a.info("[AngularJS - Leaflet] loading legend service."):(n(k)&&(k.removeFrom(c),k=null),k=L.control({position:j}),"arcgis"===l&&(k.onAdd=d.getOnAddArrayLegend(b,i)),void k.addTo(c)):void a.warn("[AngularJS - Leaflet] legend.colors and legend.labels must be set."):void(n(k)&&(k.removeFrom(c),k=null))}),p.$watch("legend.url",function(e){n(e)&&b.get(e).success(function(a){n(k)?d.updateLegend(k.getContainer(),a,l,e):(k=L.control({position:j}),k.onAdd=d.getOnAddLegend(a,i,l,e),k.addTo(c)),n(q.loadedData)&&o(q.loadedData)&&q.loadedData()}).error(function(){a.warn("[AngularJS - Leaflet] legend.url not loaded.")})})})}}}]),angular.module("leaflet-directive").directive("markers",["$log","$rootScope","$q","leafletData","leafletHelpers","leafletMapDefaults","leafletMarkersHelpers","leafletMarkerEvents","leafletIterators","leafletWatchHelpers","leafletDirectiveControlsHelpers",function(a,b,c,d,e,f,g,h,i,j,k){var l=e.isDefined,m=e.errorHeader,n=e,o=e.isString,p=g.addMarkerWatcher,q=g.updateMarker,r=g.listenMarkerEvents,s=g.addMarkerToGroup,t=g.createMarker,u=g.deleteMarker,v=i,w=e.watchOptions,x=j.maybeWatch,y=k.extend,z=function(a,b,c){if(Object.keys(a).length){if(c&&o(c)){if(!a[c]||!Object.keys(a[c]).length)return;return a[c][b]}return a[b]}},A=function(a,b,c,d){return d&&o(d)?(l(b[d])||(b[d]={}),b[d][c]=a):b[c]=a,a},B=function(b,c,d,e,f,g){if(!o(b))return a.error(m+" A layername must be a string"),!1;if(!l(c))return a.error(m+" You must add layers to the directive if the markers are going to use this functionality."),!1;if(!l(c.overlays)||!l(c.overlays[b]))return a.error(m+' A marker can only be added to a layer of type "group"'),!1;var h=c.overlays[b];return h instanceof L.LayerGroup||h instanceof L.FeatureGroup?(h.addLayer(e),!f&&g.hasLayer(e)&&d.focus===!0&&e.openPopup(),!0):(a.error(m+' Adding a marker to an overlay needs a overlay of the type "group" or "featureGroup"'),!1)},C=function(b,c,d,e,f,g,i,j,k,o){for(var u in c)if(!o[u])if(-1===u.search("-")){var v=n.copy(c[u]),w=n.getObjectDotPath(k?[k,u]:[u]),x=z(g,u,k);if(l(x)){var y=l(y)?d[u]:void 0;q(v,y,x,w,i,f,e)}else{var C=t(v),D=(v?v.layer:void 0)||k;if(!l(C)){a.error(m+" Received invalid data on the marker "+u+".");continue}if(A(C,g,u,k),l(v.message)&&C.bindPopup(v.message,v.popupOptions),l(v.group)){var E=l(v.groupOption)?v.groupOption:null;s(C,v.group,E,e)}if(n.LabelPlugin.isLoaded()&&l(v.label)&&l(v.label.message)&&C.bindLabel(v.label.message,v.label.options),l(v)&&(l(v.layer)||l(k))){var F=B(D,f,v,C,j.individual.doWatch,e);if(!F)continue}else l(v.group)||(e.addLayer(C),j.individual.doWatch||v.focus!==!0||C.openPopup());j.individual.doWatch&&p(C,w,i,f,e,j.individual.isDeep),r(C,v,i,j.individual.doWatch,e),h.bindEvents(b,C,w,v,i,D)}}else a.error('The marker can\'t use a "-" on his key name: "'+u+'".')},D=function(b,c,d,e,f){var g,h,i=!1,j=!1,k=l(c);for(var o in d)i||(a.debug(m+"[markers] destroy: "),i=!0),k&&(h=b[o],g=c[o],j=angular.equals(h,g)&&e),l(b)&&Object.keys(b).length&&l(b[o])&&Object.keys(b[o]).length&&!j||f&&n.isFunction(f)&&f(h,g,o)},E=function(b,c,d,e,f){D(b,c,d,!1,function(b,c,g){a.debug(m+"[marker] is deleting marker: "+g),u(d[g],e,f),delete d[g]})},F=function(b,c,d){var e={};return D(b,c,d,!0,function(b,c,d){a.debug(m+"[marker] is already rendered, marker: "+d),e[d]=b}),e};return{restrict:"A",scope:!1,replace:!1,require:["leaflet","?layers"],link:function(a,b,e,f){var g=f[0],h=g.getLeafletScope();g.getMap().then(function(a){var b,g={};b=l(f[1])?f[1].getLayers:function(){var a=c.defer();return a.resolve(),a.promise};var i=h.markersWatchOptions||w;l(e.watchMarkers)&&(i.doWatch=i.individual.doWatch=!l(e.watchMarkers)||n.isTruthy(e.watchMarkers));var j=l(e.markersNested)&&n.isTruthy(e.markersNested);b().then(function(b){var c=function(c,d){return j?void v.each(c,function(c,e){var f=l(f)?d[e]:void 0;E(c,f,g[e],a,b)}):void E(c,d,g,a,b)},f=function(d,f){c(d,f);var k=null;return j?void v.each(d,function(c,j){var m=l(m)?f[j]:void 0;k=F(d[j],m,g[j]),C(e.id,c,f,a,b,g,h,i,j,k)}):(k=F(d,f,g),void C(e.id,d,f,a,b,g,h,i,void 0,k))};y(e.id,"markers",f,c),d.setMarkers(g,e.id),x(h,"markers",i,function(a,b){f(a,b)})})})}}}]),angular.module("leaflet-directive").directive("maxbounds",["$log","leafletMapDefaults","leafletBoundsHelpers","leafletHelpers",function(a,b,c,d){return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(a,b,e,f){var g=f.getLeafletScope(),h=c.isValidBounds,i=d.isNumber;f.getMap().then(function(a){g.$watch("maxbounds",function(b){if(!h(b))return void a.setMaxBounds();var d=c.createLeafletBounds(b);i(b.pad)&&(d=d.pad(b.pad)),a.setMaxBounds(d),e.center||e.lfCenter||a.fitBounds(d)})})}}}]),angular.module("leaflet-directive").directive("paths",["$log","$q","leafletData","leafletMapDefaults","leafletHelpers","leafletPathsHelpers","leafletPathEvents",function(a,b,c,d,e,f,g){return{restrict:"A",scope:!1,replace:!1,require:["leaflet","?layers"],link:function(h,i,j,k){var l=k[0],m=e.isDefined,n=e.isString,o=l.getLeafletScope(),p=o.paths,q=f.createPath,r=g.bindPathEvents,s=f.setPathOptions;l.getMap().then(function(f){var g,h=d.getDefaults(j.id);g=m(k[1])?k[1].getLayers:function(){var a=b.defer();return a.resolve(),a.promise},m(p)&&g().then(function(b){var d={};c.setPaths(d,j.id);var g=!m(j.watchPaths)||"true"===j.watchPaths,i=function(a,c){var d=o.$watch('paths["'+c+'"]',function(c,e){if(!m(c)){if(m(e.layer))for(var g in b.overlays){var h=b.overlays[g];h.removeLayer(a)}return f.removeLayer(a),void d()}s(a,c.type,c)},!0)};o.$watchCollection("paths",function(c){for(var k in d)m(c[k])||(f.removeLayer(d[k]),delete d[k]);for(var l in c)if(0!==l.search("\\$"))if(-1===l.search("-")){if(!m(d[l])){var p=c[l],t=q(l,c[l],h);if(m(t)&&m(p.message)&&t.bindPopup(p.message,p.popupOptions),e.LabelPlugin.isLoaded()&&m(p.label)&&m(p.label.message)&&t.bindLabel(p.label.message,p.label.options),m(p)&&m(p.layer)){if(!n(p.layer)){a.error("[AngularJS - Leaflet] A layername must be a string");continue}if(!m(b)){a.error("[AngularJS - Leaflet] You must add layers to the directive if the markers are going to use this functionality.");continue}if(!m(b.overlays)||!m(b.overlays[p.layer])){a.error('[AngularJS - Leaflet] A path can only be added to a layer of type "group"');continue}var u=b.overlays[p.layer];if(!(u instanceof L.LayerGroup||u instanceof L.FeatureGroup)){a.error('[AngularJS - Leaflet] Adding a path to an overlay needs a overlay of the type "group" or "featureGroup"');continue}d[l]=t,u.addLayer(t),g?i(t,l):s(t,p.type,p)}else m(t)&&(d[l]=t,f.addLayer(t),g?i(t,l):s(t,p.type,p));r(j.id,t,l,p,o)}}else a.error('[AngularJS - Leaflet] The path name "'+l+'" is not valid. It must not include "-" and a number.')})})})}}}]),angular.module("leaflet-directive").directive("tiles",["$log","leafletData","leafletMapDefaults","leafletHelpers",function(a,b,c,d){return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(e,f,g,h){var i=d.isDefined,j=h.getLeafletScope(),k=j.tiles;return i(k)&&i(k.url)?void h.getMap().then(function(a){var d,e=c.getDefaults(g.id);j.$watch("tiles",function(c,f){var h=e.tileLayerOptions,j=e.tileLayer;return!i(c.url)&&i(d)?void a.removeLayer(d):i(d)?!i(c.url)||!i(c.options)||c.type===f.type&&angular.equals(c.options,h)?void(i(c.url)&&d.setUrl(c.url)):(a.removeLayer(d),h=e.tileLayerOptions,angular.copy(c.options,h),j=c.url,d="wms"===c.type?L.tileLayer.wms(j,h):L.tileLayer(j,h),d.addTo(a),void b.setTiles(d,g.id)):(i(c.options)&&angular.copy(c.options,h),i(c.url)&&(j=c.url),d="wms"===c.type?L.tileLayer.wms(j,h):L.tileLayer(j,h),d.addTo(a),void b.setTiles(d,g.id))},!0)}):void a.warn("[AngularJS - Leaflet] The 'tiles' definition doesn't have the 'url' property.")}}}]),["markers","geojson"].forEach(function(a){angular.module("leaflet-directive").directive(a+"WatchOptions",["$log","$rootScope","$q","leafletData","leafletHelpers",function(b,c,d,e,f){var g=f.isDefined,h=f.errorHeader,i=f.isObject,j=f.watchOptions;return{restrict:"A",scope:!1,replace:!1,require:["leaflet"],link:function(c,d,e,f){var k=f[0],l=k.getLeafletScope();k.getMap().then(function(){g(c[a+"WatchOptions"])&&(i(c[a+"WatchOptions"])?angular.extend(j,c[a+"WatchOptions"]):b.error(h+"["+a+"WatchOptions] is not an object"),l[a+"WatchOptions"]=j)})}}}])}),angular.module("leaflet-directive").factory("leafletEventsHelpersFactory",["$rootScope","$q","$log","leafletHelpers",function(a,b,c,d){var e=d.safeApply,f=d.isDefined,g=d.isObject,h=d.isArray,i=d.errorHeader,j=function(a,b){this.rootBroadcastName=a,c.debug("leafletEventsHelpersFactory: lObjectType: "+b+"rootBroadcastName: "+a),this.lObjectType=b};return j.prototype.getAvailableEvents=function(){return[]},j.prototype.genDispatchEvent=function(a,b,d,e,f,g,h,i,j){var k=this;return a=a||"",a&&(a="."+a),function(l){var m=k.rootBroadcastName+a+"."+b;c.debug(m),k.fire(e,m,d,l,l.target||f,h,g,i,j)}},j.prototype.fire=function(b,c,d,g,h,i,j,k,l){e(b,function(){var e={leafletEvent:g,leafletObject:h,modelName:j,model:i};f(k)&&angular.extend(e,{layerName:k}),"emit"===d?b.$emit(c,e):a.$broadcast(c,e)})},j.prototype.bindEvents=function(a,b,d,e,j,k,l){var m=[],n="emit",o=this;if(f(j.eventBroadcast))if(g(j.eventBroadcast))if(f(j.eventBroadcast[o.lObjectType]))if(g(j.eventBroadcast[o.lObjectType])){f(j.eventBroadcast[this.lObjectType].logic)&&"emit"!==j.eventBroadcast[o.lObjectType].logic&&"broadcast"!==j.eventBroadcast[o.lObjectType].logic&&c.warn(i+"Available event propagation logic are: 'emit' or 'broadcast'.");var p=!1,q=!1;f(j.eventBroadcast[o.lObjectType].enable)&&h(j.eventBroadcast[o.lObjectType].enable)&&(p=!0),f(j.eventBroadcast[o.lObjectType].disable)&&h(j.eventBroadcast[o.lObjectType].disable)&&(q=!0),p&&q?c.warn(i+"can not enable and disable events at the same time"):p||q?p?j.eventBroadcast[this.lObjectType].enable.forEach(function(a){-1!==m.indexOf(a)?c.warn(i+"This event "+a+" is already enabled"):-1===o.getAvailableEvents().indexOf(a)?c.warn(i+"This event "+a+" does not exist"):m.push(a)}):(m=this.getAvailableEvents(),j.eventBroadcast[o.lObjectType].disable.forEach(function(a){var b=m.indexOf(a);-1===b?c.warn(i+"This event "+a+" does not exist or has been already disabled"):m.splice(b,1)})):c.warn(i+"must enable or disable events")}else c.warn(i+"event-broadcast."+[o.lObjectType]+" must be an object check your model.");else m=this.getAvailableEvents();else c.error(i+"event-broadcast must be an object check your model.");else m=this.getAvailableEvents();return m.forEach(function(c){b.on(c,o.genDispatchEvent(a,c,n,j,b,d,e,k,l))}),n},j}]).service("leafletEventsHelpers",["leafletEventsHelpersFactory",function(a){return new a}]),angular.module("leaflet-directive").factory("leafletGeoJsonEvents",["$rootScope","$q","$log","leafletHelpers","leafletEventsHelpersFactory","leafletData",function(a,b,c,d,e,f){var g=d.safeApply,h=e,i=function(){h.call(this,"leafletDirectiveGeoJson","geojson")};return i.prototype=new h,i.prototype.genDispatchEvent=function(b,c,d,e,i,j,k,l,m){var n=h.prototype.genDispatchEvent.call(this,b,c,d,e,i,j,k,l),o=this;return function(b){"mouseout"===c&&(m.resetStyleOnMouseout&&f.getGeoJSON(m.mapId).then(function(a){var c=l?a[l]:a;c.resetStyle(b.target)}),g(e,function(){a.$broadcast(o.rootBroadcastName+".mouseout",b)})),n(b)}},i.prototype.getAvailableEvents=function(){return["click","dblclick","mouseover","mouseout"]},new i}]),angular.module("leaflet-directive").factory("leafletLabelEvents",["$rootScope","$q","$log","leafletHelpers","leafletEventsHelpersFactory",function(a,b,c,d,e){var f=d,g=e,h=function(){g.call(this,"leafletDirectiveLabel","markers")};return h.prototype=new g,h.prototype.genDispatchEvent=function(a,b,c,d,e,f,h,i){var j=f.replace("markers.","");return g.prototype.genDispatchEvent.call(this,a,b,c,d,e,j,h,i)},h.prototype.getAvailableEvents=function(){return["click","dblclick","mousedown","mouseover","mouseout","contextmenu"]},h.prototype.genEvents=function(a,b,c,d,e,g,h,i){var j=this,k=this.getAvailableEvents(),l=f.getObjectArrayPath("markers."+g);k.forEach(function(b){e.label.on(b,j.genDispatchEvent(a,b,c,d,e.label,l,h,i))})},h.prototype.bindEvents=function(a,b,c,d,e,f){},new h}]),angular.module("leaflet-directive").factory("leafletMapEvents",["$rootScope","$q","$log","leafletHelpers","leafletEventsHelpers","leafletIterators",function(a,b,c,d,e,f){var g=d.isDefined,h=e.fire,i=function(){return["click","dblclick","mousedown","mouseup","mouseover","mouseout","mousemove","contextmenu","focus","blur","preclick","load","unload","viewreset","movestart","move","moveend","dragstart","drag","dragend","zoomstart","zoomanim","zoomend","zoomlevelschange","resize","autopanstart","layeradd","layerremove","baselayerchange","overlayadd","overlayremove","locationfound","locationerror","popupopen","popupclose","draw:created","draw:edited","draw:deleted","draw:drawstart","draw:drawstop","draw:editstart","draw:editstop","draw:deletestart","draw:deletestop"]},j=function(a,b,d,e){return e&&(e+="."),function(f){var g="leafletDirectiveMap."+e+b;c.debug(g),h(a,g,d,f,f.target,a)}},k=function(a){a.$broadcast("boundsChanged")},l=function(a,b,c,d){if(g(c.urlHashCenter)){var e=b.getCenter(),f=e.lat.toFixed(4)+":"+e.lng.toFixed(4)+":"+b.getZoom();g(d.c)&&d.c===f||a.$emit("centerUrlHash",f)}},m=function(a,b,c,d,e){f.each(b,function(b){var f={};f[c]=b,a.on(b,j(d,b,e,a._container.id||""),f)})};return{getAvailableMapEvents:i,genDispatchMapEvent:j,notifyCenterChangedToBounds:k,notifyCenterUrlHashChanged:l,addEvents:m}}]),angular.module("leaflet-directive").factory("leafletMarkerEvents",["$rootScope","$q","$log","leafletHelpers","leafletEventsHelpersFactory","leafletLabelEvents",function(a,b,c,d,e,f){var g=d.safeApply,h=d.isDefined,i=d,j=f,k=e,l=function(){k.call(this,"leafletDirectiveMarker","markers")};return l.prototype=new k,l.prototype.genDispatchEvent=function(b,c,d,e,f,h,i,j){var l=k.prototype.genDispatchEvent.call(this,b,c,d,e,f,h,i,j);return function(b){"click"===c?g(e,function(){a.$broadcast("leafletDirectiveMarkersClick",h)}):"dragend"===c&&(g(e,function(){i.lat=f.getLatLng().lat,i.lng=f.getLatLng().lng}),i.message&&i.focus===!0&&f.openPopup()),l(b)}},l.prototype.getAvailableEvents=function(){return["click","dblclick","mousedown","mouseover","mouseout","contextmenu","dragstart","drag","dragend","move","remove","popupopen","popupclose","touchend","touchstart","touchmove","touchcancel","touchleave"]},l.prototype.bindEvents=function(a,b,c,d,e,f){var g=k.prototype.bindEvents.call(this,a,b,c,d,e,f);i.LabelPlugin.isLoaded()&&h(b.label)&&j.genEvents(a,c,g,e,b,d,f)},new l}]),angular.module("leaflet-directive").factory("leafletPathEvents",["$rootScope","$q","$log","leafletHelpers","leafletLabelEvents","leafletEventsHelpers",function(a,b,c,d,e,f){var g=d.isDefined,h=d.isObject,i=d,j=d.errorHeader,k=e,l=f.fire,m=function(a,b,d,e,f,g,h,i){return a=a||"",a&&(a="."+a),function(j){var k="leafletDirectivePath"+a+"."+b;c.debug(k),l(e,k,d,j,j.target||f,h,g,i)}},n=function(a,b,d,e,f){var l,n,p=[],q="broadcast";if(g(f.eventBroadcast))if(h(f.eventBroadcast))if(g(f.eventBroadcast.path))if(h(f.eventBroadcast.paths))c.warn(j+"event-broadcast.path must be an object check your model.");else{void 0!==f.eventBroadcast.path.logic&&null!==f.eventBroadcast.path.logic&&("emit"!==f.eventBroadcast.path.logic&&"broadcast"!==f.eventBroadcast.path.logic?c.warn(j+"Available event propagation logic are: 'emit' or 'broadcast'."):"emit"===f.eventBroadcast.path.logic&&(q="emit"));var r=!1,s=!1;if(void 0!==f.eventBroadcast.path.enable&&null!==f.eventBroadcast.path.enable&&"object"==typeof f.eventBroadcast.path.enable&&(r=!0),void 0!==f.eventBroadcast.path.disable&&null!==f.eventBroadcast.path.disable&&"object"==typeof f.eventBroadcast.path.disable&&(s=!0),r&&s)c.warn(j+"can not enable and disable events at the same time");else if(r||s)if(r)for(l=0;l
',
+ controller: ["$scope", function ($scope) {
+ this._leafletMap = $q.defer();
+ this.getMap = function () {
+ return this._leafletMap.promise;
+ };
+
+ this.getLeafletScope = function() {
+ return $scope;
+ };
+ }],
+
+ link: function(scope, element, attrs, ctrl) {
+ var isDefined = leafletHelpers.isDefined,
+ defaults = leafletMapDefaults.setDefaults(scope.defaults, attrs.id),
+ mapEvents = leafletMapEvents.getAvailableMapEvents(),
+ addEvents = leafletMapEvents.addEvents;
+
+ scope.mapId = attrs.id;
+ leafletData.setDirectiveControls({}, attrs.id);
+
+ // Set width and height utility functions
+ function updateWidth() {
+ if (isNaN(attrs.width)) {
+ element.css('width', attrs.width);
+ } else {
+ element.css('width', attrs.width + 'px');
+ }
+ }
+
+ function updateHeight() {
+ if (isNaN(attrs.height)) {
+ element.css('height', attrs.height);
+ } else {
+ element.css('height', attrs.height + 'px');
+ }
+ }
+
+ // If the width attribute defined update css
+ // Then watch if bound property changes and update css
+ if (isDefined(attrs.width)) {
+ updateWidth();
+
+ scope.$watch(
+ function () {
+ return element[0].getAttribute('width');
+ },
+ function () {
+ updateWidth();
+ map.invalidateSize();
+ });
+ }
+
+ // If the height attribute defined update css
+ // Then watch if bound property changes and update css
+ if (isDefined(attrs.height)) {
+ updateHeight();
+
+ scope.$watch(
+ function () {
+ return element[0].getAttribute('height');
+ },
+ function () {
+ updateHeight();
+ map.invalidateSize();
+ });
+ }
+
+ // Create the Leaflet Map Object with the options
+ var map = new L.Map(element[0], leafletMapDefaults.getMapCreationDefaults(attrs.id));
+ ctrl._leafletMap.resolve(map);
+
+ if (!isDefined(attrs.center) && !isDefined(attrs.lfCenter)) {
+ map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ }
+
+ // If no layers nor tiles defined, set the default tileLayer
+ if (!isDefined(attrs.tiles) && (!isDefined(attrs.layers))) {
+ var tileLayerObj = L.tileLayer(defaults.tileLayer, defaults.tileLayerOptions);
+ tileLayerObj.addTo(map);
+ leafletData.setTiles(tileLayerObj, attrs.id);
+ }
+
+ // Set zoom control configuration
+ if (isDefined(map.zoomControl) &&
+ isDefined(defaults.zoomControlPosition)) {
+ map.zoomControl.setPosition(defaults.zoomControlPosition);
+ }
+
+ if (isDefined(map.zoomControl) &&
+ defaults.zoomControl===false) {
+ map.zoomControl.removeFrom(map);
+ }
+
+ if (isDefined(map.zoomsliderControl) &&
+ isDefined(defaults.zoomsliderControl) &&
+ defaults.zoomsliderControl===false) {
+ map.zoomsliderControl.removeFrom(map);
+ }
+
+
+ // if no event-broadcast attribute, all events are broadcasted
+ if (!isDefined(attrs.eventBroadcast)) {
+ var logic = "broadcast";
+ addEvents(map, mapEvents, "eventName", scope, logic);
+ }
+
+ // Resolve the map object to the promises
+ map.whenReady(function() {
+ leafletData.setMap(map, attrs.id);
+ });
+
+ scope.$on('$destroy', function () {
+ leafletMapDefaults.reset();
+ map.remove();
+ leafletData.unresolveMap(attrs.id);
+ });
+
+ //Handle request to invalidate the map size
+ //Up scope using $scope.$emit('invalidateSize')
+ //Down scope using $scope.$broadcast('invalidateSize')
+ scope.$on('invalidateSize', function() {
+ map.invalidateSize();
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletBoundsHelpers', ["$log", "leafletHelpers", function ($log, leafletHelpers) {
+
+ var isArray = leafletHelpers.isArray,
+ isNumber = leafletHelpers.isNumber,
+ isFunction = leafletHelpers.isFunction,
+ isDefined = leafletHelpers.isDefined;
+
+ function _isValidBounds(bounds) {
+ return angular.isDefined(bounds) && angular.isDefined(bounds.southWest) &&
+ angular.isDefined(bounds.northEast) && angular.isNumber(bounds.southWest.lat) &&
+ angular.isNumber(bounds.southWest.lng) && angular.isNumber(bounds.northEast.lat) &&
+ angular.isNumber(bounds.northEast.lng);
+ }
+
+ return {
+ createLeafletBounds: function(bounds) {
+ if (_isValidBounds(bounds)) {
+ return L.latLngBounds([bounds.southWest.lat, bounds.southWest.lng],
+ [bounds.northEast.lat, bounds.northEast.lng ]);
+ }
+ },
+
+ isValidBounds: _isValidBounds,
+
+ createBoundsFromArray: function(boundsArray) {
+ if (!(isArray(boundsArray) && boundsArray.length === 2 &&
+ isArray(boundsArray[0]) && isArray(boundsArray[1]) &&
+ boundsArray[0].length === 2 && boundsArray[1].length === 2 &&
+ isNumber(boundsArray[0][0]) && isNumber(boundsArray[0][1]) &&
+ isNumber(boundsArray[1][0]) && isNumber(boundsArray[1][1]))) {
+ $log.error("[AngularJS - Leaflet] The bounds array is not valid.");
+ return;
+ }
+
+ return {
+ northEast: {
+ lat: boundsArray[0][0],
+ lng: boundsArray[0][1]
+ },
+ southWest: {
+ lat: boundsArray[1][0],
+ lng: boundsArray[1][1]
+ }
+ };
+ },
+
+ createBoundsFromLeaflet: function(lfBounds) {
+ if (!(isDefined(lfBounds) && isFunction(lfBounds.getNorthEast) && isFunction(lfBounds.getSouthWest))) {
+ $log.error("[AngularJS - Leaflet] The leaflet bounds is not valid object.");
+ return;
+ }
+
+ var northEast = lfBounds.getNorthEast(),
+ southWest = lfBounds.getSouthWest();
+
+ return {
+ northEast: {
+ lat: northEast.lat,
+ lng: northEast.lng
+ },
+ southWest: {
+ lat: southWest.lat,
+ lng: southWest.lng
+ }
+ };
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletControlHelpers', ["$rootScope", "$log", "leafletHelpers", "leafletLayerHelpers", "leafletMapDefaults", function ($rootScope, $log, leafletHelpers, leafletLayerHelpers, leafletMapDefaults) {
+ var isDefined = leafletHelpers.isDefined,
+ isObject = leafletHelpers.isObject,
+ createLayer = leafletLayerHelpers.createLayer,
+ _controls = {},
+ errorHeader = leafletHelpers.errorHeader + ' [Controls] ';
+
+ var _controlLayersMustBeVisible = function(baselayers, overlays, mapId) {
+ var defaults = leafletMapDefaults.getDefaults(mapId);
+ if(!defaults.controls.layers.visible) {
+ return false;
+ }
+
+ var atLeastOneControlItemMustBeShown = false;
+
+ if (isObject(baselayers)) {
+ Object.keys(baselayers).forEach(function(key) {
+ var layer = baselayers[key];
+ if (!isDefined(layer.layerOptions) || layer.layerOptions.showOnSelector !== false) {
+ atLeastOneControlItemMustBeShown = true;
+ }
+ });
+ }
+
+ if (isObject(overlays)) {
+ Object.keys(overlays).forEach(function(key) {
+ var layer = overlays[key];
+ if (!isDefined(layer.layerParams) || layer.layerParams.showOnSelector !== false) {
+ atLeastOneControlItemMustBeShown = true;
+ }
+ });
+ }
+
+ return atLeastOneControlItemMustBeShown;
+ };
+
+ var _createLayersControl = function(mapId) {
+ var defaults = leafletMapDefaults.getDefaults(mapId);
+ var controlOptions = {
+ collapsed: defaults.controls.layers.collapsed,
+ position: defaults.controls.layers.position,
+ autoZIndex: false
+ };
+
+ angular.extend(controlOptions, defaults.controls.layers.options);
+
+ var control;
+ if(defaults.controls.layers && isDefined(defaults.controls.layers.control)) {
+ control = defaults.controls.layers.control.apply(this, [[], [], controlOptions]);
+ } else {
+ control = new L.control.layers([], [], controlOptions);
+ }
+
+ return control;
+ };
+
+ var controlTypes = {
+ draw: {
+ isPluginLoaded: function() {
+ if (!angular.isDefined(L.Control.Draw)) {
+ $log.error(errorHeader + ' Draw plugin is not loaded.');
+ return false;
+ }
+ return true;
+ },
+ checkValidParams: function(/* params */) {
+ return true;
+ },
+ createControl: function(params) {
+ return new L.Control.Draw(params);
+ }
+ },
+ scale: {
+ isPluginLoaded: function() {
+ return true;
+ },
+ checkValidParams: function(/* params */) {
+ return true;
+ },
+ createControl: function(params) {
+ return new L.control.scale(params);
+ }
+ },
+ fullscreen: {
+ isPluginLoaded: function() {
+ if (!angular.isDefined(L.Control.Fullscreen)) {
+ $log.error(errorHeader + ' Fullscreen plugin is not loaded.');
+ return false;
+ }
+ return true;
+ },
+ checkValidParams: function(/* params */) {
+ return true;
+ },
+ createControl: function(params) {
+ return new L.Control.Fullscreen(params);
+ }
+ },
+ search: {
+ isPluginLoaded: function() {
+ if (!angular.isDefined(L.Control.Search)) {
+ $log.error(errorHeader + ' Search plugin is not loaded.');
+ return false;
+ }
+ return true;
+ },
+ checkValidParams: function(/* params */) {
+ return true;
+ },
+ createControl: function(params) {
+ return new L.Control.Search(params);
+ }
+ },
+ custom: {},
+ minimap: {
+ isPluginLoaded: function() {
+ if (!angular.isDefined(L.Control.MiniMap)) {
+ $log.error(errorHeader + ' Minimap plugin is not loaded.');
+ return false;
+ }
+
+ return true;
+ },
+ checkValidParams: function(params) {
+ if(!isDefined(params.layer)) {
+ $log.warn(errorHeader +' minimap "layer" option should be defined.');
+ return false;
+ }
+ return true;
+ },
+ createControl: function(params) {
+ var layer = createLayer(params.layer);
+
+ if (!isDefined(layer)) {
+ $log.warn(errorHeader + ' minimap control "layer" could not be created.');
+ return;
+ }
+
+ return new L.Control.MiniMap(layer, params);
+ }
+ }
+ };
+
+ return {
+ layersControlMustBeVisible: _controlLayersMustBeVisible,
+
+ isValidControlType: function(type) {
+ return Object.keys(controlTypes).indexOf(type) !== -1;
+ },
+
+ createControl: function (type, params) {
+ if (!controlTypes[type].checkValidParams(params)) {
+ return;
+ }
+
+ return controlTypes[type].createControl(params);
+ },
+
+ updateLayersControl: function(map, mapId, loaded, baselayers, overlays, leafletLayers) {
+ var i;
+ var _layersControl = _controls[mapId];
+ var mustBeLoaded = _controlLayersMustBeVisible(baselayers, overlays, mapId);
+
+ if (isDefined(_layersControl) && loaded) {
+ for (i in leafletLayers.baselayers) {
+ _layersControl.removeLayer(leafletLayers.baselayers[i]);
+ }
+ for (i in leafletLayers.overlays) {
+ _layersControl.removeLayer(leafletLayers.overlays[i]);
+ }
+ map.removeControl(_layersControl);
+ delete _controls[mapId];
+ }
+
+ if (mustBeLoaded) {
+ _layersControl = _createLayersControl(mapId);
+ _controls[mapId] = _layersControl;
+ for (i in baselayers) {
+ var hideOnSelector = isDefined(baselayers[i].layerOptions) &&
+ baselayers[i].layerOptions.showOnSelector === false;
+ if (!hideOnSelector && isDefined(leafletLayers.baselayers[i])) {
+ _layersControl.addBaseLayer(leafletLayers.baselayers[i], baselayers[i].name);
+ }
+ }
+ for (i in overlays) {
+ var hideOverlayOnSelector = isDefined(overlays[i].layerParams) &&
+ overlays[i].layerParams.showOnSelector === false;
+ if (!hideOverlayOnSelector && isDefined(leafletLayers.overlays[i])) {
+ _layersControl.addOverlay(leafletLayers.overlays[i], overlays[i].name);
+ }
+ }
+
+ map.addControl(_layersControl);
+ }
+ return mustBeLoaded;
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").service('leafletData', ["$log", "$q", "leafletHelpers", function ($log, $q, leafletHelpers) {
+ var getDefer = leafletHelpers.getDefer,
+ getUnresolvedDefer = leafletHelpers.getUnresolvedDefer,
+ setResolvedDefer = leafletHelpers.setResolvedDefer;
+
+ var _private = {};
+ var self = this;
+
+ var upperFirst = function (string) {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+ };
+
+ var _privateItems = [
+ 'map',
+ 'tiles',
+ 'layers',
+ 'paths',
+ 'markers',
+ 'geoJSON',
+ 'UTFGrid', //odd ball on naming convention keeping to not break
+ 'decorations',
+ 'directiveControls'];
+
+ //init
+ _privateItems.forEach(function(itemName){
+ _private[itemName] = {};
+ });
+
+ this.unresolveMap = function (scopeId) {
+ var id = leafletHelpers.obtainEffectiveMapId(_private.map, scopeId);
+ _privateItems.forEach(function (itemName) {
+ _private[itemName][id] = undefined;
+ });
+ };
+
+ //int repetitive stuff (get and sets)
+ _privateItems.forEach(function (itemName) {
+ var name = upperFirst(itemName);
+ self['set' + name] = function (lObject, scopeId) {
+ var defer = getUnresolvedDefer(_private[itemName], scopeId);
+ defer.resolve(lObject);
+ setResolvedDefer(_private[itemName], scopeId);
+ };
+
+ self['get' + name] = function (scopeId) {
+ var defer = getDefer(_private[itemName], scopeId);
+ return defer.promise;
+ };
+ });
+}]);
+
+angular.module("leaflet-directive")
+.service('leafletDirectiveControlsHelpers', ["$log", "leafletData", "leafletHelpers", function ($log, leafletData, leafletHelpers) {
+ var _isDefined = leafletHelpers.isDefined,
+ _isString = leafletHelpers.isString,
+ _isObject = leafletHelpers.isObject,
+ _mainErrorHeader = leafletHelpers.errorHeader;
+
+ var _errorHeader = _mainErrorHeader + '[leafletDirectiveControlsHelpers';
+
+ var _extend = function(id, thingToAddName, createFn, cleanFn){
+ var _fnHeader = _errorHeader + '.extend] ';
+ var extender = {};
+ if(!_isDefined(thingToAddName)){
+ $log.error(_fnHeader + 'thingToAddName cannot be undefined');
+ return;
+ }
+
+ if(_isString(thingToAddName) && _isDefined(createFn) && _isDefined(cleanFn)){
+ extender[thingToAddName] = {
+ create: createFn,
+ clean: cleanFn
+ };
+ }
+ else if(_isObject(thingToAddName) && !_isDefined(createFn) && !_isDefined(cleanFn)){
+ extender = thingToAddName;
+ }
+ else{
+ $log.error(_fnHeader + 'incorrect arguments');
+ return;
+ }
+
+ //add external control to create / destroy markers without a watch
+ leafletData.getDirectiveControls().then(function(controls){
+ angular.extend(controls, extender);
+ leafletData.setDirectiveControls(controls, id);
+ });
+ };
+
+ return {
+ extend: _extend
+ };
+}]);
+
+angular.module("leaflet-directive")
+.service('leafletGeoJsonHelpers', ["leafletHelpers", "leafletIterators", function (leafletHelpers, leafletIterators) {
+ var lHlp = leafletHelpers,
+ lIt = leafletIterators;
+ var Point = function(lat,lng){
+ this.lat = lat;
+ this.lng = lng;
+ return this;
+ };
+
+ var _getLat = function(value) {
+ if (Array.isArray(value) && value.length === 2) {
+ return value[1];
+ } else if (lHlp.isDefined(value.type) && value.type === 'Point') {
+ return +value.coordinates[1];
+ } else {
+ return +value.lat;
+ }
+ };
+
+ var _getLng = function(value) {
+ if (Array.isArray(value) && value.length === 2) {
+ return value[0];
+ } else if (lHlp.isDefined(value.type) && value.type === 'Point') {
+ return +value.coordinates[0];
+ } else {
+ return +value.lng;
+ }
+ };
+
+ var _validateCoords = function(coords) {
+ if (lHlp.isUndefined(coords)) {
+ return false;
+ }
+ if (lHlp.isArray(coords)) {
+ if (coords.length === 2 && lHlp.isNumber(coords[0]) && lHlp.isNumber(coords[1])) {
+ return true;
+ }
+ } else if (lHlp.isDefined(coords.type)) {
+ if (
+ coords.type === 'Point' && lHlp.isArray(coords.coordinates) &&
+ coords.coordinates.length === 2 &&
+ lHlp.isNumber(coords.coordinates[0]) &&
+ lHlp.isNumber(coords.coordinates[1])) {
+ return true;
+ }
+ }
+
+ var ret = lIt.all(['lat', 'lng'], function(pos){
+ return lHlp.isDefined(coords[pos]) && lHlp.isNumber(coords[pos]);
+ });
+ return ret;
+ };
+
+ var _getCoords = function(value) {
+ if (!value || !_validateCoords(value)) {
+ return;
+ }
+ var p = null;
+ if (Array.isArray(value) && value.length === 2) {
+ p = new Point(value[1], value[0]);
+ } else if (lHlp.isDefined(value.type) && value.type === 'Point') {
+ p = new Point(value.coordinates[1], value.coordinates[0]);
+ } else {
+ return value;
+ }
+ //note angular.merge is avail in angular 1.4.X we might want to fill it here
+ return angular.extend(value, p);//tap on lat, lng if it doesnt exist
+ };
+
+
+ return {
+ getLat: _getLat,
+ getLng: _getLng,
+ validateCoords: _validateCoords,
+ getCoords: _getCoords
+ };
+ }]);
+
+angular.module("leaflet-directive").service('leafletHelpers', ["$q", "$log", function ($q, $log) {
+ var _errorHeader = '[AngularJS - Leaflet] ';
+ var _copy = angular.copy;
+ var _clone = _copy;
+ /*
+ For parsing paths to a field in an object
+
+ Example:
+ var obj = {
+ bike:{
+ 1: 'hi'
+ 2: 'foo'
+ }
+ };
+ _getObjectValue(obj,"bike.1") returns 'hi'
+ this is getPath in ui-gmap
+ */
+ var _getObjectValue = function(object, pathStr) {
+ var obj;
+ if(!object || !angular.isObject(object))
+ return;
+ //if the key is not a sting then we already have the value
+ if ((pathStr === null) || !angular.isString(pathStr)) {
+ return pathStr;
+ }
+ obj = object;
+ pathStr.split('.').forEach(function(value) {
+ if (obj) {
+ obj = obj[value];
+ }
+ });
+ return obj;
+ };
+
+ /*
+ Object Array Notation
+ _getObjectArrayPath("bike.one.two")
+ returns:
+ 'bike["one"]["two"]'
+ */
+ var _getObjectArrayPath = function(pathStr){
+ return pathStr.split('.').reduce(function(previous, current) {
+ return previous + '["'+ current + '"]';
+ });
+ };
+
+ /* Object Dot Notation
+ _getObjectPath(["bike","one","two"])
+ returns:
+ "bike.one.two"
+ */
+ var _getObjectDotPath = function(arrayOfStrings){
+ return arrayOfStrings.reduce(function(previous, current) {
+ return previous + '.' + current;
+ });
+ };
+
+ function _obtainEffectiveMapId(d, mapId) {
+ var id, i;
+ if (!angular.isDefined(mapId)) {
+ if (Object.keys(d).length === 0) {
+ id = "main";
+ } else if (Object.keys(d).length >= 1) {
+ for (i in d) {
+ if (d.hasOwnProperty(i)) {
+ id = i;
+ }
+ }
+ } else {
+ $log.error(_errorHeader + "- You have more than 1 map on the DOM, you must provide the map ID to the leafletData.getXXX call");
+ }
+ } else {
+ id = mapId;
+ }
+
+ return id;
+ }
+
+ function _getUnresolvedDefer(d, mapId) {
+ var id = _obtainEffectiveMapId(d, mapId),
+ defer;
+
+ if (!angular.isDefined(d[id]) || d[id].resolvedDefer === true) {
+ defer = $q.defer();
+ d[id] = {
+ defer: defer,
+ resolvedDefer: false
+ };
+ } else {
+ defer = d[id].defer;
+ }
+
+ return defer;
+ }
+
+ var _isDefined = function(value) {
+ return angular.isDefined(value) && value !== null;
+ };
+ var _isUndefined = function(value){
+ return !_isDefined(value);
+ };
+
+ // BEGIN DIRECT PORT FROM AngularJS code base
+
+ var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
+
+ var MOZ_HACK_REGEXP = /^moz([A-Z])/;
+
+ var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
+
+ /**
+ Converts snake_case to camelCase.
+ Also there is special case for Moz prefix starting with upper case letter.
+ @param name Name to normalize
+ */
+
+ var camelCase = function(name) {
+ return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
+ if (offset) {
+ return letter.toUpperCase();
+ } else {
+ return letter;
+ }
+ }).replace(MOZ_HACK_REGEXP, "Moz$1");
+ };
+
+
+ /**
+ Converts all accepted directives format into proper directive name.
+ @param name Name to normalize
+ */
+
+ var directiveNormalize = function(name) {
+ return camelCase(name.replace(PREFIX_REGEXP, ""));
+ };
+
+ // END AngularJS port
+
+ return {
+ camelCase: camelCase,
+ directiveNormalize: directiveNormalize,
+ copy:_copy,
+ clone:_clone,
+ errorHeader: _errorHeader,
+ getObjectValue: _getObjectValue,
+ getObjectArrayPath:_getObjectArrayPath,
+ getObjectDotPath: _getObjectDotPath,
+ defaultTo: function(val, _default){
+ return _isDefined(val) ? val : _default;
+ },
+ //mainly for checking attributes of directives lets keep this minimal (on what we accept)
+ isTruthy: function(val){
+ return val === 'true' || val === true;
+ },
+ //Determine if a reference is {}
+ isEmpty: function(value) {
+ return Object.keys(value).length === 0;
+ },
+
+ //Determine if a reference is undefined or {}
+ isUndefinedOrEmpty: function (value) {
+ return (angular.isUndefined(value) || value === null) || Object.keys(value).length === 0;
+ },
+
+ // Determine if a reference is defined
+ isDefined: _isDefined,
+ isUndefined:_isUndefined,
+ isNumber: angular.isNumber,
+ isString: angular.isString,
+ isArray: angular.isArray,
+ isObject: angular.isObject,
+ isFunction: angular.isFunction,
+ equals: angular.equals,
+
+ isValidCenter: function(center) {
+ return angular.isDefined(center) && angular.isNumber(center.lat) &&
+ angular.isNumber(center.lng) && angular.isNumber(center.zoom);
+ },
+
+ isValidPoint: function(point) {
+ if (!angular.isDefined(point)) {
+ return false;
+ }
+ if (angular.isArray(point)) {
+ return point.length === 2 && angular.isNumber(point[0]) && angular.isNumber(point[1]);
+ }
+ return angular.isNumber(point.lat) && angular.isNumber(point.lng);
+ },
+
+ isSameCenterOnMap: function(centerModel, map) {
+ var mapCenter = map.getCenter();
+ var zoom = map.getZoom();
+ if (centerModel.lat && centerModel.lng &&
+ mapCenter.lat.toFixed(4) === centerModel.lat.toFixed(4) &&
+ mapCenter.lng.toFixed(4) === centerModel.lng.toFixed(4) &&
+ zoom === centerModel.zoom) {
+ return true;
+ }
+ return false;
+ },
+
+ safeApply: function($scope, fn) {
+ var phase = $scope.$root.$$phase;
+ if (phase === '$apply' || phase === '$digest') {
+ $scope.$eval(fn);
+ } else {
+ $scope.$evalAsync(fn);
+ }
+ },
+
+ obtainEffectiveMapId: _obtainEffectiveMapId,
+
+ getDefer: function(d, mapId) {
+ var id = _obtainEffectiveMapId(d, mapId),
+ defer;
+ if (!angular.isDefined(d[id]) || d[id].resolvedDefer === false) {
+ defer = _getUnresolvedDefer(d, mapId);
+ } else {
+ defer = d[id].defer;
+ }
+ return defer;
+ },
+
+ getUnresolvedDefer: _getUnresolvedDefer,
+
+ setResolvedDefer: function(d, mapId) {
+ var id = _obtainEffectiveMapId(d, mapId);
+ d[id].resolvedDefer = true;
+ },
+
+ rangeIsSupported: function() {
+ var testrange = document.createElement('input');
+ testrange.setAttribute('type', 'range');
+ return testrange.type === 'range';
+ },
+
+ FullScreenControlPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.Control.Fullscreen);
+ }
+ },
+
+ MiniMapControlPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.Control.MiniMap);
+ }
+ },
+
+ AwesomeMarkersPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.AwesomeMarkers) && angular.isDefined(L.AwesomeMarkers.Icon);
+ },
+ is: function(icon) {
+ if (this.isLoaded()) {
+ return icon instanceof L.AwesomeMarkers.Icon;
+ } else {
+ return false;
+ }
+ },
+ equal: function (iconA, iconB) {
+ if (!this.isLoaded()) {
+ return false;
+ }
+ if (this.is(iconA)) {
+ return angular.equals(iconA, iconB);
+ } else {
+ return false;
+ }
+ }
+ },
+
+ VectorMarkersPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.VectorMarkers) && angular.isDefined(L.VectorMarkers.Icon);
+ },
+ is: function(icon) {
+ if (this.isLoaded()) {
+ return icon instanceof L.VectorMarkers.Icon;
+ } else {
+ return false;
+ }
+ },
+ equal: function (iconA, iconB) {
+ if (!this.isLoaded()) {
+ return false;
+ }
+ if (this.is(iconA)) {
+ return angular.equals(iconA, iconB);
+ } else {
+ return false;
+ }
+ }
+ },
+
+ DomMarkersPlugin: {
+ isLoaded: function () {
+ if (angular.isDefined(L.DomMarkers) && angular.isDefined(L.DomMarkers.Icon)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ is: function (icon) {
+ if (this.isLoaded()) {
+ return icon instanceof L.DomMarkers.Icon;
+ } else {
+ return false;
+ }
+ },
+ equal: function (iconA, iconB) {
+ if (!this.isLoaded()) {
+ return false;
+ }
+ if (this.is(iconA)) {
+ return angular.equals(iconA, iconB);
+ } else {
+ return false;
+ }
+ }
+ },
+
+ PolylineDecoratorPlugin: {
+ isLoaded: function() {
+ if (angular.isDefined(L.PolylineDecorator)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ is: function(decoration) {
+ if (this.isLoaded()) {
+ return decoration instanceof L.PolylineDecorator;
+ } else {
+ return false;
+ }
+ },
+ equal: function(decorationA, decorationB) {
+ if (!this.isLoaded()) {
+ return false;
+ }
+ if (this.is(decorationA)) {
+ return angular.equals(decorationA, decorationB);
+ } else {
+ return false;
+ }
+ }
+ },
+
+ MakiMarkersPlugin: {
+ isLoaded: function() {
+ if (angular.isDefined(L.MakiMarkers) && angular.isDefined(L.MakiMarkers.Icon)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ is: function(icon) {
+ if (this.isLoaded()) {
+ return icon instanceof L.MakiMarkers.Icon;
+ } else {
+ return false;
+ }
+ },
+ equal: function (iconA, iconB) {
+ if (!this.isLoaded()) {
+ return false;
+ }
+ if (this.is(iconA)) {
+ return angular.equals(iconA, iconB);
+ } else {
+ return false;
+ }
+ }
+ },
+ ExtraMarkersPlugin: {
+ isLoaded: function () {
+ if (angular.isDefined(L.ExtraMarkers) && angular.isDefined(L.ExtraMarkers.Icon)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ is: function (icon) {
+ if (this.isLoaded()) {
+ return icon instanceof L.ExtraMarkers.Icon;
+ } else {
+ return false;
+ }
+ },
+ equal: function (iconA, iconB) {
+ if (!this.isLoaded()) {
+ return false;
+ }
+ if (this.is(iconA)) {
+ return angular.equals(iconA, iconB);
+ } else {
+ return false;
+ }
+ }
+ },
+ LabelPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.Label);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.MarkerClusterGroup;
+ } else {
+ return false;
+ }
+ }
+ },
+ MarkerClusterPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.MarkerClusterGroup);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.MarkerClusterGroup;
+ } else {
+ return false;
+ }
+ }
+ },
+ GoogleLayerPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.Google);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.Google;
+ } else {
+ return false;
+ }
+ }
+ },
+ LeafletProviderPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.TileLayer.Provider);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.TileLayer.Provider;
+ } else {
+ return false;
+ }
+ }
+ },
+ ChinaLayerPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.tileLayer.chinaProvider);
+ }
+ },
+ HeatLayerPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.heatLayer);
+ }
+ },
+ WebGLHeatMapLayerPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.TileLayer.WebGLHeatMap);
+ }
+ },
+ BingLayerPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.BingLayer);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.BingLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ WFSLayerPlugin: {
+ isLoaded: function() {
+ return L.GeoJSON.WFS !== undefined;
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.GeoJSON.WFS;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSBaseLayerPlugin: {
+ isLoaded: function() {
+ return L.esri !== undefined && L.esri.basemapLayer !== undefined;
+ },
+ is: function (layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.esri.basemapLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSLayerPlugin: {
+ isLoaded: function() {
+ return lvector !== undefined && lvector.AGS !== undefined;
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof lvector.AGS;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSFeatureLayerPlugin: {
+ isLoaded: function() {
+ return L.esri !== undefined && L.esri.featureLayer !== undefined;
+ },
+ is: function (layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.esri.featureLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSTiledMapLayerPlugin: {
+ isLoaded: function() {
+ return L.esri !== undefined && L.esri.tiledMapLayer !== undefined;
+ },
+ is: function (layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.esri.tiledMapLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSDynamicMapLayerPlugin: {
+ isLoaded: function () {
+ return L.esri !== undefined && L.esri.dynamicMapLayer !== undefined;
+ },
+ is: function (layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.esri.dynamicMapLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSImageMapLayerPlugin: {
+ isLoaded: function () {
+ return L.esri !== undefined && L.esri.imageMapLayer !== undefined;
+ },
+ is: function (layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.esri.imageMapLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSClusteredLayerPlugin: {
+ isLoaded: function () {
+ return L.esri !== undefined && L.esri.clusteredFeatureLayer !== undefined;
+ },
+ is: function (layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.esri.clusteredFeatureLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ AGSHeatmapLayerPlugin: {
+ isLoaded: function () {
+ return L.esri !== undefined && L.esri.heatmapFeatureLayer !== undefined;
+ },
+ is: function (layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.esri.heatmapFeatureLayer;
+ } else {
+ return false;
+ }
+ }
+ },
+ YandexLayerPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.Yandex);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.Yandex;
+ } else {
+ return false;
+ }
+ }
+ },
+ GeoJSONPlugin: {
+ isLoaded: function(){
+ return angular.isDefined(L.TileLayer.GeoJSON);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.TileLayer.GeoJSON;
+ } else {
+ return false;
+ }
+ }
+ },
+ UTFGridPlugin: {
+ isLoaded: function(){
+ return angular.isDefined(L.UtfGrid);
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof L.UtfGrid;
+ } else {
+ $log.error('[AngularJS - Leaflet] No UtfGrid plugin found.');
+ return false;
+ }
+ }
+ },
+ CartoDB: {
+ isLoaded: function(){
+ return cartodb;
+ },
+ is: function(/*layer*/) {
+ return true;
+ /*
+ if (this.isLoaded()) {
+ return layer instanceof L.TileLayer.GeoJSON;
+ } else {
+ return false;
+ }*/
+ }
+ },
+ Leaflet: {
+ DivIcon: {
+ is: function(icon) {
+ return icon instanceof L.DivIcon;
+ },
+ equal: function(iconA, iconB) {
+ if (this.is(iconA)) {
+ return angular.equals(iconA, iconB);
+ } else {
+ return false;
+ }
+ }
+ },
+ Icon: {
+ is: function(icon) {
+ return icon instanceof L.Icon;
+ },
+ equal: function(iconA, iconB) {
+ if (this.is(iconA)) {
+ return angular.equals(iconA, iconB);
+ } else {
+ return false;
+ }
+ }
+ }
+ },
+ /*
+ watchOptions - object to set deep nested watches and turn off watches all together
+ (rely on control / functional updates)
+ watchOptions - Object
+ doWatch:boolean
+ isDeep:boolean (sets $watch(function,isDeep))
+ individual
+ doWatch:boolean
+ isDeep:boolean
+ */
+ //legacy defaults
+ watchOptions: {
+ doWatch:true,
+ isDeep: true,
+ individual:{
+ doWatch:true,
+ isDeep: true
+ }
+ }
+ };
+}]);
+
+angular.module('leaflet-directive').service('leafletIterators', ["$log", "leafletHelpers", function ($log, leafletHelpers) {
+
+ var lHlp = leafletHelpers,
+ errorHeader = leafletHelpers.errorHeader + 'leafletIterators: ';
+
+ //BEGIN COPY from underscore
+ var _keys = Object.keys;
+ var _isFunction = lHlp.isFunction;
+ var _isObject = lHlp.isObject;
+
+ // Helper for collection methods to determine whether a collection
+ // should be iterated as an array or as an object
+ // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
+ var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
+
+ var _isArrayLike = function(collection) {
+ var length = collection !== null && collection.length;
+ return lHlp.isNumber(length) && length >= 0 && length <= MAX_ARRAY_INDEX;
+ };
+
+ // Keep the identity function around for default iteratees.
+ var _identity = function(value) {
+ return value;
+ };
+
+ var _property = function(key) {
+ return function(obj) {
+ return obj === null ? void 0 : obj[key];
+ };
+ };
+
+ // Internal function that returns an efficient (for current engines) version
+ // of the passed-in callback, to be repeatedly applied in other Underscore
+ // functions.
+ var optimizeCb = function(func, context, argCount) {
+ if (context === void 0) return func;
+ switch (argCount === null ? 3 : argCount) {
+ case 1: return function(value) {
+ return func.call(context, value);
+ };
+ case 2: return function(value, other) {
+ return func.call(context, value, other);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(context, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(context, accumulator, value, index, collection);
+ };
+ }
+ return function() {
+ return func.apply(context, arguments);
+ };
+ };
+
+ // An internal function for creating assigner functions.
+ var createAssigner = function(keysFunc, undefinedOnly) {
+ return function(obj) {
+ var length = arguments.length;
+ if (length < 2 || obj === null) return obj;
+ for (var index = 1; index < length; index++) {
+ var source = arguments[index],
+ keys = keysFunc(source),
+ l = keys.length;
+ for (var i = 0; i < l; i++) {
+ var key = keys[i];
+ if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
+ }
+ }
+ return obj;
+ };
+ };
+
+ // Assigns a given object with all the own properties in the passed-in object(s)
+ // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
+ var _extendOwn, _assign = null;
+ _extendOwn = _assign = createAssigner(_keys);
+
+ // Returns whether an object has a given set of `key:value` pairs.
+ var _isMatch = function(object, attrs) {
+ var keys = _keys(attrs), length = keys.length;
+ if (object === null) return !length;
+ var obj = Object(object);
+ for (var i = 0; i < length; i++) {
+ var key = keys[i];
+ if (attrs[key] !== obj[key] || !(key in obj)) return false;
+ }
+ return true;
+ };
+
+ // Returns a predicate for checking whether an object has a given set of
+ // `key:value` pairs.
+ var _matcher, _matches = null;
+ _matcher = _matches = function(attrs) {
+ attrs = _extendOwn({}, attrs);
+ return function(obj) {
+ return _isMatch(obj, attrs);
+ };
+ };
+
+
+ // A mostly-internal function to generate callbacks that can be applied
+ // to each element in a collection, returning the desired result — either
+ // identity, an arbitrary callback, a property matcher, or a property accessor.
+ var cb = function(value, context, argCount) {
+ if (value === null) return _identity;
+ if (_isFunction(value)) return optimizeCb(value, context, argCount);
+ if (_isObject(value)) return _matcher(value);
+ return _property(value);
+ };
+
+ var _every, _all = null;
+ _every = _all = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var keys = !_isArrayLike(obj) && _keys(obj),
+ length = (keys || obj).length;
+ for (var index = 0; index < length; index++) {
+ var currentKey = keys ? keys[index] : index;
+ if (!predicate(obj[currentKey], currentKey, obj)) return false;
+ }
+ return true;
+ };
+
+ //END COPY fron underscore
+
+ var _hasErrors = function(collection, cb, ignoreCollection, cbName){
+ if(!ignoreCollection) {
+ if (!lHlp.isDefined(collection) || !lHlp.isDefined(cb)) {
+ return true;
+ }
+ }
+ if(!lHlp.isFunction(cb)){
+ cbName = lHlp.defaultTo(cb,'cb');
+ $log.error(errorHeader + cbName + ' is not a function');
+ return true;
+ }
+ return false;
+ };
+
+ var _iterate = function(collection, externalCb, internalCb){
+ if(_hasErrors(undefined, internalCb, true, 'internalCb')){
+ return;
+ }
+ if(!_hasErrors(collection, externalCb)){
+ for(var key in collection){
+ if (collection.hasOwnProperty(key)) {
+ internalCb(collection[key], key);
+ }
+ }
+ }
+ };
+
+ //see http://jsperf.com/iterators/3
+ //utilizing for in is way faster
+ var _each = function(collection, cb){
+ _iterate(collection, cb, function(val, key){
+ cb(val, key);
+ });
+ };
+
+ return {
+ each:_each,
+ forEach: _each,
+ every: _every,
+ all: _all
+ };
+}]);
+
+angular.module("leaflet-directive")
+.factory('leafletLayerHelpers', ["$rootScope", "$log", "$q", "leafletHelpers", "leafletIterators", function ($rootScope, $log, $q, leafletHelpers, leafletIterators) {
+ var Helpers = leafletHelpers;
+ var isString = leafletHelpers.isString;
+ var isObject = leafletHelpers.isObject;
+ var isArray = leafletHelpers.isArray;
+ var isDefined = leafletHelpers.isDefined;
+ var errorHeader = leafletHelpers.errorHeader;
+ var $it = leafletIterators;
+
+ var utfGridCreateLayer = function(params) {
+ if (!Helpers.UTFGridPlugin.isLoaded()) {
+ $log.error('[AngularJS - Leaflet] The UTFGrid plugin is not loaded.');
+ return;
+ }
+ var utfgrid = new L.UtfGrid(params.url, params.pluginOptions);
+
+ utfgrid.on('mouseover', function(e) {
+ $rootScope.$broadcast('leafletDirectiveMap.utfgridMouseover', e);
+ });
+
+ utfgrid.on('mouseout', function(e) {
+ $rootScope.$broadcast('leafletDirectiveMap.utfgridMouseout', e);
+ });
+
+ utfgrid.on('click', function(e) {
+ $rootScope.$broadcast('leafletDirectiveMap.utfgridClick', e);
+ });
+
+ utfgrid.on('mousemove', function(e) {
+ $rootScope.$broadcast('leafletDirectiveMap.utfgridMousemove', e);
+ });
+
+ return utfgrid;
+ };
+
+ var layerTypes = {
+ xyz: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return L.tileLayer(params.url, params.options);
+ }
+ },
+ mapbox: {
+ mustHaveKey: true,
+ createLayer: function(params) {
+ var version = 3;
+ if(isDefined(params.options.version) && params.options.version === 4) {
+ version = params.options.version;
+ }
+ var url = version === 3?
+ '//{s}.tiles.mapbox.com/v3/' + params.key + '/{z}/{x}/{y}.png':
+ '//api.tiles.mapbox.com/v4/' + params.key + '/{z}/{x}/{y}.png?access_token=' + params.apiKey;
+ return L.tileLayer(url, params.options);
+ }
+ },
+ geoJSON: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.GeoJSONPlugin.isLoaded()) {
+ return;
+ }
+ return new L.TileLayer.GeoJSON(params.url, params.pluginOptions, params.options);
+ }
+ },
+ geoJSONShape: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ return new L.GeoJSON(params.data,
+ params.options);
+ }
+ },
+ geoJSONAwesomeMarker: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ return new L.geoJson(params.data, {
+ pointToLayer: function (feature, latlng) {
+ return L.marker(latlng, {icon: L.AwesomeMarkers.icon(params.icon)});
+ }
+ });
+ }
+ },
+ geoJSONVectorMarker: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ return new L.geoJson(params.data, {
+ pointToLayer: function (feature, latlng) {
+ return L.marker(latlng, {icon: L.VectorMarkers.icon(params.icon)});
+ }
+ });
+ }
+ },
+ utfGrid: {
+ mustHaveUrl: true,
+ createLayer: utfGridCreateLayer
+ },
+ cartodbTiles: {
+ mustHaveKey: true,
+ createLayer: function(params) {
+ var url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/{z}/{x}/{y}.png';
+ return L.tileLayer(url, params.options);
+ }
+ },
+ cartodbUTFGrid: {
+ mustHaveKey: true,
+ mustHaveLayer : true,
+ createLayer: function(params) {
+ params.url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/' + params.layer + '/{z}/{x}/{y}.grid.json';
+ return utfGridCreateLayer(params);
+ }
+ },
+ cartodbInteractive: {
+ mustHaveKey: true,
+ mustHaveLayer : true,
+ createLayer: function(params) {
+ var tilesURL = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/{z}/{x}/{y}.png';
+ var tileLayer = L.tileLayer(tilesURL, params.options);
+ params.url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/' + params.layer + '/{z}/{x}/{y}.grid.json';
+ var utfLayer = utfGridCreateLayer(params);
+ return L.layerGroup([tileLayer, utfLayer]);
+ }
+ },
+ wms: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return L.tileLayer.wms(params.url, params.options);
+ }
+ },
+ wmts: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return L.tileLayer.wmts(params.url, params.options);
+ }
+ },
+ wfs: {
+ mustHaveUrl: true,
+ mustHaveLayer : true,
+ createLayer: function(params) {
+ if (!Helpers.WFSLayerPlugin.isLoaded()) {
+ return;
+ }
+ var options = angular.copy(params.options);
+ if(options.crs && 'string' === typeof options.crs) {
+ /*jshint -W061 */
+ options.crs = eval(options.crs);
+ }
+ return new L.GeoJSON.WFS(params.url, params.layer, options);
+ }
+ },
+ group: {
+ mustHaveUrl: false,
+ createLayer: function (params) {
+ var lyrs = [];
+ $it.each(params.options.layers, function(l){
+ lyrs.push(createLayer(l));
+ });
+ params.options.loadedDefer = function() {
+ var defers = [];
+ if(isDefined(params.options.layers)) {
+ for (var i = 0; i < params.options.layers.length; i++) {
+ var d = params.options.layers[i].layerOptions.loadedDefer;
+ if(isDefined(d)) {
+ defers.push(d);
+ }
+ }
+ }
+ return defers;
+ };
+ return L.layerGroup(lyrs);
+ }
+ },
+ featureGroup: {
+ mustHaveUrl: false,
+ createLayer: function () {
+ return L.featureGroup();
+ }
+ },
+ google: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ var type = params.type || 'SATELLITE';
+ if (!Helpers.GoogleLayerPlugin.isLoaded()) {
+ return;
+ }
+ return new L.Google(type, params.options);
+ }
+ },
+ here: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ var provider = params.provider || 'HERE.terrainDay';
+ if (!Helpers.LeafletProviderPlugin.isLoaded()) {
+ return;
+ }
+ return new L.TileLayer.Provider(provider, params.options);
+ }
+ },
+ china:{
+ mustHaveUrl:false,
+ createLayer:function(params){
+ var type = params.type || '';
+ if(!Helpers.ChinaLayerPlugin.isLoaded()){
+ return;
+ }
+ return L.tileLayer.chinaProvider(type, params.options);
+ }
+ },
+ agsBase: {
+ mustHaveLayer : true,
+ createLayer: function (params) {
+ if (!Helpers.AGSBaseLayerPlugin.isLoaded()) {
+ return;
+ }
+ return L.esri.basemapLayer(params.layer, params.options);
+ }
+ },
+ ags: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.AGSLayerPlugin.isLoaded()) {
+ return;
+ }
+
+ var options = angular.copy(params.options);
+ angular.extend(options, {
+ url: params.url
+ });
+ var layer = new lvector.AGS(options);
+ layer.onAdd = function(map) {
+ this.setMap(map);
+ };
+ layer.onRemove = function() {
+ this.setMap(null);
+ };
+ return layer;
+ }
+ },
+ agsFeature: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.AGSFeatureLayerPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The esri plugin is not loaded.');
+ return;
+ }
+
+ params.options.url = params.url;
+
+ var layer = L.esri.featureLayer(params.options);
+ var load = function() {
+ if(isDefined(params.options.loadedDefer)) {
+ params.options.loadedDefer.resolve();
+ }
+ };
+ layer.on('loading', function() {
+ params.options.loadedDefer = $q.defer();
+ layer.off('load', load);
+ layer.on('load', load);
+ });
+
+ return layer;
+ }
+ },
+ agsTiled: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.AGSTiledMapLayerPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The esri plugin is not loaded.');
+ return;
+ }
+
+ params.options.url = params.url;
+
+ return L.esri.tiledMapLayer(params.options);
+ }
+ },
+ agsDynamic: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.AGSDynamicMapLayerPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The esri plugin is not loaded.');
+ return;
+ }
+
+ params.options.url = params.url;
+
+ return L.esri.dynamicMapLayer(params.options);
+ }
+ },
+ agsImage: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.AGSImageMapLayerPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The esri plugin is not loaded.');
+ return;
+ }
+ params.options.url = params.url;
+
+ return L.esri.imageMapLayer(params.options);
+ }
+ },
+ agsClustered: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.AGSClusteredLayerPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The esri clustered layer plugin is not loaded.');
+ return;
+ }
+
+ if(!Helpers.MarkerClusterPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The markercluster plugin is not loaded.');
+ return;
+ }
+ return L.esri.clusteredFeatureLayer(params.url, params.options);
+ }
+ },
+ agsHeatmap: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.AGSHeatmapLayerPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The esri heatmap layer plugin is not loaded.');
+ return;
+ }
+
+ if(!Helpers.HeatLayerPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The heatlayer plugin is not loaded.');
+ return;
+ }
+ return L.esri.heatmapFeatureLayer(params.url, params.options);
+ }
+ },
+ markercluster: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ if (!Helpers.MarkerClusterPlugin.isLoaded()) {
+ $log.warn(errorHeader + ' The markercluster plugin is not loaded.');
+ return;
+ }
+ return new L.MarkerClusterGroup(params.options);
+ }
+ },
+ bing: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ if (!Helpers.BingLayerPlugin.isLoaded()) {
+ return;
+ }
+ return new L.BingLayer(params.key, params.options);
+ }
+ },
+ webGLHeatmap: {
+ mustHaveUrl: false,
+ mustHaveData: true,
+ createLayer: function(params) {
+ if (!Helpers.WebGLHeatMapLayerPlugin.isLoaded()) {
+ return;
+ }
+ var layer = new L.TileLayer.WebGLHeatMap(params.options);
+ if (isDefined(params.data)) {
+ layer.setData(params.data);
+ }
+
+ return layer;
+ }
+ },
+ heat: {
+ mustHaveUrl: false,
+ mustHaveData: true,
+ createLayer: function(params) {
+ if (!Helpers.HeatLayerPlugin.isLoaded()) {
+ return;
+ }
+ var layer = new L.heatLayer();
+
+ if (isArray(params.data)) {
+ layer.setLatLngs(params.data);
+ }
+
+ if (isObject(params.options)) {
+ layer.setOptions(params.options);
+ }
+
+ return layer;
+ }
+ },
+ yandex: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ var type = params.type || 'map';
+ if (!Helpers.YandexLayerPlugin.isLoaded()) {
+ return;
+ }
+ return new L.Yandex(type, params.options);
+ }
+ },
+ imageOverlay: {
+ mustHaveUrl: true,
+ mustHaveBounds : true,
+ createLayer: function(params) {
+ return L.imageOverlay(params.url, params.bounds, params.options);
+ }
+ },
+ iip: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return L.tileLayer.iip(params.url, params.options);
+ }
+ },
+
+ // This "custom" type is used to accept every layer that user want to define himself.
+ // We can wrap these custom layers like heatmap or yandex, but it means a lot of work/code to wrap the world,
+ // so we let user to define their own layer outside the directive,
+ // and pass it on "createLayer" result for next processes
+ custom: {
+ createLayer: function (params) {
+ if (params.layer instanceof L.Class) {
+ return angular.copy(params.layer);
+ }
+ else {
+ $log.error('[AngularJS - Leaflet] A custom layer must be a leaflet Class');
+ }
+ }
+ },
+ cartodb: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return cartodb.createLayer(params.map, params.url);
+ }
+ }
+ };
+
+ function isValidLayerType(layerDefinition) {
+ // Check if the baselayer has a valid type
+ if (!isString(layerDefinition.type)) {
+ $log.error('[AngularJS - Leaflet] A layer must have a valid type defined.');
+ return false;
+ }
+
+ if (Object.keys(layerTypes).indexOf(layerDefinition.type) === -1) {
+ $log.error('[AngularJS - Leaflet] A layer must have a valid type: ' + Object.keys(layerTypes));
+ return false;
+ }
+
+ // Check if the layer must have an URL
+ if (layerTypes[layerDefinition.type].mustHaveUrl && !isString(layerDefinition.url)) {
+ $log.error('[AngularJS - Leaflet] A base layer must have an url');
+ return false;
+ }
+
+ if (layerTypes[layerDefinition.type].mustHaveData && !isDefined(layerDefinition.data)) {
+ $log.error('[AngularJS - Leaflet] The base layer must have a "data" array attribute');
+ return false;
+ }
+
+ if(layerTypes[layerDefinition.type].mustHaveLayer && !isDefined(layerDefinition.layer)) {
+ $log.error('[AngularJS - Leaflet] The type of layer ' + layerDefinition.type + ' must have an layer defined');
+ return false;
+ }
+
+ if (layerTypes[layerDefinition.type].mustHaveBounds && !isDefined(layerDefinition.bounds)) {
+ $log.error('[AngularJS - Leaflet] The type of layer ' + layerDefinition.type + ' must have bounds defined');
+ return false ;
+ }
+
+ if (layerTypes[layerDefinition.type].mustHaveKey && !isDefined(layerDefinition.key)) {
+ $log.error('[AngularJS - Leaflet] The type of layer ' + layerDefinition.type + ' must have key defined');
+ return false ;
+ }
+ return true;
+ }
+
+ function createLayer(layerDefinition) {
+ if (!isValidLayerType(layerDefinition)) {
+ return;
+ }
+
+ if (!isString(layerDefinition.name)) {
+ $log.error('[AngularJS - Leaflet] A base layer must have a name');
+ return;
+ }
+ if (!isObject(layerDefinition.layerParams)) {
+ layerDefinition.layerParams = {};
+ }
+ if (!isObject(layerDefinition.layerOptions)) {
+ layerDefinition.layerOptions = {};
+ }
+
+ // Mix the layer specific parameters with the general Leaflet options. Although this is an overhead
+ // the definition of a base layers is more 'clean' if the two types of parameters are differentiated
+ for (var attrname in layerDefinition.layerParams) {
+ layerDefinition.layerOptions[attrname] = layerDefinition.layerParams[attrname];
+ }
+
+ var params = {
+ url: layerDefinition.url,
+ data: layerDefinition.data,
+ options: layerDefinition.layerOptions,
+ layer: layerDefinition.layer,
+ icon: layerDefinition.icon,
+ type: layerDefinition.layerType,
+ bounds: layerDefinition.bounds,
+ key: layerDefinition.key,
+ apiKey: layerDefinition.apiKey,
+ pluginOptions: layerDefinition.pluginOptions,
+ user: layerDefinition.user
+ };
+
+ //TODO Add $watch to the layer properties
+ return layerTypes[layerDefinition.type].createLayer(params);
+ }
+
+ function safeAddLayer(map, layer) {
+ if (layer && typeof layer.addTo === 'function') {
+ layer.addTo(map);
+ } else {
+ map.addLayer(layer);
+ }
+ }
+
+ function safeRemoveLayer(map, layer, layerOptions) {
+ if(isDefined(layerOptions) && isDefined(layerOptions.loadedDefer)) {
+ if(angular.isFunction(layerOptions.loadedDefer)) {
+ var defers = layerOptions.loadedDefer();
+ $log.debug('Loaded Deferred', defers);
+ var count = defers.length;
+ if(count > 0) {
+ var resolve = function() {
+ count--;
+ if(count === 0) {
+ map.removeLayer(layer);
+ }
+ };
+
+ for(var i = 0; i < defers.length; i++) {
+ defers[i].promise.then(resolve);
+ }
+ } else {
+ map.removeLayer(layer);
+ }
+ } else {
+ layerOptions.loadedDefer.promise.then(function() {
+ map.removeLayer(layer);
+ });
+ }
+ } else {
+ map.removeLayer(layer);
+ }
+ }
+
+ return {
+ createLayer: createLayer,
+ safeAddLayer: safeAddLayer,
+ safeRemoveLayer: safeRemoveLayer
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletLegendHelpers', function () {
+ var _updateLegend = function(div, legendData, type, url) {
+ div.innerHTML = '';
+ if(legendData.error) {
+ div.innerHTML += '' + legendData.error.message + '
';
+ } else {
+ if (type === 'arcgis') {
+ for (var i = 0; i < legendData.layers.length; i++) {
+ var layer = legendData.layers[i];
+ div.innerHTML += '' + layer.layerName + '
';
+ for(var j = 0; j < layer.legend.length; j++) {
+ var leg = layer.legend[j];
+ div.innerHTML +=
+ '' +
+ '' + leg.label + '
';
+ }
+ }
+ }
+ else if (type === 'image') {
+ div.innerHTML = ' ';
+ }
+ }
+ };
+
+ var _getOnAddLegend = function(legendData, legendClass, type, url) {
+ return function(/*map*/) {
+ var div = L.DomUtil.create('div', legendClass);
+
+ if (!L.Browser.touch) {
+ L.DomEvent.disableClickPropagation(div);
+ L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation);
+ } else {
+ L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation);
+ }
+ _updateLegend(div, legendData, type, url);
+ return div;
+ };
+ };
+
+ var _getOnAddArrayLegend = function(legend, legendClass) {
+ return function(/*map*/) {
+ var div = L.DomUtil.create('div', legendClass);
+ for (var i = 0; i < legend.colors.length; i++) {
+ div.innerHTML +=
+ '
' +
+ '' + legend.labels[i] + '
';
+ }
+ if (!L.Browser.touch) {
+ L.DomEvent.disableClickPropagation(div);
+ L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation);
+ } else {
+ L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation);
+ }
+ return div;
+ };
+ };
+
+ return {
+ getOnAddLegend: _getOnAddLegend,
+ getOnAddArrayLegend: _getOnAddArrayLegend,
+ updateLegend: _updateLegend
+ };
+});
+
+angular.module("leaflet-directive").factory('leafletMapDefaults', ["$q", "leafletHelpers", function ($q, leafletHelpers) {
+ function _getDefaults() {
+ return {
+ keyboard: true,
+ dragging: true,
+ worldCopyJump: false,
+ doubleClickZoom: true,
+ scrollWheelZoom: true,
+ tap: true,
+ touchZoom: true,
+ zoomControl: true,
+ zoomsliderControl: false,
+ zoomControlPosition: 'topleft',
+ attributionControl: true,
+ controls: {
+ layers: {
+ visible: true,
+ position: 'topright',
+ collapsed: true
+ }
+ },
+ nominatim: {
+ server: ' http://nominatim.openstreetmap.org/search'
+ },
+ crs: L.CRS.EPSG3857,
+ tileLayer: '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
+ tileLayerOptions: {
+ attribution: '© OpenStreetMap contributors'
+ },
+ path: {
+ weight: 10,
+ opacity: 1,
+ color: '#0000ff'
+ },
+ center: {
+ lat: 0,
+ lng: 0,
+ zoom: 1
+ }
+ };
+ }
+
+ var isDefined = leafletHelpers.isDefined,
+ isObject = leafletHelpers.isObject,
+ obtainEffectiveMapId = leafletHelpers.obtainEffectiveMapId,
+ defaults = {};
+
+ // Get the _defaults dictionary, and override the properties defined by the user
+ return {
+ reset: function () {
+ defaults = {};
+ },
+ getDefaults: function (scopeId) {
+ var mapId = obtainEffectiveMapId(defaults, scopeId);
+ return defaults[mapId];
+ },
+
+ getMapCreationDefaults: function (scopeId) {
+ var mapId = obtainEffectiveMapId(defaults, scopeId);
+ var d = defaults[mapId];
+
+ var mapDefaults = {
+ maxZoom: d.maxZoom,
+ keyboard: d.keyboard,
+ dragging: d.dragging,
+ zoomControl: d.zoomControl,
+ doubleClickZoom: d.doubleClickZoom,
+ scrollWheelZoom: d.scrollWheelZoom,
+ tap: d.tap,
+ touchZoom: d.touchZoom,
+ attributionControl: d.attributionControl,
+ worldCopyJump: d.worldCopyJump,
+ crs: d.crs
+ };
+
+ if (isDefined(d.minZoom)) {
+ mapDefaults.minZoom = d.minZoom;
+ }
+
+ if (isDefined(d.zoomAnimation)) {
+ mapDefaults.zoomAnimation = d.zoomAnimation;
+ }
+
+ if (isDefined(d.fadeAnimation)) {
+ mapDefaults.fadeAnimation = d.fadeAnimation;
+ }
+
+ if (isDefined(d.markerZoomAnimation)) {
+ mapDefaults.markerZoomAnimation = d.markerZoomAnimation;
+ }
+
+ if (d.map) {
+ for (var option in d.map) {
+ mapDefaults[option] = d.map[option];
+ }
+ }
+
+ return mapDefaults;
+ },
+
+ setDefaults: function (userDefaults, scopeId) {
+ var newDefaults = _getDefaults();
+
+ if (isDefined(userDefaults)) {
+ newDefaults.doubleClickZoom = isDefined(userDefaults.doubleClickZoom) ? userDefaults.doubleClickZoom : newDefaults.doubleClickZoom;
+ newDefaults.scrollWheelZoom = isDefined(userDefaults.scrollWheelZoom) ? userDefaults.scrollWheelZoom : newDefaults.doubleClickZoom;
+ newDefaults.tap = isDefined(userDefaults.tap) ? userDefaults.tap : newDefaults.tap;
+ newDefaults.touchZoom = isDefined(userDefaults.touchZoom) ? userDefaults.touchZoom : newDefaults.doubleClickZoom;
+ newDefaults.zoomControl = isDefined(userDefaults.zoomControl) ? userDefaults.zoomControl : newDefaults.zoomControl;
+ newDefaults.zoomsliderControl = isDefined(userDefaults.zoomsliderControl) ? userDefaults.zoomsliderControl : newDefaults.zoomsliderControl;
+ newDefaults.attributionControl = isDefined(userDefaults.attributionControl) ? userDefaults.attributionControl : newDefaults.attributionControl;
+ newDefaults.tileLayer = isDefined(userDefaults.tileLayer) ? userDefaults.tileLayer : newDefaults.tileLayer;
+ newDefaults.zoomControlPosition = isDefined(userDefaults.zoomControlPosition) ? userDefaults.zoomControlPosition : newDefaults.zoomControlPosition;
+ newDefaults.keyboard = isDefined(userDefaults.keyboard) ? userDefaults.keyboard : newDefaults.keyboard;
+ newDefaults.dragging = isDefined(userDefaults.dragging) ? userDefaults.dragging : newDefaults.dragging;
+
+ if (isDefined(userDefaults.controls)) {
+ angular.extend(newDefaults.controls, userDefaults.controls);
+ }
+
+ if (isObject(userDefaults.crs)) {
+ newDefaults.crs = userDefaults.crs;
+ } else if (isDefined(L.CRS[userDefaults.crs])) {
+ newDefaults.crs = L.CRS[userDefaults.crs];
+ }
+
+ if (isDefined(userDefaults.center)) {
+ angular.copy(userDefaults.center, newDefaults.center);
+ }
+
+ if (isDefined(userDefaults.tileLayerOptions)) {
+ angular.copy(userDefaults.tileLayerOptions, newDefaults.tileLayerOptions);
+ }
+
+ if (isDefined(userDefaults.maxZoom)) {
+ newDefaults.maxZoom = userDefaults.maxZoom;
+ }
+
+ if (isDefined(userDefaults.minZoom)) {
+ newDefaults.minZoom = userDefaults.minZoom;
+ }
+
+ if (isDefined(userDefaults.zoomAnimation)) {
+ newDefaults.zoomAnimation = userDefaults.zoomAnimation;
+ }
+
+ if (isDefined(userDefaults.fadeAnimation)) {
+ newDefaults.fadeAnimation = userDefaults.fadeAnimation;
+ }
+
+ if (isDefined(userDefaults.markerZoomAnimation)) {
+ newDefaults.markerZoomAnimation = userDefaults.markerZoomAnimation;
+ }
+
+ if (isDefined(userDefaults.worldCopyJump)) {
+ newDefaults.worldCopyJump = userDefaults.worldCopyJump;
+ }
+
+ if (isDefined(userDefaults.map)) {
+ newDefaults.map = userDefaults.map;
+ }
+
+ if (isDefined(userDefaults.path)) {
+ newDefaults.path = userDefaults.path;
+ }
+ }
+
+ var mapId = obtainEffectiveMapId(defaults, scopeId);
+ defaults[mapId] = newDefaults;
+ return newDefaults;
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").service('leafletMarkersHelpers', ["$rootScope", "$timeout", "leafletHelpers", "$log", "$compile", "leafletGeoJsonHelpers", function ($rootScope, $timeout, leafletHelpers, $log, $compile, leafletGeoJsonHelpers) {
+ var isDefined = leafletHelpers.isDefined,
+ defaultTo = leafletHelpers.defaultTo,
+ MarkerClusterPlugin = leafletHelpers.MarkerClusterPlugin,
+ AwesomeMarkersPlugin = leafletHelpers.AwesomeMarkersPlugin,
+ VectorMarkersPlugin = leafletHelpers.VectorMarkersPlugin,
+ MakiMarkersPlugin = leafletHelpers.MakiMarkersPlugin,
+ ExtraMarkersPlugin = leafletHelpers.ExtraMarkersPlugin,
+ DomMarkersPlugin = leafletHelpers.DomMarkersPlugin,
+ safeApply = leafletHelpers.safeApply,
+ Helpers = leafletHelpers,
+ isString = leafletHelpers.isString,
+ isNumber = leafletHelpers.isNumber,
+ isObject = leafletHelpers.isObject,
+ groups = {},
+ geoHlp = leafletGeoJsonHelpers,
+ errorHeader = leafletHelpers.errorHeader;
+
+ var _string = function (marker) {
+ //this exists since JSON.stringify barfs on cyclic
+ var retStr = '';
+ ['_icon', '_latlng', '_leaflet_id', '_map', '_shadow'].forEach(function (prop) {
+ retStr += prop + ': ' + defaultTo(marker[prop], 'undefined') + ' \n';
+ });
+ return '[leafletMarker] : \n' + retStr;
+ };
+ var _log = function (marker, useConsole) {
+ var logger = useConsole ? console : $log;
+ logger.debug(_string(marker));
+ };
+
+ var createLeafletIcon = function (iconData) {
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'awesomeMarker') {
+ if (!AwesomeMarkersPlugin.isLoaded()) {
+ $log.error(errorHeader + ' The AwesomeMarkers Plugin is not loaded.');
+ }
+
+ return new L.AwesomeMarkers.icon(iconData);
+ }
+
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'vectorMarker') {
+ if (!VectorMarkersPlugin.isLoaded()) {
+ $log.error(errorHeader + ' The VectorMarkers Plugin is not loaded.');
+ }
+
+ return new L.VectorMarkers.icon(iconData);
+ }
+
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'makiMarker') {
+ if (!MakiMarkersPlugin.isLoaded()) {
+ $log.error(errorHeader + 'The MakiMarkers Plugin is not loaded.');
+ }
+
+ return new L.MakiMarkers.icon(iconData);
+ }
+
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'extraMarker') {
+ if (!ExtraMarkersPlugin.isLoaded()) {
+ $log.error(errorHeader + 'The ExtraMarkers Plugin is not loaded.');
+ }
+ return new L.ExtraMarkers.icon(iconData);
+ }
+
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'div') {
+ return new L.divIcon(iconData);
+ }
+
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'dom') {
+ if (!DomMarkersPlugin.isLoaded()) {
+ $log.error(errorHeader + 'The DomMarkers Plugin is not loaded.');
+ }
+ var markerScope = angular.isFunction(iconData.getMarkerScope) ? iconData.getMarkerScope() : $rootScope,
+ template = $compile(iconData.template)(markerScope),
+ iconDataCopy = angular.copy(iconData);
+ iconDataCopy.element = template[0];
+ return new L.DomMarkers.icon(iconDataCopy);
+ }
+
+ // allow for any custom icon to be used... assumes the icon has already been initialized
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'icon') {
+ return iconData.icon;
+ }
+
+ var base64icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAGmklEQVRYw7VXeUyTZxjvNnfELFuyIzOabermMZEeQC/OclkO49CpOHXOLJl/CAURuYbQi3KLgEhbrhZ1aDwmaoGqKII6odATmH/scDFbdC7LvFqOCc+e95s2VG50X/LLm/f4/Z7neY/ne18aANCmAr5E/xZf1uDOkTcGcWR6hl9247tT5U7Y6SNvWsKT63P58qbfeLJG8M5qcgTknrvvrdDbsT7Ml+tv82X6vVxJE33aRmgSyYtcWVMqX97Yv2JvW39UhRE2HuyBL+t+gK1116ly06EeWFNlAmHxlQE0OMiV6mQCScusKRlhS3QLeVJdl1+23h5dY4FNB3thrbYboqptEFlphTC1hSpJnbRvxP4NWgsE5Jyz86QNNi/5qSUTGuFk1gu54tN9wuK2wc3o+Wc13RCmsoBwEqzGcZsxsvCSy/9wJKf7UWf1mEY8JWfewc67UUoDbDjQC+FqK4QqLVMGGR9d2wurKzqBk3nqIT/9zLxRRjgZ9bqQgub+DdoeCC03Q8j+0QhFhBHR/eP3U/zCln7Uu+hihJ1+bBNffLIvmkyP0gpBZWYXhKussK6mBz5HT6M1Nqpcp+mBCPXosYQfrekGvrjewd59/GvKCE7TbK/04/ZV5QZYVWmDwH1mF3xa2Q3ra3DBC5vBT1oP7PTj4C0+CcL8c7C2CtejqhuCnuIQHaKHzvcRfZpnylFfXsYJx3pNLwhKzRAwAhEqG0SpusBHfAKkxw3w4627MPhoCH798z7s0ZnBJ/MEJbZSbXPhER2ih7p2ok/zSj2cEJDd4CAe+5WYnBCgR2uruyEw6zRoW6/DWJ/OeAP8pd/BGtzOZKpG8oke0SX6GMmRk6GFlyAc59K32OTEinILRJRchah8HQwND8N435Z9Z0FY1EqtxUg+0SO6RJ/mmXz4VuS+DpxXC3gXmZwIL7dBSH4zKE50wESf8qwVgrP1EIlTO5JP9Igu0aexdh28F1lmAEGJGfh7jE6ElyM5Rw/FDcYJjWhbeiBYoYNIpc2FT/SILivp0F1ipDWk4BIEo2VuodEJUifhbiltnNBIXPUFCMpthtAyqws/BPlEF/VbaIxErdxPphsU7rcCp8DohC+GvBIPJS/tW2jtvTmmAeuNO8BNOYQeG8G/2OzCJ3q+soYB5i6NhMaKr17FSal7GIHheuV3uSCY8qYVuEm1cOzqdWr7ku/R0BDoTT+DT+ohCM6/CCvKLKO4RI+dXPeAuaMqksaKrZ7L3FE5FIFbkIceeOZ2OcHO6wIhTkNo0ffgjRGxEqogXHYUPHfWAC/lADpwGcLRY3aeK4/oRGCKYcZXPVoeX/kelVYY8dUGf8V5EBRbgJXT5QIPhP9ePJi428JKOiEYhYXFBqou2Guh+p/mEB1/RfMw6rY7cxcjTrneI1FrDyuzUSRm9miwEJx8E/gUmqlyvHGkneiwErR21F3tNOK5Tf0yXaT+O7DgCvALTUBXdM4YhC/IawPU+2PduqMvuaR6eoxSwUk75ggqsYJ7VicsnwGIkZBSXKOUww73WGXyqP+J2/b9c+gi1YAg/xpwck3gJuucNrh5JvDPvQr0WFXf0piyt8f8/WI0hV4pRxxkQZdJDfDJNOAmM0Ag8jyT6hz0WGXWuP94Yh2jcfjmXAGvHCMslRimDHYuHuDsy2QtHuIavznhbYURq5R57KpzBBRZKPJi8eQg48h4j8SDdowifdIrEVdU+gbO6QNvRRt4ZBthUaZhUnjlYObNagV3keoeru3rU7rcuceqU1mJBxy+BWZYlNEBH+0eH4vRiB+OYybU2hnblYlTvkHinM4m54YnxSyaZYSF6R3jwgP7udKLGIX6r/lbNa9N6y5MFynjWDtrHd75ZvTYAPO/6RgF0k76mQla3FGq7dO+cH8sKn0Vo7nDllwAhqwLPkxrHwWmHJOo+AKJ4rab5OgrM7rVu8eWb2Pu0Dh4eDgXoOfvp7Y7QeqknRmvcTBEyq9m/HQQSCSz6LHq3z0yzsNySRfMS253wl2KyRDbcZPcfJKjZmSEOjcxyi+Y8dUOtsIEH6R2wNykdqrkYJ0RV92H0W58pkfQk7cKevsLK10Py8SdMGfXNXATY+pPbyJR/ET6n9nIfztNtZYRV9XniQu9IA2vOVgy4ir7GCLVmmd+zjkH0eAF9Po6K61pmCXHxU5rHMYd1ftc3owjwRSVRzLjKvqZEty6cRUD7jGqiOdu5HG6MdHjNcNYGqfDm5YRzLBBCCDl/2bk8a8gdbqcfwECu62Fg/HrggAAAABJRU5ErkJggg==";
+ var base64shadow = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAYAAACoYAD2AAAC5ElEQVRYw+2YW4/TMBCF45S0S1luXZCABy5CgLQgwf//S4BYBLTdJLax0fFqmB07nnQfEGqkIydpVH85M+NLjPe++dcPc4Q8Qh4hj5D/AaQJx6H/4TMwB0PeBNwU7EGQAmAtsNfAzoZkgIa0ZgLMa4Aj6CxIAsjhjOCoL5z7Glg1JAOkaicgvQBXuncwJAWjksLtBTWZe04CnYRktUGdilALppZBOgHGZcBzL6OClABvMSVIzyBjazOgrvACf1ydC5mguqAVg6RhdkSWQFj2uxfaq/BrIZOLEWgZdALIDvcMcZLD8ZbLC9de4yR1sYMi4G20S4Q/PWeJYxTOZn5zJXANZHIxAd4JWhPIloTJZhzMQduM89WQ3MUVAE/RnhAXpTycqys3NZALOBbB7kFrgLesQl2h45Fcj8L1tTSohUwuxhy8H/Qg6K7gIs+3kkaigQCOcyEXCHN07wyQazhrmIulvKMQAwMcmLNqyCVyMAI+BuxSMeTk3OPikLY2J1uE+VHQk6ANrhds+tNARqBeaGc72cK550FP4WhXmFmcMGhTwAR1ifOe3EvPqIegFmF+C8gVy0OfAaWQPMR7gF1OQKqGoBjq90HPMP01BUjPOqGFksC4emE48tWQAH0YmvOgF3DST6xieJgHAWxPAHMuNhrImIdvoNOKNWIOcE+UXE0pYAnkX6uhWsgVXDxHdTfCmrEEmMB2zMFimLVOtiiajxiGWrbU52EeCdyOwPEQD8LqyPH9Ti2kgYMf4OhSKB7qYILbBv3CuVTJ11Y80oaseiMWOONc/Y7kJYe0xL2f0BaiFTxknHO5HaMGMublKwxFGzYdWsBF174H/QDknhTHmHHN39iWFnkZx8lPyM8WHfYELmlLKtgWNmFNzQcC1b47gJ4hL19i7o65dhH0Negbca8vONZoP7doIeOC9zXm8RjuL0Gf4d4OYaU5ljo3GYiqzrWQHfJxA6ALhDpVKv9qYeZA8eM3EhfPSCmpuD0AAAAASUVORK5CYII=";
+
+ if (!isDefined(iconData) || !isDefined(iconData.iconUrl)) {
+ return new L.Icon.Default({
+ iconUrl: base64icon,
+ shadowUrl: base64shadow,
+ iconSize: [25, 41],
+ iconAnchor: [12, 41],
+ popupAnchor: [1, -34],
+ shadowSize: [41, 41]
+ });
+ }
+
+ return new L.Icon(iconData);
+ };
+
+ var _resetMarkerGroup = function (groupName) {
+ if (isDefined(groups[groupName])) {
+ groups.splice(groupName, 1);
+ }
+ };
+
+ var _resetMarkerGroups = function () {
+ groups = {};
+ };
+
+ var _deleteMarker = function (marker, map, layers) {
+ marker.closePopup();
+ // There is no easy way to know if a marker is added to a layer, so we search for it
+ // if there are overlays
+ if (isDefined(layers) && isDefined(layers.overlays)) {
+ for (var key in layers.overlays) {
+ if (layers.overlays[key] instanceof L.LayerGroup || layers.overlays[key] instanceof L.FeatureGroup) {
+ if (layers.overlays[key].hasLayer(marker)) {
+ layers.overlays[key].removeLayer(marker);
+ return;
+ }
+ }
+ }
+ }
+
+ if (isDefined(groups)) {
+ for (var groupKey in groups) {
+ if (groups[groupKey].hasLayer(marker)) {
+ groups[groupKey].removeLayer(marker);
+ }
+ }
+ }
+
+ if (map.hasLayer(marker)) {
+ map.removeLayer(marker);
+ }
+ };
+
+ var adjustPopupPan = function(marker, map) {
+ var containerHeight = marker._popup._container.offsetHeight,
+ layerPos = new L.Point(marker._popup._containerLeft, -containerHeight - marker._popup._containerBottom),
+ containerPos = map.layerPointToContainerPoint(layerPos);
+ if (containerPos !== null) {
+ marker._popup._adjustPan();
+ }
+ };
+
+ var compilePopup = function(marker, markerScope) {
+ $compile(marker._popup._contentNode)(markerScope);
+ };
+
+ var updatePopup = function (marker, markerScope, map) {
+ //The innerText should be more than 1 once angular has compiled.
+ //We need to keep trying until angular has compiled before we _updateLayout and _updatePosition
+ //This should take care of any scenario , eg ngincludes, whatever.
+ //Is there a better way to check for this?
+ var innerText = marker._popup._contentNode.innerText || marker._popup._contentNode.textContent;
+ if (innerText.length < 1) {
+ $timeout(function () {
+ updatePopup(marker, markerScope, map);
+ });
+ }
+
+ //cause a reflow - this is also very important - if we don't do this then the widths are from before $compile
+ var reflow = marker._popup._contentNode.offsetWidth;
+
+ marker._popup._updateLayout();
+ marker._popup._updatePosition();
+
+ if (marker._popup.options.autoPan) {
+ adjustPopupPan(marker, map);
+ }
+
+ //using / returning reflow so jshint doesn't moan
+ return reflow;
+ };
+
+ var _manageOpenPopup = function (marker, markerData, map) {
+ // The marker may provide a scope returning function used to compile the message
+ // default to $rootScope otherwise
+ var markerScope = angular.isFunction(markerData.getMessageScope) ? markerData.getMessageScope() : $rootScope,
+ compileMessage = isDefined(markerData.compileMessage) ? markerData.compileMessage : true;
+
+ if (compileMessage) {
+ if (!isDefined(marker._popup) || !isDefined(marker._popup._contentNode)) {
+ $log.error(errorHeader + 'Popup is invalid or does not have any content.');
+ return false;
+ }
+
+ compilePopup(marker, markerScope);
+ updatePopup(marker, markerData, map);
+ }
+ };
+
+
+ var _manageOpenLabel = function (marker, markerData) {
+ var markerScope = angular.isFunction(markerData.getMessageScope) ? markerData.getMessageScope() : $rootScope,
+ labelScope = angular.isFunction(markerData.getLabelScope) ? markerData.getLabelScope() : markerScope,
+ compileMessage = isDefined(markerData.compileMessage) ? markerData.compileMessage : true;
+
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(markerData.label)) {
+ if (isDefined(markerData.label.options) && markerData.label.options.noHide === true) {
+ marker.showLabel();
+ }
+ if (compileMessage && isDefined(marker.label)) {
+ $compile(marker.label._container)(labelScope);
+ }
+ }
+ };
+
+ var _updateMarker = function (markerData, oldMarkerData, marker, name, leafletScope, layers, map) {
+ if (!isDefined(oldMarkerData)) {
+ return;
+ }
+
+ // Update the lat-lng property (always present in marker properties)
+ if (!geoHlp.validateCoords(markerData)) {
+ $log.warn('There are problems with lat-lng data, please verify your marker model');
+ _deleteMarker(marker, map, layers);
+ return;
+ }
+
+ // watch is being initialized if old and new object is the same
+ var isInitializing = markerData === oldMarkerData;
+
+ // Update marker rotation
+ if (isDefined(markerData.iconAngle) && oldMarkerData.iconAngle !== markerData.iconAngle) {
+ marker.setIconAngle(markerData.iconAngle);
+ }
+
+ // It is possible that the layer has been removed or the layer marker does not exist
+ // Update the layer group if present or move it to the map if not
+ if (!isString(markerData.layer)) {
+ // There is no layer information, we move the marker to the map if it was in a layer group
+ if (isString(oldMarkerData.layer)) {
+ // Remove from the layer group that is supposed to be
+ if (isDefined(layers.overlays[oldMarkerData.layer]) && layers.overlays[oldMarkerData.layer].hasLayer(marker)) {
+ layers.overlays[oldMarkerData.layer].removeLayer(marker);
+ marker.closePopup();
+ }
+ // Test if it is not on the map and add it
+ if (!map.hasLayer(marker)) {
+ map.addLayer(marker);
+ }
+ }
+ }
+
+ if ((isNumber(markerData.opacity) || isNumber(parseFloat(markerData.opacity))) && markerData.opacity !== oldMarkerData.opacity) {
+ // There was a different opacity so we update it
+ marker.setOpacity(markerData.opacity);
+ }
+
+ if (isString(markerData.layer) && oldMarkerData.layer !== markerData.layer) {
+ // If it was on a layer group we have to remove it
+ if (isString(oldMarkerData.layer) && isDefined(layers.overlays[oldMarkerData.layer]) && layers.overlays[oldMarkerData.layer].hasLayer(marker)) {
+ layers.overlays[oldMarkerData.layer].removeLayer(marker);
+ }
+ marker.closePopup();
+
+ // Remove it from the map in case the new layer is hidden or there is an error in the new layer
+ if (map.hasLayer(marker)) {
+ map.removeLayer(marker);
+ }
+
+ // The markerData.layer is defined so we add the marker to the layer if it is different from the old data
+ if (!isDefined(layers.overlays[markerData.layer])) {
+ $log.error(errorHeader + 'You must use a name of an existing layer');
+ return;
+ }
+ // Is a group layer?
+ var layerGroup = layers.overlays[markerData.layer];
+ if (!(layerGroup instanceof L.LayerGroup || layerGroup instanceof L.FeatureGroup)) {
+ $log.error(errorHeader + 'A marker can only be added to a layer of type "group" or "featureGroup"');
+ return;
+ }
+ // The marker goes to a correct layer group, so first of all we add it
+ layerGroup.addLayer(marker);
+ // The marker is automatically added to the map depending on the visibility
+ // of the layer, so we only have to open the popup if the marker is in the map
+ if (map.hasLayer(marker) && markerData.focus === true) {
+ marker.openPopup();
+ }
+ }
+
+ // Update the draggable property
+ if (markerData.draggable !== true && oldMarkerData.draggable === true && (isDefined(marker.dragging))) {
+ marker.dragging.disable();
+ }
+
+ if (markerData.draggable === true && oldMarkerData.draggable !== true) {
+ // The markerData.draggable property must be true so we update if there wasn't a previous value or it wasn't true
+ if (marker.dragging) {
+ marker.dragging.enable();
+ } else {
+ if (L.Handler.MarkerDrag) {
+ marker.dragging = new L.Handler.MarkerDrag(marker);
+ marker.options.draggable = true;
+ marker.dragging.enable();
+ }
+ }
+ }
+
+ // Update the icon property
+ if (!isObject(markerData.icon)) {
+ // If there is no icon property or it's not an object
+ if (isObject(oldMarkerData.icon)) {
+ // If there was an icon before restore to the default
+ marker.setIcon(createLeafletIcon());
+ marker.closePopup();
+ marker.unbindPopup();
+ if (isString(markerData.message)) {
+ marker.bindPopup(markerData.message, markerData.popupOptions);
+ }
+ }
+ }
+
+ if (isObject(markerData.icon) && isObject(oldMarkerData.icon) && !angular.equals(markerData.icon, oldMarkerData.icon)) {
+ var dragG = false;
+ if (marker.dragging) {
+ dragG = marker.dragging.enabled();
+ }
+ marker.setIcon(createLeafletIcon(markerData.icon));
+ if (dragG) {
+ marker.dragging.enable();
+ }
+ marker.closePopup();
+ marker.unbindPopup();
+ if (isString(markerData.message)) {
+ marker.bindPopup(markerData.message, markerData.popupOptions);
+ // if marker has been already focused, reopen popup
+ if (map.hasLayer(marker) && markerData.focus === true) {
+ marker.openPopup();
+ }
+ }
+ }
+
+ // Update the Popup message property
+ if (!isString(markerData.message) && isString(oldMarkerData.message)) {
+ marker.closePopup();
+ marker.unbindPopup();
+ }
+
+ // Update the label content or bind a new label if the old one has been removed.
+ if (Helpers.LabelPlugin.isLoaded()) {
+ if (isDefined(markerData.label) && isDefined(markerData.label.message)) {
+ if ('label' in oldMarkerData && 'message' in oldMarkerData.label && !angular.equals(markerData.label.message, oldMarkerData.label.message)) {
+ marker.updateLabelContent(markerData.label.message);
+ } else if (!angular.isFunction(marker.getLabel) || angular.isFunction(marker.getLabel) && !isDefined(marker.getLabel())) {
+ marker.bindLabel(markerData.label.message, markerData.label.options);
+ _manageOpenLabel(marker, markerData);
+ } else {
+ _manageOpenLabel(marker, markerData);
+ }
+ } else if (!('label' in markerData && !('message' in markerData.label))) {
+ if (angular.isFunction(marker.unbindLabel)) {
+ marker.unbindLabel();
+ }
+ }
+ }
+
+ // There is some text in the popup, so we must show the text or update existing
+ if (isString(markerData.message) && !isString(oldMarkerData.message)) {
+ // There was no message before so we create it
+ marker.bindPopup(markerData.message, markerData.popupOptions);
+ }
+
+ if (isString(markerData.message) && isString(oldMarkerData.message) && markerData.message !== oldMarkerData.message) {
+ // There was a different previous message so we update it
+ marker.setPopupContent(markerData.message);
+ }
+
+ // Update the focus property
+ var updatedFocus = false;
+ if (markerData.focus !== true && oldMarkerData.focus === true) {
+ // If there was a focus property and was true we turn it off
+ marker.closePopup();
+ updatedFocus = true;
+ }
+
+ // The markerData.focus property must be true so we update if there wasn't a previous value or it wasn't true
+ if (markerData.focus === true && ( !isDefined(oldMarkerData.focus) || oldMarkerData.focus === false) || (isInitializing && markerData.focus === true)) {
+ // Reopen the popup when focus is still true
+ marker.openPopup();
+ updatedFocus = true;
+ }
+
+ // zIndexOffset adjustment
+ if (oldMarkerData.zIndexOffset !== markerData.zIndexOffset) {
+ marker.setZIndexOffset(markerData.zIndexOffset);
+ }
+
+ var markerLatLng = marker.getLatLng();
+ var isCluster = (isString(markerData.layer) && Helpers.MarkerClusterPlugin.is(layers.overlays[markerData.layer]));
+ // If the marker is in a cluster it has to be removed and added to the layer when the location is changed
+ if (isCluster) {
+ // The focus has changed even by a user click or programatically
+ if (updatedFocus) {
+ // We only have to update the location if it was changed programatically, because it was
+ // changed by a user drag the marker data has already been updated by the internal event
+ // listened by the directive
+ if ((markerData.lat !== oldMarkerData.lat) || (markerData.lng !== oldMarkerData.lng)) {
+ layers.overlays[markerData.layer].removeLayer(marker);
+ marker.setLatLng([markerData.lat, markerData.lng]);
+ layers.overlays[markerData.layer].addLayer(marker);
+ }
+ } else {
+ // The marker has possibly moved. It can be moved by a user drag (marker location and data are equal but old
+ // data is diferent) or programatically (marker location and data are diferent)
+ if ((markerLatLng.lat !== markerData.lat) || (markerLatLng.lng !== markerData.lng)) {
+ // The marker was moved by a user drag
+ layers.overlays[markerData.layer].removeLayer(marker);
+ marker.setLatLng([markerData.lat, markerData.lng]);
+ layers.overlays[markerData.layer].addLayer(marker);
+ } else if ((markerData.lat !== oldMarkerData.lat) || (markerData.lng !== oldMarkerData.lng)) {
+ // The marker was moved programatically
+ layers.overlays[markerData.layer].removeLayer(marker);
+ marker.setLatLng([markerData.lat, markerData.lng]);
+ layers.overlays[markerData.layer].addLayer(marker);
+ } else if (isObject(markerData.icon) && isObject(oldMarkerData.icon) && !angular.equals(markerData.icon, oldMarkerData.icon)) {
+ layers.overlays[markerData.layer].removeLayer(marker);
+ layers.overlays[markerData.layer].addLayer(marker);
+ }
+ }
+ } else if (markerLatLng.lat !== markerData.lat || markerLatLng.lng !== markerData.lng) {
+ marker.setLatLng([markerData.lat, markerData.lng]);
+ }
+ };
+ return {
+ resetMarkerGroup: _resetMarkerGroup,
+
+ resetMarkerGroups: _resetMarkerGroups,
+
+ deleteMarker: _deleteMarker,
+
+ manageOpenPopup: _manageOpenPopup,
+
+ manageOpenLabel: _manageOpenLabel,
+
+ createMarker: function (markerData) {
+ if (!isDefined(markerData) || !geoHlp.validateCoords(markerData)) {
+ $log.error(errorHeader + 'The marker definition is not valid.');
+ return;
+ }
+ var coords = geoHlp.getCoords(markerData);
+
+ if (!isDefined(coords)) {
+ $log.error(errorHeader + 'Unable to get coordinates from markerData.');
+ return;
+ }
+
+ var markerOptions = {
+ icon: createLeafletIcon(markerData.icon),
+ title: isDefined(markerData.title) ? markerData.title : '',
+ draggable: isDefined(markerData.draggable) ? markerData.draggable : false,
+ clickable: isDefined(markerData.clickable) ? markerData.clickable : true,
+ riseOnHover: isDefined(markerData.riseOnHover) ? markerData.riseOnHover : false,
+ zIndexOffset: isDefined(markerData.zIndexOffset) ? markerData.zIndexOffset : 0,
+ iconAngle: isDefined(markerData.iconAngle) ? markerData.iconAngle : 0
+ };
+ // Add any other options not added above to markerOptions
+ for (var markerDatum in markerData) {
+ if (markerData.hasOwnProperty(markerDatum) && !markerOptions.hasOwnProperty(markerDatum)) {
+ markerOptions[markerDatum] = markerData[markerDatum];
+ }
+ }
+
+ var marker = new L.marker(coords, markerOptions);
+
+ if (!isString(markerData.message)) {
+ marker.unbindPopup();
+ }
+
+ return marker;
+ },
+
+ addMarkerToGroup: function (marker, groupName, groupOptions, map) {
+ if (!isString(groupName)) {
+ $log.error(errorHeader + 'The marker group you have specified is invalid.');
+ return;
+ }
+
+ if (!MarkerClusterPlugin.isLoaded()) {
+ $log.error(errorHeader + "The MarkerCluster plugin is not loaded.");
+ return;
+ }
+ if (!isDefined(groups[groupName])) {
+ groups[groupName] = new L.MarkerClusterGroup(groupOptions);
+ map.addLayer(groups[groupName]);
+ }
+ groups[groupName].addLayer(marker);
+ },
+
+ listenMarkerEvents: function (marker, markerData, leafletScope, doWatch, map) {
+ marker.on("popupopen", function (/* event */) {
+ safeApply(leafletScope, function () {
+ if (isDefined(marker._popup) || isDefined(marker._popup._contentNode)) {
+ markerData.focus = true;
+ _manageOpenPopup(marker, markerData, map);//needed since markerData is now a copy
+ }
+ });
+ });
+ marker.on("popupclose", function (/* event */) {
+ safeApply(leafletScope, function () {
+ markerData.focus = false;
+ });
+ });
+ marker.on("add", function (/* event */) {
+ safeApply(leafletScope, function () {
+ if ('label' in markerData)
+ _manageOpenLabel(marker, markerData);
+ });
+ });
+ },
+
+ updateMarker: _updateMarker,
+
+ addMarkerWatcher: function (marker, name, leafletScope, layers, map, isDeepWatch) {
+ var markerWatchPath = Helpers.getObjectArrayPath("markers." + name);
+ isDeepWatch = defaultTo(isDeepWatch, true);
+
+ var clearWatch = leafletScope.$watch(markerWatchPath, function(markerData, oldMarkerData) {
+ if (!isDefined(markerData)) {
+ _deleteMarker(marker, map, layers);
+ clearWatch();
+ return;
+ }
+ _updateMarker(markerData, oldMarkerData, marker, name, leafletScope, layers, map);
+ } , isDeepWatch);
+ },
+ string: _string,
+ log: _log
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletPathsHelpers', ["$rootScope", "$log", "leafletHelpers", function ($rootScope, $log, leafletHelpers) {
+ var isDefined = leafletHelpers.isDefined,
+ isArray = leafletHelpers.isArray,
+ isNumber = leafletHelpers.isNumber,
+ isValidPoint = leafletHelpers.isValidPoint;
+
+ var availableOptions = [
+ // Path options
+ 'stroke', 'weight', 'color', 'opacity',
+ 'fill', 'fillColor', 'fillOpacity',
+ 'dashArray', 'lineCap', 'lineJoin', 'clickable',
+ 'pointerEvents', 'className',
+
+ // Polyline options
+ 'smoothFactor', 'noClip'
+ ];
+ function _convertToLeafletLatLngs(latlngs) {
+ return latlngs.filter(function(latlng) {
+ return isValidPoint(latlng);
+ }).map(function (latlng) {
+ return _convertToLeafletLatLng(latlng);
+ });
+ }
+
+ function _convertToLeafletLatLng(latlng) {
+ if (isArray(latlng)) {
+ return new L.LatLng(latlng[0], latlng[1]);
+ } else {
+ return new L.LatLng(latlng.lat, latlng.lng);
+ }
+ }
+
+ function _convertToLeafletMultiLatLngs(paths) {
+ return paths.map(function(latlngs) {
+ return _convertToLeafletLatLngs(latlngs);
+ });
+ }
+
+ function _getOptions(path, defaults) {
+ var options = {};
+ for (var i = 0; i < availableOptions.length; i++) {
+ var optionName = availableOptions[i];
+
+ if (isDefined(path[optionName])) {
+ options[optionName] = path[optionName];
+ } else if (isDefined(defaults.path[optionName])) {
+ options[optionName] = defaults.path[optionName];
+ }
+ }
+
+ return options;
+ }
+
+ var _updatePathOptions = function (path, data) {
+ var updatedStyle = {};
+ for (var i = 0; i < availableOptions.length; i++) {
+ var optionName = availableOptions[i];
+ if (isDefined(data[optionName])) {
+ updatedStyle[optionName] = data[optionName];
+ }
+ }
+ path.setStyle(data);
+ };
+
+ var _isValidPolyline = function(latlngs) {
+ if (!isArray(latlngs)) {
+ return false;
+ }
+ for (var i = 0; i < latlngs.length; i++) {
+ var point = latlngs[i];
+ if (!isValidPoint(point)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ var pathTypes = {
+ polyline: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+ return _isValidPolyline(latlngs);
+ },
+ createPath: function(options) {
+ return new L.Polyline([], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ },
+ multiPolyline: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+ if (!isArray(latlngs)) {
+ return false;
+ }
+
+ for (var i in latlngs) {
+ var polyline = latlngs[i];
+ if (!_isValidPolyline(polyline)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ createPath: function(options) {
+ return new L.multiPolyline([[[0,0],[1,1]]], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletMultiLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ } ,
+ polygon: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+ return _isValidPolyline(latlngs);
+ },
+ createPath: function(options) {
+ return new L.Polygon([], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ },
+ multiPolygon: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+
+ if (!isArray(latlngs)) {
+ return false;
+ }
+
+ for (var i in latlngs) {
+ var polyline = latlngs[i];
+ if (!_isValidPolyline(polyline)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ createPath: function(options) {
+ return new L.MultiPolygon([[[0,0],[1,1],[0,1]]], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletMultiLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ },
+ rectangle: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+
+ if (!isArray(latlngs) || latlngs.length !== 2) {
+ return false;
+ }
+
+ for (var i in latlngs) {
+ var point = latlngs[i];
+ if (!isValidPoint(point)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ createPath: function(options) {
+ return new L.Rectangle([[0,0],[1,1]], options);
+ },
+ setPath: function(path, data) {
+ path.setBounds(new L.LatLngBounds(_convertToLeafletLatLngs(data.latlngs)));
+ _updatePathOptions(path, data);
+ }
+ },
+ circle: {
+ isValid: function(pathData) {
+ var point= pathData.latlngs;
+ return isValidPoint(point) && isNumber(pathData.radius);
+ },
+ createPath: function(options) {
+ return new L.Circle([0,0], 1, options);
+ },
+ setPath: function(path, data) {
+ path.setLatLng(_convertToLeafletLatLng(data.latlngs));
+ if (isDefined(data.radius)) {
+ path.setRadius(data.radius);
+ }
+ _updatePathOptions(path, data);
+ }
+ },
+ circleMarker: {
+ isValid: function(pathData) {
+ var point= pathData.latlngs;
+ return isValidPoint(point) && isNumber(pathData.radius);
+ },
+ createPath: function(options) {
+ return new L.CircleMarker([0,0], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLng(_convertToLeafletLatLng(data.latlngs));
+ if (isDefined(data.radius)) {
+ path.setRadius(data.radius);
+ }
+ _updatePathOptions(path, data);
+ }
+ }
+ };
+
+ var _getPathData = function(path) {
+ var pathData = {};
+ if (path.latlngs) {
+ pathData.latlngs = path.latlngs;
+ }
+
+ if (path.radius) {
+ pathData.radius = path.radius;
+ }
+
+ return pathData;
+ };
+
+ return {
+ setPathOptions: function(leafletPath, pathType, data) {
+ if(!isDefined(pathType)) {
+ pathType = "polyline";
+ }
+ pathTypes[pathType].setPath(leafletPath, data);
+ },
+ createPath: function(name, path, defaults) {
+ if(!isDefined(path.type)) {
+ path.type = "polyline";
+ }
+ var options = _getOptions(path, defaults);
+ var pathData = _getPathData(path);
+
+ if (!pathTypes[path.type].isValid(pathData)) {
+ $log.error("[AngularJS - Leaflet] Invalid data passed to the " + path.type + " path");
+ return;
+ }
+
+ return pathTypes[path.type].createPath(options);
+ }
+ };
+}]);
+
+angular.module("leaflet-directive")
+.service('leafletWatchHelpers', function (){
+
+ var _maybe = function(scope, watchFunctionName, thingToWatchStr, watchOptions, initCb){
+ //watchOptions.isDeep is/should be ignored in $watchCollection
+ var unWatch = scope[watchFunctionName](thingToWatchStr, function(newValue, oldValue) {
+ initCb(newValue, oldValue);
+ if(!watchOptions.doWatch)
+ unWatch();
+ }, watchOptions.isDeep);
+
+ return unWatch;
+ };
+
+ /*
+ @name: maybeWatch
+ @description: Utility to watch something once or forever.
+ @returns unWatch function
+ @param watchOptions - see markersWatchOptions and or derrivatives. This object is used
+ to set watching to once and its watch depth.
+ */
+ var _maybeWatch = function(scope, thingToWatchStr, watchOptions, initCb){
+ return _maybe(scope, '$watch', thingToWatchStr, watchOptions, initCb);
+ };
+
+ /*
+ @name: _maybeWatchCollection
+ @description: Utility to watch something once or forever.
+ @returns unWatch function
+ @param watchOptions - see markersWatchOptions and or derrivatives. This object is used
+ to set watching to once and its watch depth.
+ */
+ var _maybeWatchCollection = function(scope, thingToWatchStr, watchOptions, initCb){
+ return _maybe(scope, '$watchCollection', thingToWatchStr, watchOptions, initCb);
+ };
+
+ return {
+ maybeWatch: _maybeWatch,
+ maybeWatchCollection: _maybeWatchCollection
+ };
+});
+
+angular.module("leaflet-directive").factory('nominatimService', ["$q", "$http", "leafletHelpers", "leafletMapDefaults", function ($q, $http, leafletHelpers, leafletMapDefaults) {
+ var isDefined = leafletHelpers.isDefined;
+
+ return {
+ query: function(address, mapId) {
+ var defaults = leafletMapDefaults.getDefaults(mapId);
+ var url = defaults.nominatim.server;
+ var df = $q.defer();
+
+ $http.get(url, { params: { format: 'json', limit: 1, q: address } }).success(function(data) {
+ if (data.length > 0 && isDefined(data[0].boundingbox)) {
+ df.resolve(data[0]);
+ } else {
+ df.reject('[Nominatim] Invalid address');
+ }
+ });
+
+ return df.promise;
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('bounds', ["$log", "$timeout", "$http", "leafletHelpers", "nominatimService", "leafletBoundsHelpers", function ($log, $timeout, $http, leafletHelpers, nominatimService, leafletBoundsHelpers) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: [ 'leaflet' ],
+
+ link: function(scope, element, attrs, controller) {
+ var isDefined = leafletHelpers.isDefined;
+ var createLeafletBounds = leafletBoundsHelpers.createLeafletBounds;
+ var leafletScope = controller[0].getLeafletScope();
+ var mapController = controller[0];
+ var errorHeader = leafletHelpers.errorHeader + ' [Bounds] ';
+
+ var emptyBounds = function(bounds) {
+ return (bounds._southWest.lat === 0 && bounds._southWest.lng === 0 &&
+ bounds._northEast.lat === 0 && bounds._northEast.lng === 0);
+ };
+
+ mapController.getMap().then(function (map) {
+ leafletScope.$on('boundsChanged', function (event) {
+ var scope = event.currentScope;
+ var bounds = map.getBounds();
+
+ if (emptyBounds(bounds) || scope.settingBoundsFromScope) {
+ return;
+ }
+ scope.settingBoundsFromLeaflet = true;
+ var newScopeBounds = {
+ northEast: {
+ lat: bounds._northEast.lat,
+ lng: bounds._northEast.lng
+ },
+ southWest: {
+ lat: bounds._southWest.lat,
+ lng: bounds._southWest.lng
+ },
+ options: bounds.options
+ };
+ if (!angular.equals(scope.bounds, newScopeBounds)) {
+ scope.bounds = newScopeBounds;
+ }
+ $timeout( function() {
+ scope.settingBoundsFromLeaflet = false;
+ });
+ });
+
+ var lastNominatimQuery;
+ leafletScope.$watch('bounds', function (bounds) {
+ if (scope.settingBoundsFromLeaflet)
+ return;
+ if (isDefined(bounds.address) && bounds.address !== lastNominatimQuery) {
+ scope.settingBoundsFromScope = true;
+ nominatimService.query(bounds.address, attrs.id).then(function(data) {
+ var b = data.boundingbox;
+ var newBounds = [ [ b[0], b[2]], [ b[1], b[3]] ];
+ map.fitBounds(newBounds);
+ }, function(errMsg) {
+ $log.error(errorHeader + ' ' + errMsg + '.');
+ });
+ lastNominatimQuery = bounds.address;
+ $timeout( function() {
+ scope.settingBoundsFromScope = false;
+ });
+ return;
+ }
+
+ var leafletBounds = createLeafletBounds(bounds);
+ if (leafletBounds && !map.getBounds().equals(leafletBounds)) {
+ scope.settingBoundsFromScope = true;
+ map.fitBounds(leafletBounds, bounds.options);
+ $timeout( function() {
+ scope.settingBoundsFromScope = false;
+ });
+ }
+ }, true);
+ });
+ }
+ };
+}]);
+
+var centerDirectiveTypes = ['center', 'lfCenter'],
+ centerDirectives = {};
+
+centerDirectiveTypes.forEach(function(directiveName) {
+ centerDirectives[directiveName] = [ '$log', '$q', '$location', '$timeout', 'leafletMapDefaults', 'leafletHelpers',
+ 'leafletBoundsHelpers', 'leafletMapEvents',
+ function($log, $q, $location, $timeout, leafletMapDefaults, leafletHelpers,
+ leafletBoundsHelpers, leafletMapEvents) {
+
+ var isDefined = leafletHelpers.isDefined,
+ isNumber = leafletHelpers.isNumber,
+ isSameCenterOnMap = leafletHelpers.isSameCenterOnMap,
+ safeApply = leafletHelpers.safeApply,
+ isValidCenter = leafletHelpers.isValidCenter,
+ isValidBounds = leafletBoundsHelpers.isValidBounds,
+ isUndefinedOrEmpty = leafletHelpers.isUndefinedOrEmpty,
+ errorHeader = leafletHelpers.errorHeader;
+
+ var shouldInitializeMapWithBounds = function(bounds, center) {
+ return isDefined(bounds) && isValidBounds(bounds) && isUndefinedOrEmpty(center);
+ };
+
+ var _leafletCenter;
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+ controller: function() {
+ _leafletCenter = $q.defer();
+ this.getCenter = function() {
+ return _leafletCenter.promise;
+ };
+ },
+ link: function(scope, element, attrs, controller) {
+ var leafletScope = controller.getLeafletScope(),
+ centerModel = leafletScope[directiveName];
+
+ controller.getMap().then(function(map) {
+ var defaults = leafletMapDefaults.getDefaults(attrs.id);
+
+ if (attrs[directiveName].search("-") !== -1) {
+ $log.error(errorHeader + ' The "center" variable can\'t use a "-" on its key name: "' + attrs[directiveName] + '".');
+ map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ return;
+ } else if (shouldInitializeMapWithBounds(leafletScope.bounds, centerModel)) {
+ map.fitBounds(leafletBoundsHelpers.createLeafletBounds(leafletScope.bounds), leafletScope.bounds.options);
+ centerModel = map.getCenter();
+ safeApply(leafletScope, function(scope) {
+ angular.extend(scope[directiveName], {
+ lat: map.getCenter().lat,
+ lng: map.getCenter().lng,
+ zoom: map.getZoom(),
+ autoDiscover: false
+ });
+ });
+ safeApply(leafletScope, function(scope) {
+ var mapBounds = map.getBounds();
+ scope.bounds = {
+ northEast: {
+ lat: mapBounds._northEast.lat,
+ lng: mapBounds._northEast.lng
+ },
+ southWest: {
+ lat: mapBounds._southWest.lat,
+ lng: mapBounds._southWest.lng
+ }
+ };
+ });
+ } else if (!isDefined(centerModel)) {
+ $log.error(errorHeader + ' The "center" property is not defined in the main scope');
+ map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ return;
+ } else if (!(isDefined(centerModel.lat) && isDefined(centerModel.lng)) && !isDefined(centerModel.autoDiscover)) {
+ angular.copy(defaults.center, centerModel);
+ }
+
+ var urlCenterHash, mapReady;
+ if (attrs.urlHashCenter === "yes") {
+ var extractCenterFromUrl = function() {
+ var search = $location.search();
+ var centerParam;
+ if (isDefined(search.c)) {
+ var cParam = search.c.split(":");
+ if (cParam.length === 3) {
+ centerParam = {
+ lat: parseFloat(cParam[0]),
+ lng: parseFloat(cParam[1]),
+ zoom: parseInt(cParam[2], 10)
+ };
+ }
+ }
+ return centerParam;
+ };
+ urlCenterHash = extractCenterFromUrl();
+
+ leafletScope.$on('$locationChangeSuccess', function(event) {
+ var scope = event.currentScope;
+ //$log.debug("updated location...");
+ var urlCenter = extractCenterFromUrl();
+ if (isDefined(urlCenter) && !isSameCenterOnMap(urlCenter, map)) {
+ //$log.debug("updating center model...", urlCenter);
+ angular.extend(scope[directiveName], {
+ lat: urlCenter.lat,
+ lng: urlCenter.lng,
+ zoom: urlCenter.zoom
+ });
+ }
+ });
+ }
+
+ leafletScope.$watch(directiveName, function(center) {
+ if (leafletScope.settingCenterFromLeaflet)
+ return;
+ //$log.debug("updated center model...");
+ // The center from the URL has priority
+ if (isDefined(urlCenterHash)) {
+ angular.copy(urlCenterHash, center);
+ urlCenterHash = undefined;
+ }
+
+ if (!isValidCenter(center) && center.autoDiscover !== true) {
+ $log.warn(errorHeader + " invalid 'center'");
+ //map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ return;
+ }
+
+ if (center.autoDiscover === true) {
+ if (!isNumber(center.zoom)) {
+ map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ }
+ if (isNumber(center.zoom) && center.zoom > defaults.center.zoom) {
+ map.locate({
+ setView: true,
+ maxZoom: center.zoom
+ });
+ } else if (isDefined(defaults.maxZoom)) {
+ map.locate({
+ setView: true,
+ maxZoom: defaults.maxZoom
+ });
+ } else {
+ map.locate({
+ setView: true
+ });
+ }
+ return;
+ }
+
+ if (mapReady && isSameCenterOnMap(center, map)) {
+ //$log.debug("no need to update map again.");
+ return;
+ }
+
+ //$log.debug("updating map center...", center);
+ leafletScope.settingCenterFromScope = true;
+ map.setView([center.lat, center.lng], center.zoom);
+ leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map);
+ $timeout(function() {
+ leafletScope.settingCenterFromScope = false;
+ //$log.debug("allow center scope updates");
+ });
+ }, true);
+
+ map.whenReady(function() {
+ mapReady = true;
+ });
+
+ map.on('moveend', function( /* event */ ) {
+ // Resolve the center after the first map position
+ _leafletCenter.resolve();
+ leafletMapEvents.notifyCenterUrlHashChanged(leafletScope, map, attrs, $location.search());
+ //$log.debug("updated center on map...");
+ if (isSameCenterOnMap(centerModel, map) || leafletScope.settingCenterFromScope) {
+ //$log.debug("same center in model, no need to update again.");
+ return;
+ }
+ leafletScope.settingCenterFromLeaflet = true;
+ safeApply(leafletScope, function(scope) {
+ if (!leafletScope.settingCenterFromScope) {
+ //$log.debug("updating center model...", map.getCenter(), map.getZoom());
+ angular.extend(scope[directiveName], {
+ lat: map.getCenter().lat,
+ lng: map.getCenter().lng,
+ zoom: map.getZoom(),
+ autoDiscover: false
+ });
+ }
+ leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map);
+ $timeout(function() {
+ leafletScope.settingCenterFromLeaflet = false;
+ });
+ });
+ });
+
+ if (centerModel.autoDiscover === true) {
+ map.on('locationerror', function() {
+ $log.warn(errorHeader + " The Geolocation API is unauthorized on this page.");
+ if (isValidCenter(centerModel)) {
+ map.setView([centerModel.lat, centerModel.lng], centerModel.zoom);
+ leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map);
+ } else {
+ map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map);
+ }
+ });
+ }
+ });
+ }
+ };
+ }
+ ];
+});
+
+centerDirectiveTypes.forEach(function(dirType){
+ angular.module("leaflet-directive").directive(dirType, centerDirectives[dirType]);
+});
+
+angular.module("leaflet-directive").directive('controls', ["$log", "leafletHelpers", "leafletControlHelpers", function ($log, leafletHelpers, leafletControlHelpers) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: '?^leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ if(!controller) {
+ return;
+ }
+
+ var createControl = leafletControlHelpers.createControl;
+ var isValidControlType = leafletControlHelpers.isValidControlType;
+ var leafletScope = controller.getLeafletScope();
+ var isDefined = leafletHelpers.isDefined;
+ var isArray = leafletHelpers.isArray;
+ var leafletControls = {};
+ var errorHeader = leafletHelpers.errorHeader + ' [Controls] ';
+
+ controller.getMap().then(function(map) {
+
+ leafletScope.$watchCollection('controls', function(newControls) {
+
+ // Delete controls from the array
+ for (var name in leafletControls) {
+ if (!isDefined(newControls[name])) {
+ if (map.hasControl(leafletControls[name])) {
+ map.removeControl(leafletControls[name]);
+ }
+ delete leafletControls[name];
+ }
+ }
+
+ for (var newName in newControls) {
+ var control;
+
+ var controlType = isDefined(newControls[newName].type) ? newControls[newName].type : newName;
+
+ if (!isValidControlType(controlType)) {
+ $log.error(errorHeader + ' Invalid control type: ' + controlType + '.');
+ return;
+ }
+
+ if (controlType !== 'custom') {
+ control = createControl(controlType, newControls[newName]);
+ map.addControl(control);
+ leafletControls[newName] = control;
+ } else {
+ var customControlValue = newControls[newName];
+ if (isArray(customControlValue)) {
+ for (var i in customControlValue) {
+ var customControl = customControlValue[i];
+ map.addControl(customControl);
+ leafletControls[newName] = !isDefined(leafletControls[newName]) ? [customControl] : leafletControls[newName].concat([customControl]);
+ }
+ } else {
+ map.addControl(customControlValue);
+ leafletControls[newName] = customControlValue;
+ }
+ }
+ }
+
+ });
+
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive("decorations", ["$log", "leafletHelpers", function($log, leafletHelpers) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var leafletScope = controller.getLeafletScope(),
+ PolylineDecoratorPlugin = leafletHelpers.PolylineDecoratorPlugin,
+ isDefined = leafletHelpers.isDefined,
+ leafletDecorations = {};
+
+ /* Creates an "empty" decoration with a set of coordinates, but no pattern. */
+ function createDecoration(options) {
+ if (isDefined(options) && isDefined(options.coordinates)) {
+ if (!PolylineDecoratorPlugin.isLoaded()) {
+ $log.error('[AngularJS - Leaflet] The PolylineDecorator Plugin is not loaded.');
+ }
+ }
+
+ return L.polylineDecorator(options.coordinates);
+ }
+
+ /* Updates the path and the patterns for the provided decoration, and returns the decoration. */
+ function setDecorationOptions(decoration, options) {
+ if (isDefined(decoration) && isDefined(options)) {
+ if (isDefined(options.coordinates) && isDefined(options.patterns)) {
+ decoration.setPaths(options.coordinates);
+ decoration.setPatterns(options.patterns);
+ return decoration;
+ }
+ }
+ }
+
+ controller.getMap().then(function(map) {
+ leafletScope.$watch("decorations", function(newDecorations) {
+ for (var name in leafletDecorations) {
+ if (!isDefined(newDecorations[name]) || !angular.equals(newDecorations[name], leafletDecorations)) {
+ map.removeLayer(leafletDecorations[name]);
+ delete leafletDecorations[name];
+ }
+ }
+
+ for (var newName in newDecorations) {
+ var decorationData = newDecorations[newName],
+ newDecoration = createDecoration(decorationData);
+
+ if (isDefined(newDecoration)) {
+ leafletDecorations[newName] = newDecoration;
+ map.addLayer(newDecoration);
+ setDecorationOptions(newDecoration, decorationData);
+ }
+ }
+ }, true);
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('eventBroadcast', ["$log", "$rootScope", "leafletHelpers", "leafletMapEvents", "leafletIterators", function ($log, $rootScope, leafletHelpers, leafletMapEvents, leafletIterators) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var isObject = leafletHelpers.isObject,
+ isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ eventBroadcast = leafletScope.eventBroadcast,
+ availableMapEvents = leafletMapEvents.getAvailableMapEvents(),
+ addEvents = leafletMapEvents.addEvents;
+
+ controller.getMap().then(function(map) {
+
+ var mapEvents = [],
+ logic = "broadcast";
+
+ // We have a possible valid object
+ if (!isDefined(eventBroadcast.map)) {
+ // We do not have events enable/disable do we do nothing (all enabled by default)
+ mapEvents = availableMapEvents;
+ } else if (!isObject(eventBroadcast.map)) {
+ // Not a valid object
+ $log.warn("[AngularJS - Leaflet] event-broadcast.map must be an object check your model.");
+ } else {
+ // We have a possible valid map object
+ // Event propadation logic
+ if (eventBroadcast.map.logic !== "emit" && eventBroadcast.map.logic !== "broadcast") {
+ // This is an error
+ $log.warn("[AngularJS - Leaflet] Available event propagation logic are: 'emit' or 'broadcast'.");
+ } else {
+ logic = eventBroadcast.map.logic;
+ }
+
+ if (!(isObject(eventBroadcast.map.enable) && eventBroadcast.map.enable.length >= 0)) {
+ $log.warn("[AngularJS - Leaflet] event-broadcast.map.enable must be an object check your model.");
+ } else {
+ // Enable events
+ leafletIterators.each(eventBroadcast.map.enable, function(eventName) {
+ // Do we have already the event enabled?
+ if (mapEvents.indexOf(eventName) === -1 && availableMapEvents.indexOf(eventName) !== -1) {
+ mapEvents.push(eventName);
+ }
+ });
+ }
+
+ }
+ // as long as the map is removed in the root leaflet directive we
+ // do not need ot clean up the events as leaflet does it itself
+ addEvents(map, mapEvents, "eventName", leafletScope, logic);
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive")
+.directive('geojson', ["$log", "$rootScope", "leafletData", "leafletHelpers", "leafletWatchHelpers", "leafletDirectiveControlsHelpers", "leafletIterators", "leafletGeoJsonEvents", function ($log, $rootScope, leafletData, leafletHelpers,
+ leafletWatchHelpers, leafletDirectiveControlsHelpers,leafletIterators, leafletGeoJsonEvents) {
+ var _maybeWatch = leafletWatchHelpers.maybeWatch,
+ _watchOptions = leafletHelpers.watchOptions,
+ _extendDirectiveControls = leafletDirectiveControlsHelpers.extend,
+ hlp = leafletHelpers,
+ $it = leafletIterators;
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ leafletGeoJSON = {},
+ _hasSetLeafletData = false;
+
+ controller.getMap().then(function(map) {
+ var watchOptions = leafletScope.geojsonWatchOptions || _watchOptions;
+
+ var _hookUpEvents = function(geojson, maybeName){
+ var onEachFeature;
+
+ if (angular.isFunction(geojson.onEachFeature)) {
+ onEachFeature = geojson.onEachFeature;
+ } else {
+ onEachFeature = function(feature, layer) {
+ if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(feature.properties.description)) {
+ layer.bindLabel(feature.properties.description);
+ }
+
+ leafletGeoJsonEvents.bindEvents(attrs.id, layer, null, feature,
+ leafletScope, maybeName,
+ {resetStyleOnMouseout: geojson.resetStyleOnMouseout,
+ mapId: attrs.id});
+ };
+ }
+ return onEachFeature;
+ };
+
+ var isNested = (hlp.isDefined(attrs.geojsonNested) &&
+ hlp.isTruthy(attrs.geojsonNested));
+
+ var _clean = function(){
+ if(!leafletGeoJSON)
+ return;
+ var _remove = function(lObject) {
+ if (isDefined(lObject) && map.hasLayer(lObject)) {
+ map.removeLayer(lObject);
+ }
+ };
+ if(isNested) {
+ $it.each(leafletGeoJSON, function(lObject) {
+ _remove(lObject);
+ });
+ return;
+ }
+ _remove(leafletGeoJSON);
+ };
+
+ var _addGeojson = function(model, maybeName){
+ var geojson = angular.copy(model);
+ if (!(isDefined(geojson) && isDefined(geojson.data))) {
+ return;
+ }
+ var onEachFeature = _hookUpEvents(geojson, maybeName);
+
+ if (!isDefined(geojson.options)) {
+ //right here is why we use a clone / copy (we modify and thus)
+ //would kick of a watcher.. we need to be more careful everywhere
+ //for stuff like this
+ geojson.options = {
+ style: geojson.style,
+ filter: geojson.filter,
+ onEachFeature: onEachFeature,
+ pointToLayer: geojson.pointToLayer
+ };
+ }
+
+ var lObject = L.geoJson(geojson.data, geojson.options);
+
+ if(maybeName && hlp.isString(maybeName)){
+ leafletGeoJSON[maybeName] = lObject;
+ }
+ else{
+ leafletGeoJSON = lObject;
+ }
+
+ lObject.addTo(map);
+
+ if(!_hasSetLeafletData){//only do this once and play with the same ref forever
+ _hasSetLeafletData = true;
+ leafletData.setGeoJSON(leafletGeoJSON, attrs.id);
+ }
+ };
+
+ var _create = function(model){
+ _clean();
+ if(isNested) {
+ if(!model || !Object.keys(model).length)
+ return;
+ $it.each(model, function(m, name) {
+ //name could be layerName and or groupName
+ //for now it is not tied to a layer
+ _addGeojson(m,name);
+ });
+ return;
+ }
+ _addGeojson(model);
+ };
+
+ _extendDirectiveControls(attrs.id, 'geojson', _create, _clean);
+
+ _maybeWatch(leafletScope,'geojson', watchOptions, function(geojson){
+ _create(geojson);
+ });
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('layercontrol', ["$filter", "$log", "leafletData", "leafletHelpers", function ($filter, $log, leafletData, leafletHelpers) {
+
+ return {
+ restrict: "E",
+ scope: {
+ icons: '=?',
+ autoHideOpacity: '=?', // Hide other opacity controls when one is activated.
+ showGroups: '=?', // Hide other opacity controls when one is activated.
+ title: '@',
+ baseTitle: '@',
+ overlaysTitle: '@'
+ },
+ replace: true,
+ transclude: false,
+ require: '^leaflet',
+ controller: ["$scope", "$element", "$sce", function ($scope, $element, $sce) {
+ $log.debug('[Angular Directive - Layers] layers', $scope, $element);
+ var safeApply = leafletHelpers.safeApply,
+ isDefined = leafletHelpers.isDefined;
+ angular.extend($scope, {
+ baselayer: '',
+ oldGroup: '',
+ layerProperties: {},
+ groupProperties: {},
+ rangeIsSupported: leafletHelpers.rangeIsSupported(),
+ changeBaseLayer: function(key, e) {
+ leafletHelpers.safeApply($scope, function(scp) {
+ scp.baselayer = key;
+ leafletData.getMap().then(function(map) {
+ leafletData.getLayers().then(function(leafletLayers) {
+ if(map.hasLayer(leafletLayers.baselayers[key])) {
+ return;
+ }
+ for(var i in scp.layers.baselayers) {
+ scp.layers.baselayers[i].icon = scp.icons.unradio;
+ if(map.hasLayer(leafletLayers.baselayers[i])) {
+ map.removeLayer(leafletLayers.baselayers[i]);
+ }
+ }
+ map.addLayer(leafletLayers.baselayers[key]);
+ scp.layers.baselayers[key].icon = $scope.icons.radio;
+ });
+ });
+ });
+ e.preventDefault();
+ },
+ moveLayer: function(ly, newIndex, e) {
+ var delta = Object.keys($scope.layers.baselayers).length;
+ if(newIndex >= (1+delta) && newIndex <= ($scope.overlaysArray.length+delta)) {
+ var oldLy;
+ for(var key in $scope.layers.overlays) {
+ if($scope.layers.overlays[key].index === newIndex) {
+ oldLy = $scope.layers.overlays[key];
+ break;
+ }
+ }
+ if(oldLy) {
+ safeApply($scope, function() {
+ oldLy.index = ly.index;
+ ly.index = newIndex;
+ });
+ }
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ },
+ initIndex: function(layer, idx) {
+ var delta = Object.keys($scope.layers.baselayers).length;
+ layer.index = isDefined(layer.index)? layer.index:idx+delta+1;
+ },
+ initGroup: function(groupName) {
+ $scope.groupProperties[groupName] = $scope.groupProperties[groupName]? $scope.groupProperties[groupName]:{};
+ },
+ toggleOpacity: function(e, layer) {
+ if(layer.visible) {
+ if($scope.autoHideOpacity && !$scope.layerProperties[layer.name].opacityControl) {
+ for(var k in $scope.layerProperties) {
+ $scope.layerProperties[k].opacityControl = false;
+ }
+ }
+ $scope.layerProperties[layer.name].opacityControl = !$scope.layerProperties[layer.name].opacityControl;
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ },
+ toggleLegend: function(layer) {
+ $scope.layerProperties[layer.name].showLegend = !$scope.layerProperties[layer.name].showLegend;
+ },
+ showLegend: function(layer) {
+ return layer.legend && $scope.layerProperties[layer.name].showLegend;
+ },
+ unsafeHTML: function(html) {
+ return $sce.trustAsHtml(html);
+ },
+ getOpacityIcon: function(layer) {
+ return layer.visible && $scope.layerProperties[layer.name].opacityControl? $scope.icons.close:$scope.icons.open;
+ },
+ getGroupIcon: function(group) {
+ return group.visible? $scope.icons.check:$scope.icons.uncheck;
+ },
+ changeOpacity: function(layer) {
+ var op = $scope.layerProperties[layer.name].opacity;
+ leafletData.getMap().then(function(map) {
+ leafletData.getLayers().then(function(leafletLayers) {
+ var ly;
+ for(var k in $scope.layers.overlays) {
+ if($scope.layers.overlays[k] === layer) {
+ ly = leafletLayers.overlays[k];
+ break;
+ }
+ }
+
+ if(map.hasLayer(ly)) {
+ if(ly.setOpacity) {
+ ly.setOpacity(op/100);
+ }
+ if(ly.getLayers && ly.eachLayer) {
+ ly.eachLayer(function(lay) {
+ if(lay.setOpacity) {
+ lay.setOpacity(op/100);
+ }
+ });
+ }
+ }
+ });
+ });
+ },
+ changeGroupVisibility: function(groupName) {
+ if(!isDefined($scope.groupProperties[groupName])) {
+ return;
+ }
+ var visible = $scope.groupProperties[groupName].visible;
+ for(var k in $scope.layers.overlays) {
+ var layer = $scope.layers.overlays[k];
+ if(layer.group === groupName) {
+ layer.visible = visible;
+ }
+ }
+ }
+ });
+
+ var div = $element.get(0);
+ if (!L.Browser.touch) {
+ L.DomEvent.disableClickPropagation(div);
+ L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation);
+ } else {
+ L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation);
+ }
+ }],
+ template:
+ '' +
+ '
{{ title }} ' +
+ '
' +
+ '
{{ baseTitle }} ' +
+ '
' +
+ '
' +
+ ' ' +
+ ' ' +
+ '{{layer.name}}
' +
+ ' ' +
+ '
' +
+ '
' +
+ '
' +
+ '
{{ overlaysTitle }} ' +
+ '
' +
+ '
' +
+ '
' +
+ ' ' +
+ ' ' +
+ '{{ layer.group }}
' +
+ ' '+
+ '
' +
+ ' ' +
+ ' ' +
+ '{{layer.name}}
' +
+ ' '+
+ '
' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '
' +
+ '
' +
+ '
' +
+ '0 ' +
+ '100 ' +
+ ' ' +
+ '
Range is not supported in this browser ' +
+ '' +
+ '
' +
+ '
' +
+ '
' +
+ '
',
+ link: function(scope, element, attrs, controller) {
+ var isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ layers = leafletScope.layers;
+
+ scope.$watch('icons', function() {
+ var defaultIcons = {
+ uncheck: 'fa fa-square-o',
+ check: 'fa fa-check-square-o',
+ radio: 'fa fa-dot-circle-o',
+ unradio: 'fa fa-circle-o',
+ up: 'fa fa-angle-up',
+ down: 'fa fa-angle-down',
+ open: 'fa fa-angle-double-down',
+ close: 'fa fa-angle-double-up',
+ toggleLegend: 'fa fa-pencil-square-o'
+ };
+ if(isDefined(scope.icons)) {
+ angular.extend(defaultIcons, scope.icons);
+ angular.extend(scope.icons, defaultIcons);
+ } else {
+ scope.icons = defaultIcons;
+ }
+ });
+
+ // Setting layer stack order.
+ attrs.order = (isDefined(attrs.order) && (attrs.order === 'normal' || attrs.order === 'reverse'))? attrs.order:'normal';
+ scope.order = attrs.order === 'normal';
+ scope.orderNumber = attrs.order === 'normal'? -1:1;
+
+ scope.layers = layers;
+ controller.getMap().then(function(map) {
+ leafletScope.$watch('layers.baselayers', function(newBaseLayers) {
+ var baselayersArray = {};
+ leafletData.getLayers().then(function(leafletLayers) {
+ var key;
+ for(key in newBaseLayers) {
+ var layer = newBaseLayers[key];
+ layer.icon = scope.icons[map.hasLayer(leafletLayers.baselayers[key])? 'radio':'unradio'];
+ baselayersArray[key] = layer;
+ }
+ scope.baselayersArray = baselayersArray;
+ });
+ });
+
+ leafletScope.$watch('layers.overlays', function(newOverlayLayers) {
+ var overlaysArray = [];
+ var groupVisibleCount = {};
+ leafletData.getLayers().then(function(leafletLayers) {
+ var key;
+ for(key in newOverlayLayers) {
+ var layer = newOverlayLayers[key];
+ layer.icon = scope.icons[(layer.visible? 'check':'uncheck')];
+ overlaysArray.push(layer);
+ if(!isDefined(scope.layerProperties[layer.name])) {
+ scope.layerProperties[layer.name] = {
+ opacity: isDefined(layer.layerOptions.opacity)? layer.layerOptions.opacity*100:100,
+ opacityControl: false,
+ showLegend: true
+ };
+ }
+ if(isDefined(layer.group)) {
+ if(!isDefined(scope.groupProperties[layer.group])) {
+ scope.groupProperties[layer.group] = {
+ visible: false
+ };
+ }
+ groupVisibleCount[layer.group] = isDefined(groupVisibleCount[layer.group])? groupVisibleCount[layer.group]:{
+ count: 0,
+ visibles: 0
+ };
+ groupVisibleCount[layer.group].count++;
+ if(layer.visible) {
+ groupVisibleCount[layer.group].visibles++;
+ }
+ }
+ if(isDefined(layer.index) && leafletLayers.overlays[key].setZIndex) {
+ leafletLayers.overlays[key].setZIndex(newOverlayLayers[key].index);
+ }
+ }
+
+ for(key in groupVisibleCount) {
+ scope.groupProperties[key].visible = groupVisibleCount[key].visibles === groupVisibleCount[key].count;
+ }
+ scope.overlaysArray = overlaysArray;
+ });
+ }, true);
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('layers', ["$log", "$q", "leafletData", "leafletHelpers", "leafletLayerHelpers", "leafletControlHelpers", function ($log, $q, leafletData, leafletHelpers, leafletLayerHelpers, leafletControlHelpers) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+ controller: ["$scope", function ($scope) {
+ $scope._leafletLayers = $q.defer();
+ this.getLayers = function () {
+ return $scope._leafletLayers.promise;
+ };
+ }],
+ link: function(scope, element, attrs, controller){
+ var isDefined = leafletHelpers.isDefined,
+ leafletLayers = {},
+ leafletScope = controller.getLeafletScope(),
+ layers = leafletScope.layers,
+ createLayer = leafletLayerHelpers.createLayer,
+ safeAddLayer = leafletLayerHelpers.safeAddLayer,
+ safeRemoveLayer = leafletLayerHelpers.safeRemoveLayer,
+ updateLayersControl = leafletControlHelpers.updateLayersControl,
+ isLayersControlVisible = false;
+
+ controller.getMap().then(function(map) {
+
+ // We have baselayers to add to the map
+ scope._leafletLayers.resolve(leafletLayers);
+ leafletData.setLayers(leafletLayers, attrs.id);
+
+ leafletLayers.baselayers = {};
+ leafletLayers.overlays = {};
+
+ var mapId = attrs.id;
+
+ // Setup all baselayers definitions
+ var oneVisibleLayer = false;
+ for (var layerName in layers.baselayers) {
+ var newBaseLayer = createLayer(layers.baselayers[layerName]);
+ if (!isDefined(newBaseLayer)) {
+ delete layers.baselayers[layerName];
+ continue;
+ }
+ leafletLayers.baselayers[layerName] = newBaseLayer;
+ // Only add the visible layer to the map, layer control manages the addition to the map
+ // of layers in its control
+ if (layers.baselayers[layerName].top === true) {
+ safeAddLayer(map, leafletLayers.baselayers[layerName]);
+ oneVisibleLayer = true;
+ }
+ }
+
+ // If there is no visible layer add first to the map
+ if (!oneVisibleLayer && Object.keys(leafletLayers.baselayers).length > 0) {
+ safeAddLayer(map, leafletLayers.baselayers[Object.keys(layers.baselayers)[0]]);
+ }
+
+ // Setup the Overlays
+ for (layerName in layers.overlays) {
+ if(layers.overlays[layerName].type === 'cartodb') {
+
+ }
+ var newOverlayLayer = createLayer(layers.overlays[layerName]);
+ if (!isDefined(newOverlayLayer)) {
+ delete layers.overlays[layerName];
+ continue;
+ }
+ leafletLayers.overlays[layerName] = newOverlayLayer;
+ // Only add the visible overlays to the map
+ if (layers.overlays[layerName].visible === true) {
+ safeAddLayer(map, leafletLayers.overlays[layerName]);
+ }
+ }
+
+ // Watch for the base layers
+ leafletScope.$watch('layers.baselayers', function(newBaseLayers, oldBaseLayers) {
+ if(angular.equals(newBaseLayers, oldBaseLayers)) {
+ isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, newBaseLayers, layers.overlays, leafletLayers);
+ return true;
+ }
+ // Delete layers from the array
+ for (var name in leafletLayers.baselayers) {
+ if (!isDefined(newBaseLayers[name]) || newBaseLayers[name].doRefresh) {
+ // Remove from the map if it's on it
+ if (map.hasLayer(leafletLayers.baselayers[name])) {
+ map.removeLayer(leafletLayers.baselayers[name]);
+ }
+ delete leafletLayers.baselayers[name];
+
+ if (newBaseLayers[name] && newBaseLayers[name].doRefresh) {
+ newBaseLayers[name].doRefresh = false;
+ }
+ }
+ }
+ // add new layers
+ for (var newName in newBaseLayers) {
+ if (!isDefined(leafletLayers.baselayers[newName])) {
+ var testBaseLayer = createLayer(newBaseLayers[newName]);
+ if (isDefined(testBaseLayer)) {
+ leafletLayers.baselayers[newName] = testBaseLayer;
+ // Only add the visible layer to the map
+ if (newBaseLayers[newName].top === true) {
+ safeAddLayer(map, leafletLayers.baselayers[newName]);
+ }
+ }
+ } else {
+ if (newBaseLayers[newName].top === true && !map.hasLayer(leafletLayers.baselayers[newName])) {
+ safeAddLayer(map, leafletLayers.baselayers[newName]);
+ } else if (newBaseLayers[newName].top === false && map.hasLayer(leafletLayers.baselayers[newName])) {
+ map.removeLayer(leafletLayers.baselayers[newName]);
+ }
+ }
+ }
+
+ //we have layers, so we need to make, at least, one active
+ var found = false;
+ // search for an active layer
+ for (var key in leafletLayers.baselayers) {
+ if (map.hasLayer(leafletLayers.baselayers[key])) {
+ found = true;
+ break;
+ }
+ }
+ // If there is no active layer make one active
+ if (!found && Object.keys(leafletLayers.baselayers).length > 0) {
+ safeAddLayer(map, leafletLayers.baselayers[Object.keys(leafletLayers.baselayers)[0]]);
+ }
+
+ // Only show the layers switch selector control if we have more than one baselayer + overlay
+ isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, newBaseLayers, layers.overlays, leafletLayers);
+ }, true);
+
+ // Watch for the overlay layers
+ leafletScope.$watch('layers.overlays', function(newOverlayLayers, oldOverlayLayers) {
+ if(angular.equals(newOverlayLayers, oldOverlayLayers)) {
+ isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, layers.baselayers, newOverlayLayers, leafletLayers);
+ return true;
+ }
+
+ // Delete layers from the array
+ for (var name in leafletLayers.overlays) {
+ if (!isDefined(newOverlayLayers[name]) || newOverlayLayers[name].doRefresh) {
+ // Remove from the map if it's on it
+ if (map.hasLayer(leafletLayers.overlays[name])) {
+ // Safe remove when ArcGIS layers is loading.
+ var options = isDefined(newOverlayLayers[name])?
+ newOverlayLayers[name].layerOptions:null;
+ safeRemoveLayer(map, leafletLayers.overlays[name], options);
+ }
+ // TODO: Depending on the layer type we will have to delete what's included on it
+ delete leafletLayers.overlays[name];
+
+ if (newOverlayLayers[name] && newOverlayLayers[name].doRefresh) {
+ newOverlayLayers[name].doRefresh = false;
+ }
+ }
+ }
+
+ // add new overlays
+ for (var newName in newOverlayLayers) {
+ if (!isDefined(leafletLayers.overlays[newName])) {
+ var testOverlayLayer = createLayer(newOverlayLayers[newName]);
+ if (!isDefined(testOverlayLayer)) {
+ // If the layer creation fails, continue to the next overlay
+ continue;
+ }
+ leafletLayers.overlays[newName] = testOverlayLayer;
+ if (newOverlayLayers[newName].visible === true) {
+ safeAddLayer(map, leafletLayers.overlays[newName]);
+ }
+ } else {
+ // check for the .visible property to hide/show overLayers
+ if (newOverlayLayers[newName].visible && !map.hasLayer(leafletLayers.overlays[newName])) {
+ safeAddLayer(map, leafletLayers.overlays[newName]);
+ } else if (newOverlayLayers[newName].visible === false && map.hasLayer(leafletLayers.overlays[newName])) {
+ // Safe remove when ArcGIS layers is loading.
+ safeRemoveLayer(map, leafletLayers.overlays[newName], newOverlayLayers[newName].layerOptions);
+ }
+ }
+
+ //refresh heatmap data if present
+ if (newOverlayLayers[newName].visible && map._loaded && newOverlayLayers[newName].data && newOverlayLayers[newName].type === "heatmap") {
+ leafletLayers.overlays[newName].setData(newOverlayLayers[newName].data);
+ leafletLayers.overlays[newName].update();
+ }
+ }
+
+ // Only add the layers switch selector control if we have more than one baselayer + overlay
+ isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, layers.baselayers, newOverlayLayers, leafletLayers);
+ }, true);
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('legend', ["$log", "$http", "leafletHelpers", "leafletLegendHelpers", function ($log, $http, leafletHelpers, leafletLegendHelpers) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function (scope, element, attrs, controller) {
+
+ var isArray = leafletHelpers.isArray,
+ isDefined = leafletHelpers.isDefined,
+ isFunction = leafletHelpers.isFunction,
+ leafletScope = controller.getLeafletScope(),
+ legend = leafletScope.legend;
+
+ var legendClass;
+ var position;
+ var leafletLegend;
+ var type;
+
+ leafletScope.$watch('legend', function (newLegend) {
+
+ if (isDefined(newLegend)) {
+
+ legendClass = newLegend.legendClass ? newLegend.legendClass : "legend";
+
+ position = newLegend.position || 'bottomright';
+
+ // default to arcgis
+ type = newLegend.type || 'arcgis';
+ }
+
+ }, true);
+
+ controller.getMap().then(function (map) {
+
+ leafletScope.$watch('legend', function (newLegend) {
+
+ if (!isDefined(newLegend)) {
+
+ if (isDefined(leafletLegend)) {
+ leafletLegend.removeFrom(map);
+ leafletLegend= null;
+ }
+
+ return;
+ }
+
+ if (!isDefined(newLegend.url) && (type === 'arcgis') && (!isArray(newLegend.colors) || !isArray(newLegend.labels) || newLegend.colors.length !== newLegend.labels.length)) {
+
+ $log.warn("[AngularJS - Leaflet] legend.colors and legend.labels must be set.");
+
+ return;
+ }
+
+ if (isDefined(newLegend.url)) {
+
+ $log.info("[AngularJS - Leaflet] loading legend service.");
+
+ return;
+ }
+
+ if (isDefined(leafletLegend)) {
+ leafletLegend.removeFrom(map);
+ leafletLegend= null;
+ }
+
+ leafletLegend = L.control({
+ position: position
+ });
+ if (type === 'arcgis') {
+ leafletLegend.onAdd = leafletLegendHelpers.getOnAddArrayLegend(newLegend, legendClass);
+ }
+ leafletLegend.addTo(map);
+
+ });
+
+ leafletScope.$watch('legend.url', function (newURL) {
+
+ if (!isDefined(newURL)) {
+ return;
+ }
+ $http.get(newURL)
+ .success(function (legendData) {
+
+ if (isDefined(leafletLegend)) {
+
+ leafletLegendHelpers.updateLegend(leafletLegend.getContainer(), legendData, type, newURL);
+
+ } else {
+
+ leafletLegend = L.control({
+ position: position
+ });
+ leafletLegend.onAdd = leafletLegendHelpers.getOnAddLegend(legendData, legendClass, type, newURL);
+ leafletLegend.addTo(map);
+ }
+
+ if (isDefined(legend.loadedData) && isFunction(legend.loadedData)) {
+ legend.loadedData();
+ }
+ })
+ .error(function () {
+ $log.warn('[AngularJS - Leaflet] legend.url not loaded.');
+ });
+ });
+
+ });
+ }
+ };
+ }]);
+
+angular.module("leaflet-directive").directive('markers',
+ ["$log", "$rootScope", "$q", "leafletData", "leafletHelpers", "leafletMapDefaults", "leafletMarkersHelpers", "leafletMarkerEvents", "leafletIterators", "leafletWatchHelpers", "leafletDirectiveControlsHelpers", function ($log, $rootScope, $q, leafletData, leafletHelpers, leafletMapDefaults,
+ leafletMarkersHelpers, leafletMarkerEvents, leafletIterators, leafletWatchHelpers,
+ leafletDirectiveControlsHelpers) {
+ //less terse vars to helpers
+ var isDefined = leafletHelpers.isDefined,
+ errorHeader = leafletHelpers.errorHeader,
+ Helpers = leafletHelpers,
+ isString = leafletHelpers.isString,
+ addMarkerWatcher = leafletMarkersHelpers.addMarkerWatcher,
+ updateMarker = leafletMarkersHelpers.updateMarker,
+ listenMarkerEvents = leafletMarkersHelpers.listenMarkerEvents,
+ addMarkerToGroup = leafletMarkersHelpers.addMarkerToGroup,
+ createMarker = leafletMarkersHelpers.createMarker,
+ deleteMarker = leafletMarkersHelpers.deleteMarker,
+ $it = leafletIterators,
+ _markersWatchOptions = leafletHelpers.watchOptions,
+ maybeWatch = leafletWatchHelpers.maybeWatch,
+ extendDirectiveControls = leafletDirectiveControlsHelpers.extend;
+
+ var _getLMarker = function(leafletMarkers, name, maybeLayerName){
+ if(!Object.keys(leafletMarkers).length) return;
+ if(maybeLayerName && isString(maybeLayerName)){
+ if(!leafletMarkers[maybeLayerName] || !Object.keys(leafletMarkers[maybeLayerName]).length)
+ return;
+ return leafletMarkers[maybeLayerName][name];
+ }
+ return leafletMarkers[name];
+ };
+
+ var _setLMarker = function(lObject, leafletMarkers, name, maybeLayerName){
+ if(maybeLayerName && isString(maybeLayerName)){
+ if(!isDefined(leafletMarkers[maybeLayerName]))
+ leafletMarkers[maybeLayerName] = {};
+ leafletMarkers[maybeLayerName][name] = lObject;
+ }
+ else
+ leafletMarkers[name] = lObject;
+ return lObject;
+ };
+
+ var _maybeAddMarkerToLayer = function(layerName, layers, model, marker, doIndividualWatch, map){
+
+ if (!isString(layerName)) {
+ $log.error(errorHeader + ' A layername must be a string');
+ return false;
+ }
+
+ if (!isDefined(layers)) {
+ $log.error(errorHeader + ' You must add layers to the directive if the markers are going to use this functionality.');
+ return false;
+ }
+
+ if (!isDefined(layers.overlays) || !isDefined(layers.overlays[layerName])) {
+ $log.error(errorHeader +' A marker can only be added to a layer of type "group"');
+ return false;
+ }
+ var layerGroup = layers.overlays[layerName];
+ if (!(layerGroup instanceof L.LayerGroup || layerGroup instanceof L.FeatureGroup)) {
+ $log.error(errorHeader + ' Adding a marker to an overlay needs a overlay of the type "group" or "featureGroup"');
+ return false;
+ }
+
+ // The marker goes to a correct layer group, so first of all we add it
+ layerGroup.addLayer(marker);
+
+ // The marker is automatically added to the map depending on the visibility
+ // of the layer, so we only have to open the popup if the marker is in the map
+ if (!doIndividualWatch && map.hasLayer(marker) && model.focus === true) {
+ marker.openPopup();
+ }
+ return true;
+ };
+ //TODO: move to leafletMarkersHelpers??? or make a new class/function file (leafletMarkersHelpers is large already)
+ var _addMarkers = function(mapId, markersToRender, oldModels, map, layers, leafletMarkers, leafletScope,
+ watchOptions, maybeLayerName, skips){
+ for (var newName in markersToRender) {
+ if(skips[newName])
+ continue;
+
+ if (newName.search("-") !== -1) {
+ $log.error('The marker can\'t use a "-" on his key name: "' + newName + '".');
+ continue;
+ }
+
+ var model = Helpers.copy(markersToRender[newName]);
+ var pathToMarker = Helpers.getObjectDotPath(maybeLayerName? [maybeLayerName, newName]: [newName]);
+ var maybeLMarker = _getLMarker(leafletMarkers,newName, maybeLayerName);
+ if (!isDefined(maybeLMarker)) {
+ //(nmccready) very important to not have model changes when lObject is changed
+ //this might be desirable in some cases but it causes two-way binding to lObject which is not ideal
+ //if it is left as the reference then all changes from oldModel vs newModel are ignored
+ //see _destroy (where modelDiff becomes meaningless if we do not copy here)
+ var marker = createMarker(model);
+ var layerName = (model? model.layer : undefined) || maybeLayerName; //original way takes pref
+ if (!isDefined(marker)) {
+ $log.error(errorHeader + ' Received invalid data on the marker ' + newName + '.');
+ continue;
+ }
+ _setLMarker(marker, leafletMarkers, newName, maybeLayerName);
+
+ // Bind message
+ if (isDefined(model.message)) {
+ marker.bindPopup(model.message, model.popupOptions);
+ }
+
+ // Add the marker to a cluster group if needed
+ if (isDefined(model.group)) {
+ var groupOptions = isDefined(model.groupOption) ? model.groupOption : null;
+ addMarkerToGroup(marker, model.group, groupOptions, map);
+ }
+
+ // Show label if defined
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(model.label) && isDefined(model.label.message)) {
+ marker.bindLabel(model.label.message, model.label.options);
+ }
+
+ // Check if the marker should be added to a layer
+ if (isDefined(model) && (isDefined(model.layer) || isDefined(maybeLayerName))){
+
+ var pass = _maybeAddMarkerToLayer(layerName, layers, model, marker,
+ watchOptions.individual.doWatch, map);
+ if(!pass)
+ continue; //something went wrong move on in the loop
+ } else if (!isDefined(model.group)) {
+ // We do not have a layer attr, so the marker goes to the map layer
+ map.addLayer(marker);
+ if (!watchOptions.individual.doWatch && model.focus === true) {
+ marker.openPopup();
+ }
+ }
+
+ if (watchOptions.individual.doWatch) {
+ addMarkerWatcher(marker, pathToMarker, leafletScope, layers, map,
+ watchOptions.individual.isDeep);
+ }
+
+ listenMarkerEvents(marker, model, leafletScope, watchOptions.individual.doWatch, map);
+ leafletMarkerEvents.bindEvents(mapId, marker, pathToMarker, model, leafletScope, layerName);
+ }
+ else {
+ var oldModel = isDefined(oldModel)? oldModels[newName] : undefined;
+ updateMarker(model, oldModel, maybeLMarker, pathToMarker, leafletScope, layers, map);
+ }
+ }
+ };
+ var _seeWhatWeAlreadyHave = function(markerModels, oldMarkerModels, lMarkers, isEqual, cb){
+ var hasLogged = false,
+ equals = false,
+ oldMarker,
+ newMarker;
+
+ var doCheckOldModel = isDefined(oldMarkerModels);
+ for (var name in lMarkers) {
+ if(!hasLogged) {
+ $log.debug(errorHeader + "[markers] destroy: ");
+ hasLogged = true;
+ }
+
+ if(doCheckOldModel){
+ //might want to make the option (in watch options) to disable deep checking
+ //ie the options to only check !== (reference check) instead of angular.equals (slow)
+ newMarker = markerModels[name];
+ oldMarker = oldMarkerModels[name];
+ equals = angular.equals(newMarker,oldMarker) && isEqual;
+ }
+ if (!isDefined(markerModels) ||
+ !Object.keys(markerModels).length ||
+ !isDefined(markerModels[name]) ||
+ !Object.keys(markerModels[name]).length ||
+ equals) {
+ if(cb && Helpers.isFunction(cb))
+ cb(newMarker, oldMarker, name);
+ }
+ }
+ };
+ var _destroy = function(markerModels, oldMarkerModels, lMarkers, map, layers){
+ _seeWhatWeAlreadyHave(markerModels, oldMarkerModels, lMarkers, false,
+ function(newMarker, oldMarker, lMarkerName){
+ $log.debug(errorHeader + '[marker] is deleting marker: ' + lMarkerName);
+ deleteMarker(lMarkers[lMarkerName], map, layers);
+ delete lMarkers[lMarkerName];
+ });
+ };
+
+ var _getNewModelsToSkipp = function(newModels, oldModels, lMarkers){
+ var skips = {};
+ _seeWhatWeAlreadyHave(newModels, oldModels, lMarkers, true,
+ function(newMarker, oldMarker, lMarkerName){
+ $log.debug(errorHeader + '[marker] is already rendered, marker: ' + lMarkerName);
+ skips[lMarkerName] = newMarker;
+ });
+ return skips;
+ };
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: ['leaflet', '?layers'],
+
+ link: function(scope, element, attrs, controller) {
+ var mapController = controller[0],
+ leafletScope = mapController.getLeafletScope();
+
+ mapController.getMap().then(function(map) {
+ var leafletMarkers = {}, getLayers;
+
+ // If the layers attribute is used, we must wait until the layers are created
+ if (isDefined(controller[1])) {
+ getLayers = controller[1].getLayers;
+ } else {
+ getLayers = function() {
+ var deferred = $q.defer();
+ deferred.resolve();
+ return deferred.promise;
+ };
+ }
+
+ var watchOptions = leafletScope.markersWatchOptions || _markersWatchOptions;
+
+ // backwards compat
+ if(isDefined(attrs.watchMarkers))
+ watchOptions.doWatch = watchOptions.individual.doWatch =
+ (!isDefined(attrs.watchMarkers) || Helpers.isTruthy(attrs.watchMarkers));
+
+ var isNested = (isDefined(attrs.markersNested) && Helpers.isTruthy(attrs.markersNested));
+
+ getLayers().then(function(layers) {
+ var _clean = function(models, oldModels){
+ if(isNested) {
+ $it.each(models, function(markerToMaybeDel, layerName) {
+ var oldModel = isDefined(oldModel)? oldModels[layerName] : undefined;
+ _destroy(markerToMaybeDel, oldModel, leafletMarkers[layerName], map, layers);
+ });
+ return;
+ }
+ _destroy(models, oldModels, leafletMarkers, map, layers);
+ };
+
+ var _create = function(models, oldModels){
+ _clean(models, oldModels);
+ var skips = null;
+ if(isNested) {
+ $it.each(models, function(markersToAdd, layerName) {
+ var oldModel = isDefined(oldModel)? oldModels[layerName] : undefined;
+ skips = _getNewModelsToSkipp(models[layerName], oldModel, leafletMarkers[layerName]);
+ _addMarkers(attrs.id, markersToAdd, oldModels, map, layers, leafletMarkers, leafletScope,
+ watchOptions, layerName, skips);
+ });
+ return;
+ }
+ skips = _getNewModelsToSkipp(models, oldModels, leafletMarkers);
+ _addMarkers(attrs.id, models, oldModels, map, layers, leafletMarkers, leafletScope,
+ watchOptions, undefined, skips);
+ };
+ extendDirectiveControls(attrs.id, 'markers', _create, _clean);
+ leafletData.setMarkers(leafletMarkers, attrs.id);
+
+ maybeWatch(leafletScope,'markers', watchOptions, function(newMarkers, oldMarkers){
+ _create(newMarkers, oldMarkers);
+ });
+ });
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('maxbounds', ["$log", "leafletMapDefaults", "leafletBoundsHelpers", "leafletHelpers", function ($log, leafletMapDefaults, leafletBoundsHelpers, leafletHelpers) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var leafletScope = controller.getLeafletScope(),
+ isValidBounds = leafletBoundsHelpers.isValidBounds,
+ isNumber = leafletHelpers.isNumber;
+
+
+ controller.getMap().then(function(map) {
+ leafletScope.$watch("maxbounds", function (maxbounds) {
+ if (!isValidBounds(maxbounds)) {
+ // Unset any previous maxbounds
+ map.setMaxBounds();
+ return;
+ }
+
+ var leafletBounds = leafletBoundsHelpers.createLeafletBounds(maxbounds);
+ if(isNumber(maxbounds.pad)) {
+ leafletBounds = leafletBounds.pad(maxbounds.pad);
+ }
+
+ map.setMaxBounds(leafletBounds);
+ if (!attrs.center && !attrs.lfCenter) {
+ map.fitBounds(leafletBounds);
+ }
+ });
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('paths', ["$log", "$q", "leafletData", "leafletMapDefaults", "leafletHelpers", "leafletPathsHelpers", "leafletPathEvents", function ($log, $q, leafletData, leafletMapDefaults, leafletHelpers, leafletPathsHelpers, leafletPathEvents) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: ['leaflet', '?layers'],
+
+ link: function(scope, element, attrs, controller) {
+ var mapController = controller[0],
+ isDefined = leafletHelpers.isDefined,
+ isString = leafletHelpers.isString,
+ leafletScope = mapController.getLeafletScope(),
+ paths = leafletScope.paths,
+ createPath = leafletPathsHelpers.createPath,
+ bindPathEvents = leafletPathEvents.bindPathEvents,
+ setPathOptions = leafletPathsHelpers.setPathOptions;
+
+ mapController.getMap().then(function(map) {
+ var defaults = leafletMapDefaults.getDefaults(attrs.id),
+ getLayers;
+
+ // If the layers attribute is used, we must wait until the layers are created
+ if (isDefined(controller[1])) {
+ getLayers = controller[1].getLayers;
+ } else {
+ getLayers = function() {
+ var deferred = $q.defer();
+ deferred.resolve();
+ return deferred.promise;
+ };
+ }
+
+ if (!isDefined(paths)) {
+ return;
+ }
+
+ getLayers().then(function(layers) {
+
+ var leafletPaths = {};
+ leafletData.setPaths(leafletPaths, attrs.id);
+
+ // Should we watch for every specific marker on the map?
+ var shouldWatch = (!isDefined(attrs.watchPaths) || attrs.watchPaths === 'true');
+
+ // Function for listening every single path once created
+ var watchPathFn = function(leafletPath, name) {
+ var clearWatch = leafletScope.$watch("paths[\""+name+"\"]", function(pathData, old) {
+ if (!isDefined(pathData)) {
+ if (isDefined(old.layer)) {
+ for (var i in layers.overlays) {
+ var overlay = layers.overlays[i];
+ overlay.removeLayer(leafletPath);
+ }
+ }
+ map.removeLayer(leafletPath);
+ clearWatch();
+ return;
+ }
+ setPathOptions(leafletPath, pathData.type, pathData);
+ }, true);
+ };
+
+ leafletScope.$watchCollection("paths", function (newPaths) {
+
+ // Delete paths (by name) from the array
+ for (var name in leafletPaths) {
+ if (!isDefined(newPaths[name])) {
+ map.removeLayer(leafletPaths[name]);
+ delete leafletPaths[name];
+ }
+ }
+
+ // Create the new paths
+ for (var newName in newPaths) {
+ if (newName.search('\\$') === 0) {
+ continue;
+ }
+ if (newName.search("-") !== -1) {
+ $log.error('[AngularJS - Leaflet] The path name "' + newName + '" is not valid. It must not include "-" and a number.');
+ continue;
+ }
+
+ if (!isDefined(leafletPaths[newName])) {
+ var pathData = newPaths[newName];
+ var newPath = createPath(newName, newPaths[newName], defaults);
+
+ // bind popup if defined
+ if (isDefined(newPath) && isDefined(pathData.message)) {
+ newPath.bindPopup(pathData.message, pathData.popupOptions);
+ }
+
+ // Show label if defined
+ if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(pathData.label) && isDefined(pathData.label.message)) {
+ newPath.bindLabel(pathData.label.message, pathData.label.options);
+ }
+
+ // Check if the marker should be added to a layer
+ if (isDefined(pathData) && isDefined(pathData.layer)) {
+
+ if (!isString(pathData.layer)) {
+ $log.error('[AngularJS - Leaflet] A layername must be a string');
+ continue;
+ }
+ if (!isDefined(layers)) {
+ $log.error('[AngularJS - Leaflet] You must add layers to the directive if the markers are going to use this functionality.');
+ continue;
+ }
+
+ if (!isDefined(layers.overlays) || !isDefined(layers.overlays[pathData.layer])) {
+ $log.error('[AngularJS - Leaflet] A path can only be added to a layer of type "group"');
+ continue;
+ }
+ var layerGroup = layers.overlays[pathData.layer];
+ if (!(layerGroup instanceof L.LayerGroup || layerGroup instanceof L.FeatureGroup)) {
+ $log.error('[AngularJS - Leaflet] Adding a path to an overlay needs a overlay of the type "group" or "featureGroup"');
+ continue;
+ }
+
+ // Listen for changes on the new path
+ leafletPaths[newName] = newPath;
+ // The path goes to a correct layer group, so first of all we add it
+ layerGroup.addLayer(newPath);
+
+ if (shouldWatch) {
+ watchPathFn(newPath, newName);
+ } else {
+ setPathOptions(newPath, pathData.type, pathData);
+ }
+ } else if (isDefined(newPath)) {
+ // Listen for changes on the new path
+ leafletPaths[newName] = newPath;
+ map.addLayer(newPath);
+
+ if (shouldWatch) {
+ watchPathFn(newPath, newName);
+ } else {
+ setPathOptions(newPath, pathData.type, pathData);
+ }
+ }
+
+ bindPathEvents(attrs.id, newPath, newName, pathData, leafletScope);
+ }
+ }
+ });
+ });
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('tiles', ["$log", "leafletData", "leafletMapDefaults", "leafletHelpers", function ($log, leafletData, leafletMapDefaults, leafletHelpers) {
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ tiles = leafletScope.tiles;
+
+ if (!isDefined(tiles) || !isDefined(tiles.url)) {
+ $log.warn("[AngularJS - Leaflet] The 'tiles' definition doesn't have the 'url' property.");
+ return;
+ }
+
+ controller.getMap().then(function(map) {
+ var defaults = leafletMapDefaults.getDefaults(attrs.id);
+ var tileLayerObj;
+ leafletScope.$watch("tiles", function(tiles, oldtiles) {
+ var tileLayerOptions = defaults.tileLayerOptions;
+ var tileLayerUrl = defaults.tileLayer;
+
+ // If no valid tiles are in the scope, remove the last layer
+ if (!isDefined(tiles.url) && isDefined(tileLayerObj)) {
+ map.removeLayer(tileLayerObj);
+ return;
+ }
+
+ // No leafletTiles object defined yet
+ if (!isDefined(tileLayerObj)) {
+ if (isDefined(tiles.options)) {
+ angular.copy(tiles.options, tileLayerOptions);
+ }
+
+ if (isDefined(tiles.url)) {
+ tileLayerUrl = tiles.url;
+ }
+
+ if (tiles.type === 'wms') {
+ tileLayerObj = L.tileLayer.wms(tileLayerUrl, tileLayerOptions);
+ } else {
+ tileLayerObj = L.tileLayer(tileLayerUrl, tileLayerOptions);
+ }
+
+ tileLayerObj.addTo(map);
+ leafletData.setTiles(tileLayerObj, attrs.id);
+ return;
+ }
+
+ // If the options of the tilelayer is changed, we need to redraw the layer
+ if (isDefined(tiles.url) && isDefined(tiles.options) &&
+ (tiles.type !== oldtiles.type || !angular.equals(tiles.options, tileLayerOptions))) {
+ map.removeLayer(tileLayerObj);
+ tileLayerOptions = defaults.tileLayerOptions;
+ angular.copy(tiles.options, tileLayerOptions);
+ tileLayerUrl = tiles.url;
+
+ if (tiles.type === 'wms') {
+ tileLayerObj = L.tileLayer.wms(tileLayerUrl, tileLayerOptions);
+ } else {
+ tileLayerObj = L.tileLayer(tileLayerUrl, tileLayerOptions);
+ }
+
+ tileLayerObj.addTo(map);
+ leafletData.setTiles(tileLayerObj, attrs.id);
+ return;
+ }
+
+ // Only the URL of the layer is changed, update the tiles object
+ if (isDefined(tiles.url)) {
+ tileLayerObj.setUrl(tiles.url);
+ }
+ }, true);
+ });
+ }
+ };
+}]);
+
+/*
+ Create multiple similar directives for watchOptions to support directiveControl
+ instead. (when watches are disabled)
+ NgAnnotate does not work here due to the functional creation
+*/
+['markers', 'geojson'].forEach(function(name){
+ angular.module("leaflet-directive").directive(name + 'WatchOptions', [
+ '$log', '$rootScope', '$q', 'leafletData', 'leafletHelpers',
+ function ($log, $rootScope, $q, leafletData, leafletHelpers) {
+
+ var isDefined = leafletHelpers.isDefined,
+ errorHeader = leafletHelpers.errorHeader,
+ isObject = leafletHelpers.isObject,
+ _watchOptions = leafletHelpers.watchOptions;
+
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: ['leaflet'],
+
+ link: function (scope, element, attrs, controller) {
+ var mapController = controller[0],
+ leafletScope = mapController.getLeafletScope();
+
+ mapController.getMap().then(function () {
+ if (isDefined(scope[name + 'WatchOptions'])) {
+ if (isObject(scope[name + 'WatchOptions']))
+ angular.extend(_watchOptions, scope[name + 'WatchOptions']);
+ else
+ $log.error(errorHeader + '[' + name + 'WatchOptions] is not an object');
+ leafletScope[name + 'WatchOptions'] = _watchOptions;
+ }
+ });
+ }
+ };
+ }]);
+});
+
+angular.module("leaflet-directive")
+.factory('leafletEventsHelpersFactory', ["$rootScope", "$q", "$log", "leafletHelpers", function ($rootScope, $q, $log, leafletHelpers) {
+ var safeApply = leafletHelpers.safeApply,
+ isDefined = leafletHelpers.isDefined,
+ isObject = leafletHelpers.isObject,
+ isArray = leafletHelpers.isArray,
+ errorHeader = leafletHelpers.errorHeader;
+
+ var EventsHelper = function(rootBroadcastName, lObjectType){
+ this.rootBroadcastName = rootBroadcastName;
+ $log.debug("leafletEventsHelpersFactory: lObjectType: " + lObjectType + "rootBroadcastName: " + rootBroadcastName);
+ //used to path/key out certain properties based on the type , "markers", "geojson"
+ this.lObjectType = lObjectType;
+ };
+
+ EventsHelper.prototype.getAvailableEvents = function(){return [];};
+
+ /*
+ argument: name: Note this can be a single string or dot notation
+ Example:
+ markerModel : {
+ m1: { lat:_, lon: _}
+ }
+ //would yield name of
+ name = "m1"
+
+ If nested:
+ markerModel : {
+ cars: {
+ m1: { lat:_, lon: _}
+ }
+ }
+ //would yield name of
+ name = "cars.m1"
+ */
+ EventsHelper.prototype.genDispatchEvent = function(maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName, extra) {
+ var _this = this;
+
+ maybeMapId = maybeMapId || '';
+ if (maybeMapId)
+ maybeMapId = '.' + maybeMapId;
+
+ return function (e) {
+ var broadcastName = _this.rootBroadcastName + maybeMapId + '.' + eventName;
+ $log.debug(broadcastName);
+ _this.fire(leafletScope, broadcastName, logic, e, e.target || lObject, model, name, layerName, extra);
+ };
+ };
+
+ EventsHelper.prototype.fire = function(scope, broadcastName, logic, event, lObject, model, modelName, layerName, extra){
+ // Safely broadcast the event
+ safeApply(scope, function(){
+ var toSend = {
+ leafletEvent: event,
+ leafletObject: lObject,
+ modelName: modelName,
+ model: model
+ };
+ if (isDefined(layerName))
+ angular.extend(toSend, {layerName: layerName});
+
+ if (logic === "emit") {
+ scope.$emit(broadcastName, toSend);
+ } else {
+ $rootScope.$broadcast(broadcastName, toSend);
+ }
+ });
+ };
+
+ EventsHelper.prototype.bindEvents = function (maybeMapId, lObject, name, model, leafletScope, layerName, extra) {
+ var events = [];
+ var logic = 'emit';
+ var _this = this;
+
+ if (!isDefined(leafletScope.eventBroadcast)) {
+ // Backward compatibility, if no event-broadcast attribute, all events are broadcasted
+ events = this.getAvailableEvents();
+ } else if (!isObject(leafletScope.eventBroadcast)) {
+ // Not a valid object
+ $log.error(errorHeader + "event-broadcast must be an object check your model.");
+ } else {
+ // We have a possible valid object
+ if (!isDefined(leafletScope.eventBroadcast[_this.lObjectType])) {
+ // We do not have events enable/disable do we do nothing (all enabled by default)
+ events = this.getAvailableEvents();
+ } else if (!isObject(leafletScope.eventBroadcast[_this.lObjectType])) {
+ // Not a valid object
+ $log.warn(errorHeader + 'event-broadcast.' + [_this.lObjectType] + ' must be an object check your model.');
+ } else {
+ // We have a possible valid map object
+ // Event propadation logic
+ if (isDefined(leafletScope.eventBroadcast[this.lObjectType].logic)) {
+ // We take care of possible propagation logic
+ if (leafletScope.eventBroadcast[_this.lObjectType].logic !== "emit" &&
+ leafletScope.eventBroadcast[_this.lObjectType].logic !== "broadcast")
+ $log.warn(errorHeader + "Available event propagation logic are: 'emit' or 'broadcast'.");
+ }
+ // Enable / Disable
+ var eventsEnable = false, eventsDisable = false;
+ if (isDefined(leafletScope.eventBroadcast[_this.lObjectType].enable) &&
+ isArray(leafletScope.eventBroadcast[_this.lObjectType].enable))
+ eventsEnable = true;
+ if (isDefined(leafletScope.eventBroadcast[_this.lObjectType].disable) &&
+ isArray(leafletScope.eventBroadcast[_this.lObjectType].disable))
+ eventsDisable = true;
+
+ if (eventsEnable && eventsDisable) {
+ // Both are active, this is an error
+ $log.warn(errorHeader + "can not enable and disable events at the same time");
+ } else if (!eventsEnable && !eventsDisable) {
+ // Both are inactive, this is an error
+ $log.warn(errorHeader + "must enable or disable events");
+ } else {
+ // At this point the object is OK, lets enable or disable events
+ if (eventsEnable) {
+ // Enable events
+ leafletScope.eventBroadcast[this.lObjectType].enable.forEach(function(eventName){
+ // Do we have already the event enabled?
+ if (events.indexOf(eventName) !== -1) {
+ // Repeated event, this is an error
+ $log.warn(errorHeader + "This event " + eventName + " is already enabled");
+ } else {
+ // Does the event exists?
+ if (_this.getAvailableEvents().indexOf(eventName) === -1) {
+ // The event does not exists, this is an error
+ $log.warn(errorHeader + "This event " + eventName + " does not exist");
+ } else {
+ // All ok enable the event
+ events.push(eventName);
+ }
+ }
+ });
+ } else {
+ // Disable events
+ events = this.getAvailableEvents();
+ leafletScope.eventBroadcast[_this.lObjectType].disable.forEach(function(eventName) {
+ var index = events.indexOf(eventName);
+ if (index === -1) {
+ // The event does not exist
+ $log.warn(errorHeader + "This event " + eventName + " does not exist or has been already disabled");
+
+ } else {
+ events.splice(index, 1);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ events.forEach(function(eventName){
+ lObject.on(eventName,_this.genDispatchEvent(maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName, extra));
+ });
+ return logic;
+ };
+
+ return EventsHelper;
+}])
+.service('leafletEventsHelpers', ["leafletEventsHelpersFactory", function(leafletEventsHelpersFactory){
+ return new leafletEventsHelpersFactory();
+}]);
+
+angular.module("leaflet-directive")
+.factory('leafletGeoJsonEvents', ["$rootScope", "$q", "$log", "leafletHelpers", "leafletEventsHelpersFactory", "leafletData", function ($rootScope, $q, $log, leafletHelpers,
+ leafletEventsHelpersFactory, leafletData) {
+ var safeApply = leafletHelpers.safeApply,
+ EventsHelper = leafletEventsHelpersFactory;
+
+ var GeoJsonEvents = function(){
+ EventsHelper.call(this,'leafletDirectiveGeoJson', 'geojson');
+ };
+
+ GeoJsonEvents.prototype = new EventsHelper();
+
+ GeoJsonEvents.prototype.genDispatchEvent = function(maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName, extra) {
+ var base = EventsHelper.prototype.genDispatchEvent.call(this, maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName),
+ _this = this;
+
+ return function(e){
+ if (eventName === 'mouseout') {
+ if (extra.resetStyleOnMouseout) {
+ leafletData.getGeoJSON(extra.mapId)
+ .then(function(leafletGeoJSON){
+ //this is broken on nested needs to traverse or user layerName (nested)
+ var lobj = layerName? leafletGeoJSON[layerName]: leafletGeoJSON;
+ lobj.resetStyle(e.target);
+ });
+
+ }
+ safeApply(leafletScope, function() {
+ $rootScope.$broadcast(_this.rootBroadcastName + '.mouseout', e);
+ });
+ }
+ base(e); //common
+ };
+ };
+
+ GeoJsonEvents.prototype.getAvailableEvents = function(){ return [
+ 'click',
+ 'dblclick',
+ 'mouseover',
+ 'mouseout',
+ ];
+ };
+
+ return new GeoJsonEvents();
+}]);
+
+angular.module("leaflet-directive")
+.factory('leafletLabelEvents', ["$rootScope", "$q", "$log", "leafletHelpers", "leafletEventsHelpersFactory", function ($rootScope, $q, $log, leafletHelpers, leafletEventsHelpersFactory) {
+ var Helpers = leafletHelpers,
+ EventsHelper = leafletEventsHelpersFactory;
+
+ var LabelEvents = function(){
+ EventsHelper.call(this,'leafletDirectiveLabel', 'markers');
+ };
+ LabelEvents.prototype = new EventsHelper();
+
+ LabelEvents.prototype.genDispatchEvent = function(maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) {
+ var markerName = name.replace('markers.', '');
+ return EventsHelper.prototype
+ .genDispatchEvent.call(this, maybeMapId, eventName, logic, leafletScope, lObject, markerName, model, layerName);
+ };
+
+ LabelEvents.prototype.getAvailableEvents = function(){
+ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseover',
+ 'mouseout',
+ 'contextmenu'
+ ];
+ };
+
+ LabelEvents.prototype.genEvents = function (maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) {
+ var _this = this;
+ var labelEvents = this.getAvailableEvents();
+ var scopeWatchName = Helpers.getObjectArrayPath("markers." + name);
+ labelEvents.forEach(function(eventName) {
+ lObject.label.on(eventName, _this.genDispatchEvent(
+ maybeMapId, eventName, logic, leafletScope, lObject.label, scopeWatchName, model, layerName));
+ });
+ };
+
+ LabelEvents.prototype.bindEvents = function (maybeMapId, lObject, name, model, leafletScope, layerName) {};
+
+ return new LabelEvents();
+}]);
+
+angular.module("leaflet-directive")
+.factory('leafletMapEvents', ["$rootScope", "$q", "$log", "leafletHelpers", "leafletEventsHelpers", "leafletIterators", function ($rootScope, $q, $log, leafletHelpers, leafletEventsHelpers, leafletIterators) {
+ var isDefined = leafletHelpers.isDefined,
+ fire = leafletEventsHelpers.fire;
+
+ var _getAvailableMapEvents = function() {
+ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseup',
+ 'mouseover',
+ 'mouseout',
+ 'mousemove',
+ 'contextmenu',
+ 'focus',
+ 'blur',
+ 'preclick',
+ 'load',
+ 'unload',
+ 'viewreset',
+ 'movestart',
+ 'move',
+ 'moveend',
+ 'dragstart',
+ 'drag',
+ 'dragend',
+ 'zoomstart',
+ 'zoomanim',
+ 'zoomend',
+ 'zoomlevelschange',
+ 'resize',
+ 'autopanstart',
+ 'layeradd',
+ 'layerremove',
+ 'baselayerchange',
+ 'overlayadd',
+ 'overlayremove',
+ 'locationfound',
+ 'locationerror',
+ 'popupopen',
+ 'popupclose',
+ 'draw:created',
+ 'draw:edited',
+ 'draw:deleted',
+ 'draw:drawstart',
+ 'draw:drawstop',
+ 'draw:editstart',
+ 'draw:editstop',
+ 'draw:deletestart',
+ 'draw:deletestop'
+ ];
+ };
+
+ var _genDispatchMapEvent = function(scope, eventName, logic, maybeMapId) {
+ if (maybeMapId)
+ maybeMapId = maybeMapId + '.';
+ return function(e) {
+ // Put together broadcast name
+ var broadcastName = 'leafletDirectiveMap.' + maybeMapId + eventName;
+ $log.debug(broadcastName);
+ // Safely broadcast the event
+ fire(scope, broadcastName, logic, e, e.target, scope);
+ };
+ };
+
+ var _notifyCenterChangedToBounds = function(scope) {
+ scope.$broadcast("boundsChanged");
+ };
+
+ var _notifyCenterUrlHashChanged = function(scope, map, attrs, search) {
+ if (!isDefined(attrs.urlHashCenter)) {
+ return;
+ }
+ var center = map.getCenter();
+ var centerUrlHash = (center.lat).toFixed(4) + ":" + (center.lng).toFixed(4) + ":" + map.getZoom();
+ if (!isDefined(search.c) || search.c !== centerUrlHash) {
+ //$log.debug("notified new center...");
+ scope.$emit("centerUrlHash", centerUrlHash);
+ }
+ };
+
+ var _addEvents = function(map, mapEvents, contextName, scope, logic){
+ leafletIterators.each(mapEvents, function(eventName) {
+ var context = {};
+ context[contextName] = eventName;
+ map.on(eventName, _genDispatchMapEvent(scope, eventName, logic, map._container.id || ''), context);
+ });
+ };
+
+ return {
+ getAvailableMapEvents: _getAvailableMapEvents,
+ genDispatchMapEvent: _genDispatchMapEvent,
+ notifyCenterChangedToBounds: _notifyCenterChangedToBounds,
+ notifyCenterUrlHashChanged: _notifyCenterUrlHashChanged,
+ addEvents: _addEvents
+ };
+}]);
+
+angular.module("leaflet-directive")
+.factory('leafletMarkerEvents', ["$rootScope", "$q", "$log", "leafletHelpers", "leafletEventsHelpersFactory", "leafletLabelEvents", function ($rootScope, $q, $log, leafletHelpers, leafletEventsHelpersFactory, leafletLabelEvents) {
+ var safeApply = leafletHelpers.safeApply,
+ isDefined = leafletHelpers.isDefined,
+ Helpers = leafletHelpers,
+ lblHelp = leafletLabelEvents,
+ EventsHelper = leafletEventsHelpersFactory;
+
+ var MarkerEvents = function(){
+ EventsHelper.call(this,'leafletDirectiveMarker', 'markers');
+ };
+
+ MarkerEvents.prototype = new EventsHelper();
+
+ MarkerEvents.prototype.genDispatchEvent = function(maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) {
+ var handle = EventsHelper.prototype
+ .genDispatchEvent.call(this, maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName);
+ return function(e){
+ // Broadcast old marker click name for backwards compatibility
+ if (eventName === "click") {
+ safeApply(leafletScope, function () {
+ $rootScope.$broadcast('leafletDirectiveMarkersClick', name);
+ });
+ } else if (eventName === 'dragend') {
+ safeApply(leafletScope, function () {
+ model.lat = lObject.getLatLng().lat;
+ model.lng = lObject.getLatLng().lng;
+ });
+ if (model.message && model.focus === true) {
+ lObject.openPopup();
+ }
+ }
+ handle(e); //common
+ };
+ };
+
+ MarkerEvents.prototype.getAvailableEvents = function(){ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseover',
+ 'mouseout',
+ 'contextmenu',
+ 'dragstart',
+ 'drag',
+ 'dragend',
+ 'move',
+ 'remove',
+ 'popupopen',
+ 'popupclose',
+ 'touchend',
+ 'touchstart',
+ 'touchmove',
+ 'touchcancel',
+ 'touchleave'
+ ];
+ };
+
+ MarkerEvents.prototype.bindEvents = function (maybeMapId, lObject, name, model, leafletScope, layerName) {
+ var logic = EventsHelper.prototype.bindEvents.call(this, maybeMapId, lObject, name, model, leafletScope, layerName);
+
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(lObject.label)) {
+ lblHelp.genEvents(maybeMapId, name, logic, leafletScope, lObject, model, layerName);
+ }
+ };
+
+ return new MarkerEvents();
+}]);
+
+angular.module("leaflet-directive")
+.factory('leafletPathEvents', ["$rootScope", "$q", "$log", "leafletHelpers", "leafletLabelEvents", "leafletEventsHelpers", function ($rootScope, $q, $log, leafletHelpers, leafletLabelEvents, leafletEventsHelpers) {
+ var isDefined = leafletHelpers.isDefined,
+ isObject = leafletHelpers.isObject,
+ Helpers = leafletHelpers,
+ errorHeader = leafletHelpers.errorHeader,
+ lblHelp = leafletLabelEvents,
+ fire = leafletEventsHelpers.fire;
+
+ /*
+ TODO (nmccready) This EventsHelper needs to be derrived from leafletEventsHelpers to elminate copy and paste code.
+ */
+
+ var _genDispatchPathEvent = function (maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) {
+ maybeMapId = maybeMapId || '';
+
+ if (maybeMapId)
+ maybeMapId = '.' + maybeMapId;
+
+ return function (e) {
+ var broadcastName = 'leafletDirectivePath' + maybeMapId + '.' + eventName;
+ $log.debug(broadcastName);
+ fire(leafletScope, broadcastName, logic, e, e.target || lObject, model, name, layerName);
+ };
+ };
+
+ var _bindPathEvents = function (maybeMapId, lObject, name, model, leafletScope) {
+ var pathEvents = [],
+ i,
+ eventName,
+ logic = "broadcast";
+
+ if (!isDefined(leafletScope.eventBroadcast)) {
+ // Backward compatibility, if no event-broadcast attribute, all events are broadcasted
+ pathEvents = _getAvailablePathEvents();
+ } else if (!isObject(leafletScope.eventBroadcast)) {
+ // Not a valid object
+ $log.error(errorHeader + "event-broadcast must be an object check your model.");
+ } else {
+ // We have a possible valid object
+ if (!isDefined(leafletScope.eventBroadcast.path)) {
+ // We do not have events enable/disable do we do nothing (all enabled by default)
+ pathEvents = _getAvailablePathEvents();
+ } else if (isObject(leafletScope.eventBroadcast.paths)) {
+ // Not a valid object
+ $log.warn(errorHeader + "event-broadcast.path must be an object check your model.");
+ } else {
+ // We have a possible valid map object
+ // Event propadation logic
+ if (leafletScope.eventBroadcast.path.logic !== undefined && leafletScope.eventBroadcast.path.logic !== null) {
+ // We take care of possible propagation logic
+ if (leafletScope.eventBroadcast.path.logic !== "emit" && leafletScope.eventBroadcast.path.logic !== "broadcast") {
+ // This is an error
+ $log.warn(errorHeader + "Available event propagation logic are: 'emit' or 'broadcast'.");
+ } else if (leafletScope.eventBroadcast.path.logic === "emit") {
+ logic = "emit";
+ }
+ }
+ // Enable / Disable
+ var pathEventsEnable = false, pathEventsDisable = false;
+ if (leafletScope.eventBroadcast.path.enable !== undefined && leafletScope.eventBroadcast.path.enable !== null) {
+ if (typeof leafletScope.eventBroadcast.path.enable === 'object') {
+ pathEventsEnable = true;
+ }
+ }
+ if (leafletScope.eventBroadcast.path.disable !== undefined && leafletScope.eventBroadcast.path.disable !== null) {
+ if (typeof leafletScope.eventBroadcast.path.disable === 'object') {
+ pathEventsDisable = true;
+ }
+ }
+ if (pathEventsEnable && pathEventsDisable) {
+ // Both are active, this is an error
+ $log.warn(errorHeader + "can not enable and disable events at the same time");
+ } else if (!pathEventsEnable && !pathEventsDisable) {
+ // Both are inactive, this is an error
+ $log.warn(errorHeader + "must enable or disable events");
+ } else {
+ // At this point the path object is OK, lets enable or disable events
+ if (pathEventsEnable) {
+ // Enable events
+ for (i = 0; i < leafletScope.eventBroadcast.path.enable.length; i++) {
+ eventName = leafletScope.eventBroadcast.path.enable[i];
+ // Do we have already the event enabled?
+ if (pathEvents.indexOf(eventName) !== -1) {
+ // Repeated event, this is an error
+ $log.warn(errorHeader + "This event " + eventName + " is already enabled");
+ } else {
+ // Does the event exists?
+ if (_getAvailablePathEvents().indexOf(eventName) === -1) {
+ // The event does not exists, this is an error
+ $log.warn(errorHeader + "This event " + eventName + " does not exist");
+ } else {
+ // All ok enable the event
+ pathEvents.push(eventName);
+ }
+ }
+ }
+ } else {
+ // Disable events
+ pathEvents = _getAvailablePathEvents();
+ for (i = 0; i < leafletScope.eventBroadcast.path.disable.length; i++) {
+ eventName = leafletScope.eventBroadcast.path.disable[i];
+ var index = pathEvents.indexOf(eventName);
+ if (index === -1) {
+ // The event does not exist
+ $log.warn(errorHeader + "This event " + eventName + " does not exist or has been already disabled");
+
+ } else {
+ pathEvents.splice(index, 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < pathEvents.length; i++) {
+ eventName = pathEvents[i];
+ lObject.on(eventName, _genDispatchPathEvent(maybeMapId, eventName, logic, leafletScope, pathEvents, name));
+ }
+
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(lObject.label)) {
+ lblHelp.genEvents(maybeMapId, name, logic, leafletScope, lObject, model);
+ }
+ };
+
+ var _getAvailablePathEvents = function () {
+ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseover',
+ 'mouseout',
+ 'contextmenu',
+ 'add',
+ 'remove',
+ 'popupopen',
+ 'popupclose'
+ ];
+ };
+
+ return {
+ getAvailablePathEvents: _getAvailablePathEvents,
+ bindPathEvents: _bindPathEvents
+ };
+}]);
+
+}(angular));
\ No newline at end of file
diff --git a/grunt/aliases.yaml b/grunt/aliases.yaml
index 0c368458..3872906c 100644
--- a/grunt/aliases.yaml
+++ b/grunt/aliases.yaml
@@ -35,12 +35,12 @@ fast-build:
- 'concat:dist'
- 'ngAnnotate'
- 'uglify'
+ - 'concat:license'
+ - 'concat:license-minified'
build:
- 'fast-build'
- - 'uglify'
- 'test-unit'
- - 'concat:license'
- 'clean:pre'
travis:
diff --git a/grunt/concat.json b/grunt/concat.json
index 7dece7b3..cbb62244 100644
--- a/grunt/concat.json
+++ b/grunt/concat.json
@@ -8,11 +8,17 @@
"src/directives/leaflet.js",
"src/services/*.js",
"src/**/*.js"
-
],
"dest": "dist/angular-leaflet-directive.pre.js"
},
"license": {
+ "src": [
+ "src/header-MIT-license.txt",
+ "dist/angular-leaflet-directive.no-header.js"
+ ],
+ "dest": "dist/angular-leaflet-directive.js"
+ },
+ "license-minified": {
"src": [
"src/header-MIT-license.txt",
"dist/angular-leaflet-directive.min.no-header.js"
diff --git a/grunt/ngAnnotate.json b/grunt/ngAnnotate.json
index f9fc860d..2522a73d 100644
--- a/grunt/ngAnnotate.json
+++ b/grunt/ngAnnotate.json
@@ -2,7 +2,7 @@
"options": {},
"dist": {
"files": {
- "dist/angular-leaflet-directive.js": [ "dist/angular-leaflet-directive.pre.js" ]
+ "dist/angular-leaflet-directive.no-header.js": [ "dist/angular-leaflet-directive.pre.js" ]
}
}
}
diff --git a/grunt/uglify.json b/grunt/uglify.json
index f257af49..f53aa71c 100644
--- a/grunt/uglify.json
+++ b/grunt/uglify.json
@@ -1,10 +1,11 @@
{
- "options": {
- "banner": "/*!\n* <%= pkg.name %> <%= pkg.version %> <%= grunt.template.today(\"yyyy-mm-dd\") %>\n* <%= pkg.description %>\n* <%= pkg.repository.type %>: <%= pkg.repository.url %>\n*/\n(function(angular){\n'use strict';\n"
- },
"dist": {
+ "options": {
+ "banner": "/*!\n* <%= pkg.name %> <%= pkg.version %> <%= grunt.template.today(\"yyyy-mm-dd\") %>\n* <%= pkg.description %>\n* <%= pkg.repository.type %>: <%= pkg.repository.url %>\n*/\n(function(angular){\n'use strict';\n",
+ "footer": "\n}(angular));"
+ },
"files": {
- "dist/<%= pkg.name %>.min.no-header.js": ["dist/angular-leaflet-directive.js"]
+ "dist/<%= pkg.name %>.min.no-header.js": ["dist/<%= pkg.name %>.no-header.js"]
}
}
}