From 59320e8d8668975e24ea8d07909d74228787caa8 Mon Sep 17 00:00:00 2001 From: Jack Reed Date: Sun, 4 May 2014 18:09:31 -0700 Subject: [PATCH] updates angular-leaflet-directive to v0.7.6 --- .../javascripts/angular-leaflet-directive.js | 3025 ++++++++++++++++- 1 file changed, 2857 insertions(+), 168 deletions(-) diff --git a/vendor/assets/javascripts/angular-leaflet-directive.js b/vendor/assets/javascripts/angular-leaflet-directive.js index e6eb6fe..3e05c23 100644 --- a/vendor/assets/javascripts/angular-leaflet-directive.js +++ b/vendor/assets/javascripts/angular-leaflet-directive.js @@ -1,179 +1,2868 @@ -var leafletDirective = angular.module("leaflet-directive", []); - -leafletDirective.directive("leaflet", ["$http", "$log", function ($http, $log) { - return { - restrict: "E", +(function () { + 'use strict'; + angular.module('leaflet-directive', []).directive('leaflet', [ + '$q', + 'leafletData', + 'leafletMapDefaults', + 'leafletHelpers', + 'leafletEvents', + function ($q, leafletData, leafletMapDefaults, leafletHelpers, leafletEvents) { + var _leafletMap; + return { + restrict: 'EA', replace: true, - transclude: true, scope: { - center: "=center", - tilelayer: "=tilelayer", - markers: "=markers", - leafletMarkers: "=leafletMarkers", - path: "=path", - maxZoom: "@maxzoom" + center: '=center', + defaults: '=defaults', + maxbounds: '=maxbounds', + bounds: '=bounds', + markers: '=markers', + legend: '=legend', + geojson: '=geojson', + paths: '=paths', + tiles: '=tiles', + layers: '=layers', + controls: '=controls', + eventBroadcast: '=eventBroadcast' }, template: '
', - link: function (scope, element, attrs, ctrl) { - var $el = element[0], - map = new L.Map($el); - - // Expose the map object, for testing purposes - if (attrs.map) { - scope.map = map; - } - - // Set maxZoom from attrs - if (attrs.maxzoom){ - scope.maxZoom = parseInt(attrs.maxzoom) - } - - // Set initial view - map.setView([0, 0], 1); - - // 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); - - // Manage map center events - if (attrs.center && scope.center) { - - 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 }); - } - - map.on("dragend", function(e) { - scope.$apply(function (s) { - s.center.lat = map.getCenter().lat; - s.center.lng = map.getCenter().lng; - }); - }); - - map.on("zoomend", function(e) { - if (scope.center.zoom !== map.getZoom()){ - scope.$apply(function (s) { - s.center.zoom = map.getZoom(); - }); - } - }); - - scope.$watch("center", function (center, oldValue) { - if(center.lat && center.lng && center.zoom){ - map.setView([center.lat, center.lng], center.zoom); + 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 if they are defined + if (isDefined(attrs.width)) { + if (isNaN(attrs.width)) { + element.css('width', attrs.width); + } else { + element.css('width', attrs.width + 'px'); + } + } + if (isDefined(attrs.height)) { + if (isNaN(attrs.height)) { + element.css('height', attrs.height); + } else { + element.css('height', attrs.height + 'px'); + } + } + // 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); + } + if (isDefined(map.zoomControl) && defaults.zoomControl === false) { + map.zoomControl.removeFrom(map); + } + if (isDefined(map.zoomsliderControl) && isDefined(defaults.zoomsliderControl) && defaults.zoomsliderControl === false) { + map.zoomsliderControl.removeFrom(map); + } + // if no event-broadcast attribute, all events are broadcasted + if (!isDefined(attrs.eventBroadcast)) { + var logic = 'broadcast'; + 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 () { + leafletData.unresolveMap(attrs.id); + }); + } + }; + } + ]); + angular.module('leaflet-directive').directive('center', [ + '$log', + '$q', + '$location', + 'leafletMapDefaults', + 'leafletHelpers', + 'leafletBoundsHelpers', + 'leafletEvents', + function ($log, $q, $location, leafletMapDefaults, leafletHelpers, leafletBoundsHelpers, leafletEvents) { + var isDefined = leafletHelpers.isDefined, isNumber = leafletHelpers.isNumber, isSameCenterOnMap = leafletHelpers.isSameCenterOnMap, safeApply = leafletHelpers.safeApply, isValidCenter = leafletHelpers.isValidCenter, isEmpty = leafletHelpers.isEmpty, isUndefinedOrEmpty = leafletHelpers.isUndefinedOrEmpty; + var shouldInitializeMapWithBounds = function (bounds, center) { + return isDefined(bounds) && !isEmpty(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; + controller.getMap().then(function (map) { + var defaults = leafletMapDefaults.getDefaults(attrs.id); + 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(); + var newScopeBounds = { + northEast: { + lat: mapBounds._northEast.lat, + lng: mapBounds._northEast.lng + }, + southWest: { + lat: mapBounds._southWest.lat, + lng: mapBounds._southWest.lng } - }, true); + }; + scope.bounds = newScopeBounds; + }); + } 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); } - - 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 - } - ); - - marker.on("dragend", function(e) { - scope.$apply(function (s) { - data.lat = marker.getLatLng().lat; - data.lng = marker.getLatLng().lng; - if (data.message) { - marker.openPopup(); - } - }); - }); - - scope.$watch('markers.' + key, function(newval, oldval) { - if (!newval) { - map.removeLayer(markers[key]); - delete leafletMarkers[key]; - if (attrs.leafletMarkers) { - delete scope.leafletMarkers[key]; - } - return; - } - - if (newval.draggable !== undefined && newval.draggable !== oldval.draggable) { - newval.draggable ? marker.dragging.enable() : marker.dragging.disable(); - } - - if (newval.focus !== undefined && newval.focus !== oldval.focus) { - newval.focus ? marker.openPopup() : marker.closePopup(); - } - - if (newval.message !== undefined && newval.message !== oldval.message) { - marker.bindPopup(newval); - } - - if (newval.lat !== oldval.lat || newval.lng !== oldval.lng) { - marker.setLatLng(new L.LatLng(newval.lat, newval.lng)); - } - }, true); - - return marker; - }; // end of create and link marker - - var leafletMarkers = {} - - // Expose the map object, for testing purposes - if (attrs.leafletMarkers) { - scope.leafletMarkers = {}; - } - - // 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; - } - } - - 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; - } - } + 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 + }; + } + }); + } + 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; + } + if (mapReady && isSameCenterOnMap(center, map)) { + //$log.debug("no need to update map again."); + return; + } + //$log.debug("updating map center...", center); + map.setView([ + center.lat, + center.lng + ], center.zoom); + leafletEvents.notifyCenterChangedToBounds(leafletScope, map); + }, true); + map.whenReady(function () { + mapReady = true; + }); + map.on('moveend', function () { + // 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)) { + //$log.debug("same center in model, no need to update again."); + return; + } + safeApply(leafletScope, function (scope) { + //$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); + }); + }); + 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; + var legendClass = legend.legendClass ? legend.legendClass : 'legend'; + var position = legend.position || 'bottomright'; + var leafletLegend; + controller.getMap().then(function (map) { + if (!isDefined(legend.url) && (!isArray(legend.colors) || !isArray(legend.labels) || legend.colors.length !== legend.labels.length)) { + $log.warn('[AngularJS - Leaflet] legend.colors and legend.labels must be set.'); + } else if (isDefined(legend.url)) { + $log.info('[AngularJS - Leaflet] loading arcgis legend service.'); + } else { + // TODO: Watch array legend. + leafletLegend = L.control({ position: position }); + leafletLegend.onAdd = leafletLegendHelpers.getOnAddArrayLegend(legend, legendClass); + leafletLegend.addTo(map); + } + leafletScope.$watch('legend.url', function (newURL) { + if (!isDefined(newURL)) { + return; + } + $http.get(newURL).success(function (legendData) { + if (isDefined(leafletLegend)) { + leafletLegendHelpers.updateArcGISLegend(leafletLegend.getContainer(), legendData); + } else { + leafletLegend = L.control({ position: position }); + leafletLegend.onAdd = leafletLegendHelpers.getOnAddArcGISLegend(legendData, legendClass); + 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.$watch('geojson', function (geojson) { + if (isDefined(leafletGeoJSON) && map.hasLayer(leafletGeoJSON)) { + map.removeLayer(leafletGeoJSON); + } + if (!(isDefined(geojson) && isDefined(geojson.data))) { + return; + } + var resetStyleOnMouseout = geojson.resetStyleOnMouseout, onEachFeature = geojson.onEachFeature; + if (!onEachFeature) { + onEachFeature = function (feature, layer) { + if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(geojson.label)) { + layer.bindLabel(feature.properties.description); + } + layer.on({ + mouseover: function (e) { + safeApply(leafletScope, function () { + geojson.selected = feature; + $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseover', e); + }); + }, + mouseout: function (e) { + if (resetStyleOnMouseout) { + leafletGeoJSON.resetStyle(e.target); + } + safeApply(leafletScope, function () { + geojson.selected = undefined; + $rootScope.$broadcast('leafletDirectiveMap.geojsonMouseout', e); + }); + }, + click: function (e) { + safeApply(leafletScope, function () { + geojson.selected = feature; + $rootScope.$broadcast('leafletDirectiveMap.geojsonClick', geojson.selected, e); + }); } + }); + }; + } + geojson.options = { + style: geojson.style, + filter: geojson.filter, + onEachFeature: onEachFeature, + pointToLayer: geojson.pointToLayer + }; + leafletGeoJSON = L.geoJson(geojson.data, geojson.options); + leafletData.setGeoJSON(leafletGeoJSON); + leafletGeoJSON.addTo(map); + }); + }); + } + }; + } + ]); + angular.module('leaflet-directive').directive('layers', [ + '$log', + '$q', + 'leafletData', + 'leafletHelpers', + 'leafletLayerHelpers', + 'leafletControlHelpers', + function ($log, $q, leafletData, leafletHelpers, leafletLayerHelpers, leafletControlHelpers) { + var _leafletLayers; + return { + restrict: 'A', + scope: false, + replace: false, + require: 'leaflet', + controller: function () { + _leafletLayers = $q.defer(); + this.getLayers = function () { + return _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; + controller.getMap().then(function (map) { + // Do we have a baselayers property? + if (!isDefined(layers) || !isDefined(layers.baselayers) || Object.keys(layers.baselayers).length === 0) { + // No baselayers property + $log.error('[AngularJS - Leaflet] At least one baselayer has to be defined'); + return; + } + // We have baselayers to add to the map + _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]]); + } + // Setup the Overlays + for (layerName in layers.overlays) { + 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]); + } + } + // 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]); + } + } + } + } + if (Object.keys(leafletLayers.baselayers).length === 0) { + $log.error('[AngularJS - Leaflet] At least one baselayer has to be defined'); + return; + } + //we have layers, so we need to make, at least, one active + var found = false; + // search for an active layer + for (var key in leafletLayers.baselayers) { + if (map.hasLayer(leafletLayers.baselayers[key])) { + found = true; + break; + } + } + // If there is no active layer make one active + if (!found) { + 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); + // 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]); + } + } + // Only add the layers switch selector control if we have more than one baselayer + overlay + isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, layers.baselayers, newOverlayLayers, leafletLayers); + }, true); + }); + } + }; + } + ]); + angular.module('leaflet-directive').directive('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) { + if (bounds._southWest.lat === 0 && bounds._southWest.lng === 0 && bounds._northEast.lat === 0 && bounds._northEast.lng === 0) { + return true; + } + return false; + }; + 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)) { + 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.'); + map.fitBounds(leafletBounds); + } + }, true); + }); + } + }; + } + ]); + 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(), markers = leafletScope.markers, 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; + }; + } + if (!isDefined(markers)) { + return; + } + 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]; + } + } + // 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)) { + addMarkerToGroup(marker, markerData.group, 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)) { + $log.error('[AngularJS - Leaflet] Adding a marker to an overlay needs a overlay of the type "group"'); + 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 (map.hasLayer(marker) && markerData.focus === true) { + marker.openPopup(); + } // 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 (markerData.focus === true) { + marker.openPopup(); + } + if (Helpers.LabelPlugin.isLoaded() && isDefined(markerData.label) && isDefined(markerData.label.options) && markerData.label.options.noHide === true) { + marker.showLabel(); + } + } + // Should we watch for every specific marker on the map? + var shouldWatch = !isDefined(attrs.watchMarkers) || attrs.watchMarkers === 'true'; + if (shouldWatch) { + addMarkerWatcher(marker, newName, leafletScope, layers, map); + listenMarkerEvents(marker, markerData, leafletScope); + } + bindMarkerEvents(marker, newName, markerData, leafletScope); + } + } + }, true); + }); + }); + } + }; + } + ]); + angular.module('leaflet-directive').directive('paths', [ + '$log', + 'leafletData', + 'leafletMapDefaults', + 'leafletHelpers', + 'leafletPathsHelpers', + 'leafletEvents', + function ($log, leafletData, leafletMapDefaults, leafletHelpers, leafletPathsHelpers, leafletEvents) { + return { + restrict: 'A', + scope: false, + replace: false, + require: 'leaflet', + link: function (scope, element, attrs, controller) { + var isDefined = leafletHelpers.isDefined, leafletScope = controller.getLeafletScope(), paths = leafletScope.paths, createPath = leafletPathsHelpers.createPath, bindPathEvents = leafletEvents.bindPathEvents, setPathOptions = leafletPathsHelpers.setPathOptions; + controller.getMap().then(function (map) { + var defaults = leafletMapDefaults.getDefaults(attrs.id); + if (!isDefined(paths)) { + return; + } + var leafletPaths = {}; + leafletData.setPaths(leafletPaths, attrs.id); + // Function for listening every single path once created + var watchPathFn = function (leafletPath, name) { + var clearWatch = leafletScope.$watch('paths.' + name, function (pathData) { + if (!isDefined(pathData)) { + map.removeLayer(leafletPath); + clearWatch(); + return; + } + setPathOptions(leafletPath, pathData.type, pathData); }, 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); + }; + leafletScope.$watch('paths', function (newPaths) { + // Create the new paths + for (var newName in newPaths) { + 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); + } + // Listen for changes on the new path + if (isDefined(newPath)) { + leafletPaths[newName] = newPath; + map.addLayer(newPath); + watchPathFn(newPath, newName); + } + bindPathEvents(newPath, newName, pathData, leafletScope); + } + } + // Delete paths (by name) from the array + for (var name in leafletPaths) { + if (!isDefined(newPaths[name])) { + delete leafletPaths[name]; + } + } + }, true); + }); + } + }; + } + ]); + 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)) { + var drawnItems = new L.FeatureGroup(); + map.addLayer(drawnItems); + var options = { edit: { featureGroup: drawnItems } }; + angular.extend(options, controls.draw.options); + var drawControl = new L.Control.Draw(options); + map.addControl(drawControl); + } + 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, 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'; + if (isObject(eventBroadcast)) { + // We have a possible valid object + if (eventBroadcast.map === undefined || eventBroadcast.map === null) { + // We do not have events enable/disable do we do nothing (all enabled by default) + mapEvents = availableMapEvents; + } else if (typeof eventBroadcast.map !== 'object') { + // 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 !== undefined && eventBroadcast.map.logic !== null) { + // We take care of possible propagation 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 if (eventBroadcast.map.logic === 'emit') { + logic = 'emit'; + } + } + // Enable / Disable + var mapEventsEnable = false, mapEventsDisable = false; + if (eventBroadcast.map.enable !== undefined && eventBroadcast.map.enable !== null) { + if (typeof eventBroadcast.map.enable === 'object') { + mapEventsEnable = true; + } + } + if (eventBroadcast.map.disable !== undefined && eventBroadcast.map.disable !== null) { + if (typeof eventBroadcast.map.disable === 'object') { + mapEventsDisable = true; + } + } + if (mapEventsEnable && mapEventsDisable) { + // Both are active, this is an error + $log.warn('[AngularJS - Leaflet] can not enable and disable events at the time'); + } else if (!mapEventsEnable && !mapEventsDisable) { + // Both are inactive, this is an error + $log.warn('[AngularJS - Leaflet] must enable or disable events'); + } else { + // At this point the map object is OK, lets enable or disable events + if (mapEventsEnable) { + // 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) { + // Repeated event, this is an error + $log.warn('[AngularJS - Leaflet] This event ' + eventName + ' is already enabled'); + } else { + // Does the event exists? + if (availableMapEvents.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 + mapEvents.push(eventName); } + } } - polyline.setLatLngs(latlngs); - }, true); - - scope.$watch("path.weight", function(weight) { - polyline.setStyle({ - weight: weight - }); - }, true); - - scope.$watch("path.color", function(color) { - polyline.setStyle({ - color: color - }); - }, true); - } // end of attrs.path - } // end of link function + } else { + // Disable events + mapEvents = availableMapEvents; + for (i = 0; i < eventBroadcast.map.disable.length; i++) { + eventName = eventBroadcast.map.disable[i]; + var index = mapEvents.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 { + mapEvents.splice(index, 1); + } + } + } + } + } + for (i = 0; i < mapEvents.length; i++) { + eventName = mapEvents[i]; + map.on(eventName, genDispatchMapEvent(leafletScope, eventName, logic), { eventName: eventName }); + } + } else { + // Not a valid object + $log.warn('[AngularJS - Leaflet] event-broadcast must be an object, check your model.'); + } + }); + } + }; + } + ]); + 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); + map.fitBounds(bounds); + }); + }); + } + }; + } + ]); + 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 = {}; + 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; + }; + 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.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; + }; + } + ]); + angular.module('leaflet-directive').factory('leafletMapDefaults', [ + '$q', + 'leafletHelpers', + function ($q, leafletHelpers) { + function _getDefaults() { + return { + keyboard: true, + dragging: true, + worldCopyJump: false, + doubleClickZoom: true, + scrollWheelZoom: true, + zoomControl: true, + zoomsliderControl: false, + zoomControlPosition: 'topleft', + attributionControl: true, + controls: { + layers: { + visible: true, + position: 'topright', + collapsed: true + } + }, + crs: L.CRS.EPSG3857, + tileLayer: 'http://{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, 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, + 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; + } + 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.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 (isDefined(userDefaults.crs) && isDefined(L.CRS[userDefaults.crs])) { + newDefaults.crs = L.CRS[userDefaults.crs]; + } + 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; + } + } + 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 = 'broadcast'; + 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'; + window.lls = leafletScope; + 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 layerTypes = { + xyz: { + mustHaveUrl: true, + createLayer: function (params) { + return L.tileLayer(params.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); + } + }, + 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 () { + return L.layerGroup(); + } + }, + 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); + } + } + }; + function isValidLayerType(layerDefinition) { + // Check if the baselayer has a valid type + if (!isString(layerDefinition.type)) { + 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; + } + return true; + } + return { + createLayer: function (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 + }; + //TODO Add $watch to the layer properties + return layerTypes[layerDefinition.type].createLayer(params); + } + }; + } + ]); + 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) { + 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 + }; + 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); + 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) { + if (isDefined(leafletLayers.baselayers[i])) { + _layersControl.addBaseLayer(leafletLayers.baselayers[i], baselayers[i].name); + } + } + for (i in overlays) { + if (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 _updateArcGISLegend = function (div, legendData) { + div.innerHTML = ''; + if (legendData.error) { + div.innerHTML += '
' + legendData.error.message + '
'; + } else { + 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 + '
'; + } + } + } + }; + var _getOnAddArcGISLegend = function (legendData, legendClass) { + return function () { + 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); + } + _updateArcGISLegend(div, legendData); + return div; + }; + }; + var _getOnAddArrayLegend = function (legend, legendClass) { + return function () { + 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 { + getOnAddArcGISLegend: _getOnAddArcGISLegend, + getOnAddArrayLegend: _getOnAddArrayLegend, + updateArcGISLegend: _updateArcGISLegend + }; + }); + 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; + function _convertToLeafletLatLngs(latlngs) { + return latlngs.filter(function (latlng) { + return isValidPoint(latlng); + }).map(function (latlng) { + return new L.LatLng(latlng.lat, latlng.lng); + }); + } + function _convertToLeafletLatLng(latlng) { + return new L.LatLng(latlng.lat, latlng.lng); + } + function _convertToLeafletMultiLatLngs(paths) { + return paths.map(function (latlngs) { + return _convertToLeafletLatLngs(latlngs); + }); + } + function _getOptions(path, defaults) { + var availableOptions = [ + 'stroke', + 'weight', + 'color', + 'opacity', + 'fill', + 'fillColor', + 'fillOpacity', + 'dashArray', + 'lineCap', + 'lineJoin', + 'clickable', + 'pointerEvents', + 'className', + 'smoothFactor', + 'noClip' + ]; + 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) { + if (isDefined(data.weight)) { + path.setStyle({ weight: data.weight }); + } + if (isDefined(data.color)) { + path.setStyle({ color: data.color }); + } + if (isDefined(data.opacity)) { + path.setStyle({ opacity: data.opacity }); + } + }; + var _isValidPolyline = function (latlngs) { + if (!isArray(latlngs)) { + return false; + } + for (var i in latlngs) { + 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', + function ($rootScope, leafletHelpers, $log) { + var isDefined = leafletHelpers.isDefined, MarkerClusterPlugin = leafletHelpers.MarkerClusterPlugin, AwesomeMarkersPlugin = leafletHelpers.AwesomeMarkersPlugin, 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 === '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)) { + return new L.Icon.Default({ + iconUrl: base64icon, + shadowUrl: base64shadow + }); + } + if (!isDefined(iconData.iconUrl)) { + iconData.iconUrl = base64icon; + iconData.shadowUrl = base64shadow; + } + return new L.Icon.Default(iconData); + }; + 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) { + 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); + } + }; + return { + deleteMarker: _deleteMarker, + 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 + }; + return new L.marker(markerData, markerOptions); + }, + addMarkerToGroup: function (marker, groupName, 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(); + map.addLayer(groups[groupName]); + } + groups[groupName].addLayer(marker); + }, + listenMarkerEvents: function (marker, markerData, leafletScope) { + marker.on('popupopen', function () { + safeApply(leafletScope, function () { + markerData.focus = true; + }); + }); + marker.on('popupclose', function () { + 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; + } + // 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 (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)) { + $log.error('[AngularJS - Leaflet] A marker can only be added to a layer of type "group"'); + return; + } + // The marker goes to a correct layer group, so first of all we add it + layerGroup.addLayer(marker); + // The marker is automatically added to the map depending on the visibility + // of the layer, so we only have to open the popup if the marker is in the map + if (map.hasLayer(marker) && markerData.focus === true) { + marker.openPopup(); + } + } + // Update the draggable property + if (markerData.draggable !== true && oldMarkerData.draggable === true && isDefined(marker.dragging)) { + marker.dragging.disable(); + } + if (markerData.draggable === true && oldMarkerData.draggable !== true) { + // The markerData.draggable property must be true so we update if there wasn't a previous value or it wasn't true + if (marker.dragging) { + marker.dragging.enable(); + } else { + if (L.Handler.MarkerDrag) { + marker.dragging = new L.Handler.MarkerDrag(marker); + marker.options.draggable = true; + marker.dragging.enable(); + } + } + } + // Update the icon property + if (!isObject(markerData.icon)) { + // If there is no icon property or it's not an object + if (isObject(oldMarkerData.icon)) { + // If there was an icon before restore to the default + marker.setIcon(createLeafletIcon()); + marker.closePopup(); + marker.unbindPopup(); + if (isString(markerData.message)) { + marker.bindPopup(markerData.message); + } + } + } + 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); + } + } + // 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); + if (markerData.focus === true) { + // If the focus is set, we must open the popup, because we do not know if it was opened before + marker.openPopup(); + } + } + 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 && oldMarkerData.focus !== true) { + marker.openPopup(); + updatedFocus = true; + } + if (oldMarkerData.focus === true && markerData.focus === true) { + // Reopen the popup when focus is still true + marker.openPopup(); + updatedFocus = true; + } + 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 (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 === 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 { + isEmpty: function (value) { + return Object.keys(value).length === 0; + }, + isUndefinedOrEmpty: function (value) { + return angular.isUndefined(value) || value === null || Object.keys(value).length === 0; + }, + isDefined: function (value) { + return angular.isDefined(value) && value !== null; + }, + isNumber: function (value) { + return angular.isNumber(value); + }, + isString: function (value) { + return angular.isString(value); + }, + isArray: function (value) { + return angular.isArray(value); + }, + isObject: function (value) { + return angular.isObject(value); + }, + isFunction: function (value) { + return angular.isFunction(value); + }, + 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) { + return angular.isDefined(point) && angular.isNumber(point.lat) && angular.isNumber(point.lng); + }, + isSameCenterOnMap: function (centerModel, map) { + var mapCenter = map.getCenter(); + var zoom = map.getZoom(); + if (mapCenter.lat === centerModel.lat && mapCenter.lng === centerModel.lng && 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; + } + } + }, + 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; + } + } + }, + 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; + } + } + } + } + }; + } + ]); +}());