(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, scope: { 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: '
', 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 } }; 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); } 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); }; 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); } } } } 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; } if (d.map) { for (var option in d.map) { mapDefaults[option] = d.map[option]; } } return mapDefaults; }, setDefaults: function (userDefaults, scopeId) { var newDefaults = _getDefaults(); if (isDefined(userDefaults)) { newDefaults.doubleClickZoom = isDefined(userDefaults.doubleClickZoom) ? userDefaults.doubleClickZoom : newDefaults.doubleClickZoom; newDefaults.scrollWheelZoom = isDefined(userDefaults.scrollWheelZoom) ? userDefaults.scrollWheelZoom : newDefaults.doubleClickZoom; newDefaults.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; } if (isDefined(userDefaults.map)) { newDefaults.map = userDefaults.map; } } var mapId = obtainEffectiveMapId(defaults, scopeId); defaults[mapId] = newDefaults; return newDefaults; } }; } ]); angular.module('leaflet-directive').factory('leafletEvents', [ '$rootScope', '$q', '$log', 'leafletHelpers', function ($rootScope, $q, $log, leafletHelpers) { var safeApply = leafletHelpers.safeApply, isDefined = leafletHelpers.isDefined, isObject = leafletHelpers.isObject, Helpers = leafletHelpers; var _getAvailableLabelEvents = function () { return [ 'click', 'dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu' ]; }; var genLabelEvents = function (leafletScope, logic, marker, name) { var labelEvents = _getAvailableLabelEvents(); var scopeWatchName = 'markers.' + name; for (var i = 0; i < labelEvents.length; i++) { var eventName = labelEvents[i]; marker.label.on(eventName, genDispatchLabelEvent(leafletScope, eventName, logic, marker.label, scopeWatchName)); } }; var genDispatchMarkerEvent = function (eventName, logic, leafletScope, marker, name, markerData) { return function (e) { var broadcastName = 'leafletDirectiveMarker.' + eventName; // Broadcast old marker click name for backwards compatibility if (eventName === 'click') { safeApply(leafletScope, function () { $rootScope.$broadcast('leafletDirectiveMarkersClick', name); }); } else if (eventName === 'dragend') { safeApply(leafletScope, function () { markerData.lat = marker.getLatLng().lat; markerData.lng = marker.getLatLng().lng; }); if (markerData.message && markerData.focus === true) { marker.openPopup(); } } safeApply(leafletScope, function (scope) { if (logic === 'emit') { scope.$emit(broadcastName, { markerName: name, leafletEvent: e }); } else { $rootScope.$broadcast(broadcastName, { markerName: name, leafletEvent: e }); } }); }; }; var genDispatchPathEvent = function (eventName, logic, leafletScope, marker, name) { return function (e) { var broadcastName = 'leafletDirectivePath.' + eventName; safeApply(leafletScope, function (scope) { if (logic === 'emit') { scope.$emit(broadcastName, { pathName: name, leafletEvent: e }); } else { $rootScope.$broadcast(broadcastName, { pathName: name, leafletEvent: e }); } }); }; }; var genDispatchLabelEvent = function (scope, eventName, logic, label, scope_watch_name) { return function (e) { // Put together broadcast name var broadcastName = 'leafletDirectiveLabel.' + eventName; var markerName = scope_watch_name.replace('markers.', ''); // Safely broadcast the event safeApply(scope, function (scope) { if (logic === 'emit') { scope.$emit(broadcastName, { leafletEvent: e, label: label, markerName: markerName }); } else if (logic === 'broadcast') { $rootScope.$broadcast(broadcastName, { leafletEvent: e, label: label, markerName: markerName }); } }); }; }; var _getAvailableMarkerEvents = function () { return [ 'click', 'dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu', 'dragstart', 'drag', 'dragend', 'move', 'remove', 'popupopen', 'popupclose' ]; }; var _getAvailablePathEvents = function () { return [ 'click', 'dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu', 'add', 'remove', 'popupopen', 'popupclose' ]; }; return { getAvailableMapEvents: function () { return [ 'click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'contextmenu', 'focus', 'blur', 'preclick', 'load', 'unload', 'viewreset', 'movestart', 'move', 'moveend', 'dragstart', 'drag', 'dragend', 'zoomstart', 'zoomend', 'zoomlevelschange', 'resize', 'autopanstart', 'layeradd', 'layerremove', 'baselayerchange', 'overlayadd', 'overlayremove', 'locationfound', 'locationerror', 'popupopen', 'popupclose', 'draw:created', 'draw:edited', 'draw:deleted', 'draw:drawstart', 'draw:drawstop', 'draw:editstart', 'draw:editstop', 'draw:deletestart', 'draw:deletestop' ]; }, genDispatchMapEvent: function (scope, eventName, logic) { return function (e) { // Put together broadcast name var broadcastName = 'leafletDirectiveMap.' + eventName; // Safely broadcast the event safeApply(scope, function (scope) { if (logic === 'emit') { scope.$emit(broadcastName, { leafletEvent: e }); } else if (logic === 'broadcast') { $rootScope.$broadcast(broadcastName, { leafletEvent: e }); } }); }; }, getAvailableMarkerEvents: _getAvailableMarkerEvents, getAvailablePathEvents: _getAvailablePathEvents, notifyCenterChangedToBounds: function (scope) { scope.$broadcast('boundsChanged'); }, notifyCenterUrlHashChanged: function (scope, map, attrs, search) { if (!isDefined(attrs.urlHashCenter)) { return; } var center = map.getCenter(); var centerUrlHash = center.lat.toFixed(4) + ':' + center.lng.toFixed(4) + ':' + map.getZoom(); if (!isDefined(search.c) || search.c !== centerUrlHash) { //$log.debug("notified new center..."); scope.$emit('centerUrlHash', centerUrlHash); } }, bindMarkerEvents: function (marker, name, markerData, leafletScope) { var markerEvents = []; var i; var eventName; var logic = '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; } } } } }; } ]); }());