From 086f2362846a680f87b39fb42ed1fef0693ed4bd Mon Sep 17 00:00:00 2001
From: blavenie <benoit.lavenier@e-is.pro>
Date: Fri, 28 Jul 2017 13:30:25 +0200
Subject: [PATCH] [fix] remove unused npm lib [fix] commit missing JS files

---
 package.json                                  |    1 -
 www/js/vendor/leaflet.awesome-markers.min.js  |    7 +
 www/js/vendor/leaflet.easy-button.js          |  379 ++
 www/js/vendor/leaflet.loading.js              |  351 +
 www/js/vendor/ui-leaflet.min.js               |   39 +
 .../js/angular/angular-leaflet-directive.js   | 5734 +++++++++++++++++
 .../angular/angular-leaflet-directive.min.js  |   40 +
 7 files changed, 6550 insertions(+), 1 deletion(-)
 create mode 100644 www/js/vendor/leaflet.awesome-markers.min.js
 create mode 100644 www/js/vendor/leaflet.easy-button.js
 create mode 100644 www/js/vendor/leaflet.loading.js
 create mode 100644 www/js/vendor/ui-leaflet.min.js
 create mode 100644 www/lib/ionic/js/angular/angular-leaflet-directive.js
 create mode 100644 www/lib/ionic/js/angular/angular-leaflet-directive.min.js

diff --git a/package.json b/package.json
index 0834b733..09dbe32a 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,6 @@
     "gulp": "^3.9.1",
     "gulp-bump": "^2.1.0",
     "gulp-concat": "^2.2.0",
-    "gulp-load-plugins": "^1.5.0",
     "gulp-rename": "^1.2.0",
     "gulp-sass": "^2.2.0",
     "ionic": "^1.7.16",
