diff --git a/Rakefile b/Rakefile
index c9c4c2d..f84371a 100644
--- a/Rakefile
+++ b/Rakefile
@@ -2,7 +2,7 @@ require "bundler/gem_tasks"
desc "Fetch new version from https://github.com/tombatossals/angular-leaflet-directive"
task :fetch do
- source = "https://raw.github.com/tombatossals/angular-leaflet-directive/master/src/angular-leaflet-directive.js"
+ source = "https://raw.githubusercontent.com/tombatossals/angular-leaflet-directive/master/dist/angular-leaflet-directive.js"
target = "vendor/assets/javascripts/angular-leaflet-directive.js"
sh "curl #{source} > #{target}"
end
diff --git a/lib/angular-leaflet-rails/version.rb b/lib/angular-leaflet-rails/version.rb
index 8290c44..f8e7317 100644
--- a/lib/angular-leaflet-rails/version.rb
+++ b/lib/angular-leaflet-rails/version.rb
@@ -1,5 +1,5 @@
module AngularLeaflet
module Rails
- VERSION = "0.1.0.6"
+ VERSION = "0.1.0.8"
end
end
diff --git a/vendor/assets/javascripts/angular-leaflet-directive.js b/vendor/assets/javascripts/angular-leaflet-directive.js
index e6eb6fe..9283416 100644
--- a/vendor/assets/javascripts/angular-leaflet-directive.js
+++ b/vendor/assets/javascripts/angular-leaflet-directive.js
@@ -1,179 +1,3886 @@
-var leafletDirective = angular.module("leaflet-directive", []);
+(function() {
-leafletDirective.directive("leaflet", ["$http", "$log", function ($http, $log) {
+"use strict";
+
+angular.module("leaflet-directive", []).directive('leaflet', ["$q", "leafletData", "leafletMapDefaults", "leafletHelpers", "leafletEvents", function ($q, leafletData, leafletMapDefaults, leafletHelpers, leafletEvents) {
+ var _leafletMap;
return {
- restrict: "E",
+ restrict: "EA",
replace: true,
- transclude: true,
scope: {
- center: "=center",
- tilelayer: "=tilelayer",
- markers: "=markers",
- leafletMarkers: "=leafletMarkers",
- path: "=path",
- maxZoom: "@maxzoom"
+ center : '=',
+ defaults : '=',
+ maxbounds : '=',
+ bounds : '=',
+ markers : '=',
+ legend : '=',
+ geojson : '=',
+ paths : '=',
+ tiles : '=',
+ layers : '=',
+ controls : '=',
+ decorations : '=',
+ eventBroadcast : '='
},
- template: '
',
- link: function (scope, element, attrs, ctrl) {
- var $el = element[0],
- map = new L.Map($el);
+ transclude: true,
+ template: '',
+ controller: ["$scope", function ($scope) {
+ _leafletMap = $q.defer();
+ this.getMap = function () {
+ return _leafletMap.promise;
+ };
+
+ this.getLeafletScope = function() {
+ return $scope;
+ };
+ }],
+
+ link: function(scope, element, attrs) {
+ var isDefined = leafletHelpers.isDefined,
+ defaults = leafletMapDefaults.setDefaults(scope.defaults, attrs.id),
+ genDispatchMapEvent = leafletEvents.genDispatchMapEvent,
+ mapEvents = leafletEvents.getAvailableMapEvents();
+
+ // 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();
- // Expose the map object, for testing purposes
- if (attrs.map) {
- scope.map = map;
+ 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));
+ _leafletMap.resolve(map);
+
+ if (!isDefined(attrs.center)) {
+ 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);
}
- // Set maxZoom from attrs
- if (attrs.maxzoom){
- scope.maxZoom = parseInt(attrs.maxzoom)
+ if (isDefined(map.zoomControl) &&
+ defaults.zoomControl===false) {
+ map.zoomControl.removeFrom(map);
}
- // Set initial view
- map.setView([0, 0], 1);
+ 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";
+ for (var i = 0; i < mapEvents.length; i++) {
+ var eventName = mapEvents[i];
+ map.on(eventName, genDispatchMapEvent(scope, eventName, logic), {
+ eventName: eventName
+ });
+ }
+ }
+
+ // Resolve the map object to the promises
+ map.whenReady(function() {
+ leafletData.setMap(map, attrs.id);
+ });
+
+ scope.$on('$destroy', function () {
+ map.remove();
+ leafletData.unresolveMap(attrs.id);
+ });
- // Set tile layer
- var tilelayer = scope.tilelayer || 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
- var maxZoom = scope.maxZoom || 12;
- L.tileLayer(tilelayer, { maxZoom: maxZoom }).addTo(map);
+ //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").directive('center',
+ ["$log", "$q", "$location", "$timeout", "leafletMapDefaults", "leafletHelpers", "leafletBoundsHelpers", "leafletEvents", function ($log, $q, $location, $timeout, leafletMapDefaults, leafletHelpers, leafletBoundsHelpers, leafletEvents) {
+
+ var isDefined = leafletHelpers.isDefined,
+ isNumber = leafletHelpers.isNumber,
+ isSameCenterOnMap = leafletHelpers.isSameCenterOnMap,
+ safeApply = leafletHelpers.safeApply,
+ isValidCenter = leafletHelpers.isValidCenter,
+ isValidBounds = leafletBoundsHelpers.isValidBounds,
+ isUndefinedOrEmpty = leafletHelpers.isUndefinedOrEmpty;
+
+ var shouldInitializeMapWithBounds = function(bounds, center) {
+ return isDefined(bounds) && isValidBounds(bounds) && isUndefinedOrEmpty(center);
+ };
+
+ var _leafletCenter;
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+ controller: function () {
+ _leafletCenter = $q.defer();
+ this.getCenter = function() {
+ return _leafletCenter.promise;
+ };
+ },
+ link: function(scope, element, attrs, controller) {
+ var leafletScope = controller.getLeafletScope(),
+ centerModel = leafletScope.center;
- // Manage map center events
- if (attrs.center && scope.center) {
+ controller.getMap().then(function(map) {
+ var defaults = leafletMapDefaults.getDefaults(attrs.id);
- if (scope.center.lat && scope.center.lng && scope.center.zoom) {
- map.setView([scope.center.lat, scope.center.lng], scope.center.zoom);
- } else if (scope.center.autoDiscover === true) {
- map.locate({ setView: true, maxZoom: maxZoom });
+ if (attrs.center.search("-") !== -1) {
+ $log.error('The "center" variable can\'t use a "-" on his key name: "' + attrs.center + '".');
+ map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ return;
+ } else if (shouldInitializeMapWithBounds(leafletScope.bounds, centerModel)) {
+ map.fitBounds(leafletBoundsHelpers.createLeafletBounds(leafletScope.bounds));
+ centerModel = map.getCenter();
+ safeApply(leafletScope, function (scope) {
+ scope.center = {
+ 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('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);
}
- map.on("dragend", function(e) {
- scope.$apply(function (s) {
- s.center.lat = map.getCenter().lat;
- s.center.lng = map.getCenter().lng;
+ var urlCenterHash, mapReady;
+ if (attrs.urlHashCenter === "yes") {
+ var extractCenterFromUrl = function() {
+ var search = $location.search();
+ var centerParam;
+ if (isDefined(search.c)) {
+ var cParam = search.c.split(":");
+ if (cParam.length === 3) {
+ centerParam = { lat: parseFloat(cParam[0]), lng: parseFloat(cParam[1]), zoom: parseInt(cParam[2], 10) };
+ }
+ }
+ return centerParam;
+ };
+ urlCenterHash = extractCenterFromUrl();
+
+ leafletScope.$on('$locationChangeSuccess', function(event) {
+ var scope = event.currentScope;
+ //$log.debug("updated location...");
+ var urlCenter = extractCenterFromUrl();
+ if (isDefined(urlCenter) && !isSameCenterOnMap(urlCenter, map)) {
+ //$log.debug("updating center model...", urlCenter);
+ scope.center = {
+ lat: urlCenter.lat,
+ lng: urlCenter.lng,
+ zoom: urlCenter.zoom
+ };
+ }
});
- });
+ }
- map.on("zoomend", function(e) {
- if (scope.center.zoom !== map.getZoom()){
- scope.$apply(function (s) {
- s.center.zoom = map.getZoom();
- });
+ leafletScope.$watch("center", function(center) {
+ //$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("[AngularJS - Leaflet] 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;
}
- });
- scope.$watch("center", function (center, oldValue) {
- if(center.lat && center.lng && center.zoom){
- map.setView([center.lat, center.lng], center.zoom);
+ 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);
+ leafletEvents.notifyCenterChangedToBounds(leafletScope, map);
+ $timeout(function() {
+ leafletScope.settingCenterFromScope = false;
+ //$log.debug("allow center scope updates");
+ });
}, true);
- }
- if (attrs.markers && scope.markers) {
- var createAndLinkMarker = function(key, scope) {
- var data = scope.markers[key];
- var marker = new L.marker(
- scope.markers[key],
- {
- draggable: data.draggable ? true:false
+ map.whenReady(function() {
+ mapReady = true;
+ });
+
+ map.on("moveend", function(/* event */) {
+ // Resolve the center after the first map position
+ _leafletCenter.resolve();
+ leafletEvents.notifyCenterUrlHashChanged(leafletScope, map, attrs, $location.search());
+ //$log.debug("updated center on map...");
+ if (isSameCenterOnMap(centerModel, map) || scope.settingCenterFromScope) {
+ //$log.debug("same center in model, no need to update again.");
+ return;
+ }
+ safeApply(leafletScope, function(scope) {
+ if (!leafletScope.settingCenterFromScope) {
+ //$log.debug("updating center model...", map.getCenter(), map.getZoom());
+ scope.center = {
+ lat: map.getCenter().lat,
+ lng: map.getCenter().lng,
+ zoom: map.getZoom(),
+ autoDiscover: false
+ };
}
- );
+ leafletEvents.notifyCenterChangedToBounds(leafletScope, map);
+ });
+ });
- marker.on("dragend", function(e) {
- scope.$apply(function (s) {
- data.lat = marker.getLatLng().lat;
- data.lng = marker.getLatLng().lng;
- if (data.message) {
- marker.openPopup();
- }
- });
+ if (centerModel.autoDiscover === true) {
+ map.on("locationerror", function() {
+ $log.warn("[AngularJS - Leaflet] The Geolocation API is unauthorized on this page.");
+ if (isValidCenter(centerModel)) {
+ map.setView([centerModel.lat, centerModel.lng], centerModel.zoom);
+ leafletEvents.notifyCenterChangedToBounds(leafletScope, map);
+ } else {
+ map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom);
+ leafletEvents.notifyCenterChangedToBounds(leafletScope, map);
+ }
});
+ }
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('tiles', ["$log", "leafletData", "leafletMapDefaults", "leafletHelpers", function ($log, leafletData, leafletMapDefaults, leafletHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ tiles = leafletScope.tiles;
+
+ if (!isDefined(tiles) && !isDefined(tiles.url)) {
+ $log.warn("[AngularJS - Leaflet] The 'tiles' definition doesn't have the 'url' property.");
+ return;
+ }
+
+ controller.getMap().then(function(map) {
+ var defaults = leafletMapDefaults.getDefaults(attrs.id);
+ var tileLayerObj;
+ leafletScope.$watch("tiles", function(tiles) {
+ 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;
+ }
+
+ 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) && !angular.equals(tiles.options, tileLayerOptions)) {
+ map.removeLayer(tileLayerObj);
+ tileLayerOptions = defaults.tileLayerOptions;
+ angular.copy(tiles.options, tileLayerOptions);
+ tileLayerUrl = tiles.url;
+ 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);
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('legend', ["$log", "$http", "leafletHelpers", "leafletLegendHelpers", function ($log, $http, leafletHelpers, leafletLegendHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function (scope, element, attrs, controller) {
+
+ var isArray = leafletHelpers.isArray,
+ isDefined = leafletHelpers.isDefined,
+ isFunction = leafletHelpers.isFunction,
+ leafletScope = controller.getLeafletScope(),
+ legend = leafletScope.legend;
- scope.$watch('markers.' + key, function(newval, oldval) {
- if (!newval) {
- map.removeLayer(markers[key]);
- delete leafletMarkers[key];
- if (attrs.leafletMarkers) {
- delete scope.leafletMarkers[key];
+ 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 (newval.draggable !== undefined && newval.draggable !== oldval.draggable) {
- newval.draggable ? marker.dragging.enable() : marker.dragging.disable();
+ if (isDefined(newLegend.url)) {
+
+ $log.info("[AngularJS - Leaflet] loading legend service.");
+
+ return;
}
- if (newval.focus !== undefined && newval.focus !== oldval.focus) {
- newval.focus ? marker.openPopup() : marker.closePopup();
+ if (isDefined(leafletLegend)) {
+ leafletLegend.removeFrom(map);
+ leafletLegend= null;
}
- if (newval.message !== undefined && newval.message !== oldval.message) {
- marker.bindPopup(newval);
+ leafletLegend = L.control({
+ position: position
+ });
+ if (type === 'arcgis') {
+ leafletLegend.onAdd = leafletLegendHelpers.getOnAddArrayLegend(newLegend, legendClass);
}
+ leafletLegend.addTo(map);
+
+ });
- if (newval.lat !== oldval.lat || newval.lng !== oldval.lng) {
- marker.setLatLng(new L.LatLng(newval.lat, newval.lng));
+ leafletScope.$watch('legend.url', function (newURL) {
+
+ if (!isDefined(newURL)) {
+ return;
}
- }, true);
+ $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('geojson', ["$log", "$rootScope", "leafletData", "leafletHelpers", function ($log, $rootScope, leafletData, leafletHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var safeApply = leafletHelpers.safeApply,
+ isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ leafletGeoJSON = {};
+
+ controller.getMap().then(function(map) {
+ leafletScope.$watchCollection("geojson", function(geojson) {
+ if (isDefined(leafletGeoJSON) && map.hasLayer(leafletGeoJSON)) {
+ map.removeLayer(leafletGeoJSON);
+ }
+
+ if (!(isDefined(geojson) && isDefined(geojson.data))) {
+ return;
+ }
+
+ var resetStyleOnMouseout = geojson.resetStyleOnMouseout;
+ var onEachFeature;
+
+ if (angular.isFunction(geojson.onEachFeature)) {
+ onEachFeature = geojson.onEachFeature;
+ } else {
+ onEachFeature = function(feature, layer) {
+ if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(geojson.label)) {
+ layer.bindLabel(feature.properties.description);
+ }
+
+ layer.on({
+ mouseover: function(e) {
+ safeApply(leafletScope, function() {
+ $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseover', feature, e);
+ });
+ },
+ mouseout: function(e) {
+ if (resetStyleOnMouseout) {
+ leafletGeoJSON.resetStyle(e.target);
+ }
+ safeApply(leafletScope, function() {
+ $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseout', e);
+ });
+ },
+ click: function(e) {
+ safeApply(leafletScope, function() {
+ $rootScope.$broadcast('leafletDirectiveMap.geojsonClick', feature, e);
+ });
+ }
+ });
+ };
+ }
- return marker;
- }; // end of create and link marker
+ if (!isDefined(geojson.options)) {
+ geojson.options = {
+ style: geojson.style,
+ filter: geojson.filter,
+ onEachFeature: onEachFeature,
+ pointToLayer: geojson.pointToLayer
+ };
+ }
+
+ leafletGeoJSON = L.geoJson(geojson.data, geojson.options);
+ leafletData.setGeoJSON(leafletGeoJSON, attrs.id);
+ leafletGeoJSON.addTo(map);
+
+ });
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('layers', ["$log", "$q", "leafletData", "leafletHelpers", "leafletLayerHelpers", "leafletControlHelpers", function ($log, $q, leafletData, leafletHelpers, leafletLayerHelpers, leafletControlHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+ controller: ["$scope", function ($scope) {
+ $scope._leafletLayers = $q.defer();
+ this.getLayers = function () {
+ return $scope._leafletLayers.promise;
+ };
+ }],
+ link: function(scope, element, attrs, controller){
+ var isDefined = leafletHelpers.isDefined,
+ leafletLayers = {},
+ leafletScope = controller.getLeafletScope(),
+ layers = leafletScope.layers,
+ createLayer = leafletLayerHelpers.createLayer,
+ updateLayersControl = leafletControlHelpers.updateLayersControl,
+ isLayersControlVisible = false;
- var leafletMarkers = {}
+ controller.getMap().then(function(map) {
- // Expose the map object, for testing purposes
- if (attrs.leafletMarkers) {
- scope.leafletMarkers = {};
+ // 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) {
+ map.addLayer(leafletLayers.baselayers[layerName]);
+ oneVisibleLayer = true;
+ }
+ }
+
+ // If there is no visible layer add first to the map
+ if (!oneVisibleLayer && Object.keys(leafletLayers.baselayers).length > 0) {
+ map.addLayer(leafletLayers.baselayers[Object.keys(layers.baselayers)[0]]);
}
- // Create the initial objects
- for (var key in scope.markers) {
- var marker = createAndLinkMarker(key, scope);
- map.addLayer(marker);
- leafletMarkers[key] = marker;
- if (attrs.leafletMarkers) {
- scope.leafletMarkers[key] = marker;
+ // 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) {
+ map.addLayer(leafletLayers.overlays[layerName]);
}
}
- scope.$watch("markers", function(newMarkerList) {
- // add new markers
- for (var key in newMarkerList) {
- if (leafletMarkers[key] === undefined) {
- var marker = createAndLinkMarker(key, scope);
- map.addLayer(marker);
- leafletMarkers[key] = marker;
- if (attrs.leafletMarkers) {
- scope.leafletMarkers[key] = marker;
+ // Watch for the base layers
+ leafletScope.$watch('layers.baselayers', function(newBaseLayers) {
+ // Delete layers from the array
+ for (var name in leafletLayers.baselayers) {
+ if (!isDefined(newBaseLayers[name])) {
+ // Remove from the map if it's on it
+ if (map.hasLayer(leafletLayers.baselayers[name])) {
+ map.removeLayer(leafletLayers.baselayers[name]);
+ }
+ delete leafletLayers.baselayers[name];
+ }
+ }
+ // 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) {
+ map.addLayer(leafletLayers.baselayers[newName]);
+ }
+ }
+ } else {
+ if (newBaseLayers[newName].top === true && !map.hasLayer(leafletLayers.baselayers[newName])) {
+ map.addLayer(leafletLayers.baselayers[newName]);
+ } else if (newBaseLayers[newName].top === false && map.hasLayer(leafletLayers.baselayers[newName])) {
+ map.removeLayer(leafletLayers.baselayers[newName]);
}
}
}
- }, true);
- } // if attrs.markers
- if (attrs.path) {
- var polyline = new L.Polyline([], { weight: 10, opacity: 1});
- map.addLayer(polyline);
- scope.$watch("path.latlngs", function(latlngs) {
- for (var idx=0, length=latlngs.length; idx < length; idx++) {
- if (latlngs[idx] === undefined || latlngs[idx].lat === undefined || latlngs[idx].lng === undefined) {
- $log.warn("Bad path point inn the $scope.path array ");
- latlngs.splice(idx, 1);
+ //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;
}
}
- polyline.setLatLngs(latlngs);
+ // If there is no active layer make one active
+ if (!found && Object.keys(layers.baselayers).length > 0) {
+ map.addLayer(leafletLayers.baselayers[Object.keys(layers.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);
- scope.$watch("path.weight", function(weight) {
- polyline.setStyle({
- weight: weight
- });
+ // Watch for the overlay layers
+ leafletScope.$watch('layers.overlays', function(newOverlayLayers) {
+ // Delete layers from the array
+ for (var name in leafletLayers.overlays) {
+ if (!isDefined(newOverlayLayers[name])) {
+ // Remove from the map if it's on it
+ if (map.hasLayer(leafletLayers.overlays[name])) {
+ map.removeLayer(leafletLayers.overlays[name]);
+ }
+ // TODO: Depending on the layer type we will have to delete what's included on it
+ delete leafletLayers.overlays[name];
+ }
+ }
+
+ // add new overlays
+ for (var newName in newOverlayLayers) {
+ if (!isDefined(leafletLayers.overlays[newName])) {
+ var testOverlayLayer = createLayer(newOverlayLayers[newName]);
+ if (isDefined(testOverlayLayer)) {
+ leafletLayers.overlays[newName] = testOverlayLayer;
+ if (newOverlayLayers[newName].visible === true) {
+ map.addLayer(leafletLayers.overlays[newName]);
+ }
+ }
+ }
+
+ // check for the .visible property to hide/show overLayers
+ if (newOverlayLayers[newName].visible && !map.hasLayer(leafletLayers.overlays[newName])) {
+ map.addLayer(leafletLayers.overlays[newName]);
+ } else if (newOverlayLayers[newName].visible === false && map.hasLayer(leafletLayers.overlays[newName])) {
+ map.removeLayer(leafletLayers.overlays[newName]);
+ }
+
+ //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);
+ });
+ }
+ };
+}]);
- scope.$watch("path.color", function(color) {
- polyline.setStyle({
- color: color
- });
+angular.module("leaflet-directive").directive('bounds', ["$log", "$timeout", "leafletHelpers", "leafletBoundsHelpers", function ($log, $timeout, leafletHelpers, leafletBoundsHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: [ 'leaflet', 'center' ],
+
+ link: function(scope, element, attrs, controller) {
+ var isDefined = leafletHelpers.isDefined,
+ createLeafletBounds = leafletBoundsHelpers.createLeafletBounds,
+ leafletScope = controller[0].getLeafletScope(),
+ mapController = controller[0];
+
+ 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();
+ //$log.debug('updated map bounds...', bounds);
+ if (emptyBounds(bounds) || scope.settingBoundsFromScope) {
+ return;
+ }
+ var newScopeBounds = {
+ northEast: {
+ lat: bounds._northEast.lat,
+ lng: bounds._northEast.lng
+ },
+ southWest: {
+ lat: bounds._southWest.lat,
+ lng: bounds._southWest.lng
+ }
+ };
+ if (!angular.equals(scope.bounds, newScopeBounds)) {
+ //$log.debug('Need to update scope bounds.');
+ scope.bounds = newScopeBounds;
+ }
+ });
+ leafletScope.$watch('bounds', function (bounds) {
+ //$log.debug('updated bounds...', bounds);
+ if (!isDefined(bounds)) {
+ $log.error('[AngularJS - Leaflet] Invalid bounds');
+ return;
+ }
+ var leafletBounds = createLeafletBounds(bounds);
+ if (leafletBounds && !map.getBounds().equals(leafletBounds)) {
+ //$log.debug('Need to update map bounds.');
+ scope.settingBoundsFromScope = true;
+ map.fitBounds(leafletBounds);
+ $timeout( function() {
+ //$log.debug('Allow bound updates.');
+ scope.settingBoundsFromScope = false;
+ });
+ }
}, true);
- } // end of attrs.path
- } // end of link function
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('markers', ["$log", "$rootScope", "$q", "leafletData", "leafletHelpers", "leafletMapDefaults", "leafletMarkersHelpers", "leafletEvents", function ($log, $rootScope, $q, leafletData, leafletHelpers, leafletMapDefaults, leafletMarkersHelpers, leafletEvents) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: ['leaflet', '?layers'],
+
+ link: function(scope, element, attrs, controller) {
+ var mapController = controller[0],
+ Helpers = leafletHelpers,
+ isDefined = leafletHelpers.isDefined,
+ isString = leafletHelpers.isString,
+ leafletScope = mapController.getLeafletScope(),
+ deleteMarker = leafletMarkersHelpers.deleteMarker,
+ addMarkerWatcher = leafletMarkersHelpers.addMarkerWatcher,
+ listenMarkerEvents = leafletMarkersHelpers.listenMarkerEvents,
+ addMarkerToGroup = leafletMarkersHelpers.addMarkerToGroup,
+ bindMarkerEvents = leafletEvents.bindMarkerEvents,
+ createMarker = leafletMarkersHelpers.createMarker;
+
+ mapController.getMap().then(function(map) {
+ var leafletMarkers = {},
+ getLayers;
+
+ // If the layers attribute is used, we must wait until the layers are created
+ if (isDefined(controller[1])) {
+ getLayers = controller[1].getLayers;
+ } else {
+ getLayers = function() {
+ var deferred = $q.defer();
+ deferred.resolve();
+ return deferred.promise;
+ };
+ }
+
+ getLayers().then(function(layers) {
+ leafletData.setMarkers(leafletMarkers, attrs.id);
+ leafletScope.$watch('markers', function(newMarkers) {
+ // Delete markers from the array
+ for (var name in leafletMarkers) {
+ if (!isDefined(newMarkers) || !isDefined(newMarkers[name])) {
+ deleteMarker(leafletMarkers[name], map, layers);
+ delete leafletMarkers[name];
+ }
+ }
+
+ // Should we watch for every specific marker on the map?
+ var shouldWatch = (!isDefined(attrs.watchMarkers) || attrs.watchMarkers === 'true');
+
+ // add new markers
+ for (var newName in newMarkers) {
+ if (newName.search("-") !== -1) {
+ $log.error('The marker can\'t use a "-" on his key name: "' + newName + '".');
+ continue;
+ }
+
+
+ if (!isDefined(leafletMarkers[newName])) {
+ var markerData = newMarkers[newName];
+ var marker = createMarker(markerData);
+ if (!isDefined(marker)) {
+ $log.error('[AngularJS - Leaflet] Received invalid data on the marker ' + newName + '.');
+ continue;
+ }
+ leafletMarkers[newName] = marker;
+
+ // Bind message
+ if (isDefined(markerData.message)) {
+ marker.bindPopup(markerData.message, markerData.popupOptions);
+ }
+
+ // Add the marker to a cluster group if needed
+ if (isDefined(markerData.group)) {
+ var groupOptions = isDefined(markerData.groupOption) ? markerData.groupOption : null;
+ addMarkerToGroup(marker, markerData.group, groupOptions, map);
+ }
+
+ // Show label if defined
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(markerData.label) && isDefined(markerData.label.message)) {
+ marker.bindLabel(markerData.label.message, markerData.label.options);
+ }
+
+ // Check if the marker should be added to a layer
+ if (isDefined(markerData) && isDefined(markerData.layer)) {
+ if (!isString(markerData.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[markerData.layer])) {
+ $log.error('[AngularJS - Leaflet] A marker can only be added to a layer of type "group"');
+ continue;
+ }
+ var layerGroup = layers.overlays[markerData.layer];
+ if (!(layerGroup instanceof L.LayerGroup || layerGroup instanceof L.FeatureGroup)) {
+ $log.error('[AngularJS - Leaflet] Adding a marker to an overlay needs a overlay of the type "group" or "featureGroup"');
+ continue;
+ }
+
+ // 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 (!shouldWatch && map.hasLayer(marker) && markerData.focus === true) {
+ leafletMarkersHelpers.manageOpenPopup(marker, markerData);
+ }
+
+ // Add the marker to the map if it hasn't been added to a layer or to a group
+ } else if (!isDefined(markerData.group)) {
+ // We do not have a layer attr, so the marker goes to the map layer
+ map.addLayer(marker);
+ if (!shouldWatch && markerData.focus === true) {
+ leafletMarkersHelpers.manageOpenPopup(marker, markerData);
+ }
+ }
+
+
+ if (shouldWatch) {
+ addMarkerWatcher(marker, newName, leafletScope, layers, map);
+ }
+
+ listenMarkerEvents(marker, markerData, leafletScope, shouldWatch);
+ bindMarkerEvents(marker, newName, markerData, leafletScope);
+ }
+ }
+ }, true);
+ });
+ });
+ }
};
}]);
+
+angular.module("leaflet-directive").directive('paths', ["$log", "$q", "leafletData", "leafletMapDefaults", "leafletHelpers", "leafletPathsHelpers", "leafletEvents", function ($log, $q, leafletData, leafletMapDefaults, leafletHelpers, leafletPathsHelpers, leafletEvents) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: ['leaflet', '?layers'],
+
+ link: function(scope, element, attrs, controller) {
+ var mapController = controller[0],
+ isDefined = leafletHelpers.isDefined,
+ isString = leafletHelpers.isString,
+ leafletScope = mapController.getLeafletScope(),
+ paths = leafletScope.paths,
+ createPath = leafletPathsHelpers.createPath,
+ bindPathEvents = leafletEvents.bindPathEvents,
+ setPathOptions = leafletPathsHelpers.setPathOptions;
+
+ mapController.getMap().then(function(map) {
+ var defaults = leafletMapDefaults.getDefaults(attrs.id),
+ getLayers;
+
+ // If the layers attribute is used, we must wait until the layers are created
+ if (isDefined(controller[1])) {
+ getLayers = controller[1].getLayers;
+ } else {
+ getLayers = function() {
+ var deferred = $q.defer();
+ deferred.resolve();
+ return deferred.promise;
+ };
+ }
+
+ if (!isDefined(paths)) {
+ return;
+ }
+
+ getLayers().then(function(layers) {
+
+ var leafletPaths = {};
+ leafletData.setPaths(leafletPaths, attrs.id);
+
+ // Should we watch for every specific marker on the map?
+ var shouldWatch = (!isDefined(attrs.watchPaths) || attrs.watchPaths === 'true');
+
+ // Function for listening every single path once created
+ var watchPathFn = function(leafletPath, name) {
+ var clearWatch = leafletScope.$watch("paths[\""+name+"\"]", function(pathData, old) {
+ if (!isDefined(pathData)) {
+ if (isDefined(old.layer)) {
+ for (var i in layers.overlays) {
+ var overlay = layers.overlays[i];
+ overlay.removeLayer(leafletPath);
+ }
+ }
+ map.removeLayer(leafletPath);
+ clearWatch();
+ return;
+ }
+ setPathOptions(leafletPath, pathData.type, pathData);
+ }, true);
+ };
+
+ leafletScope.$watchCollection("paths", function (newPaths) {
+
+ // Delete paths (by name) from the array
+ for (var name in leafletPaths) {
+ if (!isDefined(newPaths[name])) {
+ map.removeLayer(leafletPaths[name]);
+ delete leafletPaths[name];
+ }
+ }
+
+ // Create the new paths
+ for (var newName in newPaths) {
+ if (newName.search('\\$') === 0) {
+ continue;
+ }
+ if (newName.search("-") !== -1) {
+ $log.error('[AngularJS - Leaflet] The path name "' + newName + '" is not valid. It must not include "-" and a number.');
+ continue;
+ }
+
+ if (!isDefined(leafletPaths[newName])) {
+ var pathData = newPaths[newName];
+ var newPath = createPath(newName, newPaths[newName], defaults);
+
+ // bind popup if defined
+ if (isDefined(newPath) && isDefined(pathData.message)) {
+ newPath.bindPopup(pathData.message);
+ }
+
+ // 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 marker 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 marker 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(newPath, newName, pathData, leafletScope);
+ }
+ }
+ });
+ });
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('controls', ["$log", "leafletHelpers", function ($log, leafletHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: '?^leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ if(!controller) {
+ return;
+ }
+
+ var isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ controls = leafletScope.controls;
+
+ controller.getMap().then(function(map) {
+ if (isDefined(L.Control.Draw) && isDefined(controls.draw)) {
+
+ if (!isDefined(controls.edit)) {
+ controls.edit = { featureGroup: new L.FeatureGroup() };
+ map.addLayer(controls.edit.featureGroup);
+ }
+
+ var drawControl = new L.Control.Draw(controls);
+ map.addControl(drawControl);
+ }
+
+ if (isDefined(controls.scale)) {
+ var scaleControl = new L.control.scale();
+ map.addControl(scaleControl);
+ }
+
+ if (isDefined(controls.custom)) {
+ for(var i in controls.custom) {
+ map.addControl(controls.custom[i]);
+ }
+ }
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('eventBroadcast', ["$log", "$rootScope", "leafletHelpers", "leafletEvents", function ($log, $rootScope, leafletHelpers, leafletEvents) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var isObject = leafletHelpers.isObject,
+ isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ eventBroadcast = leafletScope.eventBroadcast,
+ availableMapEvents = leafletEvents.getAvailableMapEvents(),
+ genDispatchMapEvent = leafletEvents.genDispatchMapEvent;
+
+ controller.getMap().then(function(map) {
+
+ var mapEvents = [];
+ var i;
+ var eventName;
+ 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
+ for (i = 0; i < eventBroadcast.map.enable.length; i++) {
+ eventName = eventBroadcast.map.enable[i];
+ // Do we have already the event enabled?
+ if (mapEvents.indexOf(eventName) === -1 && availableMapEvents.indexOf(eventName) !== -1) {
+ mapEvents.push(eventName);
+ }
+ }
+ }
+
+ }
+
+ for (i = 0; i < mapEvents.length; i++) {
+ eventName = mapEvents[i];
+ map.on(eventName, genDispatchMapEvent(leafletScope, eventName, logic), {
+ eventName: eventName
+ });
+ }
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive('maxbounds', ["$log", "leafletMapDefaults", "leafletBoundsHelpers", function ($log, leafletMapDefaults, leafletBoundsHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var leafletScope = controller.getLeafletScope(),
+ isValidBounds = leafletBoundsHelpers.isValidBounds;
+
+
+ controller.getMap().then(function(map) {
+ leafletScope.$watch("maxbounds", function (maxbounds) {
+ if (!isValidBounds(maxbounds)) {
+ // Unset any previous maxbounds
+ map.setMaxBounds();
+ return;
+ }
+ var bounds = [
+ [ maxbounds.southWest.lat, maxbounds.southWest.lng ],
+ [ maxbounds.northEast.lat, maxbounds.northEast.lng ]
+ ];
+
+ map.setMaxBounds(bounds);
+ if (!attrs.center) {
+ map.fitBounds(bounds);
+ }
+ });
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").directive("decorations", ["$log", "leafletHelpers", function($log, leafletHelpers) {
+ return {
+ restrict: "A",
+ scope: false,
+ replace: false,
+ require: 'leaflet',
+
+ link: function(scope, element, attrs, controller) {
+ var leafletScope = controller.getLeafletScope(),
+ PolylineDecoratorPlugin = leafletHelpers.PolylineDecoratorPlugin,
+ isDefined = leafletHelpers.isDefined,
+ leafletDecorations = {};
+
+ /* Creates an "empty" decoration with a set of coordinates, but no pattern. */
+ function createDecoration(options) {
+ if (isDefined(options) && isDefined(options.coordinates)) {
+ if (!PolylineDecoratorPlugin.isLoaded()) {
+ $log.error('[AngularJS - Leaflet] The PolylineDecorator Plugin is not loaded.');
+ }
+ }
+
+ return L.polylineDecorator(options.coordinates);
+ }
+
+ /* Updates the path and the patterns for the provided decoration, and returns the decoration. */
+ function setDecorationOptions(decoration, options) {
+ if (isDefined(decoration) && isDefined(options)) {
+ if (isDefined(options.coordinates) && isDefined(options.patterns)) {
+ decoration.setPaths(options.coordinates);
+ decoration.setPatterns(options.patterns);
+ return decoration;
+ }
+ }
+ }
+
+ controller.getMap().then(function(map) {
+ leafletScope.$watch("decorations", function(newDecorations) {
+ for (var name in leafletDecorations) {
+ if (!isDefined(newDecorations) || !isDefined(newDecorations[name])) {
+ map.removeLayer(leafletDecorations[name]);
+ delete leafletDecorations[name];
+ }
+ }
+
+ for (var newName in newDecorations) {
+ var decorationData = newDecorations[newName],
+ newDecoration = createDecoration(decorationData);
+
+ if (isDefined(newDecoration)) {
+ leafletDecorations[newName] = newDecoration;
+ map.addLayer(newDecoration);
+ setDecorationOptions(newDecoration, decorationData);
+ }
+ }
+ }, true);
+ });
+ }
+ };
+}]);
+angular.module("leaflet-directive").directive('layercontrol', ["$log", "leafletData", "leafletHelpers", function ($log, leafletData, leafletHelpers) {
+ return {
+ restrict: "E",
+ scope: {
+ },
+ replace: true,
+ transclude: false,
+ require: '^leaflet',
+ controller: ["$scope", "$element", "$sce", function ($scope, $element, $sce) {
+ $log.debug('[Angular Directive - Layers] layers', $scope, $element);
+ var safeApply = leafletHelpers.safeApply,
+ isDefined = leafletHelpers.isDefined;
+ angular.extend($scope, {
+ baselayer: '',
+ icons: {
+ uncheck: 'fa fa-check-square-o',
+ check: 'fa fa-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'
+ },
+ 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;
+ },
+ toggleOpacity: function(e, layer) {
+ $log.debug('Event', e);
+ if(layer.visible) {
+ var el = angular.element(e.currentTarget);
+ el.toggleClass($scope.icons.close + ' ' + $scope.icons.open);
+ el = el.parents('.lf-row').find('.lf-opacity');
+ el.toggle('fast', function() {
+ safeApply($scope, function() {
+ layer.opacityControl = !layer.opacityControl;
+ });
+ });
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ },
+ unsafeHTML: function(html) {
+ return $sce.trustAsHtml(html);
+ }
+ });
+
+ 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:
+ '' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
'+
+ '
' +
+ '
' +
+ '' +
+ '
' +
+ '
' +
+ '
' +
+ '
' +
+ '
',
+ link: function(scope, element, attrs, controller) {
+ var isDefined = leafletHelpers.isDefined,
+ leafletScope = controller.getLeafletScope(),
+ layers = leafletScope.layers;
+
+ // 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) {
+ leafletData.getLayers().then(function(leafletLayers) {
+ var key;
+ for(key in newBaseLayers) {
+ if(map.hasLayer(leafletLayers.baselayers[key])) {
+ newBaseLayers[key].icon = scope.icons.radio;
+ } else {
+ newBaseLayers[key].icon = scope.icons.unradio;
+ }
+ }
+ });
+ });
+
+ leafletScope.$watch('layers.overlays', function(newOverlayLayers) {
+ var overlaysArray = [];
+ leafletData.getLayers().then(function(leafletLayers) {
+ for(var key in newOverlayLayers) {
+ newOverlayLayers[key].icon = scope.icons[(newOverlayLayers[key].visible? 'uncheck':'check')];
+ overlaysArray.push(newOverlayLayers[key]);
+ if(isDefined(newOverlayLayers[key].index) && leafletLayers.overlays[key].setZIndex) {
+ leafletLayers.overlays[key].setZIndex(newOverlayLayers[key].index);
+ }
+ }
+ });
+
+ var unreg = scope.$watch(function() {
+ if(element.children().size() > 1) {
+ element.find('.lf-overlays').trigger('resize');
+ return element.find('.lf-opacity').size() === Object.keys(layers.overlays).length;
+ }
+ }, function(el) {
+ if(el === true) {
+ if(isDefined(element.find('.lf-opacity-control').ionRangeSlider)) {
+ element.find('.lf-opacity-control').each(function(idx, inp) {
+ var delta = Object.keys(layers.baselayers).length,
+ lyAux;
+ for(var key in scope.overlaysArray) {
+ if(scope.overlaysArray[key].index === idx+delta+1) {
+ lyAux = scope.overlaysArray[key];
+ }
+ }
+
+ var input = angular.element(inp),
+ op = isDefined(lyAux) && isDefined(lyAux.layerOptions)?
+ lyAux.layerOptions.opacity:undefined;
+ input.ionRangeSlider({
+ min: 0,
+ from: isDefined(op)? Math.ceil(op*100):100,
+ step: 1,
+ max: 100,
+ prettify: false,
+ hasGrid: false,
+ hideMinMax: true,
+ onChange: function(val) {
+ leafletData.getLayers().then(function(leafletLayers) {
+ var key = val.input.data().key;
+ var ly, layer;
+ for(var k in layers.overlays) {
+ if(layers.overlays[k].index === key) {
+ ly = leafletLayers.overlays[k];
+ layer = layers.overlays[k];
+ break;
+ }
+ }
+ if(map.hasLayer(ly)) {
+ layer.layerOptions = isDefined(layer.layerOptions)? layer.layerOptions:{};
+ layer.layerOptions.opacity = val.input.val()/100;
+ if(ly.setOpacity) {
+ ly.setOpacity(val.input.val()/100);
+ }
+ if(ly.getLayers && ly.eachLayer) {
+ ly.eachLayer(function(lay) {
+ if(lay.setOpacity) {
+ lay.setOpacity(val.input.val()/100);
+ }
+ });
+ }
+ }
+ });
+ }
+ });
+ });
+ } else {
+ $log.warn('[AngularJS - Leaflet] Ion Slide Range Plugin is not loaded');
+ }
+ unreg();
+ }
+ });
+
+ scope.overlaysArray = overlaysArray;
+ }, true);
+ });
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").service('leafletData', ["$log", "$q", "leafletHelpers", function ($log, $q, leafletHelpers) {
+ var getDefer = leafletHelpers.getDefer,
+ getUnresolvedDefer = leafletHelpers.getUnresolvedDefer,
+ setResolvedDefer = leafletHelpers.setResolvedDefer;
+
+ var maps = {};
+ var tiles = {};
+ var layers = {};
+ var paths = {};
+ var markers = {};
+ var geoJSON = {};
+ var utfGrid = {};
+ var decorations = {};
+
+ this.setMap = function(leafletMap, scopeId) {
+ var defer = getUnresolvedDefer(maps, scopeId);
+ defer.resolve(leafletMap);
+ setResolvedDefer(maps, scopeId);
+ };
+
+ this.getMap = function(scopeId) {
+ var defer = getDefer(maps, scopeId);
+ return defer.promise;
+ };
+
+ this.unresolveMap = function (scopeId) {
+ var id = leafletHelpers.obtainEffectiveMapId(maps, scopeId);
+ maps[id] = undefined;
+ tiles[id] = undefined;
+ layers[id] = undefined;
+ paths[id] = undefined;
+ markers[id] = undefined;
+ geoJSON[id] = undefined;
+ utfGrid[id] = undefined;
+ decorations[id] = undefined;
+ };
+
+ this.getPaths = function(scopeId) {
+ var defer = getDefer(paths, scopeId);
+ return defer.promise;
+ };
+
+ this.setPaths = function(leafletPaths, scopeId) {
+ var defer = getUnresolvedDefer(paths, scopeId);
+ defer.resolve(leafletPaths);
+ setResolvedDefer(paths, scopeId);
+ };
+
+ this.getMarkers = function(scopeId) {
+ var defer = getDefer(markers, scopeId);
+ return defer.promise;
+ };
+
+ this.setMarkers = function(leafletMarkers, scopeId) {
+ var defer = getUnresolvedDefer(markers, scopeId);
+ defer.resolve(leafletMarkers);
+ setResolvedDefer(markers, scopeId);
+ };
+
+ this.getLayers = function(scopeId) {
+ var defer = getDefer(layers, scopeId);
+ return defer.promise;
+ };
+
+ this.setLayers = function(leafletLayers, scopeId) {
+ var defer = getUnresolvedDefer(layers, scopeId);
+ defer.resolve(leafletLayers);
+ setResolvedDefer(layers, scopeId);
+ };
+
+ this.getUTFGrid = function(scopeId) {
+ var defer = getDefer(utfGrid, scopeId);
+ return defer.promise;
+ };
+
+ this.setUTFGrid = function(leafletUTFGrid, scopeId) {
+ var defer = getUnresolvedDefer(utfGrid, scopeId);
+ defer.resolve(leafletUTFGrid);
+ setResolvedDefer(utfGrid, scopeId);
+ };
+
+ this.setTiles = function(leafletTiles, scopeId) {
+ var defer = getUnresolvedDefer(tiles, scopeId);
+ defer.resolve(leafletTiles);
+ setResolvedDefer(tiles, scopeId);
+ };
+
+ this.getTiles = function(scopeId) {
+ var defer = getDefer(tiles, scopeId);
+ return defer.promise;
+ };
+
+ this.setGeoJSON = function(leafletGeoJSON, scopeId) {
+ var defer = getUnresolvedDefer(geoJSON, scopeId);
+ defer.resolve(leafletGeoJSON);
+ setResolvedDefer(geoJSON, scopeId);
+ };
+
+ this.getGeoJSON = function(scopeId) {
+ var defer = getDefer(geoJSON, scopeId);
+ return defer.promise;
+ };
+
+ this.setDecorations = function(leafletDecorations, scopeId) {
+ var defer = getUnresolvedDefer(decorations, scopeId);
+ defer.resolve(leafletDecorations);
+ setResolvedDefer(decorations, scopeId);
+ };
+
+ this.getDecorations = function(scopeId) {
+ var defer = getDefer(decorations, scopeId);
+ return defer.promise;
+ };
+}]);
+
+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
+ }
+ },
+ crs: L.CRS.EPSG3857,
+ tileLayer: '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
+ tileLayerOptions: {
+ attribution: '© OpenStreetMap contributors'
+ },
+ path: {
+ weight: 10,
+ opacity: 1,
+ color: '#0000ff'
+ },
+ center: {
+ lat: 0,
+ lng: 0,
+ zoom: 1
+ }
+ };
+ }
+
+ var isDefined = leafletHelpers.isDefined,
+ isObject = leafletHelpers.isObject,
+ obtainEffectiveMapId = leafletHelpers.obtainEffectiveMapId,
+ defaults = {};
+
+ // Get the _defaults dictionary, and override the properties defined by the user
+ return {
+ 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;
+ }
+ }
+
+ var mapId = obtainEffectiveMapId(defaults, scopeId);
+ defaults[mapId] = newDefaults;
+ return newDefaults;
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletEvents', ["$rootScope", "$q", "$log", "leafletHelpers", function ($rootScope, $q, $log, leafletHelpers) {
+ var safeApply = leafletHelpers.safeApply,
+ isDefined = leafletHelpers.isDefined,
+ isObject = leafletHelpers.isObject,
+ Helpers = leafletHelpers;
+
+ var _getAvailableLabelEvents = function() {
+ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseover',
+ 'mouseout',
+ 'contextmenu'
+ ];
+ };
+
+ var genLabelEvents = function(leafletScope, logic, marker, name) {
+ var labelEvents = _getAvailableLabelEvents();
+ var scopeWatchName = "markers." + name;
+ for (var i = 0; i < labelEvents.length; i++) {
+ var eventName = labelEvents[i];
+ marker.label.on(eventName, genDispatchLabelEvent(leafletScope, eventName, logic, marker.label, scopeWatchName));
+ }
+ };
+
+ var genDispatchMarkerEvent = function(eventName, logic, leafletScope, marker, name, markerData) {
+ return function(e) {
+ var broadcastName = 'leafletDirectiveMarker.' + eventName;
+
+ // 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() {
+ markerData.lat = marker.getLatLng().lat;
+ markerData.lng = marker.getLatLng().lng;
+ });
+ if (markerData.message && markerData.focus === true) {
+ marker.openPopup();
+ }
+ }
+
+ safeApply(leafletScope, function(scope){
+ if (logic === "emit") {
+ scope.$emit(broadcastName, {
+ markerName: name,
+ leafletEvent: e
+ });
+ } else {
+ $rootScope.$broadcast(broadcastName, {
+ markerName: name,
+ leafletEvent: e
+ });
+ }
+ });
+ };
+ };
+
+ var genDispatchPathEvent = function(eventName, logic, leafletScope, marker, name) {
+ return function(e) {
+ var broadcastName = 'leafletDirectivePath.' + eventName;
+
+ safeApply(leafletScope, function(scope){
+ if (logic === "emit") {
+ scope.$emit(broadcastName, {
+ pathName: name,
+ leafletEvent: e
+ });
+ } else {
+ $rootScope.$broadcast(broadcastName, {
+ pathName: name,
+ leafletEvent: e
+ });
+ }
+ });
+ };
+ };
+
+ var genDispatchLabelEvent = function(scope, eventName, logic, label, scope_watch_name) {
+ return function(e) {
+ // Put together broadcast name
+ var broadcastName = 'leafletDirectiveLabel.' + eventName;
+ var markerName = scope_watch_name.replace('markers.', '');
+
+ // Safely broadcast the event
+ safeApply(scope, function(scope) {
+ if (logic === "emit") {
+ scope.$emit(broadcastName, {
+ leafletEvent : e,
+ label: label,
+ markerName: markerName
+ });
+ } else if (logic === "broadcast") {
+ $rootScope.$broadcast(broadcastName, {
+ leafletEvent : e,
+ label: label,
+ markerName: markerName
+ });
+ }
+ });
+ };
+ };
+
+ var _getAvailableMarkerEvents = function() {
+ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseover',
+ 'mouseout',
+ 'contextmenu',
+ 'dragstart',
+ 'drag',
+ 'dragend',
+ 'move',
+ 'remove',
+ 'popupopen',
+ 'popupclose'
+ ];
+ };
+
+ var _getAvailablePathEvents = function() {
+ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseover',
+ 'mouseout',
+ 'contextmenu',
+ 'add',
+ 'remove',
+ 'popupopen',
+ 'popupclose'
+ ];
+ };
+
+ return {
+ getAvailableMapEvents: function() {
+ return [
+ 'click',
+ 'dblclick',
+ 'mousedown',
+ 'mouseup',
+ 'mouseover',
+ 'mouseout',
+ 'mousemove',
+ 'contextmenu',
+ 'focus',
+ 'blur',
+ 'preclick',
+ 'load',
+ 'unload',
+ 'viewreset',
+ 'movestart',
+ 'move',
+ 'moveend',
+ 'dragstart',
+ 'drag',
+ 'dragend',
+ 'zoomstart',
+ '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'
+ ];
+ },
+
+ genDispatchMapEvent: function(scope, eventName, logic) {
+ return function(e) {
+ // Put together broadcast name
+ var broadcastName = 'leafletDirectiveMap.' + eventName;
+ // Safely broadcast the event
+ safeApply(scope, function(scope) {
+ if (logic === "emit") {
+ scope.$emit(broadcastName, {
+ leafletEvent : e
+ });
+ } else if (logic === "broadcast") {
+ $rootScope.$broadcast(broadcastName, {
+ leafletEvent : e
+ });
+ }
+ });
+ };
+ },
+
+ getAvailableMarkerEvents: _getAvailableMarkerEvents,
+
+ getAvailablePathEvents: _getAvailablePathEvents,
+
+ notifyCenterChangedToBounds: function(scope) {
+ scope.$broadcast("boundsChanged");
+ },
+
+ 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);
+ }
+ },
+
+ bindMarkerEvents: function(marker, name, markerData, leafletScope) {
+ var markerEvents = [];
+ var i;
+ var eventName;
+ var logic = "emit";
+
+ if (!isDefined(leafletScope.eventBroadcast)) {
+ // Backward compatibility, if no event-broadcast attribute, all events are broadcasted
+ markerEvents = _getAvailableMarkerEvents();
+ } else if (!isObject(leafletScope.eventBroadcast)) {
+ // Not a valid object
+ $log.error("[AngularJS - Leaflet] event-broadcast must be an object check your model.");
+ } else {
+ // We have a possible valid object
+ if (!isDefined(leafletScope.eventBroadcast.marker)) {
+ // We do not have events enable/disable do we do nothing (all enabled by default)
+ markerEvents = _getAvailableMarkerEvents();
+ } else if (!isObject(leafletScope.eventBroadcast.marker)) {
+ // Not a valid object
+ $log.warn("[AngularJS - Leaflet] event-broadcast.marker must be an object check your model.");
+ } else {
+ // We have a possible valid map object
+ // Event propadation logic
+ if (leafletScope.eventBroadcast.marker.logic !== undefined && leafletScope.eventBroadcast.marker.logic !== null) {
+ // We take care of possible propagation logic
+ if (leafletScope.eventBroadcast.marker.logic !== "emit" && leafletScope.eventBroadcast.marker.logic !== "broadcast") {
+ // This is an error
+ $log.warn("[AngularJS - Leaflet] Available event propagation logic are: 'emit' or 'broadcast'.");
+ } else if (leafletScope.eventBroadcast.marker.logic === "emit") {
+ logic = "emit";
+ }
+ }
+ // Enable / Disable
+ var markerEventsEnable = false, markerEventsDisable = false;
+ if (leafletScope.eventBroadcast.marker.enable !== undefined && leafletScope.eventBroadcast.marker.enable !== null) {
+ if (typeof leafletScope.eventBroadcast.marker.enable === 'object') {
+ markerEventsEnable = true;
+ }
+ }
+ if (leafletScope.eventBroadcast.marker.disable !== undefined && leafletScope.eventBroadcast.marker.disable !== null) {
+ if (typeof leafletScope.eventBroadcast.marker.disable === 'object') {
+ markerEventsDisable = true;
+ }
+ }
+ if (markerEventsEnable && markerEventsDisable) {
+ // Both are active, this is an error
+ $log.warn("[AngularJS - Leaflet] can not enable and disable events at the same time");
+ } else if (!markerEventsEnable && !markerEventsDisable) {
+ // Both are inactive, this is an error
+ $log.warn("[AngularJS - Leaflet] must enable or disable events");
+ } else {
+ // At this point the marker object is OK, lets enable or disable events
+ if (markerEventsEnable) {
+ // Enable events
+ for (i = 0; i < leafletScope.eventBroadcast.marker.enable.length; i++) {
+ eventName = leafletScope.eventBroadcast.marker.enable[i];
+ // Do we have already the event enabled?
+ if (markerEvents.indexOf(eventName) !== -1) {
+ // Repeated event, this is an error
+ $log.warn("[AngularJS - Leaflet] This event " + eventName + " is already enabled");
+ } else {
+ // Does the event exists?
+ if (_getAvailableMarkerEvents().indexOf(eventName) === -1) {
+ // The event does not exists, this is an error
+ $log.warn("[AngularJS - Leaflet] This event " + eventName + " does not exist");
+ } else {
+ // All ok enable the event
+ markerEvents.push(eventName);
+ }
+ }
+ }
+ } else {
+ // Disable events
+ markerEvents = _getAvailableMarkerEvents();
+ for (i = 0; i < leafletScope.eventBroadcast.marker.disable.length; i++) {
+ eventName = leafletScope.eventBroadcast.marker.disable[i];
+ var index = markerEvents.indexOf(eventName);
+ if (index === -1) {
+ // The event does not exist
+ $log.warn("[AngularJS - Leaflet] This event " + eventName + " does not exist or has been already disabled");
+
+ } else {
+ markerEvents.splice(index, 1);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < markerEvents.length; i++) {
+ eventName = markerEvents[i];
+ marker.on(eventName, genDispatchMarkerEvent(eventName, logic, leafletScope, marker, name, markerData));
+ }
+
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(marker.label)) {
+ genLabelEvents(leafletScope, logic, marker, name);
+ }
+ },
+
+ bindPathEvents: function(path, name, pathData, 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("[AngularJS - Leaflet] 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("[AngularJS - Leaflet] 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("[AngularJS - Leaflet] Available event propagation logic are: 'emit' or 'broadcast'.");
+ } else if (leafletScope.eventBroadcast.path.logic === "emit") {
+ logic = "emit";
+ }
+ }
+ // Enable / Disable
+ var pathEventsEnable = false, pathEventsDisable = false;
+ if (leafletScope.eventBroadcast.path.enable !== undefined && leafletScope.eventBroadcast.path.enable !== null) {
+ if (typeof leafletScope.eventBroadcast.path.enable === 'object') {
+ pathEventsEnable = true;
+ }
+ }
+ if (leafletScope.eventBroadcast.path.disable !== undefined && leafletScope.eventBroadcast.path.disable !== null) {
+ if (typeof leafletScope.eventBroadcast.path.disable === 'object') {
+ pathEventsDisable = true;
+ }
+ }
+ if (pathEventsEnable && pathEventsDisable) {
+ // Both are active, this is an error
+ $log.warn("[AngularJS - Leaflet] can not enable and disable events at the same time");
+ } else if (!pathEventsEnable && !pathEventsDisable) {
+ // Both are inactive, this is an error
+ $log.warn("[AngularJS - Leaflet] 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("[AngularJS - Leaflet] 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("[AngularJS - Leaflet] 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("[AngularJS - Leaflet] 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];
+ path.on(eventName, genDispatchPathEvent(eventName, logic, leafletScope, pathEvents, name));
+ }
+
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(path.label)) {
+ genLabelEvents(leafletScope, logic, path, name);
+ }
+ }
+
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletLayerHelpers', ["$rootScope", "$log", "leafletHelpers", function ($rootScope, $log, leafletHelpers) {
+ var Helpers = leafletHelpers,
+ isString = leafletHelpers.isString,
+ isObject = leafletHelpers.isObject,
+ isDefined = leafletHelpers.isDefined;
+
+ 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);
+ });
+
+ return utfgrid;
+ };
+
+ var layerTypes = {
+ xyz: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return L.tileLayer(params.url, params.options);
+ }
+ },
+ mapbox: {
+ mustHaveKey: true,
+ createLayer: function(params) {
+ var url = '//{s}.tiles.mapbox.com/v3/' + params.key + '/{z}/{x}/{y}.png';
+ 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);
+ }
+ },
+ utfGrid: {
+ mustHaveUrl: true,
+ createLayer: utfGridCreateLayer
+ },
+ cartodbTiles: {
+ mustHaveKey: true,
+ createLayer: function(params) {
+ var url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/{z}/{x}/{y}.png';
+ return L.tileLayer(url, params.options);
+ }
+ },
+ cartodbUTFGrid: {
+ mustHaveKey: true,
+ mustHaveLayer : true,
+ createLayer: function(params) {
+ params.url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/' + params.layer + '/{z}/{x}/{y}.grid.json';
+ return utfGridCreateLayer(params);
+ }
+ },
+ cartodbInteractive: {
+ mustHaveKey: true,
+ mustHaveLayer : true,
+ createLayer: function(params) {
+ var tilesURL = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/{z}/{x}/{y}.png';
+ var tileLayer = L.tileLayer(tilesURL, params.options);
+ params.url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/' + params.layer + '/{z}/{x}/{y}.grid.json';
+ var utfLayer = utfGridCreateLayer(params);
+ return L.layerGroup([tileLayer, utfLayer]);
+ }
+ },
+ wms: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return L.tileLayer.wms(params.url, params.options);
+ }
+ },
+ wmts: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ return L.tileLayer.wmts(params.url, params.options);
+ }
+ },
+ wfs: {
+ mustHaveUrl: true,
+ mustHaveLayer : true,
+ createLayer: function(params) {
+ if (!Helpers.WFSLayerPlugin.isLoaded()) {
+ return;
+ }
+ var options = angular.copy(params.options);
+ if(options.crs && 'string' === typeof options.crs) {
+ /*jshint -W061 */
+ options.crs = eval(options.crs);
+ }
+ return new L.GeoJSON.WFS(params.url, params.layer, options);
+ }
+ },
+ group: {
+ mustHaveUrl: false,
+ createLayer: function (params) {
+ var lyrs = [];
+ angular.forEach(params.options.layers, function(l){
+ lyrs.push(createLayer(l));
+ });
+ 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);
+ }
+ },
+ china:{
+ mustHaveUrl:false,
+ createLayer:function(params){
+ var type = params.type || '';
+ if(!Helpers.ChinaLayerPlugin.isLoaded()){
+ return;
+ }
+ return L.tileLayer.chinaProvider(type, 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;
+ }
+ },
+ dynamic: {
+ mustHaveUrl: true,
+ createLayer: function(params) {
+ if (!Helpers.DynamicMapLayerPlugin.isLoaded()) {
+ return;
+ }
+ return L.esri.dynamicMapLayer(params.url, params.options);
+ }
+ },
+ markercluster: {
+ mustHaveUrl: false,
+ createLayer: function(params) {
+ if (!Helpers.MarkerClusterPlugin.isLoaded()) {
+ $log.error('[AngularJS - Leaflet] 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);
+ }
+ },
+ heatmap: {
+ mustHaveUrl: false,
+ mustHaveData: true,
+ createLayer: function(params) {
+ if (!Helpers.HeatMapLayerPlugin.isLoaded()) {
+ return;
+ }
+ var layer = new L.TileLayer.WebGLHeatMap(params.options);
+ if (isDefined(params.data)) {
+ layer.setData(params.data);
+ }
+
+ 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);
+ }
+ },
+
+ // 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,
+ type: layerDefinition.layerType,
+ bounds: layerDefinition.bounds,
+ key: layerDefinition.key,
+ pluginOptions: layerDefinition.pluginOptions,
+ user: layerDefinition.user
+ };
+
+ //TODO Add $watch to the layer properties
+ return layerTypes[layerDefinition.type].createLayer(params);
+ }
+
+ return {
+ createLayer: createLayer
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletControlHelpers', ["$rootScope", "$log", "leafletHelpers", "leafletMapDefaults", function ($rootScope, $log, leafletHelpers, leafletMapDefaults) {
+ var isObject = leafletHelpers.isObject,
+ isDefined = leafletHelpers.isDefined;
+ var _layersControl;
+
+ var _controlLayersMustBeVisible = function(baselayers, overlays, mapId) {
+ var defaults = leafletMapDefaults.getDefaults(mapId);
+ if(!defaults.controls.layers.visible) {
+ return false;
+ }
+
+ var numberOfLayers = 0;
+ if (isObject(baselayers)) {
+ numberOfLayers += Object.keys(baselayers).length;
+ }
+ if (isObject(overlays)) {
+ numberOfLayers += Object.keys(overlays).length;
+ }
+ return numberOfLayers > 1;
+ };
+
+ var _createLayersControl = function(mapId) {
+ var defaults = leafletMapDefaults.getDefaults(mapId);
+ var controlOptions = {
+ collapsed: defaults.controls.layers.collapsed,
+ position: defaults.controls.layers.position
+ };
+
+ 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;
+ };
+
+ return {
+ layersControlMustBeVisible: _controlLayersMustBeVisible,
+
+ updateLayersControl: function(map, mapId, loaded, baselayers, overlays, leafletLayers) {
+ var i;
+
+ 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]);
+ }
+ _layersControl.removeFrom(map);
+ }
+
+ if (mustBeLoaded) {
+ _layersControl = _createLayersControl(mapId);
+ 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].layerOptions) &&
+ overlays[i].layerOptions.showOnSelector === false;
+ if (!hideOverlayOnSelector && isDefined(leafletLayers.overlays[i])) {
+ _layersControl.addOverlay(leafletLayers.overlays[i], overlays[i].name);
+ }
+ }
+ _layersControl.addTo(map);
+ }
+ return mustBeLoaded;
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletLegendHelpers', function () {
+ var _updateLegend = function(div, legendData, type, url) {
+ div.innerHTML = '';
+ if(legendData.error) {
+ div.innerHTML += '' + legendData.error.message + '
';
+ } else {
+ if (type === 'arcgis') {
+ for (var i = 0; i < legendData.layers.length; i++) {
+ var layer = legendData.layers[i];
+ div.innerHTML += '' + layer.layerName + '
';
+ for(var j = 0; j < layer.legend.length; j++) {
+ var leg = layer.legend[j];
+ div.innerHTML +=
+ '' +
+ '' + leg.label + '
';
+ }
+ }
+ }
+ else if (type === 'image') {
+ div.innerHTML = '';
+ }
+ }
+ };
+
+ var _getOnAddLegend = function(legendData, legendClass, type, url) {
+ return function(/*map*/) {
+ var div = L.DomUtil.create('div', legendClass);
+
+ if (!L.Browser.touch) {
+ L.DomEvent.disableClickPropagation(div);
+ L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation);
+ } else {
+ L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation);
+ }
+ _updateLegend(div, legendData, type, url);
+ return div;
+ };
+ };
+
+ var _getOnAddArrayLegend = function(legend, legendClass) {
+ return function(/*map*/) {
+ var div = L.DomUtil.create('div', legendClass);
+ for (var i = 0; i < legend.colors.length; i++) {
+ div.innerHTML +=
+ '
' +
+ '' + legend.labels[i] + '
';
+ }
+ if (!L.Browser.touch) {
+ L.DomEvent.disableClickPropagation(div);
+ L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation);
+ } else {
+ L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation);
+ }
+ return div;
+ };
+ };
+
+ return {
+ getOnAddLegend: _getOnAddLegend,
+ getOnAddArrayLegend: _getOnAddArrayLegend,
+ updateLegend: _updateLegend,
+ };
+});
+
+angular.module("leaflet-directive").factory('leafletPathsHelpers', ["$rootScope", "$log", "leafletHelpers", function ($rootScope, $log, leafletHelpers) {
+ var isDefined = leafletHelpers.isDefined,
+ isArray = leafletHelpers.isArray,
+ isNumber = leafletHelpers.isNumber,
+ isValidPoint = leafletHelpers.isValidPoint;
+ var availableOptions = [
+ // Path options
+ 'stroke', 'weight', 'color', 'opacity',
+ 'fill', 'fillColor', 'fillOpacity',
+ 'dashArray', 'lineCap', 'lineJoin', 'clickable',
+ 'pointerEvents', 'className',
+
+ // Polyline options
+ 'smoothFactor', 'noClip'
+ ];
+ function _convertToLeafletLatLngs(latlngs) {
+ return latlngs.filter(function(latlng) {
+ return isValidPoint(latlng);
+ }).map(function (latlng) {
+ return _convertToLeafletLatLng(latlng);
+ });
+ }
+
+ function _convertToLeafletLatLng(latlng) {
+ if (isArray(latlng)) {
+ return new L.LatLng(latlng[0], latlng[1]);
+ } else {
+ return new L.LatLng(latlng.lat, latlng.lng);
+ }
+ }
+
+ function _convertToLeafletMultiLatLngs(paths) {
+ return paths.map(function(latlngs) {
+ return _convertToLeafletLatLngs(latlngs);
+ });
+ }
+
+ function _getOptions(path, defaults) {
+ var options = {};
+ for (var i = 0; i < availableOptions.length; i++) {
+ var optionName = availableOptions[i];
+
+ if (isDefined(path[optionName])) {
+ options[optionName] = path[optionName];
+ } else if (isDefined(defaults.path[optionName])) {
+ options[optionName] = defaults.path[optionName];
+ }
+ }
+
+ return options;
+ }
+
+ var _updatePathOptions = function (path, data) {
+ var updatedStyle = {};
+ for (var i = 0; i < availableOptions.length; i++) {
+ var optionName = availableOptions[i];
+ if (isDefined(data[optionName])) {
+ updatedStyle[optionName] = data[optionName];
+ }
+ }
+ path.setStyle(data);
+ };
+
+ var _isValidPolyline = function(latlngs) {
+ if (!isArray(latlngs)) {
+ return false;
+ }
+ for (var i = 0; i < latlngs.length; i++) {
+ var point = latlngs[i];
+ if (!isValidPoint(point)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ var pathTypes = {
+ polyline: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+ return _isValidPolyline(latlngs);
+ },
+ createPath: function(options) {
+ return new L.Polyline([], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ },
+ multiPolyline: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+ if (!isArray(latlngs)) {
+ return false;
+ }
+
+ for (var i in latlngs) {
+ var polyline = latlngs[i];
+ if (!_isValidPolyline(polyline)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ createPath: function(options) {
+ return new L.multiPolyline([[[0,0],[1,1]]], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletMultiLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ } ,
+ polygon: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+ return _isValidPolyline(latlngs);
+ },
+ createPath: function(options) {
+ return new L.Polygon([], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ },
+ multiPolygon: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+
+ if (!isArray(latlngs)) {
+ return false;
+ }
+
+ for (var i in latlngs) {
+ var polyline = latlngs[i];
+ if (!_isValidPolyline(polyline)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ createPath: function(options) {
+ return new L.MultiPolygon([[[0,0],[1,1],[0,1]]], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLngs(_convertToLeafletMultiLatLngs(data.latlngs));
+ _updatePathOptions(path, data);
+ return;
+ }
+ },
+ rectangle: {
+ isValid: function(pathData) {
+ var latlngs = pathData.latlngs;
+
+ if (!isArray(latlngs) || latlngs.length !== 2) {
+ return false;
+ }
+
+ for (var i in latlngs) {
+ var point = latlngs[i];
+ if (!isValidPoint(point)) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+ createPath: function(options) {
+ return new L.Rectangle([[0,0],[1,1]], options);
+ },
+ setPath: function(path, data) {
+ path.setBounds(new L.LatLngBounds(_convertToLeafletLatLngs(data.latlngs)));
+ _updatePathOptions(path, data);
+ }
+ },
+ circle: {
+ isValid: function(pathData) {
+ var point= pathData.latlngs;
+ return isValidPoint(point) && isNumber(pathData.radius);
+ },
+ createPath: function(options) {
+ return new L.Circle([0,0], 1, options);
+ },
+ setPath: function(path, data) {
+ path.setLatLng(_convertToLeafletLatLng(data.latlngs));
+ if (isDefined(data.radius)) {
+ path.setRadius(data.radius);
+ }
+ _updatePathOptions(path, data);
+ }
+ },
+ circleMarker: {
+ isValid: function(pathData) {
+ var point= pathData.latlngs;
+ return isValidPoint(point) && isNumber(pathData.radius);
+ },
+ createPath: function(options) {
+ return new L.CircleMarker([0,0], options);
+ },
+ setPath: function(path, data) {
+ path.setLatLng(_convertToLeafletLatLng(data.latlngs));
+ if (isDefined(data.radius)) {
+ path.setRadius(data.radius);
+ }
+ _updatePathOptions(path, data);
+ }
+ }
+ };
+
+ var _getPathData = function(path) {
+ var pathData = {};
+ if (path.latlngs) {
+ pathData.latlngs = path.latlngs;
+ }
+
+ if (path.radius) {
+ pathData.radius = path.radius;
+ }
+
+ return pathData;
+ };
+
+ return {
+ setPathOptions: function(leafletPath, pathType, data) {
+ if(!isDefined(pathType)) {
+ pathType = "polyline";
+ }
+ pathTypes[pathType].setPath(leafletPath, data);
+ },
+ createPath: function(name, path, defaults) {
+ if(!isDefined(path.type)) {
+ path.type = "polyline";
+ }
+ var options = _getOptions(path, defaults);
+ var pathData = _getPathData(path);
+
+ if (!pathTypes[path.type].isValid(pathData)) {
+ $log.error("[AngularJS - Leaflet] Invalid data passed to the " + path.type + " path");
+ return;
+ }
+
+ return pathTypes[path.type].createPath(options);
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletBoundsHelpers', ["$log", "leafletHelpers", function ($log, leafletHelpers) {
+
+ var isArray = leafletHelpers.isArray,
+ isNumber = leafletHelpers.isNumber;
+
+ 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]
+ }
+ };
+
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletMarkersHelpers', ["$rootScope", "leafletHelpers", "$log", "$compile", function ($rootScope, leafletHelpers, $log, $compile) {
+
+ var isDefined = leafletHelpers.isDefined,
+ MarkerClusterPlugin = leafletHelpers.MarkerClusterPlugin,
+ AwesomeMarkersPlugin = leafletHelpers.AwesomeMarkersPlugin,
+ MakiMarkersPlugin = leafletHelpers.MakiMarkersPlugin,
+ ExtraMarkersPlugin = leafletHelpers.ExtraMarkersPlugin,
+ safeApply = leafletHelpers.safeApply,
+ Helpers = leafletHelpers,
+ isString = leafletHelpers.isString,
+ isNumber = leafletHelpers.isNumber,
+ isObject = leafletHelpers.isObject,
+ groups = {};
+
+ var createLeafletIcon = function(iconData) {
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'awesomeMarker') {
+ if (!AwesomeMarkersPlugin.isLoaded()) {
+ $log.error('[AngularJS - Leaflet] The AwesomeMarkers Plugin is not loaded.');
+ }
+
+ return new L.AwesomeMarkers.icon(iconData);
+ }
+
+ if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'makiMarker') {
+ if (!MakiMarkersPlugin.isLoaded()) {
+ $log.error('[AngularJS - Leaflet] 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('[AngularJS - Leaflet] 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);
+ }
+
+ 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 _manageOpenPopup = function(marker, markerData) {
+ marker.openPopup();
+
+ //the marker may have angular templates to compile
+ var popup = marker.getPopup(),
+ //the marker may provide a scope returning function used to compile the message
+ //default to $rootScope otherwise
+ markerScope = angular.isFunction(markerData.getMessageScope) ? markerData.getMessageScope() : $rootScope,
+ compileMessage = isDefined(markerData.compileMessage) ? markerData.compileMessage : true;
+
+ if (isDefined(popup)) {
+ var updatePopup = function(popup) {
+ popup._updateLayout();
+ popup._updatePosition();
+ };
+
+ if (compileMessage) {
+ $compile(popup._contentNode)(markerScope);
+ //in case of an ng-include, we need to update the content after template load
+ if (isDefined(popup._contentNode) && popup._contentNode.innerHTML.indexOf("ngInclude") > -1) {
+ var unregister = markerScope.$on('$includeContentLoaded', function() {
+ updatePopup(popup);
+ unregister();
+ });
+ }
+ else {
+ updatePopup(popup);
+ }
+ }
+ }
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(markerData.label) && isDefined(markerData.label.options) && markerData.label.options.noHide === true) {
+ if (compileMessage) {
+ $compile(marker.label._container)(markerScope);
+ }
+ marker.showLabel();
+ }
+ };
+
+ return {
+ resetMarkerGroup: _resetMarkerGroup,
+
+ resetMarkerGroups: _resetMarkerGroups,
+
+ deleteMarker: _deleteMarker,
+
+ manageOpenPopup: _manageOpenPopup,
+
+ createMarker: function(markerData) {
+ if (!isDefined(markerData)) {
+ $log.error('[AngularJS - Leaflet] The marker definition is not valid.');
+ 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(markerData, markerOptions);
+
+ if (!isString(markerData.message)) {
+ marker.unbindPopup();
+ }
+
+ return marker;
+ },
+
+ addMarkerToGroup: function(marker, groupName, groupOptions, map) {
+ if (!isString(groupName)) {
+ $log.error('[AngularJS - Leaflet] The marker group you have specified is invalid.');
+ return;
+ }
+
+ if (!MarkerClusterPlugin.isLoaded()) {
+ $log.error("[AngularJS - Leaflet] 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, watching) {
+ marker.on("popupopen", function(/* event */) {
+ if (watching) {
+ safeApply(leafletScope, function() {
+ markerData.focus = true;
+ });
+ } else {
+ _manageOpenPopup(marker, markerData);
+ }
+ });
+ marker.on("popupclose", function(/* event */) {
+ if (watching) {
+ safeApply(leafletScope, function() {
+ markerData.focus = false;
+ });
+ }
+ });
+ },
+
+ addMarkerWatcher: function(marker, name, leafletScope, layers, map) {
+ var clearWatch = leafletScope.$watch("markers[\""+name+"\"]", function(markerData, oldMarkerData) {
+ if (!isDefined(markerData)) {
+ _deleteMarker(marker, map, layers);
+ clearWatch();
+ return;
+ }
+
+ if (!isDefined(oldMarkerData)) {
+ return;
+ }
+
+ // Update the lat-lng property (always present in marker properties)
+ if (!(isNumber(markerData.lat) && isNumber(markerData.lng))) {
+ $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('[AngularJS - Leaflet] 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('[AngularJS - Leaflet] 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) {
+ _manageOpenPopup(marker, markerData);
+ }
+ }
+
+ // 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);
+ }
+ }
+
+ // Update the Popup message property
+ if (!isString(markerData.message) && isString(oldMarkerData.message)) {
+ marker.closePopup();
+ marker.unbindPopup();
+ }
+
+ // Update the label content
+ if (Helpers.LabelPlugin.isLoaded() && isDefined(markerData.label) && isDefined(markerData.label.message) && !angular.equals(markerData.label.message, oldMarkerData.label.message)) {
+ marker.updateLabelContent(markerData.label.message);
+ }
+
+ // 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
+ _manageOpenPopup(marker, markerData);
+ 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]);
+ }
+ }, true);
+ }
+ };
+}]);
+
+angular.module("leaflet-directive").factory('leafletHelpers', ["$q", "$log", function ($q, $log) {
+
+ function _obtainEffectiveMapId(d, mapId) {
+ var id, i;
+ if (!angular.isDefined(mapId)) {
+ if (Object.keys(d).length === 0) {
+ id = "main";
+ } else if (Object.keys(d).length >= 1) {
+ for (i in d) {
+ if (d.hasOwnProperty(i)) {
+ id = i;
+ }
+ }
+ } else if (Object.keys(d).length === 0) {
+ id = "main";
+ } else {
+ $log.error("[AngularJS - Leaflet] - You have more than 1 map on the DOM, you must provide the map ID to the leafletData.getXXX call");
+ }
+ } else {
+ id = mapId;
+ }
+
+ return id;
+ }
+
+ function _getUnresolvedDefer(d, mapId) {
+ var id = _obtainEffectiveMapId(d, mapId),
+ defer;
+
+ if (!angular.isDefined(d[id]) || d[id].resolvedDefer === true) {
+ defer = $q.defer();
+ d[id] = {
+ defer: defer,
+ resolvedDefer: false
+ };
+ } else {
+ defer = d[id].defer;
+ }
+
+ return defer;
+ }
+
+ return {
+ //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: function(value) {
+ return angular.isDefined(value) && value !== null;
+ },
+
+ // Determine if a reference is a number
+ isNumber: function(value) {
+ return angular.isNumber(value);
+ },
+
+ // Determine if a reference is a string
+ isString: function(value) {
+ return angular.isString(value);
+ },
+
+ // Determine if a reference is an array
+ isArray: function(value) {
+ return angular.isArray(value);
+ },
+
+ // Determine if a reference is an object
+ isObject: function(value) {
+ return angular.isObject(value);
+ },
+
+ // Determine if a reference is a function.
+ isFunction: function(value) {
+ return angular.isFunction(value);
+ },
+
+ // Determine if two objects have the same properties
+ equals: function(o1, o2) {
+ return angular.equals(o1, o2);
+ },
+
+ 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.$apply(fn);
+ }
+ },
+
+ obtainEffectiveMapId: _obtainEffectiveMapId,
+
+ getDefer: function(d, mapId) {
+ var id = _obtainEffectiveMapId(d, mapId),
+ defer;
+ if (!angular.isDefined(d[id]) || d[id].resolvedDefer === false) {
+ defer = _getUnresolvedDefer(d, mapId);
+ } else {
+ defer = d[id].defer;
+ }
+ return defer;
+ },
+
+ getUnresolvedDefer: _getUnresolvedDefer,
+
+ setResolvedDefer: function(d, mapId) {
+ var id = _obtainEffectiveMapId(d, mapId);
+ d[id].resolvedDefer = true;
+ },
+
+ AwesomeMarkersPlugin: {
+ isLoaded: function() {
+ if (angular.isDefined(L.AwesomeMarkers) && angular.isDefined(L.AwesomeMarkers.Icon)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+ 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;
+ }
+ }
+ },
+
+ 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;
+ }
+ }
+ },
+ ChinaLayerPlugin: {
+ isLoaded: function() {
+ return angular.isDefined(L.tileLayer.chinaProvider);
+ }
+ },
+ HeatMapLayerPlugin: {
+ 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;
+ }
+ }
+ },
+ AGSLayerPlugin: {
+ isLoaded: function() {
+ return lvector !== undefined && lvector.AGS !== undefined;
+ },
+ is: function(layer) {
+ if (this.isLoaded()) {
+ return layer instanceof lvector.AGS;
+ } 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;
+ }
+ }
+ },
+ DynamicMapLayerPlugin: {
+ 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;
+ }
+ }
+ },
+ 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;
+ }
+ }
+ }
+ }
+ };
+}]);
+
+}());
\ No newline at end of file