diff --git a/www/js/vendor/leaflet.awesome-markers.min.js b/www/js/vendor/leaflet.awesome-markers.min.js
new file mode 100644
index 00000000..376e57e6
--- /dev/null
+++ b/www/js/vendor/leaflet.awesome-markers.min.js
@@ -0,0 +1,7 @@
+/*
+  Leaflet.AwesomeMarkers, a plugin that adds colorful iconic markers for Leaflet, based on the Font Awesome icons
+  (c) 2012-2013, Lennard Voogdt
+
+  http://leafletjs.com
+  https://github.com/lvoogdt
+*//*global L*/(function(e,t,n){"use strict";L.AwesomeMarkers={};L.AwesomeMarkers.version="2.0.1";L.AwesomeMarkers.Icon=L.Icon.extend({options:{iconSize:[35,45],iconAnchor:[17,42],popupAnchor:[1,-32],shadowAnchor:[10,12],shadowSize:[36,16],className:"awesome-marker",prefix:"glyphicon",spinClass:"fa-spin",icon:"home",markerColor:"blue",iconColor:"white"},initialize:function(e){e=L.Util.setOptions(this,e)},createIcon:function(){var e=t.createElement("div"),n=this.options;n.icon&&(e.innerHTML=this._createInner());n.bgPos&&(e.style.backgroundPosition=-n.bgPos.x+"px "+ -n.bgPos.y+"px");this._setIconStyles(e,"icon-"+n.markerColor);return e},_createInner:function(){var e,t="",n="",r="",i=this.options;i.icon.slice(0,i.prefix.length+1)===i.prefix+"-"?e=i.icon:e=i.prefix+"-"+i.icon;i.spin&&typeof i.spinClass=="string"&&(t=i.spinClass);i.iconColor&&(i.iconColor==="white"||i.iconColor==="black"?n="icon-"+i.iconColor:r="style='color: "+i.iconColor+"' ");return"<i "+r+"class='"+i.prefix+" "+e+" "+t+" "+n+"'></i>"},_setIconStyles:function(e,t){var n=this.options,r=L.point(n[t==="shadow"?"shadowSize":"iconSize"]),i;t==="shadow"?i=L.point(n.shadowAnchor||n.iconAnchor):i=L.point(n.iconAnchor);!i&&r&&(i=r.divideBy(2,!0));e.className="awesome-marker-"+t+" "+n.className;if(i){e.style.marginLeft=-i.x+"px";e.style.marginTop=-i.y+"px"}if(r){e.style.width=r.x+"px";e.style.height=r.y+"px"}},createShadow:function(){var e=t.createElement("div");this._setIconStyles(e,"shadow");return e}});L.AwesomeMarkers.icon=function(e){return new L.AwesomeMarkers.Icon(e)}})(this,document);
diff --git a/www/js/vendor/leaflet.easy-button.js b/www/js/vendor/leaflet.easy-button.js
new file mode 100644
index 00000000..f71e0729
--- /dev/null
+++ b/www/js/vendor/leaflet.easy-button.js
@@ -0,0 +1,379 @@
+(function(){
+
+// This is for grouping buttons into a bar
+// takes an array of `L.easyButton`s and
+// then the usual `.addTo(map)`
+L.Control.EasyBar = L.Control.extend({
+
+  options: {
+    position:       'topleft',  // part of leaflet's defaults
+    id:             null,       // an id to tag the Bar with
+    leafletClasses: true        // use leaflet classes?
+  },
+
+
+  initialize: function(buttons, options){
+
+    if(options){
+      L.Util.setOptions( this, options );
+    }
+
+    this._buildContainer();
+    this._buttons = [];
+
+    for(var i = 0; i < buttons.length; i++){
+      buttons[i]._bar = this;
+      buttons[i]._container = buttons[i].button;
+      this._buttons.push(buttons[i]);
+      this.container.appendChild(buttons[i].button);
+    }
+
+  },
+
+
+  _buildContainer: function(){
+    this._container = this.container = L.DomUtil.create('div', '');
+    this.options.leafletClasses && L.DomUtil.addClass(this.container, 'leaflet-bar easy-button-container leaflet-control');
+    this.options.id && (this.container.id = this.options.id);
+  },
+
+
+  enable: function(){
+    L.DomUtil.addClass(this.container, 'enabled');
+    L.DomUtil.removeClass(this.container, 'disabled');
+    this.container.setAttribute('aria-hidden', 'false');
+    return this;
+  },
+
+
+  disable: function(){
+    L.DomUtil.addClass(this.container, 'disabled');
+    L.DomUtil.removeClass(this.container, 'enabled');
+    this.container.setAttribute('aria-hidden', 'true');
+    return this;
+  },
+
+
+  onAdd: function () {
+    return this.container;
+  },
+
+  addTo: function (map) {
+    this._map = map;
+
+    for(var i = 0; i < this._buttons.length; i++){
+      this._buttons[i]._map = map;
+    }
+
+    var container = this._container = this.onAdd(map),
+        pos = this.getPosition(),
+        corner = map._controlCorners[pos];
+
+    L.DomUtil.addClass(container, 'leaflet-control');
+
+    if (pos.indexOf('bottom') !== -1) {
+      corner.insertBefore(container, corner.firstChild);
+    } else {
+      corner.appendChild(container);
+    }
+
+    return this;
+  }
+
+});
+
+L.easyBar = function(){
+  var args = [L.Control.EasyBar];
+  for(var i = 0; i < arguments.length; i++){
+    args.push( arguments[i] );
+  }
+  return new (Function.prototype.bind.apply(L.Control.EasyBar, args));
+};
+
+// L.EasyButton is the actual buttons
+// can be called without being grouped into a bar
+L.Control.EasyButton = L.Control.extend({
+
+  options: {
+    position:  'topleft',       // part of leaflet's defaults
+
+    id:        null,            // an id to tag the button with
+
+    type:      'replace',       // [(replace|animate)]
+                                // replace swaps out elements
+                                // animate changes classes with all elements inserted
+
+    states:    [],              // state names look like this
+                                // {
+                                //   stateName: 'untracked',
+                                //   onClick: function(){ handle_nav_manually(); };
+                                //   title: 'click to make inactive',
+                                //   icon: 'fa-circle',    // wrapped with <a>
+                                // }
+
+    leafletClasses:   true,     // use leaflet styles for the button
+    tagName:          'button',
+  },
+
+
+
+  initialize: function(icon, onClick, title, id){
+
+    // clear the states manually
+    this.options.states = [];
+
+    // add id to options
+    if(id != null){
+      this.options.id = id;
+    }
+
+    // storage between state functions
+    this.storage = {};
+
+    // is the last item an object?
+    if( typeof arguments[arguments.length-1] === 'object' ){
+
+      // if so, it should be the options
+      L.Util.setOptions( this, arguments[arguments.length-1] );
+    }
+
+    // if there aren't any states in options
+    // use the early params
+    if( this.options.states.length === 0 &&
+        typeof icon  === 'string' &&
+        typeof onClick === 'function'){
+
+      // turn the options object into a state
+      this.options.states.push({
+        icon: icon,
+        onClick: onClick,
+        title: typeof title === 'string' ? title : ''
+      });
+    }
+
+    // curate and move user's states into
+    // the _states for internal use
+    this._states = [];
+
+    for(var i = 0; i < this.options.states.length; i++){
+      this._states.push( new State(this.options.states[i], this) );
+    }
+
+    this._buildButton();
+
+    this._activateState(this._states[0]);
+
+  },
+
+  _buildButton: function(){
+
+    this.button = L.DomUtil.create(this.options.tagName, '');
+
+    // the next three if statements should be collapsed into the options 
+    // when it's time for breaking changes.
+    if (this.tagName === 'button') {
+        this.button.type = 'button';
+    }
+    
+    if (this.options.id ){
+      this.button.id = this.options.id;
+    }
+
+    if (this.options.leafletClasses){
+      L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part leaflet-interactive');
+    }
+
+    // don't let double clicks and mousedown get to the map
+    L.DomEvent.addListener(this.button, 'dblclick', L.DomEvent.stop);
+    L.DomEvent.addListener(this.button, 'mousedown', L.DomEvent.stop);
+
+    // take care of normal clicks
+    L.DomEvent.addListener(this.button,'click', function(e){
+      L.DomEvent.stop(e);
+      this._currentState.onClick(this, this._map ? this._map : null );
+      this._map.getContainer().focus();
+    }, this);
+
+    // prep the contents of the control
+    if(this.options.type == 'replace'){
+      this.button.appendChild(this._currentState.icon);
+    } else {
+      for(var i=0;i<this._states.length;i++){
+        this.button.appendChild(this._states[i].icon);
+      }
+    }
+  },
+
+
+  _currentState: {
+    // placeholder content
+    stateName: 'unnamed',
+    icon: (function(){ return document.createElement('span'); })()
+  },
+
+
+
+  _states: null, // populated on init
+
+
+
+  state: function(newState){
+
+    // activate by name
+    if(typeof newState == 'string'){
+
+      this._activateStateNamed(newState);
+
+    // activate by index
+    } else if (typeof newState == 'number'){
+
+      this._activateState(this._states[newState]);
+    }
+
+    return this;
+  },
+
+
+  _activateStateNamed: function(stateName){
+    for(var i = 0; i < this._states.length; i++){
+      if( this._states[i].stateName == stateName ){
+        this._activateState( this._states[i] );
+      }
+    }
+  },
+
+  _activateState: function(newState){
+
+    if( newState === this._currentState ){
+
+      // don't touch the dom if it'll just be the same after
+      return;
+
+    } else {
+
+      // swap out elements... if you're into that kind of thing
+      if( this.options.type == 'replace' ){
+        this.button.appendChild(newState.icon);
+        this.button.removeChild(this._currentState.icon);
+      }
+
+      if( newState.title ){
+        this.button.title = newState.title;
+      } else {
+        this.button.removeAttribute('title');
+      }
+
+      // update classes for animations
+      for(var i=0;i<this._states.length;i++){
+        L.DomUtil.removeClass(this._states[i].icon, this._currentState.stateName + '-active');
+        L.DomUtil.addClass(this._states[i].icon, newState.stateName + '-active');
+      }
+
+      // update classes for animations
+      L.DomUtil.removeClass(this.button, this._currentState.stateName + '-active');
+      L.DomUtil.addClass(this.button, newState.stateName + '-active');
+
+      // update the record
+      this._currentState = newState;
+
+    }
+  },
+
+
+
+  enable: function(){
+    L.DomUtil.addClass(this.button, 'enabled');
+    L.DomUtil.removeClass(this.button, 'disabled');
+    this.button.setAttribute('aria-hidden', 'false');
+    return this;
+  },
+
+
+
+  disable: function(){
+    L.DomUtil.addClass(this.button, 'disabled');
+    L.DomUtil.removeClass(this.button, 'enabled');
+    this.button.setAttribute('aria-hidden', 'true');
+    return this;
+  },
+
+
+  removeFrom: function (map) {
+
+    this._container.parentNode.removeChild(this._container);
+    this._map = null;
+
+    return this;
+  },
+
+  onAdd: function(){
+    var containerObj = L.easyBar([this], {
+      position: this.options.position,
+      leafletClasses: this.options.leafletClasses
+    });
+    this._container = containerObj.container;
+    return this._container;
+  }
+
+
+});
+
+L.easyButton = function(/* args will pass automatically */){
+  var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments);
+  return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
+};
+
+/*************************
+ *
+ * util functions
+ *
+ *************************/
+
+// constructor for states so only curated
+// states end up getting called
+function State(template, easyButton){
+
+  this.title = template.title;
+  this.stateName = template.stateName ? template.stateName : 'unnamed-state';
+
+  // build the wrapper
+  this.icon = L.DomUtil.create('span', '');
+
+  L.DomUtil.addClass(this.icon, 'button-state state-' + this.stateName.replace(/(^\s*|\s*$)/g,''));
+  this.icon.innerHTML = buildIcon(template.icon);
+  this.onClick = L.Util.bind(template.onClick?template.onClick:function(){}, easyButton);
+}
+
+function buildIcon(ambiguousIconString) {
+
+  var tmpIcon;
+
+  // does this look like html? (i.e. not a class)
+  if( ambiguousIconString.match(/[&;=<>"']/) ){
+
+    // if so, the user should have put in html
+    // so move forward as such
+    tmpIcon = ambiguousIconString;
+
+  // then it wasn't html, so
+  // it's a class list, figure out what kind
+  } else {
+      ambiguousIconString = ambiguousIconString.replace(/(^\s*|\s*$)/g,'');
+      tmpIcon = L.DomUtil.create('span', '');
+
+      if( ambiguousIconString.indexOf('fa-') === 0 ){
+        L.DomUtil.addClass(tmpIcon, 'fa '  + ambiguousIconString)
+      } else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
+        L.DomUtil.addClass(tmpIcon, 'glyphicon ' + ambiguousIconString)
+      } else {
+        L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
+      }
+
+      // make this a string so that it's easy to set innerHTML below
+      tmpIcon = tmpIcon.outerHTML;
+  }
+
+  return tmpIcon;
+}
+
+})();
diff --git a/www/js/vendor/leaflet.loading.js b/www/js/vendor/leaflet.loading.js
new file mode 100644
index 00000000..7fb7b780
--- /dev/null
+++ b/www/js/vendor/leaflet.loading.js
@@ -0,0 +1,351 @@
+/*
+ * L.Control.Loading is a control that shows a loading indicator when tiles are
+ * loading or when map-related AJAX requests are taking place.
+ */
+
+(function () {
+
+    var console = window.console || {
+        error: function () {},
+        warn: function () {}
+    };
+
+    function defineLeafletLoading(L) {
+        L.Control.Loading = L.Control.extend({
+            options: {
+                delayIndicator: null,
+                position: 'topleft',
+                separate: false,
+                zoomControl: null,
+                spinjs: false,
+                spin: { 
+                    lines: 7, 
+                    length: 3, 
+                    width: 3, 
+                    radius: 5, 
+                    rotate: 13, 
+                    top: "83%"
+                }
+            },
+
+            initialize: function(options) {
+                L.setOptions(this, options);
+                this._dataLoaders = {};
+
+                // Try to set the zoom control this control is attached to from the 
+                // options
+                if (this.options.zoomControl !== null) {
+                    this.zoomControl = this.options.zoomControl;
+                }
+            },
+
+            onAdd: function(map) {
+                if (this.options.spinjs && (typeof Spinner !== 'function')) {
+                    return console.error("Leaflet.loading cannot load because you didn't load spin.js (http://fgnass.github.io/spin.js/), even though you set it in options.");
+                }
+                this._addLayerListeners(map);
+                this._addMapListeners(map);
+
+                // Try to set the zoom control this control is attached to from the map
+                // the control is being added to
+                if (!this.options.separate && !this.zoomControl) {
+                    if (map.zoomControl) {
+                        this.zoomControl = map.zoomControl;
+                    } else if (map.zoomsliderControl) {
+                        this.zoomControl = map.zoomsliderControl;
+                    }
+                }
+
+                // Create the loading indicator
+                var classes = 'leaflet-control-loading';
+                var container;
+                if (this.zoomControl && !this.options.separate) {
+                    // If there is a zoom control, hook into the bottom of it
+                    container = this.zoomControl._container;
+                    // These classes are no longer used as of Leaflet 0.6
+                    classes += ' leaflet-bar-part-bottom leaflet-bar-part last';
+
+                    // Loading control will be added to the zoom control. So the visible last element is not the
+                    // last dom element anymore. So add the part-bottom class.
+                    L.DomUtil.addClass(this._getLastControlButton(), 'leaflet-bar-part-bottom');
+                }
+                else {
+                    // Otherwise, create a container for the indicator
+                    container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-control-layer-container leaflet-bar');
+                }
+                this._indicatorContainer = container;
+                this._indicator = L.DomUtil.create('a', classes, container);
+                if (this.options.spinjs) {
+                    this._spinner = new Spinner(this.options.spin).spin();
+                    this._indicator.appendChild(this._spinner.el);
+                }
+                return container;
+            },
+
+            onRemove: function(map) {
+                this._removeLayerListeners(map);
+                this._removeMapListeners(map);
+            },
+
+            removeFrom: function (map) {
+                if (this.zoomControl && !this.options.separate) {
+                    // Override Control.removeFrom() to avoid clobbering the entire
+                    // _container, which is the same as zoomControl's
+                    this._container.removeChild(this._indicator);
+                    this._map = null;
+                    this.onRemove(map);
+                    return this;
+                }
+                else {
+                    // If this control is separate from the zoomControl, call the
+                    // parent method so we don't leave behind an empty container
+                    return L.Control.prototype.removeFrom.call(this, map);
+                }
+            },
+
+            addLoader: function(id) {
+                this._dataLoaders[id] = true;
+                if (this.options.delayIndicator && !this.delayIndicatorTimeout) {
+                    // If we are delaying showing the indicator and we're not
+                    // already waiting for that delay, set up a timeout.
+                    var that = this;
+                    this.delayIndicatorTimeout = setTimeout(function () {
+                        that.updateIndicator();
+                        that.delayIndicatorTimeout = null;
+                    }, this.options.delayIndicator);
+                }
+                else {
+                    // Otherwise show the indicator immediately
+                    this.updateIndicator();
+                }
+            },
+
+            removeLoader: function(id) {
+                delete this._dataLoaders[id];
+                this.updateIndicator();
+
+                // If removing this loader means we're in no danger of loading,
+                // clear the timeout. This prevents old delays from instantly 
+                // triggering the indicator.
+                if (this.options.delayIndicator && this.delayIndicatorTimeout && !this.isLoading()) {
+                    clearTimeout(this.delayIndicatorTimeout);
+                    this.delayIndicatorTimeout = null;
+                }
+            },
+
+            updateIndicator: function() {
+                if (this.isLoading()) {
+                    this._showIndicator();
+                }
+                else {
+                    this._hideIndicator();
+                }
+            },
+
+            isLoading: function() {
+                return this._countLoaders() > 0;
+            },
+
+            _countLoaders: function() {
+                var size = 0, key;
+                for (key in this._dataLoaders) {
+                    if (this._dataLoaders.hasOwnProperty(key)) size++;
+                }
+                return size;
+            },
+
+            _showIndicator: function() {
+                // Show loading indicator
+                L.DomUtil.addClass(this._indicator, 'is-loading');
+                L.DomUtil.addClass(this._indicatorContainer, 'is-loading');
+
+                // If zoomControl exists, make the zoom-out button not last
+                if (!this.options.separate) {
+                    if (this.zoomControl instanceof L.Control.Zoom) {
+                        L.DomUtil.removeClass(this._getLastControlButton(), 'leaflet-bar-part-bottom');
+                    }
+                    else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {
+                        L.DomUtil.removeClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');
+                    }
+                }
+            },
+
+            _hideIndicator: function() {
+                // Hide loading indicator
+                L.DomUtil.removeClass(this._indicator, 'is-loading');
+                L.DomUtil.removeClass(this._indicatorContainer, 'is-loading');
+
+                // If zoomControl exists, make the zoom-out button last
+                if (!this.options.separate) {
+                    if (this.zoomControl instanceof L.Control.Zoom) {
+                        L.DomUtil.addClass(this._getLastControlButton(), 'leaflet-bar-part-bottom');
+                    }
+                    else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {
+                        L.DomUtil.addClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');
+                    }
+                }
+            },
+
+            _getLastControlButton: function() {
+                var container = this.zoomControl._container,
+                    index = container.children.length - 1;
+
+                // Find the last visible control button that is not our loading
+                // indicator
+                while (index > 0) {
+                    var button = container.children[index];
+                    if (!(this._indicator === button || button.offsetWidth === 0 || button.offsetHeight === 0)) {
+                        break;
+                    }
+                    index--;
+                }
+
+                return container.children[index];
+            },
+
+            _handleLoading: function(e) {
+                this.addLoader(this.getEventId(e));
+            },
+
+            _handleBaseLayerChange: function (e) {
+                var that = this;
+
+                // Check for a target 'layer' that contains multiple layers, such as
+                // L.LayerGroup. This will happen if you have an L.LayerGroup in an
+                // L.Control.Layers.
+                if (e.layer && e.layer.eachLayer && typeof e.layer.eachLayer === 'function') {
+                    e.layer.eachLayer(function (layer) {
+                        that._handleBaseLayerChange({ layer: layer });
+                    });
+                }
+                else {
+                    // If we're changing to a canvas layer, don't handle loading
+                    // as canvas layers will not fire load events.
+                    if (!(L.TileLayer.Canvas && e.layer instanceof L.TileLayer.Canvas)) {
+                        that._handleLoading(e);
+                    }
+                }
+            },
+
+            _handleLoad: function(e) {
+                this.removeLoader(this.getEventId(e));
+            },
+
+            getEventId: function(e) {
+                if (e.id) {
+                    return e.id;
+                }
+                else if (e.layer) {
+                    return e.layer._leaflet_id;
+                }
+                return e.target._leaflet_id;
+            },
+
+            _layerAdd: function(e) {
+                if (!e.layer || !e.layer.on) return
+                try {
+                    e.layer.on({
+                        loading: this._handleLoading,
+                        load: this._handleLoad
+                    }, this);
+                }
+                catch (exception) {
+                    console.warn('L.Control.Loading: Tried and failed to add ' +
+                                 ' event handlers to layer', e.layer);
+                    console.warn('L.Control.Loading: Full details', exception);
+                }
+            },
+
+            _layerRemove: function(e) {
+                if (!e.layer || !e.layer.off) return;
+                try {
+                    e.layer.off({
+                        loading: this._handleLoading,
+                        load: this._handleLoad
+                    }, this);
+                }
+                catch (exception) {
+                    console.warn('L.Control.Loading: Tried and failed to remove ' +
+                                 'event handlers from layer', e.layer);
+                    console.warn('L.Control.Loading: Full details', exception);
+                }
+            },
+
+            _addLayerListeners: function(map) {
+                // Add listeners for begin and end of load to any layers already on the 
+                // map
+                map.eachLayer(function(layer) {
+                    if (!layer.on) return;
+                    layer.on({
+                        loading: this._handleLoading,
+                        load: this._handleLoad
+                    }, this);
+                }, this);
+
+                // When a layer is added to the map, add listeners for begin and end
+                // of load
+                map.on('layeradd', this._layerAdd, this);
+                map.on('layerremove', this._layerRemove, this);
+            },
+
+            _removeLayerListeners: function(map) {
+                // Remove listeners for begin and end of load from all layers
+                map.eachLayer(function(layer) {
+                    if (!layer.off) return;
+                    layer.off({
+                        loading: this._handleLoading,
+                        load: this._handleLoad
+                    }, this);
+                }, this);
+
+                // Remove layeradd/layerremove listener from map
+                map.off('layeradd', this._layerAdd, this);
+                map.off('layerremove', this._layerRemove, this);
+            },
+
+            _addMapListeners: function(map) {
+                // Add listeners to the map for (custom) dataloading and dataload
+                // events, eg, for AJAX calls that affect the map but will not be
+                // reflected in the above layer events.
+                map.on({
+                    baselayerchange: this._handleBaseLayerChange,
+                    dataloading: this._handleLoading,
+                    dataload: this._handleLoad,
+                    layerremove: this._handleLoad
+                }, this);
+            },
+
+            _removeMapListeners: function(map) {
+                map.off({
+                    baselayerchange: this._handleBaseLayerChange,
+                    dataloading: this._handleLoading,
+                    dataload: this._handleLoad,
+                    layerremove: this._handleLoad
+                }, this);
+            }
+        });
+
+        L.Map.addInitHook(function () {
+            if (this.options.loadingControl) {
+                this.loadingControl = new L.Control.Loading();
+                this.addControl(this.loadingControl);
+            }
+        });
+
+        L.Control.loading = function(options) {
+            return new L.Control.Loading(options);
+        };
+    }
+
+    if (typeof define === 'function' && define.amd) {
+        // Try to add leaflet.loading to Leaflet using AMD
+        define(['leaflet'], function (L) {
+            defineLeafletLoading(L);
+        });
+    }
+    else {
+        // Else use the global L
+        defineLeafletLoading(L);
+    }
+
+})();
diff --git a/www/js/vendor/ui-leaflet.min.js b/www/js/vendor/ui-leaflet.min.js
new file mode 100644
index 00000000..3c28209f
--- /dev/null
+++ b/www/js/vendor/ui-leaflet.min.js
@@ -0,0 +1,39 @@
+/**!
+ * The MIT License
+ *
+ * Copyright (c) the ui-leaflet Team, http://angular-ui.github.io/ui-leaflet
+ *
+ * Original Copyright (c) https://github.com/angular-ui/ui-leaflet
+ *
+ * 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.
+ *
+ * ui-leaflet
+ * https://github.com/angular-ui/ui-leaflet
+ *
+ * @authors https://github.com/angular-ui/ui-leaflet/graphs/contributors
+ */
+
+/*!
+*  ui-leaflet 2.0.0 2016-10-04
+*  ui-leaflet - An AngularJS directive to easily interact with Leaflet maps
+*  git: https://github.com/angular-ui/ui-leaflet
+*/
+!function(a){"use strict";a.module("ui-leaflet",["nemLogging"]).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:"=",watchOptions:"=",id:"@"},transclude:!0,template:'<div class="angular-leaflet-map"><div ng-transclude></div></div>',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);var o=new L.Map(f[0],c.getMapCreationDefaults(g.id));if(h._leafletMap.resolve(o),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()})),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,g.id,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()})}}}]),function(){a.module("ui-leaflet").factory("eventManager",[function(){var a=function(){this.listeners={}};return a.prototype={addEventListener:function(a,b,c){for(var d=[],e=arguments.length,f=0;f<e;f++)d.push(arguments[f]);d=d.length>3?d.splice(3,d.length-1):[],"undefined"!=typeof this.listeners[a]?this.listeners[a].push({scope:c,callback:b,args:d}):this.listeners[a]=[{scope:c,callback:b,args:d}]},removeEventListener:function(a,b,c){if("undefined"!=typeof this.listeners[a]){for(var d=this.listeners[a].length,e=[],f=0;f<d;f++){var g=this.listeners[a][f];g.scope===c&&g.callback===b||e.push(g)}this.listeners[a]=e}},hasEventListener:function(a,b,c){if("undefined"!=typeof this.listeners[a]){var d=this.listeners[a].length;if(void 0===b&&void 0===c)return d>0;for(var e=0;e<d;e++){var f=this.listeners[a][e];if((!c||f.scope===c)&&f.callback===b)return!0}}return!1},dispatch:function(a,b){for(var c=0,d={type:a,target:b},e=[],f=arguments.length,g=0;g<f;g++)e.push(arguments[g]);if(e=e.length>2?e.splice(2,e.length-1):[],e=[d].concat(e),"undefined"!=typeof this.listeners[a])for(var h=this.listeners[a].length,i=0;i<h;i++){var j=this.listeners[a][i];if(j&&j.callback){var k=e.concat(j.args);j.callback.apply(j.scope,k),c+=1}}},getEvents:function(){var a="";for(var b in this.listeners)for(var c=this.listeners[b].length,d=0;d<c;d++){var e=this.listeners[b][d];a+=e.scope&&e.scope.className?e.scope.className:"anonymous",a+=" listen for '"+b+"'\n"}return a}},a}]).service("eventManager",["EventManager",function(a){return new a}])}(),a.module("ui-leaflet").factory("leafletBoundsHelpers",["leafletLogger","leafletHelpers",function(b,c){function d(b){return a.isDefined(b)&&a.isDefined(b.southWest)&&a.isDefined(b.northEast)&&a.isNumber(b.southWest.lat)&&a.isNumber(b.southWest.lng)&&a.isNumber(b.northEast.lat)&&a.isNumber(b.northEast.lng)}var e=c.isArray,f=c.isNumber,g=c.isFunction,h=c.isDefined,i=b;return{createLeafletBounds:function(a){if(d(a))return L.latLngBounds([a.southWest.lat,a.southWest.lng],[a.northEast.lat,a.northEast.lng])},isValidBounds:d,createBoundsFromArray:function(a){return e(a)&&2===a.length&&e(a[0])&&e(a[1])&&2===a[0].length&&2===a[1].length&&f(a[0][0])&&f(a[0][1])&&f(a[1][0])&&f(a[1][1])?{northEast:{lat:a[0][0],lng:a[0][1]},southWest:{lat:a[1][0],lng:a[1][1]}}:void i.error("[AngularJS - Leaflet] The bounds array is not valid.")},createBoundsFromLeaflet:function(a){if(!(h(a)&&g(a.getNorthEast)&&g(a.getSouthWest)))return void i.error("[AngularJS - Leaflet] The leaflet bounds is not valid object.");var b=a.getNorthEast(),c=a.getSouthWest();return{northEast:{lat:b.lat,lng:b.lng},southWest:{lat:c.lat,lng:c.lng}}}}}]),a.module("ui-leaflet").factory("leafletControlHelpers",["$rootScope","leafletLogger","leafletHelpers","leafletLayerHelpers","leafletMapDefaults",function(b,c,d,e,f){var g=d.isDefined,h=d.isObject,i=e.createLayer,j={},k=d.errorHeader+" [Controls] ",l=c,m=function(a,b,c){var d=f.getDefaults(c);if(!d.controls.layers.visible)return!1;var e=!1;return h(a)&&Object.keys(a).forEach(function(b){var c=a[b];g(c.layerOptions)&&c.layerOptions.showOnSelector===!1||(e=!0)}),h(b)&&Object.keys(b).forEach(function(a){var c=b[a];g(c.layerParams)&&c.layerParams.showOnSelector===!1||(e=!0)}),e},n=function(b){var c=f.getDefaults(b),d={collapsed:c.controls.layers.collapsed,position:c.controls.layers.position,autoZIndex:!1};a.extend(d,c.controls.layers.options);var e;return e=c.controls.layers&&g(c.controls.layers.control)?c.controls.layers.control.apply(this,[[],[],d]):new L.control.layers([],[],d)},o={draw:{isPluginLoaded:function(){return!!a.isDefined(L.Control.Draw)||(l.error(k+" 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!!a.isDefined(L.Control.Fullscreen)||(l.error(k+" Fullscreen plugin is not loaded."),!1)},checkValidParams:function(){return!0},createControl:function(a){return new L.Control.Fullscreen(a)}},search:{isPluginLoaded:function(){return!!a.isDefined(L.Control.Search)||(l.error(k+" Search plugin is not loaded."),!1)},checkValidParams:function(){return!0},createControl:function(a){return new L.Control.Search(a)}},custom:{},minimap:{isPluginLoaded:function(){return!!a.isDefined(L.Control.MiniMap)||(l.error(k+" Minimap plugin is not loaded."),!1)},checkValidParams:function(a){return!!g(a.layer)||(l.warn(k+' minimap "layer" option should be defined.'),!1)},createControl:function(a){var b=i(a.layer);return g(b)?new L.Control.MiniMap(b,a):void l.warn(k+' minimap control "layer" could not be created.')}}};return{layersControlMustBeVisible:m,isValidControlType:function(a){return Object.keys(o).indexOf(a)!==-1},createControl:function(a,b){if(o[a].checkValidParams(b))return o[a].createControl(b)},updateLayersControl:function(a,b,c,d,e,f){var h,i=j[b],k=m(d,e,b);if(g(i)&&c){for(h in f.baselayers)i.removeLayer(f.baselayers[h]);for(h in f.overlays)i.removeLayer(f.overlays[h]);a.removeControl(i),delete j[b]}if(k){i=n(b),j[b]=i;for(h in d){var l=g(d[h].layerOptions)&&d[h].layerOptions.showOnSelector===!1;!l&&g(f.baselayers[h])&&i.addBaseLayer(f.baselayers[h],d[h].name)}for(h in e){var o=g(e[h].layerParams)&&e[h].layerParams.showOnSelector===!1;!o&&g(f.overlays[h])&&i.addOverlay(f.overlays[h],e[h].name)}a.addControl(i)}return k},destroyMapLayersControl:function(a){delete j[a]}}}]),a.module("ui-leaflet").service("leafletData",["leafletLogger","$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}})}]),a.module("ui-leaflet").service("leafletDirectiveControlsHelpers",["leafletLogger","leafletData","leafletHelpers",function(b,c,d){var e=d.isDefined,f=d.isString,g=d.isObject,h=d.errorHeader,i=b,j=h+"[leafletDirectiveControlsHelpers",k=function(b,d,h,k){var l=j+".extend] ",m={};if(!e(d))return void i.error(l+"thingToAddName cannot be undefined");if(f(d)&&e(h)&&e(k))m[d]={create:h,clean:k};else{if(!g(d)||e(h)||e(k))return void i.error(l+"incorrect arguments");m=d}c.getDirectiveControls().then(function(d){a.extend(d,m),c.setDirectiveControls(d,b)})};return{extend:k}}]),a.module("ui-leaflet").service("leafletGeoJsonHelpers",["leafletHelpers","leafletIterators",function(b,c){var d=b,e=c,f=function(a,b){return this.lat=a,this.lng=b,this},g=function(a){return Array.isArray(a)&&2===a.length?a[1]:d.isDefined(a.type)&&"Point"===a.type?+a.coordinates[1]:+a.lat},h=function(a){return Array.isArray(a)&&2===a.length?a[0]:d.isDefined(a.type)&&"Point"===a.type?+a.coordinates[0]:+a.lng},i=function(a){if(d.isUndefined(a))return!1;if(d.isArray(a)){if(2===a.length&&d.isNumber(a[0])&&d.isNumber(a[1]))return!0}else if(d.isDefined(a.type)&&"Point"===a.type&&d.isArray(a.coordinates)&&2===a.coordinates.length&&d.isNumber(a.coordinates[0])&&d.isNumber(a.coordinates[1]))return!0;var b=e.all(["lat","lng"],function(b){return d.isDefined(a[b])&&d.isNumber(a[b])});return b},j=function(b){if(b&&i(b)){var c=null;if(Array.isArray(b)&&2===b.length)c=new f(b[1],b[0]);else{if(!d.isDefined(b.type)||"Point"!==b.type)return b;c=new f(b.coordinates[1],b.coordinates[0])}return a.extend(b,c)}};return{getLat:g,getLng:h,validateCoords:i,getCoords:j}}]),a.module("ui-leaflet").service("leafletHelpers",["$q","$log","$timeout",function(b,c,d){function e(b,d){var e,f;if(a.isDefined(d))e=d;else if(0===Object.keys(b).length)e="main";else if(Object.keys(b).length>=1)for(f in b)b.hasOwnProperty(f)&&(e=f);else c.error(g+"- You have more than 1 map on the DOM, you must provide the map ID to the leafletData.getXXX call");return e}function f(c,d){var f,g=e(c,d);return a.isDefined(c[g])&&c[g].resolvedDefer!==!0?f=c[g].defer:(f=b.defer(),c[g]={defer:f,resolvedDefer:!1}),f}var g="[ui-leaflet] ",h=a.copy,i=h,j=function(b,c){var d;if(b&&a.isObject(b))return null!==c&&a.isString(c)?(d=b,c.split(".").forEach(function(a){d&&(d=d[a])}),d):c},k=function(a){return a.split(".").reduce(function(a,b){return a+'["'+b+'"]'})},l=function(a){return a.reduce(function(a,b){return a+"."+b})},m=function(b){return a.isDefined(b)&&null!==b},n=function(a){return!m(a)},o=/([\:\-\_]+(.))/g,p=/^moz([A-Z])/,q=/^((?:x|data)[\:\-_])/i,r=function(a){return a.replace(o,function(a,b,c,d){return d?c.toUpperCase():c}).replace(p,"Moz$1")},s=function(a){return r(a.replace(q,""))},t=10,u=function(a,b,c){if(!a)throw new Error(g+"trapObj is undefined");if(!b)throw new Error(g+"trapField is undefined");a[b]=!0;var e=c();return d(function(){a[b]=!1},t),e};return{watchTrapDelayMilliSec:t,modelChangeInDirective:u,camelCase:r,directiveNormalize:s,copy:h,clone:i,errorHeader:g,getObjectValue:j,getObjectArrayPath:k,getObjectDotPath:l,defaultTo:function(a,b){return m(a)?a:b},isTruthy:function(a){return"true"===a||a===!0},isEmpty:function(a){return 0===Object.keys(a).length},isUndefinedOrEmpty:function(b){return a.isUndefined(b)||null===b||0===Object.keys(b).length},isDefined:m,isUndefined:n,isNumber:a.isNumber,isString:a.isString,isArray:a.isArray,isObject:a.isObject,isFunction:a.isFunction,equals:a.equals,isValidCenter:function(b){return a.isDefined(b)&&a.isNumber(b.lat)&&a.isNumber(b.lng)&&a.isNumber(b.zoom)},isValidPoint:function(b){return!!a.isDefined(b)&&(a.isArray(b)?2===b.length&&a.isNumber(b[0])&&a.isNumber(b[1]):a.isNumber(b.lat)&&a.isNumber(b.lng))},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)},safeApply:function(a,b){var c=a.$root.$$phase;"$apply"===c||"$digest"===c?a.$eval(b):a.$evalAsync(b)},obtainEffectiveMapId:e,getDefer:function(b,c){var d,g=e(b,c);return d=a.isDefined(b[g])&&b[g].resolvedDefer!==!1?b[g].defer:f(b,c)},getUnresolvedDefer:f,setResolvedDefer:function(a,b){var c=e(a,b);a[c].resolvedDefer=!0},rangeIsSupported:function(){var a=document.createElement("input");return a.setAttribute("type","range"),"range"===a.type},FullScreenControlPlugin:{isLoaded:function(){return a.isDefined(L.Control.Fullscreen)}},MiniMapControlPlugin:{isLoaded:function(){return a.isDefined(L.Control.MiniMap)}},AwesomeMarkersPlugin:{isLoaded:function(){return a.isDefined(L.AwesomeMarkers)&&a.isDefined(L.AwesomeMarkers.Icon)},is:function(a){return!!this.isLoaded()&&a instanceof L.AwesomeMarkers.Icon},equal:function(b,c){return!!this.isLoaded()&&(!!this.is(b)&&a.equals(b,c))}},VectorMarkersPlugin:{isLoaded:function(){return a.isDefined(L.VectorMarkers)&&a.isDefined(L.VectorMarkers.Icon)},is:function(a){return!!this.isLoaded()&&a instanceof L.VectorMarkers.Icon},equal:function(b,c){return!!this.isLoaded()&&(!!this.is(b)&&a.equals(b,c))}},DomMarkersPlugin:{isLoaded:function(){return!(!a.isDefined(L.DomMarkers)||!a.isDefined(L.DomMarkers.Icon))},is:function(a){return!!this.isLoaded()&&a instanceof L.DomMarkers.Icon},equal:function(b,c){return!!this.isLoaded()&&(!!this.is(b)&&a.equals(b,c))}},PolylineDecoratorPlugin:{isLoaded:function(){return!!a.isDefined(L.PolylineDecorator)},is:function(a){return!!this.isLoaded()&&a instanceof L.PolylineDecorator},equal:function(b,c){return!!this.isLoaded()&&(!!this.is(b)&&a.equals(b,c))}},MakiMarkersPlugin:{isLoaded:function(){return!(!a.isDefined(L.MakiMarkers)||!a.isDefined(L.MakiMarkers.Icon))},is:function(a){return!!this.isLoaded()&&a instanceof L.MakiMarkers.Icon},equal:function(b,c){return!!this.isLoaded()&&(!!this.is(b)&&a.equals(b,c))}},ExtraMarkersPlugin:{isLoaded:function(){return!(!a.isDefined(L.ExtraMarkers)||!a.isDefined(L.ExtraMarkers.Icon))},is:function(a){return!!this.isLoaded()&&a instanceof L.ExtraMarkers.Icon},equal:function(b,c){return!!this.isLoaded()&&(!!this.is(b)&&a.equals(b,c))}},LabelPlugin:{isLoaded:function(){return a.isDefined(L.Label)},is:function(a){return!!this.isLoaded()&&a instanceof L.MarkerClusterGroup}},MarkerClusterPlugin:{isLoaded:function(){return a.isDefined(L.MarkerClusterGroup)},is:function(a){return!!this.isLoaded()&&a instanceof L.MarkerClusterGroup}},GeoJSONPlugin:{isLoaded:function(){return a.isDefined(L.TileLayer.GeoJSON)},is:function(a){return!!this.isLoaded()&&a instanceof L.TileLayer.GeoJSON}},CartoDB:{isLoaded:function(){return cartodb},is:function(){return!0}},Leaflet:{DivIcon:{is:function(a){return a instanceof L.DivIcon},equal:function(b,c){return!!this.is(b)&&a.equals(b,c)}},Icon:{is:function(a){return a instanceof L.Icon},equal:function(b,c){return!!this.is(b)&&a.equals(b,c)}}},watchOptions:{type:"watchDeep",individual:{type:"watchDeep"}}}}]),a.module("ui-leaflet").service("leafletIterators",["leafletLogger","leafletHelpers",function(a,b){var c,d=b,e=b.errorHeader+"leafletIterators: ",f=Object.keys,g=d.isFunction,h=d.isObject,i=a,j=Math.pow(2,53)-1,k=function(a){var b=null!==a&&a.length;return d.isNumber(b)&&b>=0&&b<=j},l=function(a){return a},m=function(a){return function(b){return null===b?void 0:b[a]}},n=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)}},o=function(a,b){return function(c){var d=arguments.length;if(d<2||null===c)return c;for(var e=1;e<d;e++)for(var f=arguments[e],g=a(f),h=g.length,i=0;i<h;i++){var j=g[i];b&&void 0!==c[j]||(c[j]=f[j])}return c}},p=null;c=p=o(f);var q,r=function(a,b){var c=f(b),d=c.length;if(null===a)return!d;for(var e=Object(a),g=0;g<d;g++){var h=c[g];if(b[h]!==e[h]||!(h in e))return!1}return!0},s=null;q=s=function(a){return a=c({},a),function(b){return r(b,a)}};var t,u=function(a,b,c){return null===a?l:g(a)?n(a,b,c):h(a)?q(a):m(a)},v=null;t=v=function(a,b,c){b=u(b,c);for(var d=!k(a)&&f(a),e=(d||a).length,g=0;g<e;g++){var h=d?d[g]:g;if(!b(a[h],h,a))return!1}return!0};var w=function(a,b,c,f){return!(c||d.isDefined(a)&&d.isDefined(b))||!d.isFunction(b)&&(f=d.defaultTo(b,"cb"),i.error(e+f+" is not a function"),!0)},x=function(a,b,c){if(!w(void 0,c,!0,"internalCb")&&!w(a,b))for(var d in a)a.hasOwnProperty(d)&&c(a[d],d)},y=function(a,b){x(a,b,function(a,c){b(a,c)})};return{each:y,forEach:y,every:t,all:v}}]),a.module("ui-leaflet").factory("leafletLayerHelpers",["$rootScope","$q","leafletLogger","leafletHelpers","leafletIterators",function(b,c,d,e,f){function g(a){return l(a.type)?Object.keys(t).indexOf(a.type)===-1?(r.error("[AngularJS - Leaflet] A layer must have a valid type: "+Object.keys(t)),!1):t[a.type].mustHaveUrl&&!l(a.url)?(r.error("[AngularJS - Leaflet] A base layer must have an url"),!1):t[a.type].mustHaveData&&!o(a.data)?(r.error('[AngularJS - Leaflet] The base layer must have a "data" array attribute'),!1):t[a.type].mustHaveLayer&&!o(a.layer)?(r.error("[AngularJS - Leaflet] The type of layer "+a.type+" must have an layer defined"),!1):t[a.type].mustHaveBounds&&!o(a.bounds)?(r.error("[AngularJS - Leaflet] The type of layer "+a.type+" must have bounds defined"),!1):!(t[a.type].mustHaveKey&&!o(a.key))||(r.error("[AngularJS - Leaflet] The type of layer "+a.type+" must have key defined"),!1):(r.error("[AngularJS - Leaflet] A layer must have a valid type defined."),!1)}function h(a){if(g(a)){if(!l(a.name))return void r.error("[AngularJS - Leaflet] A base layer must have a name");m(a.layerParams)||(a.layerParams={}),m(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,$parent:a};return t[a.type].createLayer(c)}}function i(a,b){b&&"function"==typeof b.addTo?b.addTo(a):a.addLayer(b)}function j(b,c,d){if(o(d)&&o(d.loadedDefer))if(a.isFunction(d.loadedDefer)){var e=d.loadedDefer();r.debug("Loaded Deferred",e);var f=e.length;if(f>0)for(var g=function(){f--,0===f&&b.removeLayer(c)},h=0;h<e.length;h++)e[h].promise.then(g);else b.removeLayer(c)}else d.loadedDefer.promise.then(function(){b.removeLayer(c)});else b.removeLayer(c)}var k=e,l=e.isString,m=e.isObject,n=e.isArray,o=e.isDefined,p=e.errorHeader,q=f,r=d,s=function(c){if(!k.UTFGridPlugin.isLoaded())return void r.error("[AngularJS - Leaflet] The UTFGrid plugin is not loaded.");var d=new L.UtfGrid(c.url,c.pluginOptions),e={model:c.$parent};return d.on("mouseover",function(c){a.extend(e,{leafletEvent:c,leafletObject:c.target}),b.$broadcast("leafletDirectiveMap.utfgridMouseover",e)}),d.on("mouseout",function(c){a.extend(e,{leafletEvent:c,leafletObject:c.target}),b.$broadcast("leafletDirectiveMap.utfgridMouseout",e)}),d.on("click",function(c){a.extend(e,{leafletEvent:c,leafletObject:c.target}),b.$broadcast("leafletDirectiveMap.utfgridClick",e)}),d.on("mousemove",function(c){a.extend(e,{leafletEvent:c,leafletObject:c.target}),b.$broadcast("leafletDirectiveMap.utfgridMousemove",e)}),d},t={xyz:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer(a.url,a.options)}},geoJSON:{mustHaveUrl:!0,createLayer:function(a){if(k.GeoJSONPlugin.isLoaded())return new L.TileLayer.GeoJSON(a.url,a.pluginOptions,a.options)}},geoJSONShape:{mustHaveUrl:!1,createLayer:function(a){return new L.GeoJSON(a.data,a.options)}},geoJSONAwesomeMarker:{mustHaveUrl:!1,createLayer:function(a){return new L.geoJson(a.data,{pointToLayer:function(b,c){return L.marker(c,{icon:L.AwesomeMarkers.icon(a.icon)})}})}},geoJSONVectorMarker:{mustHaveUrl:!1,createLayer:function(a){return new L.geoJson(a.data,{pointToLayer:function(b,c){return L.marker(c,{icon:L.VectorMarkers.icon(a.icon)})}})}},cartodbTiles:{mustHaveKey:!0,createLayer:function(a){var b=o(a.url)?a.url+"/"+a.user:"//"+a.user+".cartodb.com";return b+="/api/v1/map/"+a.key+"/{z}/{x}/{y}.png",L.tileLayer(b,a.options)}},cartodbUTFGrid:{mustHaveKey:!0,mustHaveLayer:!0,createLayer:function(a){var b=o(a.url)?a.url+"/"+a.user:"//"+a.user+".cartodb.com";return a.url=b+"/api/v1/map/"+a.key+"/"+a.layer+"/{z}/{x}/{y}.grid.json",s(a)}},cartodbInteractive:{mustHaveKey:!0,mustHaveLayer:!0,createLayer:function(b){var c=o(b.url)?b.url+"/"+b.user:"//"+b.user+".cartodb.com",d=c+"/api/v1/map/"+b.key+"/{z}/{x}/{y}.png",e=L.tileLayer(d,b.options),f=[e],g=function(b,d,e){var f=a.copy(d);f.url=c+"/api/v1/map/"+f.key+"/"+e+"/{z}/{x}/{y}.grid.json",b.push(s(f))};if(n(b.layer))for(var h=0;h<b.layer.length;h++)g(f,b,b.layer[h]);else g(f,b,b.layer);return L.layerGroup(f)}},wms:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer.wms(a.url,a.options)}},wmts:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer.wmts(a.url,a.options)}},group:{mustHaveUrl:!1,createLayer:function(a){var b=[];return q.each(a.options.layers,function(a){b.push(h(a))}),a.options.loadedDefer=function(){var b=[];if(o(a.options.layers))for(var c=0;c<a.options.layers.length;c++){var d=a.options.layers[c].layerOptions.loadedDefer;o(d)&&b.push(d)}return b},L.layerGroup(b)}},featureGroup:{mustHaveUrl:!1,createLayer:function(){return L.featureGroup()}},markercluster:{mustHaveUrl:!1,createLayer:function(a){return k.MarkerClusterPlugin.isLoaded()?new L.MarkerClusterGroup(a.options):void r.warn(p+" The markercluster plugin is not loaded.")}},imageOverlay:{mustHaveUrl:!0,mustHaveBounds:!0,createLayer:function(a){return L.imageOverlay(a.url,a.bounds,a.options)}},iip:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer.iip(a.url,a.options)}},custom:{createLayer:function(b){return b.layer instanceof L.Class?a.copy(b.layer):void r.error("[AngularJS - Leaflet] A custom layer must be a leaflet Class")}},cartodb:{mustHaveUrl:!0,createLayer:function(a){return cartodb.createLayer(a.map,a.url)}}},u=function(a){return function(b){o(b.setOpacity)&&b.setOpacity(a)}};return{createLayer:h,layerTypes:t,safeAddLayer:i,safeRemoveLayer:j,changeOpacityListener:u}}]),a.module("ui-leaflet").factory("leafletLegendHelpers",["$http","$q","$log","leafletHelpers",function(a,b,c,d){var e={},f=d.isDefined,g=function b(c){var d=e[c],f=d[0];a(f.c).then(function(a){d.shift(),f.d.resolve(a),d.length>0&&b(c)},function(a){d.shift(),f.d.reject(a),d.length>0&&b(c)})},h=function(a,b,c,d){if(a.innerHTML="",b.error)a.innerHTML+='<div class="info-title alert alert-danger">'+b.error.message+"</div>";else if("arcgis"===c)for(var e=0;e<b.layers.length;e++){var f=b.layers[e];a.innerHTML+='<div class="info-title" data-layerid="'+f.layerId+'">'+f.layerName+"</div>";for(var g=0;g<f.legend.length;g++){var h=f.legend[g];a.innerHTML+='<div class="inline" data-layerid="'+f.layerId+'"><img src="data:'+h.contentType+";base64,"+h.imageData+'" /></div><div class="info-label" data-layerid="'+f.layerId+'">'+h.label+"</div>"}}else"image"===c&&(a.innerHTML='<img src="'+d+'"/>')},i=function(a,b,c,d){return function(){var e=L.DomUtil.create("div",b);return L.Browser.touch?L.DomEvent.on(e,"click",L.DomEvent.stopPropagation):(L.DomEvent.disableClickPropagation(e),L.DomEvent.on(e,"mousewheel",L.DomEvent.stopPropagation)),h(e,a,c,d),e}},j=function(a,b){return function(){for(var c=L.DomUtil.create("div",b),d=0;d<a.colors.length;d++)c.innerHTML+='<div class="outline"><i style="background:'+a.colors[d]+'"></i></div><div class="info-label">'+a.labels[d]+"</div>";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:i,getOnAddArrayLegend:j,updateLegend:h,addLegendURL:function(a,c){var d=b.defer();return f(e[a])||(e[a]=[]),e[a].push({c:c,d:d}),1===e[a].length&&g(a),d.promise}}}]),a.module("ui-leaflet").factory("leafletMapDefaults",["$q","leafletHelpers",function(b,c){function d(){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:'&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'},path:{weight:10,opacity:1,color:"#0000ff"},center:{lat:0,lng:0,zoom:1},trackResize:!0}}var e=c.isDefined,f=c.isObject,g=c.obtainEffectiveMapId,h={};return{reset:function(){h={}},getDefaults:function(a){var b=g(h,a);return h[b]},getMapCreationDefaults:function(a){var b=g(h,a),c=h[b],d={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,trackResize:c.trackResize};if(e(c.minZoom)&&(d.minZoom=c.minZoom),e(c.zoomAnimation)&&(d.zoomAnimation=c.zoomAnimation),e(c.fadeAnimation)&&(d.fadeAnimation=c.fadeAnimation),e(c.markerZoomAnimation)&&(d.markerZoomAnimation=c.markerZoomAnimation),c.map)for(var f in c.map)d[f]=c.map[f];return d},setDefaults:function(b,c){var i=d();e(b)&&(i.doubleClickZoom=e(b.doubleClickZoom)?b.doubleClickZoom:i.doubleClickZoom,i.scrollWheelZoom=e(b.scrollWheelZoom)?b.scrollWheelZoom:i.doubleClickZoom,i.tap=e(b.tap)?b.tap:i.tap,i.touchZoom=e(b.touchZoom)?b.touchZoom:i.doubleClickZoom,i.zoomControl=e(b.zoomControl)?b.zoomControl:i.zoomControl,i.zoomsliderControl=e(b.zoomsliderControl)?b.zoomsliderControl:i.zoomsliderControl,i.attributionControl=e(b.attributionControl)?b.attributionControl:i.attributionControl,i.tileLayer=e(b.tileLayer)?b.tileLayer:i.tileLayer,i.zoomControlPosition=e(b.zoomControlPosition)?b.zoomControlPosition:i.zoomControlPosition,i.keyboard=e(b.keyboard)?b.keyboard:i.keyboard,i.dragging=e(b.dragging)?b.dragging:i.dragging,i.trackResize=e(b.trackResize)?b.trackResize:i.trackResize,e(b.controls)&&a.extend(i.controls,b.controls),f(b.crs)?i.crs=b.crs:e(L.CRS[b.crs])&&(i.crs=L.CRS[b.crs]),e(b.center)&&a.copy(b.center,i.center),e(b.tileLayerOptions)&&a.copy(b.tileLayerOptions,i.tileLayerOptions),e(b.maxZoom)&&(i.maxZoom=b.maxZoom),e(b.minZoom)&&(i.minZoom=b.minZoom),e(b.zoomAnimation)&&(i.zoomAnimation=b.zoomAnimation),e(b.fadeAnimation)&&(i.fadeAnimation=b.fadeAnimation),e(b.markerZoomAnimation)&&(i.markerZoomAnimation=b.markerZoomAnimation),e(b.worldCopyJump)&&(i.worldCopyJump=b.worldCopyJump),e(b.map)&&(i.map=b.map),e(b.path)&&(i.path=b.path));var j=g(h,c);return h[j]=i,i}}}]),a.module("ui-leaflet").service("leafletMarkersHelpers",["$rootScope","$timeout","leafletHelpers","leafletLogger","$compile","leafletGeoJsonHelpers","leafletWatchHelpers",function(b,c,d,e,f,g,h){var i=d.isDefined,j=d.defaultTo,k=d.MarkerClusterPlugin,l=d.AwesomeMarkersPlugin,m=d.VectorMarkersPlugin,n=d.MakiMarkersPlugin,o=d.ExtraMarkersPlugin,p=d.DomMarkersPlugin,q=d.safeApply,r=d,s=d.isString,t=d.isNumber,u=d.isObject,v={},w=g,x=d.errorHeader,y=h.maybeWatch,z=e,A=function(a){var b="";return["_icon","_latlng","_leaflet_id","_map","_shadow"].forEach(function(c){b+=c+": "+j(a[c],"undefined")+" \n"}),"[leafletMarker] : \n"+b},B=function(a,b){var c=b?console:z;c.debug(A(a))},C=function(b){return a.element(v[b]._map._container).parent().length>0},D=function(c){if(i(c)&&i(c.type)&&"awesomeMarker"===c.type)return l.isLoaded()||z.error(x+" The AwesomeMarkers Plugin is not loaded."),new L.AwesomeMarkers.icon(c);if(i(c)&&i(c.type)&&"vectorMarker"===c.type)return m.isLoaded()||z.error(x+" The VectorMarkers Plugin is not loaded."),new L.VectorMarkers.icon(c);if(i(c)&&i(c.type)&&"makiMarker"===c.type)return n.isLoaded()||z.error(x+"The MakiMarkers Plugin is not loaded."),new L.MakiMarkers.icon(c);if(i(c)&&i(c.type)&&"extraMarker"===c.type)return o.isLoaded()||z.error(x+"The ExtraMarkers Plugin is not loaded."),new L.ExtraMarkers.icon(c);if(i(c)&&i(c.type)&&"div"===c.type)return new L.divIcon(c);if(i(c)&&i(c.type)&&"dom"===c.type){p.isLoaded()||z.error(x+"The DomMarkers Plugin is not loaded.");var d=a.isFunction(c.getMarkerScope)?c.getMarkerScope().$new():b,e=f(c.template)(d),g=a.copy(c);return g.ngElement=e,g.element=e[0],a.isFunction(c.getMarkerScope)&&(g.scope=d),new L.DomMarkers.icon(g)}if(i(c)&&i(c.type)&&"icon"===c.type)return c.icon;var h="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==",j="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 i(c)&&i(c.iconUrl)?new L.Icon(c):new L.Icon.Default({iconUrl:h,shadowUrl:j,iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]})},E=function(a){i(v[a])&&delete v[a]},F=function(){v={}},G=function(){for(var a in v)C(a)||E(a)},H=function(a){a.options.icon.options.ngElement&&a.options.icon.options.ngElement.remove(),a.options.icon.options.scope&&a.options.icon.options.scope.$destroy()},I=function(a,b,c){if(a.closePopup(),a.options.icon&&a.options.icon.options&&"dom"===a.options.icon.options.type&&H(a),i(c)&&i(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(i(v))for(var e in v)v[e].hasLayer(a)&&v[e].removeLayer(a);b.hasLayer(a)&&b.removeLayer(a)},J=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()},K=function(a,b){f(a._popup._contentNode)(b)},M=function a(b,d,e){var f=b._popup._contentNode.innerText||b._popup._contentNode.textContent;f.length<1&&c(function(){a(b,d,e)});var g=b._popup._contentNode.offsetWidth;return b._popup._updateLayout(),b._popup._updatePosition(),b._popup.options.autoPan&&J(b,e),g},N=function(c,d,e){var f=a.isFunction(d.getMessageScope)?d.getMessageScope():b,g=!i(d.compileMessage)||d.compileMessage;if(g){if(!i(c._popup)||!i(c._popup._contentNode))return z.error(x+"Popup is invalid or does not have any content."),!1;K(c,f),M(c,d,e)}},O=function(c,d){var e=a.isFunction(d.getMessageScope)?d.getMessageScope():b,g=a.isFunction(d.getLabelScope)?d.getLabelScope():e,h=!i(d.compileMessage)||d.compileMessage;r.LabelPlugin.isLoaded()&&i(d.label)&&(i(d.label.options)&&d.label.options.noHide===!0&&c.showLabel(),h&&i(c.label)&&f(c.label._container)(g))},P=function(b,c,d,e,f,g,h){if(i(c)){if(!w.validateCoords(b))return z.warn("There are problems with lat-lng data, please verify your marker model"),void I(d,h,g);var j=b===c;if(i(b.iconAngle)&&c.iconAngle!==b.iconAngle&&d.setIconAngle(b.iconAngle),s(b.layer)||s(c.layer)&&(i(g.overlays[c.layer])&&g.overlays[c.layer].hasLayer(d)&&(g.overlays[c.layer].removeLayer(d),d.closePopup()),h.hasLayer(d)||h.addLayer(d)),(t(b.opacity)||t(parseFloat(b.opacity)))&&b.opacity!==c.opacity&&d.setOpacity(b.opacity),s(b.layer)&&c.layer!==b.layer){if(s(c.layer)&&i(g.overlays[c.layer])&&g.overlays[c.layer].hasLayer(d)&&g.overlays[c.layer].removeLayer(d),d.closePopup(),h.hasLayer(d)&&h.removeLayer(d),!i(g.overlays[b.layer]))return void z.error(x+"You must use a name of an existing layer");var k=g.overlays[b.layer];if(!(k instanceof L.LayerGroup||k instanceof L.FeatureGroup))return void z.error(x+'A marker can only be added to a layer of type "group" or "featureGroup"');k.addLayer(d),h.hasLayer(d)&&b.focus===!0&&d.openPopup()}if(b.draggable!==!0&&c.draggable===!0&&i(d.dragging)&&d.dragging.disable(),b.draggable===!0&&c.draggable!==!0&&(d.dragging?d.dragging.enable():L.Handler.MarkerDrag&&(d.dragging=new L.Handler.MarkerDrag(d),d.options.draggable=!0,d.dragging.enable())),u(b.icon)||u(c.icon)&&("dom"===c.icon.type&&H(d),d.setIcon(D()),d.closePopup(),d.unbindPopup(),s(b.message)&&d.bindPopup(b.message,b.popupOptions)),u(b.icon)&&u(c.icon)&&!a.equals(b.icon,c.icon)){var l=!1;d.dragging&&(l=d.dragging.enabled()),"dom"===c.icon.type&&H(d),d.setIcon(D(b.icon)),l&&d.dragging.enable(),d.closePopup(),d.unbindPopup(),s(b.message)&&(d.bindPopup(b.message,b.popupOptions),h.hasLayer(d)&&b.focus===!0&&d.openPopup())}!s(b.message)&&s(c.message)&&(d.closePopup(),d.unbindPopup()),r.LabelPlugin.isLoaded()&&(i(b.label)&&i(b.label.message)?"label"in c&&"message"in c.label&&!a.equals(b.label.message,c.label.message)?d.updateLabelContent(b.label.message):!a.isFunction(d.getLabel)||a.isFunction(d.getLabel)&&!i(d.getLabel())?(d.bindLabel(b.label.message,b.label.options),O(d,b)):O(d,b):"label"in b&&!("message"in b.label)||a.isFunction(d.unbindLabel)&&d.unbindLabel()),s(b.message)&&!s(c.message)&&d.bindPopup(b.message,b.popupOptions),s(b.message)&&s(c.message)&&b.message!==c.message&&d.setPopupContent(b.message);var m=!1;b.focus!==!0&&c.focus===!0&&(d.closePopup(),m=!0),(b.focus===!0&&(!i(c.focus)||c.focus===!1)||j&&b.focus===!0)&&(d.openPopup(),m=!0),c.zIndexOffset!==b.zIndexOffset&&d.setZIndexOffset(b.zIndexOffset);var n=d.getLatLng(),o=s(b.layer)&&r.MarkerClusterPlugin.is(g.overlays[b.layer]);o?m?b.lat===c.lat&&b.lng===c.lng||(g.overlays[b.layer].removeLayer(d),d.setLatLng([b.lat,b.lng]),g.overlays[b.layer].addLayer(d)):n.lat!==b.lat||n.lng!==b.lng?(g.overlays[b.layer].removeLayer(d),d.setLatLng([b.lat,b.lng]),g.overlays[b.layer].addLayer(d)):b.lat!==c.lat||b.lng!==c.lng?(g.overlays[b.layer].removeLayer(d),d.setLatLng([b.lat,b.lng]),g.overlays[b.layer].addLayer(d)):u(b.icon)&&u(c.icon)&&!a.equals(b.icon,c.icon)&&(g.overlays[b.layer].removeLayer(d),g.overlays[b.layer].addLayer(d)):n.lat===b.lat&&n.lng===b.lng||d.setLatLng([b.lat,b.lng])}},Q=function(a,b){if(i(a))return b?a[b]:a},R=function(a,b,c){if(i(a))return b?c?a[c][b]:a[b]:void z.error(x+"marker id missing in getMarker")};return{resetMarkerGroup:E,resetMarkerGroups:F,resetUnusedMarkerGroups:G,deleteMarker:I,manageOpenPopup:N,manageOpenLabel:O,createMarker:function(a){if(!i(a)||!w.validateCoords(a))return void z.error(x+"The marker definition is not valid.");var b=w.getCoords(a);if(!i(b))return void z.error(x+"Unable to get coordinates from markerData.");var c={icon:D(a.icon),title:i(a.title)?a.title:"",draggable:!!i(a.draggable)&&a.draggable,clickable:!i(a.clickable)||a.clickable,riseOnHover:!!i(a.riseOnHover)&&a.riseOnHover,zIndexOffset:i(a.zIndexOffset)?a.zIndexOffset:0,iconAngle:i(a.iconAngle)?a.iconAngle:0};for(var d in a)a.hasOwnProperty(d)&&!c.hasOwnProperty(d)&&(c[d]=a[d]);var e=new L.marker(b,c);return s(a.message)||e.unbindPopup(),e},addMarkerToGroup:function(a,b,c,d){return s(b)?k.isLoaded()?(i(v[b])||(v[b]=new L.MarkerClusterGroup(c),d.addLayer(v[b])),void v[b].addLayer(a)):void z.error(x+"The MarkerCluster plugin is not loaded."):void z.error(x+"The marker group you have specified is invalid.")},listenMarkerEvents:function(a,b,c,d,e){a.on("popupopen",function(){q(c,function(){(i(a._popup)||i(a._popup._contentNode))&&(b.focus=!0,N(a,b,e))})}),a.on("popupclose",function(){q(c,function(){b.focus=!1})}),a.on("add",function(){q(c,function(){"label"in b&&O(a,b)})})},updateMarker:P,addMarkerWatcher:function(a,b,c,d,e,f){var g=r.getObjectArrayPath("markers."+b);y(c,g,f,function(f,g,h){return i(f)?void P(f,g,a,b,c,d,e):(I(a,e,d),void h())})},string:A,log:B,getModelFromModels:R,getLayerModels:Q}}]),a.module("ui-leaflet").factory("leafletPathsHelpers",["$rootScope","leafletLogger","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;d<m.length;d++){var e=m[d];h(a[e])?c[e]=a[e]:h(b.path[e])&&(c[e]=b.path[e])}return c}var h=c.isDefined,i=c.isArray,j=c.isNumber,k=c.isValidPoint,l=b,m=["stroke","weight","color","opacity","fill","fillColor","fillOpacity","dashArray","lineCap","lineJoin","clickable","pointerEvents","className","smoothFactor","noClip"],n=function(a,b){for(var c={},d=0;d<m.length;d++){var e=m[d];h(b[e])&&(c[e]=b[e])}a.setStyle(b)},o=function(a){if(!i(a))return!1;for(var b=0;b<a.length;b++){var c=a[b];if(!k(c))return!1}return!0},p={polyline:{isValid:function(a){var b=a.latlngs;return o(b)},createPath:function(a){return new L.Polyline([],a)},setPath:function(a,b){a.setLatLngs(d(b.latlngs)),n(a,b)}},multiPolyline:{isValid:function(a){var b=a.latlngs;if(!i(b))return!1;for(var c in b){var d=b[c];if(!o(d))return!1}return!0},createPath:function(a){return new L.multiPolyline([[[0,0],[1,1]]],a)},setPath:function(a,b){a.setLatLngs(f(b.latlngs)),n(a,b)}},polygon:{isValid:function(a){var b=a.latlngs;return o(b)},createPath:function(a){return new L.Polygon([],a)},setPath:function(a,b){a.setLatLngs(d(b.latlngs)),n(a,b)}},multiPolygon:{isValid:function(a){var b=a.latlngs;if(!i(b))return!1;for(var c in b){var d=b[c];if(!o(d))return!1}return!0},createPath:function(a){return new L.MultiPolygon([[[0,0],[1,1],[0,1]]],a)},setPath:function(a,b){a.setLatLngs(f(b.latlngs)),n(a,b)}},rectangle:{isValid:function(a){var b=a.latlngs;if(!i(b)||2!==b.length)return!1;for(var c in b){var d=b[c];if(!k(d))return!1}return!0},createPath:function(a){return new L.Rectangle([[0,0],[1,1]],a)},setPath:function(a,b){a.setBounds(new L.LatLngBounds(d(b.latlngs))),n(a,b)}},circle:{isValid:function(a){var b=a.latlngs;return k(b)&&j(a.radius)},createPath:function(a){return new L.Circle([0,0],1,a)},setPath:function(a,b){a.setLatLng(e(b.latlngs)),h(b.radius)&&a.setRadius(b.radius),n(a,b)}},circleMarker:{isValid:function(a){var b=a.latlngs;return k(b)&&j(a.radius)},createPath:function(a){return new L.CircleMarker([0,0],a)},setPath:function(a,b){a.setLatLng(e(b.latlngs)),h(b.radius)&&a.setRadius(b.radius),n(a,b)}}},q=function(a){var b={};return a.latlngs&&(b.latlngs=a.latlngs),a.radius&&(b.radius=a.radius),b};return{setPathOptions:function(a,b,c){h(b)||(b="polyline"),p[b].setPath(a,c)},createPath:function(a,b,c){h(b.type)||(b.type="polyline");var d=g(b,c),e=q(b);return p[b.type].isValid(e)?p[b.type].createPath(d):void l.error("[AngularJS - Leaflet] Invalid data passed to the "+b.type+" path")}}}]),a.module("ui-leaflet").service("leafletWatchHelpers",function(){var a=function(a,b,c,d,e){var f=a[b](c,function(a,b){e(a,b,f),null===d.type&&f()},"watchDeep"===d.type);return f},b=function(b,c,d,e){var f;return f="watchCollection"===d.type?"$watchCollection":"$watch",a(b,f,c,d,e)};return{maybeWatch:b}}),a.module("ui-leaflet").service("leafletLogger",["nemSimpleLogger",function(a){return a.spawn()}]),a.module("ui-leaflet").factory("nominatimService",["$q","$http","leafletHelpers","leafletMapDefaults",function(a,b,c,d){var e=c.isDefined;return{query:function(c,f){var g=d.getDefaults(f),h=g.nominatim.server,i=a.defer();return b.get(h,{params:{format:"json",limit:1,q:c}}).success(function(a){a.length>0&&e(a[0].boundingbox)?i.resolve(a[0]):i.reject("[Nominatim] Invalid address")}),i.promise}}}]),a.module("ui-leaflet").directive("bounds",["leafletLogger","$timeout","$http","leafletHelpers","nominatimService","leafletBoundsHelpers",function(b,c,d,e,f,g){var h=b;return{restrict:"A",scope:!1,replace:!1,require:["leaflet"],link:function(b,d,i,j){var k=e.isDefined,l=g.createLeafletBounds,m=j[0].getLeafletScope(),n=j[0],o=e.errorHeader+" [Bounds] ",p=function(a){return 0===a._southWest.lat&&0===a._southWest.lng&&0===a._northEast.lat&&0===a._northEast.lng};n.getMap().then(function(d){m.$on("boundsChanged",function(b){var e=b.currentScope,f=d.getBounds();if(!p(f)&&!e.settingBoundsFromScope){e.settingBoundsFromLeaflet=!0;var g={northEast:{lat:f._northEast.lat,lng:f._northEast.lng},southWest:{lat:f._southWest.lat,lng:f._southWest.lng},options:f.options};a.equals(e.bounds,g)||(e.bounds=g),c(function(){e.settingBoundsFromLeaflet=!1})}});var e;m.$watch("bounds",function(a){if(!b.settingBoundsFromLeaflet){if(k(a.address)&&a.address!==e)return b.settingBoundsFromScope=!0,f.query(a.address,i.id).then(function(a){var b=a.boundingbox,c=[[b[0],b[2]],[b[1],b[3]]];d.fitBounds(c)},function(a){h.error(o+" "+a+".")}),e=a.address,void c(function(){b.settingBoundsFromScope=!1});var g=l(a);g&&!d.getBounds().equals(g)&&(b.settingBoundsFromScope=!0,d.fitBounds(g,a.options),c(function(){b.settingBoundsFromScope=!1}))}},!0)})}}}]);var b=["center","lfCenter"],c={};b.forEach(function(b){c[b]=["leafletLogger","$q","$location","$timeout","leafletMapDefaults","leafletHelpers","leafletBoundsHelpers","leafletMapEvents",function(c,d,e,f,g,h,i,j){var k,l=h.isDefined,m=h.isNumber,n=h.isSameCenterOnMap,o=h.safeApply,p=h.isValidCenter,q=i.isValidBounds,r=h.isUndefinedOrEmpty,s=h.errorHeader,t=c,u=function(a,b){return l(a)&&q(a)&&r(b)};return{restrict:"A",scope:!1,replace:!1,require:"leaflet",controller:function(){k=d.defer(),this.getCenter=function(){return k.promise}},link:function(c,d,h,q){var r=q.getLeafletScope(),v=r[b];q.getMap().then(function(c){var d=g.getDefaults(h.id);if(h[b].search("-")!==-1)return t.error(s+' The "center" variable can\'t use a "-" on its key name: "'+h[b]+'".'),void c.setView([d.center.lat,d.center.lng],d.center.zoom);if(u(r.bounds,v))c.fitBounds(i.createLeafletBounds(r.bounds),r.bounds.options),v=c.getCenter(),o(r,function(d){a.extend(d[b],{lat:c.getCenter().lat,lng:c.getCenter().lng,zoom:c.getZoom(),autoDiscover:!1})}),o(r,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(!l(v))return t.error(s+' The "center" property is not defined in the main scope'),void c.setView([d.center.lat,d.center.lng],d.center.zoom);l(v.lat)&&l(v.lng)||l(v.autoDiscover)||a.copy(d.center,v)}var q,w;if("yes"===h.urlHashCenter){var x=function(){var a,b=e.search(),c=h.urlHashParam?h.urlHashParam:"c";if(l(b[c])){var d=b[c].split(":");3===d.length&&(a={lat:parseFloat(d[0]),lng:parseFloat(d[1]),zoom:parseInt(d[2],10)})}return a};q=x(),r.$on("$locationChangeSuccess",function(d){var e=d.currentScope,f=x();l(f)&&!n(f,c)&&a.extend(e[b],{lat:f.lat,lng:f.lng,zoom:f.zoom})})}r.$watch(b,function(b){if(!r.settingCenterFromLeaflet)return l(q)&&(a.copy(q,b),q=void 0),p(b)||b.autoDiscover===!0?b.autoDiscover===!0?(m(b.zoom)||c.setView([d.center.lat,d.center.lng],d.center.zoom),void(m(b.zoom)&&b.zoom>d.center.zoom?c.locate({setView:!0,maxZoom:b.zoom}):l(d.maxZoom)?c.locate({setView:!0,maxZoom:d.maxZoom}):c.locate({setView:!0}))):void(w&&n(b,c)||(r.settingCenterFromScope=!0,c.setView([b.lat,b.lng],b.zoom),j.notifyCenterChangedToBounds(r,c),f(function(){r.settingCenterFromScope=!1}))):void t.warn(s+" invalid 'center'")},!0),c.whenReady(function(){w=!0}),c.on("moveend",function(){k.resolve(),j.notifyCenterUrlHashChanged(r,c,h,e.search()),n(v,c)||r.settingCenterFromScope||(r.settingCenterFromLeaflet=!0,o(r,function(d){r.settingCenterFromScope||a.extend(d[b],{lat:c.getCenter().lat,lng:c.getCenter().lng,zoom:c.getZoom(),autoDiscover:!1}),j.notifyCenterChangedToBounds(r,c),f(function(){r.settingCenterFromLeaflet=!1})}))}),v.autoDiscover===!0&&c.on("locationerror",function(){t.warn(s+" The Geolocation API is unauthorized on this page."),p(v)?(c.setView([v.lat,v.lng],v.zoom),j.notifyCenterChangedToBounds(r,c)):(c.setView([d.center.lat,d.center.lng],d.center.zoom),j.notifyCenterChangedToBounds(r,c))})})}}}]}),b.forEach(function(b){a.module("ui-leaflet").directive(b,c[b])}),a.module("ui-leaflet").directive("controls",["leafletLogger","leafletHelpers","leafletControlHelpers",function(a,b,c){var d=a;return{restrict:"A",scope:!1,replace:!1,require:"?^leaflet",link:function(a,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] ";a.$on("$destroy",function(){c.destroyMapLayersControl(a.mapId)}),g.getMap().then(function(a){j.$watchCollection("controls",function(b){for(var c in m)k(b[c])||(a.hasControl(m[c])&&a.removeControl(m[c]),delete m[c]);for(var e in b){var f,g=k(b[e].type)?b[e].type:e;if(!i(g))return void d.error(n+" Invalid control type: "+g+".");if("custom"!==g)f=h(g,b[e]),a.addControl(f),m[e]=f;else{var j=b[e];if(l(j))for(var o=0;o<j.length;o++){var p=j[o];a.addControl(p),m[e]=k(m[e])?m[e].concat([p]):[p]}else a.addControl(j),m[e]=j}}})})}}}}]),a.module("ui-leaflet").directive("decorations",["leafletLogger","leafletHelpers",function(b,c){var d=b;return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(b,e,f,g){function h(a){return l(a)&&l(a.coordinates)&&(k.isLoaded()||d.error("[AngularJS - Leaflet] The PolylineDecorator Plugin is not loaded.")),L.polylineDecorator(a.coordinates)}function i(a,b){if(l(a)&&l(b)&&l(b.coordinates)&&l(b.patterns))return a.setPaths(b.coordinates),a.setPatterns(b.patterns),a}var j=g.getLeafletScope(),k=c.PolylineDecoratorPlugin,l=c.isDefined,m={};g.getMap().then(function(b){j.$watch("decorations",function(c){for(var d in m)l(c[d])&&a.equals(c[d],m)||(b.removeLayer(m[d]),delete m[d]);for(var e in c){var f=c[e],g=h(f);l(g)&&(m[e]=g,b.addLayer(g),i(g,f))}},!0)})}}}]),a.module("ui-leaflet").directive("eventBroadcast",["leafletLogger","$rootScope","leafletHelpers","leafletMapEvents","leafletIterators",function(a,b,c,d,e){var f=a;return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(a,b,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(a){var b=[],c="broadcast";j(l.map)?i(l.map)?("emit"!==l.map.logic&&"broadcast"!==l.map.logic?f.warn("[AngularJS - Leaflet] Available event propagation logic are: 'emit' or 'broadcast'."):c=l.map.logic,i(l.map.enable)&&l.map.enable.length>=0?e.each(l.map.enable,function(a){b.indexOf(a)===-1&&m.indexOf(a)!==-1&&b.push(a)}):f.warn("[AngularJS - Leaflet] event-broadcast.map.enable must be an object check your model.")):f.warn("[AngularJS - Leaflet] event-broadcast.map must be an object check your model."):b=m,n(a,g.id,b,"eventName",k,c)})}}}]),a.module("ui-leaflet").directive("geojson",["$timeout","leafletLogger","leafletData","leafletHelpers","leafletWatchHelpers","leafletDirectiveControlsHelpers","leafletIterators","leafletGeoJsonEvents",function(b,c,d,e,f,g,h,i){var j=f.maybeWatch,k=e.watchOptions,l=g.extend,m=e,n=h,o={changeFromDirective:!1};return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(b,c,f,g){var h=e.isDefined,p=g.getLeafletScope(),q={},r=!1;g.getMap().then(function(b){var c;c=p.watchOptions&&p.watchOptions.geojson?p.watchOptions.geojson:k;var g=function(b,c){var d;return d=a.isFunction(b.onEachFeature)?b.onEachFeature:function(a,d){e.LabelPlugin.isLoaded()&&h(a.properties.description)&&d.bindLabel(a.properties.description),i.bindEvents(f.id,d,null,a,p,c,{resetStyleOnMouseout:b.resetStyleOnMouseout,mapId:f.id})}},s=m.isDefined(f.geojsonNested)&&m.isTruthy(f.geojsonNested),t=function(){if(q){var a=function(a){h(a)&&b.hasLayer(a)&&b.removeLayer(a)};return s?void n.each(q,function(b){a(b)}):void a(q)}},u=function(a,c){if(h(a)&&h(a.data)){var e=g(a,c);h(a.options)||m.modelChangeInDirective(o,"changeFromDirective",function(){a.options={style:a.style,filter:a.filter,onEachFeature:e,pointToLayer:a.pointToLayer}});var i=L.geoJson(a.data,a.options);c&&m.isString(c)?q[c]=i:q=i,i.addTo(b),r||(r=!0,d.setGeoJSON(q,f.id))}},v=function(a){if(t(),s){if(!a||!Object.keys(a).length)return;return void n.each(a,function(a,b){u(a,b)})}u(a)};l(f.id,"geojson",v,t),j(p,"geojson",c,function(a){o.changeFromDirective||v(a)})})}}}]),a.module("ui-leaflet").directive("layercontrol",["$filter","leafletLogger","leafletData","leafletHelpers",function(b,c,d,e){var f=c;return{restrict:"E",scope:{icons:"=?",autoHideOpacity:"=?",showGroups:"=?",title:"@",baseTitle:"@",overlaysTitle:"@"},replace:!0,transclude:!1,require:"^leaflet",controller:["$scope","$element","$sce",function(b,c,g){f.debug("[Angular Directive - Layers] layers",b,c);var h=e.safeApply,i=e.isDefined;a.extend(b,{baselayer:"",oldGroup:"",layerProperties:{},groupProperties:{},rangeIsSupported:e.rangeIsSupported(),changeBaseLayer:function(a,c){e.safeApply(b,function(c){c.baselayer=a,d.getMap().then(function(e){d.getLayers().then(function(d){if(!e.hasLayer(d.baselayers[a])){for(var f in c.layers.baselayers)c.layers.baselayers[f].icon=c.icons.unradio,e.hasLayer(d.baselayers[f])&&e.removeLayer(d.baselayers[f]);e.addLayer(d.baselayers[a]),c.layers.baselayers[a].icon=b.icons.radio}})})}),c.preventDefault()},moveLayer:function(a,c,d){var e=Object.keys(b.layers.baselayers).length;if(c>=1+e&&c<=b.overlaysArray.length+e){var f;for(var g in b.layers.overlays)if(b.layers.overlays[g].index===c){f=b.layers.overlays[g];break}f&&h(b,function(){f.index=a.index,a.index=c})}d.stopPropagation(),d.preventDefault()},initIndex:function(a,c){var d=Object.keys(b.layers.baselayers).length;a.index=i(a.index)?a.index:c+d+1},initGroup:function(a){b.groupProperties[a]=b.groupProperties[a]?b.groupProperties[a]:{}},toggleOpacity:function(a,c){if(c.visible){if(b.autoHideOpacity&&!b.layerProperties[c.name].opacityControl)for(var d in b.layerProperties)b.layerProperties[d].opacityControl=!1;b.layerProperties[c.name].opacityControl=!b.layerProperties[c.name].opacityControl}a.stopPropagation(),a.preventDefault()},toggleLegend:function(a){b.layerProperties[a.name].showLegend=!b.layerProperties[a.name].showLegend},showLegend:function(a){return a.legend&&b.layerProperties[a.name].showLegend},unsafeHTML:function(a){return g.trustAsHtml(a)},getOpacityIcon:function(a){return a.visible&&b.layerProperties[a.name].opacityControl?b.icons.close:b.icons.open},getGroupIcon:function(a){return a.visible?b.icons.check:b.icons.uncheck},changeGroupVisibility:function(a){if(i(b.groupProperties[a])){var c=b.groupProperties[a].visible;for(var d in b.layers.overlays){var e=b.layers.overlays[d];e.group===a&&(e.visible=c)}}}});var j=c.get(0);L.Browser.touch?L.DomEvent.on(j,"click",L.DomEvent.stopPropagation):(L.DomEvent.disableClickPropagation(j),L.DomEvent.on(j,"mousewheel",L.DomEvent.stopPropagation))}],template:'<div class="angular-leaflet-control-layers" ng-show="overlaysArray.length"><h4 ng-if="title">{{ title }}</h4><div class="lf-baselayers"><h5 class="lf-title" ng-if="baseTitle">{{ baseTitle }}</h5><div class="lf-row" ng-repeat="(key, layer) in baselayersArray"><label class="lf-icon-bl" ng-click="changeBaseLayer(key, $event)"><input class="leaflet-control-layers-selector" type="radio" name="lf-radio" ng-show="false" ng-checked="baselayer === key" ng-value="key" /> <i class="lf-icon lf-icon-radio" ng-class="layer.icon"></i><div class="lf-text">{{layer.name}}</div></label></div></div><div class="lf-overlays"><h5 class="lf-title" ng-if="overlaysTitle">{{ overlaysTitle }}</h5><div class="lf-container"><div class="lf-row" ng-repeat="layer in (o = (overlaysArray | orderBy:\'index\':order))" ng-init="initIndex(layer, $index)"><label class="lf-icon-ol-group" ng-if="showGroups &amp;&amp; layer.group &amp;&amp; layer.group != o[$index-1].group"><input class="lf-control-layers-selector" type="checkbox" ng-show="false" ng-change="changeGroupVisibility(layer.group)" ng-model="groupProperties[layer.group].visible"/> <i class="lf-icon lf-icon-check" ng-class="getGroupIcon(groupProperties[layer.group])"></i><div class="lf-text">{{ layer.group }}</div></label><label class="lf-icon-ol"><input class="lf-control-layers-selector" type="checkbox" ng-show="false" ng-model="layer.visible"/> <i class="lf-icon lf-icon-check" ng-class="layer.icon"></i><div class="lf-text">{{layer.name}}</div></label><div class="lf-icons"><i class="lf-icon lf-up" ng-class="icons.up" ng-click="moveLayer(layer, layer.index - orderNumber, $event)"></i> <i class="lf-icon lf-down" ng-class="icons.down" ng-click="moveLayer(layer, layer.index + orderNumber, $event)"></i> <i class="lf-icon lf-toggle-legend" ng-class="icons.toggleLegend" ng-if="layer.legend" ng-click="toggleLegend(layer)"></i> <i class="lf-icon lf-open" ng-class="getOpacityIcon(layer)" ng-click="toggleOpacity($event, layer)"></i></div><div class="lf-legend" ng-if="showLegend(layer)" ng-bind-html="unsafeHTML(layer.legend)"></div><div class="lf-opacity clearfix" ng-if="layer.visible &amp;&amp; layerProperties[layer.name].opacityControl"><label ng-if="rangeIsSupported" class="pull-left" style="width: 50%">0</label><label ng-if="rangeIsSupported" class="pull-left text-right" style="width: 50%">100</label><input ng-if="rangeIsSupported" class="clearfix" type="range" min="0" max="1" step="0.05" class="lf-opacity-control" ng-model="layerProperties[layer.name].layerOptions.opacity"/><h6 ng-if="!rangeIsSupported">Range is not supported in this browser</h6></div></div></div></div></div>',link:function(b,c,f,g){var h=e.isDefined,i=g.getLeafletScope(),j=i.layers;b.$watch("icons",function(){var c={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"};h(b.icons)?(a.extend(c,b.icons),a.extend(b.icons,c)):b.icons=c}),f.order=!h(f.order)||"normal"!==f.order&&"reverse"!==f.order?"normal":f.order,b.order="normal"===f.order,b.orderNumber="normal"===f.order?-1:1,b.layers=j,g.getMap().then(function(a){i.$watch("layers.baselayers",function(c){var e={};d.getLayers().then(function(d){var f;for(f in c){var g=c[f];g.icon=b.icons[a.hasLayer(d.baselayers[f])?"radio":"unradio"],e[f]=g}b.baselayersArray=e})}),i.$watch("layers.overlays",function(a){var c=[],e={};d.getLayers().then(function(){var d;for(d in a){var f=a[d];f.icon=b.icons[f.visible?"check":"uncheck"],c.push(f),h(b.layerProperties[f.name])||(h(f.layerOptions.opacity)&&(f.layerOptions.opacity=1),b.layerProperties[f.name]={opacityControl:!1,showLegend:!0,layerOptions:f.layerOptions}),h(f.group)&&(h(b.groupProperties[f.group])||(b.groupProperties[f.group]={visible:!1}),e[f.group]=h(e[f.group])?e[f.group]:{count:0,visibles:0},e[f.group].count++,f.visible&&e[f.group].visibles++)}for(d in e)b.groupProperties[d].visible=e[d].visibles===e[d].count;b.overlaysArray=c})},!0)})}}}]),a.module("ui-leaflet").directive("layers",["leafletLogger","$q","leafletData","leafletHelpers","leafletLayerHelpers","leafletControlHelpers",function(b,c,d,e,f,g){return{restrict:"A",scope:!1,replace:!1,require:"leaflet",controller:["$scope",function(a){a._leafletLayers=c.defer(),this.getLayers=function(){return a._leafletLayers.promise}}],link:function(b,c,h,i){var j=e.isDefined,k={},l=i.getLeafletScope(),m=l.layers,n=f.createLayer,o=f.safeAddLayer,p=f.safeRemoveLayer,q=f.changeOpacityListener,r=g.updateLayersControl,s=!1;b.$on("$destroy",function(){g.destroyMapLayersControl(b.mapId)}),i.getMap().then(function(c){b._leafletLayers.resolve(k),d.setLayers(k,h.id),k.baselayers={},k.overlays={};var e=h.id,f=!1;for(var g in m.baselayers){var i=n(m.baselayers[g]);j(i)?(k.baselayers[g]=i,m.baselayers[g].top===!0&&(o(c,k.baselayers[g]),f=!0)):delete m.baselayers[g]}!f&&Object.keys(k.baselayers).length>0&&o(c,k.baselayers[Object.keys(m.baselayers)[0]]);for(g in m.overlays){"cartodb"===m.overlays[g].type;var t=n(m.overlays[g]);j(t)?(k.overlays[g]=t,m.overlays[g].visible===!0&&o(c,k.overlays[g])):delete m.overlays[g]}l.$watch("layers.baselayers",function(b,d){if(a.equals(b,d))return s=r(c,e,s,b,m.overlays,k),!0;for(var f in k.baselayers)j(b[f])&&!b[f].doRefresh||(c.hasLayer(k.baselayers[f])&&c.removeLayer(k.baselayers[f]),delete k.baselayers[f],b[f]&&b[f].doRefresh&&(b[f].doRefresh=!1));for(var g in b)if(j(k.baselayers[g]))b[g].top!==!0||c.hasLayer(k.baselayers[g])?b[g].top===!1&&c.hasLayer(k.baselayers[g])&&c.removeLayer(k.baselayers[g]):o(c,k.baselayers[g]);else{var h=n(b[g]);j(h)&&(k.baselayers[g]=h,b[g].top===!0&&o(c,k.baselayers[g]))}var i=!1;for(var l in k.baselayers)if(c.hasLayer(k.baselayers[l])){i=!0;break}!i&&Object.keys(k.baselayers).length>0&&o(c,k.baselayers[Object.keys(k.baselayers)[0]]),s=r(c,e,s,b,m.overlays,k)},!0),l.$watch("layers.overlays",function(b,d){if(a.equals(b,d))return s=r(c,e,s,m.baselayers,b,k),!0;for(var f in k.overlays)if(!j(b[f])||b[f].doRefresh){if(c.hasLayer(k.overlays[f])){var g=j(b[f])?b[f].layerOptions:null;p(c,k.overlays[f],g)}delete k.overlays[f],b[f]&&b[f].doRefresh&&(b[f].doRefresh=!1)}for(var h in b){if(j(k.overlays[h])){b[h].visible&&!c.hasLayer(k.overlays[h])?o(c,k.overlays[h]):b[h].visible===!1&&c.hasLayer(k.overlays[h])&&p(c,k.overlays[h],b[h].layerOptions);var i=k.overlays[h];c.hasLayer(k.overlays[h])&&(b[h].layerOptions.opacity!==d[h].layerOptions.opacity&&(j(i.setOpacity)&&i.setOpacity(b[h].layerOptions.opacity),j(i.getLayers)&&j(i.eachLayer)&&i.eachLayer(q(b[h].layerOptions.opacity))),j(b[h].index)&&i.setZIndex&&b[h].index!==d[h].index&&i.setZIndex(b[h].index))}else{var l=n(b[h]);if(!j(l))continue;k.overlays[h]=l,b[h].visible===!0&&o(c,k.overlays[h]),j(b[h].index)&&k.overlays[h].setZIndex&&k.overlays[h].setZIndex(b[h].index)}b[h].visible&&c._loaded&&b[h].data&&"heatmap"===b[h].type&&(k.overlays[h].setData(b[h].data),k.overlays[h].update())}s=r(c,e,s,m.baselayers,b,k)},!0)})}}}]),a.module("ui-leaflet").directive("legend",["leafletLogger","$http","$timeout","leafletHelpers","leafletLegendHelpers",function(a,b,c,d,e){var f=a,g=d.errorHeader+" [Legend] ";return{restrict:"A",scope:!1,replace:!1,require:"leaflet",transclude:!1,link:function(a,b,c,h){var i,j,k,l,m=d.isArray,n=d.isString,o=d.isDefined,p=d.isFunction,q=h.getLeafletScope(),r=q.legend;q.$watch("legend",function(a){o(a)&&(i=a.legendClass?a.legendClass:"legend",j=a.position||"bottomright",l=a.type||"arcgis")},!0);var s=function(a,b,c){b&&b.layers&&b.layers.length>0&&(o(k)?e.updateLegend(k.getContainer(),b,l,c):(k=L.control({position:j}),k.onAdd=e.getOnAddLegend(b,i,l,c),k.addTo(a)),o(r.loadedData)&&p(r.loadedData)&&r.loadedData())};h.getMap().then(function(a){q.$watch("legend",function(b){return o(b)?o(b.url)||"arcgis"!==l||m(b.colors)&&m(b.labels)&&b.colors.length===b.labels.length?o(b.url)?void f.info(g+" loading legend service."):(o(k)&&(k.removeFrom(a),k=null),k=L.control({position:j}),"arcgis"===l&&(k.onAdd=e.getOnAddArrayLegend(b,i)),void k.addTo(a)):void f.warn(g+" legend.colors and legend.labels must be set."):void(o(k)&&(k.removeFrom(a),k=null))}),q.$watch("legend.url",function(b){if(o(b)){if(!m(b)&&!n(b))return void f.warn(g+" legend.url must be an array or string.");for(var d,h=n(b)?[b]:b,i=function(c,e){return function(i){o(i.data.error)?f.warn(g+"Error loadin legend from: "+e,i.data.error.message):d&&d.layers&&d.layers.length>0?d.layers=d.layers.concat(i.data.layers):d=i.data,c===h.length-1&&s(a,d,b)}},j=function(a){f.warn(g+" legend.url not loaded.",a)},k=0;k<h.length;k++)e.addLegendURL(c.id,{url:h[k],method:"GET"}).then(i(k)).catch(j)}}),q.$watch("legend.legendData",function(b){f.debug("legendData",b),!o(q.legend.url)&&o(b)&&s(a,b)},!0)})}}}]),a.module("ui-leaflet").directive("markers",["leafletLogger","$rootScope","$q","leafletData","leafletHelpers","leafletMapDefaults","leafletMarkersHelpers","leafletMarkerEvents","leafletIterators","leafletWatchHelpers","leafletDirectiveControlsHelpers",function(b,c,d,e,f,g,h,i,j,k,l){var m=f.isDefined,n=f.errorHeader,o=f,p=f.isString,q=h.addMarkerWatcher,r=h.updateMarker,s=h.listenMarkerEvents,t=h.addMarkerToGroup,u=h.createMarker,v=h.deleteMarker,w=h.getModelFromModels,x=h.getLayerModels,y=h.resetUnusedMarkerGroups,z=j,A=f.watchOptions,B=k.maybeWatch,C=l.extend,D=b,E={changeFromDirective:!1},F=function(a,b,c){if(Object.keys(a).length){if(c&&p(c)){if(!a[c]||!Object.keys(a[c]).length)return;return a[c][b]}return a[b]}},G=function(a,b,c,d){return d&&p(d)?(m(b[d])||(b[d]={}),b[d][c]=a):b[c]=a,a},H=function(a,b,c,d,e,f){if(!p(a))return D.error(n+" A layername must be a string"),!1;if(!m(b))return D.error(n+" You must add layers to the directive if the markers are going to use this functionality."),!1;if(!m(b.overlays)||!m(b.overlays[a]))return D.error(n+' A marker can only be added to a layer of type "group"'),!1;var g=b.overlays[a];return g instanceof L.LayerGroup||g instanceof L.FeatureGroup?(g.addLayer(d),null===e&&f.hasLayer(d)&&c.focus===!0&&d.openPopup(),!0):(D.error(n+' Adding a marker to an overlay needs a overlay of the type "group" or "featureGroup"'),!1)},I=function(a,b,c,d,e,f,g,h,j,k){z.each(b,function(b,l){if(!k[l]){if(l.search("-")!==-1)return void D.error('The marker can\'t use a "-" on his key name: "'+l+'".');var p=o.getObjectDotPath(j?[j,l]:[l]),v=F(f,l,j);o.modelChangeInDirective(E,"changeFromDirective",function(){if(m(v)){var k=w(c,l,j);r(b,k,v,p,g,e,d)}else{var x=u(b),y=(b?b.layer:void 0)||j;if(!m(x))return void D.error(n+" Received invalid data on the marker "+l+".");if(G(x,f,l,j),
+m(b.message)&&x.bindPopup(b.message,b.popupOptions),m(b.group)){var z=m(b.groupOption)?b.groupOption:null;t(x,b.group,z,d)}if(o.LabelPlugin.isLoaded()&&m(b.label)&&m(b.label.message)&&x.bindLabel(b.label.message,b.label.options),m(b)&&(m(b.layer)||m(j))){var A=H(y,e,b,x,h.individual.type,d);if(!A)return}else m(b.group)||(d.addLayer(x),null===h.individual.type&&b.focus===!0&&x.openPopup());null!==h.individual.type&&q(x,p,g,e,d,h.individual),s(x,b,g,h.individual.type,d),i.bindEvents(a,x,p,b,g,y)}})}})},J=function(b,c,d,e,f){var g,h,i=!1,j=!1,k=m(c);for(var l in d)i||(D.debug(n+"[markers] destroy: "),i=!0),k&&(h=b[l],g=c[l],j=e&&a.equals(h,g)),m(b)&&Object.keys(b).length&&m(b[l])&&Object.keys(b[l]).length&&!j||f&&o.isFunction(f)&&f(h,g,l)},K=function(a,b,c,d,e){J(a,b,c,!1,function(a,b,f){D.debug(n+"[marker] is deleting marker: "+f),v(c[f],d,e),delete c[f]})},M=function(a,b,c){var d={};return J(a,b,c,!0,function(a,b,c){D.debug(n+"[marker] is already rendered, marker: "+c),d[c]=a}),d};return{restrict:"A",scope:!1,replace:!1,require:["leaflet","?layers"],link:function(a,b,c,f){var g=f[0],h=g.getLeafletScope();g.getMap().then(function(b){var g,i={};g=m(f[1])?f[1].getLayers:function(){var a=d.defer();return a.resolve(),a.promise};var j;j=h.watchOptions&&h.watchOptions.markers?h.watchOptions.markers:A;var k=m(c.markersNested)&&o.isTruthy(c.markersNested);g().then(function(d){var f=function(a,c){return y(),k?void z.each(a,function(a,e){var f=x(c,e);K(a,f,i[e],b,d)}):void K(a,c,i,b,d)},g=function(a,e){f(a,e);var g=null;return k?void z.each(a,function(f,k){var l=x(e,k),m=x(a,k);g=M(m,l,i[k]),I(c.id,f,e,b,d,i,h,j,k,g)}):(g=M(a,e,i),void I(c.id,a,e,b,d,i,h,j,void 0,g))};C(c.id,"markers",g,f),e.setMarkers(i,c.id),B(h,"markers",j,function(a,b){E.changeFromDirective||g(a,b)}),a.$on("$destroy",function(){K(h.markers,{},i,b,d)})})})}}}]),a.module("ui-leaflet").directive("maxbounds",["leafletLogger","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)})})}}}]),a.module("ui-leaflet").directive("paths",["leafletLogger","$q","leafletData","leafletMapDefaults","leafletHelpers","leafletPathsHelpers","leafletPathEvents","leafletWatchHelpers",function(a,b,c,d,e,f,g,h){var i=a;return{restrict:"A",scope:!1,replace:!1,require:["leaflet","?layers"],link:function(a,j,k,l){var m=l[0],n=e.isDefined,o=e.isString,p=m.getLeafletScope(),q=p.paths,r=f.createPath,s=g.bindPathEvents,t=f.setPathOptions,u=h.maybeWatch;m.getMap().then(function(a){var f,g=d.getDefaults(k.id);if(f=n(l[1])?l[1].getLayers:function(){var a=b.defer();return a.resolve(),a.promise},n(q)){var h,j={type:"watchCollection",individual:{type:"watchDeep"}};h=p.watchOptions&&p.watchOptions.paths?p.watchOptions.paths:j,f().then(function(b){var d={};c.setPaths(d,k.id);var f=function(c,d,e){var f='paths["'+d+'"]';u(p,f,e,function(d,e,f){if(!n(d)){if(n(e.layer))for(var g in b.overlays){var h=b.overlays[g];h.removeLayer(c)}return a.removeLayer(c),void f()}t(c,d.type,d)})},j=function(b){for(var c in d)n(b[c])||(a.removeLayer(d[c]),delete d[c])},l=function(c){j(c);for(var l in c)if(0!==l.search("\\$"))if(l.search("-")===-1){if(!n(d[l])){var m=c[l],q=r(l,c[l],g);if(n(q)&&n(m.message)&&q.bindPopup(m.message,m.popupOptions),e.LabelPlugin.isLoaded()&&n(m.label)&&n(m.label.message)&&q.bindLabel(m.label.message,m.label.options),n(m)&&n(m.layer)){if(!o(m.layer)){i.error("[AngularJS - Leaflet] A layername must be a string");continue}if(!n(b)){i.error("[AngularJS - Leaflet] You must add layers to the directive if the markers are going to use this functionality.");continue}if(!n(b.overlays)||!n(b.overlays[m.layer])){i.error('[AngularJS - Leaflet] A path can only be added to a layer of type "group"');continue}var u=b.overlays[m.layer];if(!(u instanceof L.LayerGroup||u instanceof L.FeatureGroup)){i.error('[AngularJS - Leaflet] Adding a path to an overlay needs a overlay of the type "group" or "featureGroup"');continue}d[l]=q,u.addLayer(q),null!==h.individual.type?f(q,l,h.individual):t(q,m.type,m)}else n(q)&&(d[l]=q,a.addLayer(q),null!==h.individual.type?f(q,l,h.individual):t(q,m.type,m));s(k.id,q,l,m,p)}}else i.error('[AngularJS - Leaflet] The path name "'+l+'" is not valid. It must not include "-" and a number.')};u(p,"paths",h,function(a){l(a)})})}})}}}]),a.module("ui-leaflet").directive("tiles",["leafletLogger","leafletData","leafletMapDefaults","leafletHelpers",function(b,c,d,e){var f=b;return{restrict:"A",scope:!1,replace:!1,require:"leaflet",link:function(b,g,h,i){var j=e.isDefined,k=i.getLeafletScope(),l=k.tiles;return j(l)&&j(l.url)?void i.getMap().then(function(b){var e,f=d.getDefaults(h.id);k.$watch("tiles",function(d){var g=f.tileLayerOptions,i=f.tileLayer;return!j(d.url)&&j(e)?void b.removeLayer(e):j(e)?j(d.url)&&j(d.options)&&!a.equals(d.options,g)?(b.removeLayer(e),g=f.tileLayerOptions,a.copy(d.options,g),i=d.url,e=L.tileLayer(i,g),e.addTo(b),void c.setTiles(e,h.id)):void(j(d.url)&&e.setUrl(d.url)):(j(d.options)&&a.copy(d.options,g),j(d.url)&&(i=d.url),e=L.tileLayer(i,g),e.addTo(b),void c.setTiles(e,h.id))},!0)}):void f.warn("[AngularJS - Leaflet] The 'tiles' definition doesn't have the 'url' property.")}}}]),a.module("ui-leaflet").directive("watchOptions",["$log","$rootScope","$q","leafletData","leafletHelpers",function(b,c,d,e,f){var g=f.isDefined,h=f.errorHeader,i=f.isObject,j=b;return{restrict:"A",scope:!1,replace:!1,require:["leaflet"],link:function(b,c,d,e){var f=e[0],k=f.getLeafletScope(),l=function(a){return"watch"===a||"watchCollection"===a||"watchDeep"===a||null===a};g(k.watchOptions)&&i(k.watchOptions)&&a.forEach(["markers","geojson","paths"],function(a){g(k.watchOptions[a])&&(l(k.watchOptions[a].type)||j.error(h+" watchOptions."+a+".type is not a valid type."),g(k.watchOptions[a].individual)?l(k.watchOptions[a].individual.type)||j.error(h+" watchOptions."+a+".individual.type is not a valid type."):j.error(h+" watchOptions."+a+".type.individual must be defined."))})}}}]),a.module("ui-leaflet").factory("leafletEventsHelpersFactory",["$rootScope","$q","leafletLogger","leafletHelpers",function(b,c,d,e){var f=e.safeApply,g=e.isDefined,h=e.isObject,i=e.isArray,j=e.errorHeader,k=d,l=function(a,b){this.rootBroadcastName=a,k.debug("leafletEventsHelpersFactory: lObjectType: "+b+"rootBroadcastName: "+a),this.lObjectType=b};return l.prototype.getAvailableEvents=function(){return[]},l.prototype.genDispatchEvent=function(a,b,c,d,e,f,g,h,i){var j=this;return a=a||"",a&&(a="."+a),function(l){var m=j.rootBroadcastName+a+"."+b;k.debug(m),j.fire(d,m,c,l,l.target||e,g,f,h,i)}},l.prototype.fire=function(c,d,e,h,i,j,k,l,m){f(c,function(){var f={leafletEvent:h,leafletObject:i,modelName:k,model:j};g(l)&&a.extend(f,{layerName:l}),"emit"===e?c.$emit(d,f):b.$broadcast(d,f)})},l.prototype.bindEvents=function(a,b,c,d,e,f,l){var m=[],n="emit",o=this;if(g(e.eventBroadcast))if(h(e.eventBroadcast))if(g(e.eventBroadcast[o.lObjectType]))if(h(e.eventBroadcast[o.lObjectType])){g(e.eventBroadcast[this.lObjectType].logic)&&"emit"!==e.eventBroadcast[o.lObjectType].logic&&"broadcast"!==e.eventBroadcast[o.lObjectType].logic&&k.warn(j+"Available event propagation logic are: 'emit' or 'broadcast'.");var p=!1,q=!1;g(e.eventBroadcast[o.lObjectType].enable)&&i(e.eventBroadcast[o.lObjectType].enable)&&(p=!0),g(e.eventBroadcast[o.lObjectType].disable)&&i(e.eventBroadcast[o.lObjectType].disable)&&(q=!0),p&&q?k.warn(j+"can not enable and disable events at the same time"):p||q?p?e.eventBroadcast[this.lObjectType].enable.forEach(function(a){m.indexOf(a)!==-1?k.warn(j+"This event "+a+" is already enabled"):o.getAvailableEvents().indexOf(a)===-1?k.warn(j+"This event "+a+" does not exist"):m.push(a)}):(m=this.getAvailableEvents(),e.eventBroadcast[o.lObjectType].disable.forEach(function(a){var b=m.indexOf(a);b===-1?k.warn(j+"This event "+a+" does not exist or has been already disabled"):m.splice(b,1)})):k.warn(j+"must enable or disable events")}else k.warn(j+"event-broadcast."+[o.lObjectType]+" must be an object check your model.");else m=this.getAvailableEvents();else k.error(j+"event-broadcast must be an object check your model.");else m=this.getAvailableEvents();return m.forEach(function(g){b.on(g,o.genDispatchEvent(a,g,n,e,b,c,d,f,l))}),n},l}]).service("leafletEventsHelpers",["leafletEventsHelpersFactory",function(a){return new a}]),a.module("ui-leaflet").factory("leafletGeoJsonEvents",["$rootScope","$q","leafletLogger","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}]),a.module("ui-leaflet").factory("leafletLabelEvents",["$rootScope","$q","leafletLogger","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}]),a.module("ui-leaflet").factory("leafletMapEvents",["$rootScope","$q","leafletLogger","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,g){f.each(c,function(c){var f={};f[d]=c,b||(b=a._container.id||""),a.on(c,j(e,c,g,b),f)})};return{getAvailableMapEvents:i,genDispatchMapEvent:j,notifyCenterChangedToBounds:k,notifyCenterUrlHashChanged:l,addEvents:m}}]),a.module("ui-leaflet").factory("leafletMarkerEvents",["$rootScope","$q","leafletLogger","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}]);var d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(a){return typeof a}:function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol?"symbol":typeof a};a.module("ui-leaflet").factory("leafletPathEvents",["$rootScope","$q","leafletLogger","leafletHelpers","leafletLabelEvents","leafletEventsHelpers",function(a,b,c,e,f,g){var h=e.isDefined,i=e.isObject,j=e,k=e.errorHeader,l=f,m=g.fire,n=c,o=function(a,b,c,d,e,f,g,h){return a=a||"",a&&(a="."+a),function(i){var j="leafletDirectivePath"+a+"."+b;n.debug(j),m(d,j,c,i,i.target||e,g,f,h)}},p=function(a,b,c,e,f){var g,m,p=[],r="broadcast";if(h(f.eventBroadcast))if(i(f.eventBroadcast))if(h(f.eventBroadcast.path))if(i(f.eventBroadcast.paths))n.warn(k+"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?n.warn(k+"Available event propagation logic are: 'emit' or 'broadcast'."):"emit"===f.eventBroadcast.path.logic&&(r="emit"));var s=!1,t=!1;if(void 0!==f.eventBroadcast.path.enable&&null!==f.eventBroadcast.path.enable&&"object"===d(f.eventBroadcast.path.enable)&&(s=!0),void 0!==f.eventBroadcast.path.disable&&null!==f.eventBroadcast.path.disable&&"object"===d(f.eventBroadcast.path.disable)&&(t=!0),s&&t)n.warn(k+"can not enable and disable events at the same time");else if(s||t)if(s)for(g=0;g<f.eventBroadcast.path.enable.length;g++)m=f.eventBroadcast.path.enable[g],p.indexOf(m)!==-1?n.warn(k+"This event "+m+" is already enabled"):q().indexOf(m)===-1?n.warn(k+"This event "+m+" does not exist"):p.push(m);else for(p=q(),g=0;g<f.eventBroadcast.path.disable.length;g++){m=f.eventBroadcast.path.disable[g];var u=p.indexOf(m);u===-1?n.warn(k+"This event "+m+" does not exist or has been already disabled"):p.splice(u,1)}else n.warn(k+"must enable or disable events")}else p=q();else n.error(k+"event-broadcast must be an object check your model.");else p=q();for(g=0;g<p.length;g++)m=p[g],b.on(m,o(a,m,r,f,p,c));j.LabelPlugin.isLoaded()&&h(b.label)&&l.genEvents(a,c,r,f,b,e)},q=function(){return["click","dblclick","mousedown","mouseover","mouseout","contextmenu","add","remove","popupopen","popupclose"]};return{getAvailablePathEvents:q,bindPathEvents:p}}])}(angular);
\ No newline at end of file
diff --git a/www/lib/ionic/js/angular/angular-leaflet-directive.js b/www/lib/ionic/js/angular/angular-leaflet-directive.js
new file mode 100644
index 00000000..b4e92c96
--- /dev/null
+++ b/www/lib/ionic/js/angular/angular-leaflet-directive.js
@@ -0,0 +1,5734 @@
+/**!
+ * 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  2015-11-06
+*  angular-leaflet-directive - An AngularJS directive to easily interact with Leaflet maps
+*  git: https://github.com/tombatossals/angular-leaflet-directive
+*/
+(function(angular){
+'use strict';
+angular.module('leaflet-directive', []).directive('leaflet', ["$q", "leafletData", "leafletMapDefaults", "leafletHelpers", "leafletMapEvents", function($q, leafletData, leafletMapDefaults, leafletHelpers, leafletMapEvents) {
+  return {
+    restrict: 'EA',
+    replace: true,
+    scope: {
+      center: '=',
+      lfCenter: '=',
+      defaults: '=',
+      maxbounds: '=',
+      bounds: '=',
+      markers: '=',
+      legend: '=',
+      geojson: '=',
+      paths: '=',
+      tiles: '=',
+      layers: '=',
+      controls: '=',
+      decorations: '=',
+      eventBroadcast: '=',
+      markersWatchOptions: '=',
+      geojsonWatchOptions: '=',
+    },
+    transclude: true,
+    template: '<div class="angular-leaflet-map"><div ng-transclude></div></div>',
+    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;
+      var defaults  = leafletMapDefaults.setDefaults(scope.defaults, attrs.id);
+      var mapEvents = leafletMapEvents.getAvailableMapEvents();
+      var 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;
+  var isNumber = leafletHelpers.isNumber;
+  var isFunction = leafletHelpers.isFunction;
+  var 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();
+      var 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;
+  var isObject = leafletHelpers.isObject;
+  var createLayer = leafletLayerHelpers.createLayer;
+  var _controls = {};
+  var 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;
+  var _isString = leafletHelpers.isString;
+  var _isObject = leafletHelpers.isObject;
+  var _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;
+  var 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;
+    var 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);
+    var 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);
+      var 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;
+  var 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];
+        var keys = keysFunc(source);
+        var 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;
+  var _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);
+    var 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;
+  var _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;
+  var _all = null;
+  _every = _all = function(obj, predicate, context) {
+    predicate = cb(predicate, context);
+    var keys = !_isArrayLike(obj) && _keys(obj);
+    var 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 && typeof options.crs === 'string') {
+          /*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 += '<div class="info-title alert alert-danger">' + legendData.error.message + '</div>';
+    } else {
+      if (type === 'arcgis') {
+        for (var i = 0; i < legendData.layers.length; i++) {
+          var layer = legendData.layers[i];
+          div.innerHTML += '<div class="info-title" data-layerid="' + layer.layerId + '">' + layer.layerName + '</div>';
+          for (var j = 0; j < layer.legend.length; j++) {
+            var leg = layer.legend[j];
+            div.innerHTML +=
+            '<div class="inline" data-layerid="' + layer.layerId + '"><img src="data:' + leg.contentType + ';base64,' + leg.imageData + '" /></div>' +
+            '<div class="info-label" data-layerid="' + layer.layerId + '">' + leg.label + '</div>';
+          }
+        }
+      }      else if (type === 'image') {
+        div.innerHTML = '<img src="' + url + '"/>';
+      }
+    }
+  };
+
+  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 +=
+            '<div class="outline"><i style="background:' + legend.colors[i] + '"></i></div>' +
+            '<div class="info-label">' + legend.labels[i] + '</div>';
+      }
+
+      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: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
+      },
+      path: {
+        weight: 10,
+        opacity: 1,
+        color: '#0000ff',
+      },
+      center: {
+        lat: 0,
+        lng: 0,
+        zoom: 1,
+      },
+    };
+  }
+
+  var isDefined = leafletHelpers.isDefined;
+  var isObject = leafletHelpers.isObject;
+  var obtainEffectiveMapId = leafletHelpers.obtainEffectiveMapId;
+  var 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;
+  var defaultTo = leafletHelpers.defaultTo;
+  var MarkerClusterPlugin = leafletHelpers.MarkerClusterPlugin;
+  var AwesomeMarkersPlugin = leafletHelpers.AwesomeMarkersPlugin;
+  var VectorMarkersPlugin = leafletHelpers.VectorMarkersPlugin;
+  var MakiMarkersPlugin = leafletHelpers.MakiMarkersPlugin;
+  var ExtraMarkersPlugin = leafletHelpers.ExtraMarkersPlugin;
+  var DomMarkersPlugin = leafletHelpers.DomMarkersPlugin;
+  var safeApply = leafletHelpers.safeApply;
+  var Helpers = leafletHelpers;
+  var isString = leafletHelpers.isString;
+  var isNumber = leafletHelpers.isNumber;
+  var isObject = leafletHelpers.isObject;
+  var groups = {};
+  var geoHlp = leafletGeoJsonHelpers;
+  var 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;
+      var template = $compile(iconData.template)(markerScope);
+      var 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;
+    var layerPos = new L.Point(marker._popup._containerLeft, -containerHeight - marker._popup._containerBottom);
+    var 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;
+    var 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;
+    var labelScope = angular.isFunction(markerData.getLabelScope) ? markerData.getLabelScope() : markerScope;
+    var 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;
+  var isArray = leafletHelpers.isArray;
+  var isNumber = leafletHelpers.isNumber;
+  var 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'];
+var 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;
+      var isNumber = leafletHelpers.isNumber;
+      var isSameCenterOnMap = leafletHelpers.isSameCenterOnMap;
+      var safeApply = leafletHelpers.safeApply;
+      var isValidCenter = leafletHelpers.isValidCenter;
+      var isValidBounds = leafletBoundsHelpers.isValidBounds;
+      var isUndefinedOrEmpty = leafletHelpers.isUndefinedOrEmpty;
+      var 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();
+          var 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;
+            var 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();
+      var PolylineDecoratorPlugin = leafletHelpers.PolylineDecoratorPlugin;
+      var isDefined = leafletHelpers.isDefined;
+      var 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];
+            var 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;
+      var isDefined = leafletHelpers.isDefined;
+      var leafletScope  = controller.getLeafletScope();
+      var eventBroadcast = leafletScope.eventBroadcast;
+      var availableMapEvents = leafletMapEvents.getAvailableMapEvents();
+      var addEvents = leafletMapEvents.addEvents;
+
+      controller.getMap().then(function(map) {
+
+        var mapEvents = [];
+        var 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;
+  var _watchOptions = leafletHelpers.watchOptions;
+  var _extendDirectiveControls = leafletDirectiveControlsHelpers.extend;
+  var hlp = leafletHelpers;
+  var $it = leafletIterators;
+
+  return {
+    restrict: 'A',
+    scope: false,
+    replace: false,
+    require: 'leaflet',
+
+    link: function(scope, element, attrs, controller) {
+      var isDefined = leafletHelpers.isDefined;
+      var leafletScope  = controller.getLeafletScope();
+      var leafletGeoJSON = {};
+      var _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;
+      var 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:
+    '<div class="angular-leaflet-control-layers" ng-show="overlaysArray.length">' +
+        '<h4 ng-if="title">{{ title }}</h4>' +
+        '<div class="lf-baselayers">' +
+            '<h5 class="lf-title" ng-if="baseTitle">{{ baseTitle }}</h5>' +
+            '<div class="lf-row" ng-repeat="(key, layer) in baselayersArray">' +
+                '<label class="lf-icon-bl" ng-click="changeBaseLayer(key, $event)">' +
+                    '<input class="leaflet-control-layers-selector" type="radio" name="lf-radio" ' +
+                        'ng-show="false" ng-checked="baselayer === key" ng-value="key" /> ' +
+                    '<i class="lf-icon lf-icon-radio" ng-class="layer.icon"></i>' +
+                    '<div class="lf-text">{{layer.name}}</div>' +
+                '</label>' +
+            '</div>' +
+        '</div>' +
+        '<div class="lf-overlays">' +
+            '<h5 class="lf-title" ng-if="overlaysTitle">{{ overlaysTitle }}</h5>' +
+            '<div class="lf-container">' +
+                '<div class="lf-row" ng-repeat="layer in (o = (overlaysArray | orderBy:\'index\':order))" ng-init="initIndex(layer, $index)">' +
+                    '<label class="lf-icon-ol-group" ng-if="showGroups &amp;&amp; layer.group &amp;&amp; layer.group != o[$index-1].group">' +
+                        '<input class="lf-control-layers-selector" type="checkbox" ng-show="false" ' +
+                            'ng-change="changeGroupVisibility(layer.group)" ng-model="groupProperties[layer.group].visible"/> ' +
+                        '<i class="lf-icon lf-icon-check" ng-class="getGroupIcon(groupProperties[layer.group])"></i>' +
+                        '<div class="lf-text">{{ layer.group }}</div>' +
+                    '</label>' +
+                    '<label class="lf-icon-ol">' +
+                        '<input class="lf-control-layers-selector" type="checkbox" ng-show="false" ng-model="layer.visible"/> ' +
+                        '<i class="lf-icon lf-icon-check" ng-class="layer.icon"></i>' +
+                        '<div class="lf-text">{{layer.name}}</div>' +
+                    '</label>' +
+                    '<div class="lf-icons">' +
+                        '<i class="lf-icon lf-up" ng-class="icons.up" ng-click="moveLayer(layer, layer.index - orderNumber, $event)"></i> ' +
+                        '<i class="lf-icon lf-down" ng-class="icons.down" ng-click="moveLayer(layer, layer.index + orderNumber, $event)"></i> ' +
+                        '<i class="lf-icon lf-toggle-legend" ng-class="icons.toggleLegend" ng-if="layer.legend" ng-click="toggleLegend(layer)"></i> ' +
+                        '<i class="lf-icon lf-open" ng-class="getOpacityIcon(layer)" ng-click="toggleOpacity($event, layer)"></i>' +
+                    '</div>' +
+                    '<div class="lf-legend" ng-if="showLegend(layer)" ng-bind-html="unsafeHTML(layer.legend)"></div>' +
+                    '<div class="lf-opacity clearfix" ng-if="layer.visible &amp;&amp; layerProperties[layer.name].opacityControl">' +
+                        '<label ng-if="rangeIsSupported" class="pull-left" style="width: 50%">0</label>' +
+                        '<label ng-if="rangeIsSupported" class="pull-left text-right" style="width: 50%">100</label>' +
+                        '<input ng-if="rangeIsSupported" class="clearfix" type="range" min="0" max="100" class="lf-opacity-control" ' +
+                            'ng-model="layerProperties[layer.name].opacity" ng-change="changeOpacity(layer)"/>' +
+                        '<h6 ng-if="!rangeIsSupported">Range is not supported in this browser</h6>' +
+                    '</div>' +
+                '</div>' +
+            '</div>' +
+        '</div>' +
+    '</div>',
+    link: function(scope, element, attrs, controller) {
+      var isDefined = leafletHelpers.isDefined;
+      var leafletScope = controller.getLeafletScope();
+      var 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;
+      var leafletLayers = {};
+      var leafletScope  = controller.getLeafletScope();
+      var layers = leafletScope.layers;
+      var createLayer = leafletLayerHelpers.createLayer;
+      var safeAddLayer = leafletLayerHelpers.safeAddLayer;
+      var safeRemoveLayer = leafletLayerHelpers.safeRemoveLayer;
+      var updateLayersControl = leafletControlHelpers.updateLayersControl;
+      var 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;
+      var isDefined = leafletHelpers.isDefined;
+      var isFunction = leafletHelpers.isFunction;
+      var leafletScope = controller.getLeafletScope();
+      var 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;
+      var errorHeader = leafletHelpers.errorHeader;
+      var Helpers = leafletHelpers;
+      var isString = leafletHelpers.isString;
+      var addMarkerWatcher = leafletMarkersHelpers.addMarkerWatcher;
+      var updateMarker = leafletMarkersHelpers.updateMarker;
+      var listenMarkerEvents = leafletMarkersHelpers.listenMarkerEvents;
+      var addMarkerToGroup = leafletMarkersHelpers.addMarkerToGroup;
+      var createMarker = leafletMarkersHelpers.createMarker;
+      var deleteMarker = leafletMarkersHelpers.deleteMarker;
+      var $it = leafletIterators;
+      var _markersWatchOptions = leafletHelpers.watchOptions;
+      var maybeWatch = leafletWatchHelpers.maybeWatch;
+      var 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;
+        var equals = false;
+        var oldMarker;
+        var 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];
+          var leafletScope  = mapController.getLeafletScope();
+
+          mapController.getMap().then(function(map) {
+            var leafletMarkers = {};
+            var 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();
+      var isValidBounds = leafletBoundsHelpers.isValidBounds;
+      var 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];
+      var isDefined = leafletHelpers.isDefined;
+      var isString = leafletHelpers.isString;
+      var leafletScope  = mapController.getLeafletScope();
+      var paths     = leafletScope.paths;
+      var createPath = leafletPathsHelpers.createPath;
+      var bindPathEvents = leafletPathEvents.bindPathEvents;
+      var setPathOptions = leafletPathsHelpers.setPathOptions;
+
+      mapController.getMap().then(function(map) {
+        var defaults = leafletMapDefaults.getDefaults(attrs.id);
+        var 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;
+      var leafletScope  = controller.getLeafletScope();
+      var 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;
+  var isDefined = leafletHelpers.isDefined;
+  var isObject = leafletHelpers.isObject;
+  var isArray = leafletHelpers.isArray;
+  var 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) {
+    // 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;
+        var 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;
+  var 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);
+    var _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;
+  var 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() {};
+
+  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;
+  var 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;
+  var isDefined = leafletHelpers.isDefined;
+  var Helpers = leafletHelpers;
+  var lblHelp = leafletLabelEvents;
+  var 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;
+  var isObject = leafletHelpers.isObject;
+  var Helpers = leafletHelpers;
+  var errorHeader = leafletHelpers.errorHeader;
+  var lblHelp = leafletLabelEvents;
+  var 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 = [];
+    var i;
+    var eventName;
+    var 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;
+        var 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/www/lib/ionic/js/angular/angular-leaflet-directive.min.js b/www/lib/ionic/js/angular/angular-leaflet-directive.min.js
new file mode 100644
index 00000000..cbcd8406
--- /dev/null
+++ b/www/lib/ionic/js/angular/angular-leaflet-directive.min.js
@@ -0,0 +1,40 @@
+/**!
+ * 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  2015-11-06
+*  angular-leaflet-directive - An AngularJS directive to easily interact with Leaflet maps
+*  git: https://github.com/tombatossals/angular-leaflet-directive
+*/
+(function(angular){
+'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:'<div class="angular-leaflet-map"><div ng-transclude></div></div>',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<d.length;g++)d[g].promise.then(f);else a.removeLayer(b)}else c.loadedDefer.promise.then(function(){a.removeLayer(b)});else a.removeLayer(b)}var Helpers=leafletHelpers,isString=leafletHelpers.isString,isObject=leafletHelpers.isObject,isArray=leafletHelpers.isArray,isDefined=leafletHelpers.isDefined,errorHeader=leafletHelpers.errorHeader,$it=leafletIterators,utfGridCreateLayer=function(a){if(!Helpers.UTFGridPlugin.isLoaded())return void $log.error("[AngularJS - Leaflet] The UTFGrid plugin is not loaded.");var b=new L.UtfGrid(a.url,a.pluginOptions);return b.on("mouseover",function(a){$rootScope.$broadcast("leafletDirectiveMap.utfgridMouseover",a)}),b.on("mouseout",function(a){$rootScope.$broadcast("leafletDirectiveMap.utfgridMouseout",a)}),b.on("click",function(a){$rootScope.$broadcast("leafletDirectiveMap.utfgridClick",a)}),b.on("mousemove",function(a){$rootScope.$broadcast("leafletDirectiveMap.utfgridMousemove",a)}),b},layerTypes={xyz:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer(a.url,a.options)}},mapbox:{mustHaveKey:!0,createLayer:function(a){var b=3;isDefined(a.options.version)&&4===a.options.version&&(b=a.options.version);var c=3===b?"//{s}.tiles.mapbox.com/v3/"+a.key+"/{z}/{x}/{y}.png":"//api.tiles.mapbox.com/v4/"+a.key+"/{z}/{x}/{y}.png?access_token="+a.apiKey;return L.tileLayer(c,a.options)}},geoJSON:{mustHaveUrl:!0,createLayer:function(a){return Helpers.GeoJSONPlugin.isLoaded()?new L.TileLayer.GeoJSON(a.url,a.pluginOptions,a.options):void 0}},geoJSONShape:{mustHaveUrl:!1,createLayer:function(a){return new L.GeoJSON(a.data,a.options)}},geoJSONAwesomeMarker:{mustHaveUrl:!1,createLayer:function(a){return new L.geoJson(a.data,{pointToLayer:function(b,c){return L.marker(c,{icon:L.AwesomeMarkers.icon(a.icon)})}})}},geoJSONVectorMarker:{mustHaveUrl:!1,createLayer:function(a){return new L.geoJson(a.data,{pointToLayer:function(b,c){return L.marker(c,{icon:L.VectorMarkers.icon(a.icon)})}})}},utfGrid:{mustHaveUrl:!0,createLayer:utfGridCreateLayer},cartodbTiles:{mustHaveKey:!0,createLayer:function(a){var b="//"+a.user+".cartodb.com/api/v1/map/"+a.key+"/{z}/{x}/{y}.png";return L.tileLayer(b,a.options)}},cartodbUTFGrid:{mustHaveKey:!0,mustHaveLayer:!0,createLayer:function(a){return a.url="//"+a.user+".cartodb.com/api/v1/map/"+a.key+"/"+a.layer+"/{z}/{x}/{y}.grid.json",utfGridCreateLayer(a)}},cartodbInteractive:{mustHaveKey:!0,mustHaveLayer:!0,createLayer:function(a){var b="//"+a.user+".cartodb.com/api/v1/map/"+a.key+"/{z}/{x}/{y}.png",c=L.tileLayer(b,a.options);a.url="//"+a.user+".cartodb.com/api/v1/map/"+a.key+"/"+a.layer+"/{z}/{x}/{y}.grid.json";var d=utfGridCreateLayer(a);return L.layerGroup([c,d])}},wms:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer.wms(a.url,a.options)}},wmts:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer.wmts(a.url,a.options)}},wfs:{mustHaveUrl:!0,mustHaveLayer:!0,createLayer:function(params){if(Helpers.WFSLayerPlugin.isLoaded()){var options=angular.copy(params.options);return options.crs&&"string"==typeof options.crs&&(options.crs=eval(options.crs)),new L.GeoJSON.WFS(params.url,params.layer,options)}}},group:{mustHaveUrl:!1,createLayer:function(a){var b=[];return $it.each(a.options.layers,function(a){b.push(createLayer(a))}),a.options.loadedDefer=function(){var b=[];if(isDefined(a.options.layers))for(var c=0;c<a.options.layers.length;c++){var d=a.options.layers[c].layerOptions.loadedDefer;isDefined(d)&&b.push(d)}return b},L.layerGroup(b)}},featureGroup:{mustHaveUrl:!1,createLayer:function(){return L.featureGroup()}},google:{mustHaveUrl:!1,createLayer:function(a){var b=a.type||"SATELLITE";if(Helpers.GoogleLayerPlugin.isLoaded())return new L.Google(b,a.options)}},here:{mustHaveUrl:!1,createLayer:function(a){var b=a.provider||"HERE.terrainDay";if(Helpers.LeafletProviderPlugin.isLoaded())return new L.TileLayer.Provider(b,a.options)}},china:{mustHaveUrl:!1,createLayer:function(a){var b=a.type||"";if(Helpers.ChinaLayerPlugin.isLoaded())return L.tileLayer.chinaProvider(b,a.options)}},agsBase:{mustHaveLayer:!0,createLayer:function(a){return Helpers.AGSBaseLayerPlugin.isLoaded()?L.esri.basemapLayer(a.layer,a.options):void 0}},ags:{mustHaveUrl:!0,createLayer:function(a){if(Helpers.AGSLayerPlugin.isLoaded()){var b=angular.copy(a.options);angular.extend(b,{url:a.url});var c=new lvector.AGS(b);return c.onAdd=function(a){this.setMap(a)},c.onRemove=function(){this.setMap(null)},c}}},agsFeature:{mustHaveUrl:!0,createLayer:function(a){if(!Helpers.AGSFeatureLayerPlugin.isLoaded())return void $log.warn(errorHeader+" The esri plugin is not loaded.");a.options.url=a.url;var b=L.esri.featureLayer(a.options),c=function(){isDefined(a.options.loadedDefer)&&a.options.loadedDefer.resolve()};return b.on("loading",function(){a.options.loadedDefer=$q.defer(),b.off("load",c),b.on("load",c)}),b}},agsTiled:{mustHaveUrl:!0,createLayer:function(a){return Helpers.AGSTiledMapLayerPlugin.isLoaded()?(a.options.url=a.url,L.esri.tiledMapLayer(a.options)):void $log.warn(errorHeader+" The esri plugin is not loaded.")}},agsDynamic:{mustHaveUrl:!0,createLayer:function(a){return Helpers.AGSDynamicMapLayerPlugin.isLoaded()?(a.options.url=a.url,L.esri.dynamicMapLayer(a.options)):void $log.warn(errorHeader+" The esri plugin is not loaded.")}},agsImage:{mustHaveUrl:!0,createLayer:function(a){return Helpers.AGSImageMapLayerPlugin.isLoaded()?(a.options.url=a.url,L.esri.imageMapLayer(a.options)):void $log.warn(errorHeader+" The esri plugin is not loaded.")}},agsClustered:{mustHaveUrl:!0,createLayer:function(a){return Helpers.AGSClusteredLayerPlugin.isLoaded()?Helpers.MarkerClusterPlugin.isLoaded()?L.esri.clusteredFeatureLayer(a.url,a.options):void $log.warn(errorHeader+" The markercluster plugin is not loaded."):void $log.warn(errorHeader+" The esri clustered layer plugin is not loaded.")}},agsHeatmap:{mustHaveUrl:!0,createLayer:function(a){return Helpers.AGSHeatmapLayerPlugin.isLoaded()?Helpers.HeatLayerPlugin.isLoaded()?L.esri.heatmapFeatureLayer(a.url,a.options):void $log.warn(errorHeader+" The heatlayer plugin is not loaded."):void $log.warn(errorHeader+" The esri heatmap layer plugin is not loaded.")}},markercluster:{mustHaveUrl:!1,createLayer:function(a){return Helpers.MarkerClusterPlugin.isLoaded()?new L.MarkerClusterGroup(a.options):void $log.warn(errorHeader+" The markercluster plugin is not loaded.")}},bing:{mustHaveUrl:!1,createLayer:function(a){return Helpers.BingLayerPlugin.isLoaded()?new L.BingLayer(a.key,a.options):void 0}},webGLHeatmap:{mustHaveUrl:!1,mustHaveData:!0,createLayer:function(a){if(Helpers.WebGLHeatMapLayerPlugin.isLoaded()){var b=new L.TileLayer.WebGLHeatMap(a.options);return isDefined(a.data)&&b.setData(a.data),b}}},heat:{mustHaveUrl:!1,mustHaveData:!0,createLayer:function(a){if(Helpers.HeatLayerPlugin.isLoaded()){var b=new L.heatLayer;return isArray(a.data)&&b.setLatLngs(a.data),isObject(a.options)&&b.setOptions(a.options),b}}},yandex:{mustHaveUrl:!1,createLayer:function(a){var b=a.type||"map";if(Helpers.YandexLayerPlugin.isLoaded())return new L.Yandex(b,a.options)}},imageOverlay:{mustHaveUrl:!0,mustHaveBounds:!0,createLayer:function(a){return L.imageOverlay(a.url,a.bounds,a.options)}},iip:{mustHaveUrl:!0,createLayer:function(a){return L.tileLayer.iip(a.url,a.options)}},custom:{createLayer:function(a){return a.layer instanceof L.Class?angular.copy(a.layer):void $log.error("[AngularJS - Leaflet] A custom layer must be a leaflet Class")}},cartodb:{mustHaveUrl:!0,createLayer:function(a){return cartodb.createLayer(a.map,a.url)}}};return{createLayer:createLayer,safeAddLayer:safeAddLayer,safeRemoveLayer:safeRemoveLayer}}]),angular.module("leaflet-directive").factory("leafletLegendHelpers",function(){var a=function(a,b,c,d){if(a.innerHTML="",b.error)a.innerHTML+='<div class="info-title alert alert-danger">'+b.error.message+"</div>";else if("arcgis"===c)for(var e=0;e<b.layers.length;e++){var f=b.layers[e];a.innerHTML+='<div class="info-title" data-layerid="'+f.layerId+'">'+f.layerName+"</div>";for(var g=0;g<f.legend.length;g++){var h=f.legend[g];a.innerHTML+='<div class="inline" data-layerid="'+f.layerId+'"><img src="data:'+h.contentType+";base64,"+h.imageData+'" /></div><div class="info-label" data-layerid="'+f.layerId+'">'+h.label+"</div>"}}else"image"===c&&(a.innerHTML='<img src="'+d+'"/>')},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.colors.length;d++)c.innerHTML+='<div class="outline"><i style="background:'+a.colors[d]+'"></i></div><div class="info-label">'+a.labels[d]+"</div>";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:'&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> 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;d<l.length;d++){var e=l[d];h(a[e])?c[e]=a[e]:h(b.path[e])&&(c[e]=b.path[e])}return c}var h=c.isDefined,i=c.isArray,j=c.isNumber,k=c.isValidPoint,l=["stroke","weight","color","opacity","fill","fillColor","fillOpacity","dashArray","lineCap","lineJoin","clickable","pointerEvents","className","smoothFactor","noClip"],m=function(a,b){for(var c={},d=0;d<l.length;d++){var e=l[d];h(b[e])&&(c[e]=b[e])}a.setStyle(b)},n=function(a){if(!i(a))return!1;for(var b=0;b<a.length;b++){var c=a[b];if(!k(c))return!1}return!0},o={polyline:{isValid:function(a){var b=a.latlngs;return n(b)},createPath:function(a){return new L.Polyline([],a)},setPath:function(a,b){a.setLatLngs(d(b.latlngs)),m(a,b)}},multiPolyline:{isValid:function(a){var b=a.latlngs;if(!i(b))return!1;for(var c in b){var d=b[c];if(!n(d))return!1}return!0},createPath:function(a){return new L.multiPolyline([[[0,0],[1,1]]],a)},setPath:function(a,b){a.setLatLngs(f(b.latlngs)),m(a,b)}},polygon:{isValid:function(a){var b=a.latlngs;return n(b)},createPath:function(a){return new L.Polygon([],a)},setPath:function(a,b){a.setLatLngs(d(b.latlngs)),m(a,b)}},multiPolygon:{isValid:function(a){var b=a.latlngs;if(!i(b))return!1;for(var c in b){var d=b[c];if(!n(d))return!1}return!0},createPath:function(a){return new L.MultiPolygon([[[0,0],[1,1],[0,1]]],a)},setPath:function(a,b){a.setLatLngs(f(b.latlngs)),m(a,b)}},rectangle:{isValid:function(a){var b=a.latlngs;if(!i(b)||2!==b.length)return!1;for(var c in b){var d=b[c];if(!k(d))return!1}return!0},createPath:function(a){return new L.Rectangle([[0,0],[1,1]],a)},setPath:function(a,b){a.setBounds(new L.LatLngBounds(d(b.latlngs))),m(a,b)}},circle:{isValid:function(a){var b=a.latlngs;return k(b)&&j(a.radius)},createPath:function(a){return new L.Circle([0,0],1,a)},setPath:function(a,b){a.setLatLng(e(b.latlngs)),h(b.radius)&&a.setRadius(b.radius),m(a,b)}},circleMarker:{isValid:function(a){var b=a.latlngs;return k(b)&&j(a.radius)},createPath:function(a){return new L.CircleMarker([0,0],a)},setPath:function(a,b){a.setLatLng(e(b.latlngs)),h(b.radius)&&a.setRadius(b.radius),m(a,b)}}},p=function(a){var b={};return a.latlngs&&(b.latlngs=a.latlngs),a.radius&&(b.radius=a.radius),b};return{setPathOptions:function(a,b,c){h(b)||(b="polyline"),o[b].setPath(a,c)},createPath:function(a,c,d){h(c.type)||(c.type="polyline");var e=g(c,d),f=p(c);return o[c.type].isValid(f)?o[c.type].createPath(e):void b.error("[AngularJS - Leaflet] Invalid data passed to the "+c.type+" path")}}}]),angular.module("leaflet-directive").service("leafletWatchHelpers",function(){var a=function(a,b,c,d,e){var f=a[b](c,function(a,b){e(a,b),d.doWatch||f()},d.isDeep);return f},b=function(b,c,d,e){return a(b,"$watch",c,d,e)},c=function(b,c,d,e){return a(b,"$watchCollection",c,d,e)};return{maybeWatch:b,maybeWatchCollection:c}}),angular.module("leaflet-directive").factory("nominatimService",["$q","$http","leafletHelpers","leafletMapDefaults",function(a,b,c,d){var e=c.isDefined;return{query:function(c,f){var g=d.getDefaults(f),h=g.nominatim.server,i=a.defer();return b.get(h,{params:{format:"json",limit:1,q:c}}).success(function(a){a.length>0&&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:'<div class="angular-leaflet-control-layers" ng-show="overlaysArray.length"><h4 ng-if="title">{{ title }}</h4><div class="lf-baselayers"><h5 class="lf-title" ng-if="baseTitle">{{ baseTitle }}</h5><div class="lf-row" ng-repeat="(key, layer) in baselayersArray"><label class="lf-icon-bl" ng-click="changeBaseLayer(key, $event)"><input class="leaflet-control-layers-selector" type="radio" name="lf-radio" ng-show="false" ng-checked="baselayer === key" ng-value="key" /> <i class="lf-icon lf-icon-radio" ng-class="layer.icon"></i><div class="lf-text">{{layer.name}}</div></label></div></div><div class="lf-overlays"><h5 class="lf-title" ng-if="overlaysTitle">{{ overlaysTitle }}</h5><div class="lf-container"><div class="lf-row" ng-repeat="layer in (o = (overlaysArray | orderBy:\'index\':order))" ng-init="initIndex(layer, $index)"><label class="lf-icon-ol-group" ng-if="showGroups &amp;&amp; layer.group &amp;&amp; layer.group != o[$index-1].group"><input class="lf-control-layers-selector" type="checkbox" ng-show="false" ng-change="changeGroupVisibility(layer.group)" ng-model="groupProperties[layer.group].visible"/> <i class="lf-icon lf-icon-check" ng-class="getGroupIcon(groupProperties[layer.group])"></i><div class="lf-text">{{ layer.group }}</div></label><label class="lf-icon-ol"><input class="lf-control-layers-selector" type="checkbox" ng-show="false" ng-model="layer.visible"/> <i class="lf-icon lf-icon-check" ng-class="layer.icon"></i><div class="lf-text">{{layer.name}}</div></label><div class="lf-icons"><i class="lf-icon lf-up" ng-class="icons.up" ng-click="moveLayer(layer, layer.index - orderNumber, $event)"></i> <i class="lf-icon lf-down" ng-class="icons.down" ng-click="moveLayer(layer, layer.index + orderNumber, $event)"></i> <i class="lf-icon lf-toggle-legend" ng-class="icons.toggleLegend" ng-if="layer.legend" ng-click="toggleLegend(layer)"></i> <i class="lf-icon lf-open" ng-class="getOpacityIcon(layer)" ng-click="toggleOpacity($event, layer)"></i></div><div class="lf-legend" ng-if="showLegend(layer)" ng-bind-html="unsafeHTML(layer.legend)"></div><div class="lf-opacity clearfix" ng-if="layer.visible &amp;&amp; layerProperties[layer.name].opacityControl"><label ng-if="rangeIsSupported" class="pull-left" style="width: 50%">0</label><label ng-if="rangeIsSupported" class="pull-left text-right" style="width: 50%">100</label><input ng-if="rangeIsSupported" class="clearfix" type="range" min="0" max="100" class="lf-opacity-control" ng-model="layerProperties[layer.name].opacity" ng-change="changeOpacity(layer)"/><h6 ng-if="!rangeIsSupported">Range is not supported in this browser</h6></div></div></div></div></div>',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){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){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(){},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<f.eventBroadcast.path.enable.length;l++)n=f.eventBroadcast.path.enable[l],-1!==p.indexOf(n)?c.warn(j+"This event "+n+" is already enabled"):-1===o().indexOf(n)?c.warn(j+"This event "+n+" does not exist"):p.push(n);else for(p=o(),l=0;l<f.eventBroadcast.path.disable.length;l++){n=f.eventBroadcast.path.disable[l];var t=p.indexOf(n);-1===t?c.warn(j+"This event "+n+" does not exist or has been already disabled"):p.splice(t,1)}else c.warn(j+"must enable or disable events")}else p=o();else c.error(j+"event-broadcast must be an object check your model.");else p=o();for(l=0;l<p.length;l++)n=p[l],b.on(n,m(a,n,q,f,p,d));i.LabelPlugin.isLoaded()&&g(b.label)&&k.genEvents(a,d,q,f,b,e)},o=function(){return["click","dblclick","mousedown","mouseover","mouseout","contextmenu","add","remove","popupopen","popupclose"]};return{getAvailablePathEvents:o,bindPathEvents:n}}])}(angular);
+}(angular));
\ No newline at end of file
-- 
GitLab