/*! * ui-leaflet 2.0.0 2018-01-05 * ui-leaflet - An AngularJS directive to easily interact with Leaflet maps * git: https://github.com/angular-ui/ui-leaflet */ (function(angular){ 'use strict'; 'use strict'; angular.module('ui-leaflet', ['nemLogging']).directive('leaflet', ["$q", "leafletData", "leafletMapDefaults", "leafletHelpers", "leafletMapEvents", function ($q, leafletData, leafletMapDefaults, leafletHelpers, leafletMapEvents) { return { restrict: "EA", replace: true, scope: { center: '=', lfCenter: '=', defaults: '=', maxbounds: '=', bounds: '=', markers: '=', legend: '=', geojson: '=', paths: '=', tiles: '=', layers: '=', controls: '=', decorations: '=', eventBroadcast: '=', watchOptions: '=', id: '@' }, transclude: true, template: '
', controller: ["$scope", function controller($scope) { this._leafletMap = $q.defer(); this.getMap = function () { return this._leafletMap.promise; }; this.getLeafletScope = function () { return $scope; }; }], link: function link(scope, element, attrs, ctrl) { var isDefined = leafletHelpers.isDefined, defaults = leafletMapDefaults.setDefaults(scope.defaults, attrs.id), mapEvents = leafletMapEvents.getAvailableMapEvents(), addEvents = leafletMapEvents.addEvents; scope.mapId = attrs.id; leafletData.setDirectiveControls({}, attrs.id); // Set width and height utility functions function updateWidth() { if (isNaN(attrs.width)) { element.css('width', attrs.width); } else { element.css('width', attrs.width + 'px'); } } function updateHeight() { if (isNaN(attrs.height)) { element.css('height', attrs.height); } else { element.css('height', attrs.height + 'px'); } } // Create the Leaflet Map Object with the options var map = new L.Map(element[0], leafletMapDefaults.getMapCreationDefaults(attrs.id)); ctrl._leafletMap.resolve(map); // If the width attribute defined update css // Then watch if bound property changes and update css if (isDefined(attrs.width)) { updateWidth(); scope.$watch(function () { return element[0].getAttribute('width'); }, function () { updateWidth(); map.invalidateSize(); }); } // If the height attribute defined update css // Then watch if bound property changes and update css if (isDefined(attrs.height)) { updateHeight(); scope.$watch(function () { return element[0].getAttribute('height'); }, function () { updateHeight(); map.invalidateSize(); }); } if (!isDefined(attrs.center) && !isDefined(attrs.lfCenter)) { map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom); } // If no layers nor tiles defined, set the default tileLayer if (!isDefined(attrs.tiles) && !isDefined(attrs.layers)) { var tileLayerObj = L.tileLayer(defaults.tileLayer, defaults.tileLayerOptions); tileLayerObj.addTo(map); leafletData.setTiles(tileLayerObj, attrs.id); } // Set zoom control configuration if (isDefined(map.zoomControl) && isDefined(defaults.zoomControlPosition)) { map.zoomControl.setPosition(defaults.zoomControlPosition); } if (isDefined(map.zoomControl) && defaults.zoomControl === false) { map.zoomControl.removeFrom(map); } if (isDefined(map.zoomsliderControl) && isDefined(defaults.zoomsliderControl) && defaults.zoomsliderControl === false) { map.zoomsliderControl.removeFrom(map); } // if no event-broadcast attribute, all events are broadcasted if (!isDefined(attrs.eventBroadcast)) { var logic = "broadcast"; addEvents(map, attrs.id, mapEvents, "eventName", scope, logic); } // Resolve the map object to the promises map.whenReady(function () { leafletData.setMap(map, attrs.id); }); scope.$on('$destroy', function () { leafletMapDefaults.reset(); map.remove(); leafletData.unresolveMap(attrs.id); }); //Handle request to invalidate the map size //Up scope using $scope.$emit('invalidateSize') //Down scope using $scope.$broadcast('invalidateSize') scope.$on('invalidateSize', function () { map.invalidateSize(); }); } }; }]); 'use strict'; (function () { angular.module('ui-leaflet').factory('eventManager', [function () { var EventManager = function EventManager() { this.listeners = {}; }; EventManager.prototype = { addEventListener: function addEventListener(type, callback, scope) { var args = []; var numOfArgs = arguments.length; for (var i = 0; i < numOfArgs; i++) { args.push(arguments[i]); } args = args.length > 3 ? args.splice(3, args.length - 1) : []; if (typeof this.listeners[type] !== "undefined") { this.listeners[type].push({ scope: scope, callback: callback, args: args }); } else { this.listeners[type] = [{ scope: scope, callback: callback, args: args }]; } }, removeEventListener: function removeEventListener(type, callback, scope) { if (typeof this.listeners[type] !== "undefined") { var numOfCallbacks = this.listeners[type].length; var newArray = []; for (var i = 0; i < numOfCallbacks; i++) { var listener = this.listeners[type][i]; if (listener.scope === scope && listener.callback === callback) {} else { newArray.push(listener); } } this.listeners[type] = newArray; } }, hasEventListener: function hasEventListener(type, callback, scope) { if (typeof this.listeners[type] !== "undefined") { var numOfCallbacks = this.listeners[type].length; if (callback === undefined && scope === undefined) { return numOfCallbacks > 0; } for (var i = 0; i < numOfCallbacks; i++) { var listener = this.listeners[type][i]; if ((scope ? listener.scope === scope : true) && listener.callback === callback) { return true; } } } return false; }, dispatch: function dispatch(type, target) { var numOfListeners = 0; var event = { type: type, target: target }; var args = []; var numOfArgs = arguments.length; for (var i = 0; i < numOfArgs; i++) { args.push(arguments[i]); } args = args.length > 2 ? args.splice(2, args.length - 1) : []; args = [event].concat(args); if (typeof this.listeners[type] !== "undefined") { var numOfCallbacks = this.listeners[type].length; for (var x = 0; x < numOfCallbacks; x++) { var listener = this.listeners[type][x]; if (listener && listener.callback) { var concatArgs = args.concat(listener.args); listener.callback.apply(listener.scope, concatArgs); numOfListeners += 1; } } } }, getEvents: function getEvents() { var str = ""; for (var type in this.listeners) { var numOfCallbacks = this.listeners[type].length; for (var i = 0; i < numOfCallbacks; i++) { var listener = this.listeners[type][i]; str += listener.scope && listener.scope.className ? listener.scope.className : "anonymous"; str += " listen for '" + type + "'\n"; } } return str; } }; return EventManager; }]).service('eventManager', ["EventManager", function (EventManager) { return new EventManager(); }]); })(); 'use strict'; angular.module('ui-leaflet').factory('leafletBoundsHelpers', ["leafletLogger", "leafletHelpers", function (leafletLogger, leafletHelpers) { var isArray = leafletHelpers.isArray, isNumber = leafletHelpers.isNumber, isFunction = leafletHelpers.isFunction, isDefined = leafletHelpers.isDefined, $log = leafletLogger; 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 createLeafletBounds(bounds) { if (_isValidBounds(bounds)) { return L.latLngBounds([bounds.southWest.lat, bounds.southWest.lng], [bounds.northEast.lat, bounds.northEast.lng]); } }, isValidBounds: _isValidBounds, createBoundsFromArray: function createBoundsFromArray(boundsArray) { if (!(isArray(boundsArray) && boundsArray.length === 2 && isArray(boundsArray[0]) && isArray(boundsArray[1]) && boundsArray[0].length === 2 && boundsArray[1].length === 2 && isNumber(boundsArray[0][0]) && isNumber(boundsArray[0][1]) && isNumber(boundsArray[1][0]) && isNumber(boundsArray[1][1]))) { $log.error("[AngularJS - Leaflet] The bounds array is not valid."); return; } return { northEast: { lat: boundsArray[0][0], lng: boundsArray[0][1] }, southWest: { lat: boundsArray[1][0], lng: boundsArray[1][1] } }; }, createBoundsFromLeaflet: function createBoundsFromLeaflet(lfBounds) { if (!(isDefined(lfBounds) && isFunction(lfBounds.getNorthEast) && isFunction(lfBounds.getSouthWest))) { $log.error("[AngularJS - Leaflet] The leaflet bounds is not valid object."); return; } var northEast = lfBounds.getNorthEast(), southWest = lfBounds.getSouthWest(); return { northEast: { lat: northEast.lat, lng: northEast.lng }, southWest: { lat: southWest.lat, lng: southWest.lng } }; } }; }]); 'use strict'; angular.module('ui-leaflet').factory('leafletControlHelpers', ["$rootScope", "leafletLogger", "leafletHelpers", "leafletLayerHelpers", "leafletMapDefaults", function ($rootScope, leafletLogger, leafletHelpers, leafletLayerHelpers, leafletMapDefaults) { var isDefined = leafletHelpers.isDefined, isObject = leafletHelpers.isObject, get = leafletHelpers.get, createLayer = leafletLayerHelpers.createLayer, _controls = {}, errorHeader = leafletHelpers.errorHeader + ' [Controls] ', $log = leafletLogger; var _controlLayersMustBeVisible = function _controlLayersMustBeVisible(baselayers, overlays, mapId) { var defaults = leafletMapDefaults.getDefaults(mapId); if (!get(defaults, 'controls.layers.visible')) { return false; } var atLeastOneControlItemMustBeShown = false; if (isObject(baselayers)) { Object.keys(baselayers).forEach(function (key) { var layer = baselayers[key]; if (!isDefined(layer.layerOptions) || layer.layerOptions.showOnSelector !== false) { atLeastOneControlItemMustBeShown = true; } }); } if (isObject(overlays)) { Object.keys(overlays).forEach(function (key) { var layer = overlays[key]; if (!isDefined(layer.layerParams) || layer.layerParams.showOnSelector !== false) { atLeastOneControlItemMustBeShown = true; } }); } return atLeastOneControlItemMustBeShown; }; var _createLayersControl = function _createLayersControl(mapId) { var defaults = leafletMapDefaults.getDefaults(mapId); var controlOptions = { collapsed: defaults.controls.layers.collapsed, position: defaults.controls.layers.position, autoZIndex: false }; angular.extend(controlOptions, defaults.controls.layers.options); var control; if (!!get(defaults, 'controls.layers.control')) { control = defaults.controls.layers.control.apply(this, [[], [], controlOptions]); } else { control = new L.control.layers([], [], controlOptions); } return control; }; var controlTypes = { draw: { isPluginLoaded: function isPluginLoaded() { if (!angular.isDefined(L.Control.Draw)) { $log.error(errorHeader + ' Draw plugin is not loaded.'); return false; } return true; }, checkValidParams: function checkValidParams() /* params */{ return true; }, createControl: function createControl(params) { return new L.Control.Draw(params); } }, scale: { isPluginLoaded: function isPluginLoaded() { return true; }, checkValidParams: function checkValidParams() /* params */{ return true; }, createControl: function createControl(params) { return new L.control.scale(params); } }, fullscreen: { isPluginLoaded: function isPluginLoaded() { if (!angular.isDefined(L.Control.Fullscreen)) { $log.error(errorHeader + ' Fullscreen plugin is not loaded.'); return false; } return true; }, checkValidParams: function checkValidParams() /* params */{ return true; }, createControl: function createControl(params) { return new L.Control.Fullscreen(params); } }, search: { isPluginLoaded: function isPluginLoaded() { if (!angular.isDefined(L.Control.Search)) { $log.error(errorHeader + ' Search plugin is not loaded.'); return false; } return true; }, checkValidParams: function checkValidParams() /* params */{ return true; }, createControl: function createControl(params) { return new L.Control.Search(params); } }, custom: {}, minimap: { isPluginLoaded: function isPluginLoaded() { if (!angular.isDefined(L.Control.MiniMap)) { $log.error(errorHeader + ' Minimap plugin is not loaded.'); return false; } return true; }, checkValidParams: function checkValidParams(params) { if (!isDefined(params.layer)) { $log.warn(errorHeader + ' minimap "layer" option should be defined.'); return false; } return true; }, createControl: function createControl(params) { var layer = createLayer(params.layer); if (!isDefined(layer)) { $log.warn(errorHeader + ' minimap control "layer" could not be created.'); return; } return new L.Control.MiniMap(layer, params); } } }; return { layersControlMustBeVisible: _controlLayersMustBeVisible, isValidControlType: function isValidControlType(type) { return Object.keys(controlTypes).indexOf(type) !== -1; }, createControl: function createControl(type, params) { if (!controlTypes[type].checkValidParams(params)) { return; } return controlTypes[type].createControl(params); }, updateLayersControl: function updateLayersControl(map, mapId, loaded, baselayers, overlays, leafletLayers) { var i; var _layersControl = _controls[mapId]; var mustBeLoaded = _controlLayersMustBeVisible(baselayers, overlays, mapId); if (isDefined(_layersControl) && loaded) { for (i in leafletLayers.baselayers) { _layersControl.removeLayer(leafletLayers.baselayers[i]); } for (i in leafletLayers.overlays) { _layersControl.removeLayer(leafletLayers.overlays[i]); } map.removeControl(_layersControl); delete _controls[mapId]; } if (mustBeLoaded) { _layersControl = _createLayersControl(mapId); _controls[mapId] = _layersControl; for (i in baselayers) { var hideOnSelector = isDefined(baselayers[i].layerOptions) && baselayers[i].layerOptions.showOnSelector === false; if (!hideOnSelector && isDefined(leafletLayers.baselayers[i])) { _layersControl.addBaseLayer(leafletLayers.baselayers[i], baselayers[i].name); } } for (i in overlays) { var hideOverlayOnSelector = isDefined(overlays[i].layerParams) && overlays[i].layerParams.showOnSelector === false; if (!hideOverlayOnSelector && isDefined(leafletLayers.overlays[i])) { _layersControl.addOverlay(leafletLayers.overlays[i], overlays[i].name); } } map.addControl(_layersControl); } return mustBeLoaded; }, destroyMapLayersControl: function destroyMapLayersControl(mapId) { delete _controls[mapId]; } }; }]); 'use strict'; angular.module('ui-leaflet').service('leafletData', ["leafletLogger", "$q", "leafletHelpers", function (leafletLogger, $q, leafletHelpers) { var getDefer = leafletHelpers.getDefer, getUnresolvedDefer = leafletHelpers.getUnresolvedDefer, setResolvedDefer = leafletHelpers.setResolvedDefer; // $log = leafletLogger; var _private = {}; var self = this; var upperFirst = function upperFirst(string) { return string.charAt(0).toUpperCase() + string.slice(1); }; var _privateItems = ['map', 'tiles', 'layers', 'paths', 'markers', 'geoJSON', 'UTFGrid', //odd ball on naming convention keeping to not break 'decorations', 'directiveControls']; //init _privateItems.forEach(function (itemName) { _private[itemName] = {}; }); this.unresolveMap = function (mapId) { var id = leafletHelpers.obtainEffectiveMapId(_private.map, mapId); _privateItems.forEach(function (itemName) { _private[itemName][id] = undefined; }); }; //int repetitive stuff (get and sets) _privateItems.forEach(function (itemName) { var name = upperFirst(itemName); self['set' + name] = function (lObject, mapId) { var defer = getUnresolvedDefer(_private[itemName], mapId); defer.resolve(lObject); setResolvedDefer(_private[itemName], mapId); }; self['get' + name] = function (mapId) { var defer = getDefer(_private[itemName], mapId); return defer.promise; }; }); }]); 'use strict'; angular.module('ui-leaflet').service('leafletDirectiveControlsHelpers', ["leafletLogger", "leafletData", "leafletHelpers", function (leafletLogger, leafletData, leafletHelpers) { var _isDefined = leafletHelpers.isDefined, _isString = leafletHelpers.isString, _isObject = leafletHelpers.isObject, _mainErrorHeader = leafletHelpers.errorHeader, $log = leafletLogger; var _errorHeader = _mainErrorHeader + '[leafletDirectiveControlsHelpers'; var extend = function extend(id, thingToAddName, createFn, cleanFn) { var _fnHeader = _errorHeader + '.extend] '; var extender = {}; if (!_isDefined(thingToAddName)) { $log.error(_fnHeader + 'thingToAddName cannot be undefined'); return; } if (_isString(thingToAddName) && _isDefined(createFn) && _isDefined(cleanFn)) { extender[thingToAddName] = { create: createFn, clean: cleanFn }; } else if (_isObject(thingToAddName) && !_isDefined(createFn) && !_isDefined(cleanFn)) { extender = thingToAddName; } else { $log.error(_fnHeader + 'incorrect arguments'); return; } //add external control to create / destroy markers without a watch leafletData.getDirectiveControls(id).then(function (controls) { angular.extend(controls, extender); leafletData.setDirectiveControls(controls, id); }); }; return { extend: extend }; }]); 'use strict'; angular.module('ui-leaflet').service('leafletGeoJsonHelpers', ["leafletHelpers", "leafletIterators", function (leafletHelpers, leafletIterators) { var lHlp = leafletHelpers, lIt = leafletIterators; var Point = function Point(lat, lng) { this.lat = lat; this.lng = lng; return this; }; var _getLat = function _getLat(value) { if (Array.isArray(value) && value.length === 2) { return value[1]; } else if (lHlp.isDefined(value.type) && value.type === 'Point') { return +value.coordinates[1]; } else { return +value.lat; } }; var _getLng = function _getLng(value) { if (Array.isArray(value) && value.length === 2) { return value[0]; } else if (lHlp.isDefined(value.type) && value.type === 'Point') { return +value.coordinates[0]; } else { return +value.lng; } }; var _validateCoords = function _validateCoords(coords) { if (lHlp.isUndefined(coords)) { return false; } if (lHlp.isArray(coords)) { if (coords.length === 2 && lHlp.isNumber(coords[0]) && lHlp.isNumber(coords[1])) { return true; } } else if (lHlp.isDefined(coords.type)) { if (coords.type === 'Point' && lHlp.isArray(coords.coordinates) && coords.coordinates.length === 2 && lHlp.isNumber(coords.coordinates[0]) && lHlp.isNumber(coords.coordinates[1])) { return true; } } var ret = lIt.all(['lat', 'lng'], function (pos) { return lHlp.isDefined(coords[pos]) && lHlp.isNumber(coords[pos]); }); return ret; }; var _getCoords = function _getCoords(value) { if (!value || !_validateCoords(value)) { return; } var p = null; if (Array.isArray(value) && value.length === 2) { p = new Point(value[1], value[0]); } else if (lHlp.isDefined(value.type) && value.type === 'Point') { p = new Point(value.coordinates[1], value.coordinates[0]); } else { return value; } //note angular.merge is avail in angular 1.4.X we might want to fill it here return angular.extend(value, p); //tap on lat, lng if it doesnt exist }; return { getLat: _getLat, getLng: _getLng, validateCoords: _validateCoords, getCoords: _getCoords }; }]); 'use strict'; angular.module('ui-leaflet').service('leafletHelpers', ["$q", "$log", "$timeout", function ($q, $log, $timeout) { var _errorHeader = '[ui-leaflet] '; var _copy = angular.copy; var _clone = _copy; /* For parsing paths to a field in an object Example: var obj = { bike:{ 1: 'hi' 2: 'foo' } }; _getObjectValue(obj,"bike.1") returns 'hi' this is getPath in ui-gmap like _.get http://stackoverflow.com/questions/2631001/javascript-test-for-existence-of-nested-object-key?page=1&tab=active#tab-top */ var _getObjectValue = function _getObjectValue(object, path) { if (!object) return; path = path.split('.'); var obj = object[path.shift()]; while (obj && path.length) { obj = obj[path.shift()]; }return obj; }; /* Object Array Notation _getObjectArrayPath("bike.one.two") returns: 'bike["one"]["two"]' */ var _getObjectArrayPath = function _getObjectArrayPath(pathStr) { return pathStr.split('.').reduce(function (previous, current) { return previous + '["' + current + '"]'; }); }; /* Object Dot Notation _getObjectPath(["bike","one","two"]) returns: "bike.one.two" */ var _getObjectDotPath = function _getObjectDotPath(arrayOfStrings) { return arrayOfStrings.reduce(function (previous, current) { return previous + '.' + current; }); }; function _obtainEffectiveMapId(d, mapId) { var id, keys = Object.keys(d); if (!angular.isDefined(mapId)) { if (keys.length === 0 || keys.length === 1 && keys[0] === 'main') { //default non id attribute map // OR one key 'main' /* Main Reason: Initally if we have only one map and no "id" then d will be undefined initially. On subsequent runs it will be just d.main so we need to return the last map. */ id = "main"; } else { throw new Error(_errorHeader + "- You have more than 1 map on the DOM, you must provide the map ID to the leafletData.getXXX call. Where one of the following mapIds " + Object.keys(d).join(',') + ' are available.'); } } else { id = mapId; } return id; } function _getUnresolvedDefer(d, mapId) { var id = _obtainEffectiveMapId(d, mapId), defer; if (!angular.isDefined(d[id]) || d[id].resolvedDefer === true) { defer = $q.defer(); d[id] = { defer: defer, resolvedDefer: false }; } else { defer = d[id].defer; } return defer; } var _isDefined = function _isDefined(value) { return angular.isDefined(value) && value !== null; }; var _isUndefined = function _isUndefined(value) { return !_isDefined(value); }; // BEGIN DIRECT PORT FROM AngularJS code base var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; var MOZ_HACK_REGEXP = /^moz([A-Z])/; var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; /** Converts snake_case to camelCase. Also there is special case for Moz prefix starting with upper case letter. @param name Name to normalize */ var camelCase = function camelCase(name) { return name.replace(SPECIAL_CHARS_REGEXP, function (_, separator, letter, offset) { if (offset) { return letter.toUpperCase(); } else { return letter; } }).replace(MOZ_HACK_REGEXP, "Moz$1"); }; /** Converts all accepted directives format into proper directive name. @param name Name to normalize */ var directiveNormalize = function directiveNormalize(name) { return camelCase(name.replace(PREFIX_REGEXP, "")); }; // END AngularJS port var _watchTrapDelayMilliSec = 10; var _modelChangeInDirective = function _modelChangeInDirective(trapObj, trapField, cbToExec) { if (!trapObj) throw new Error(_errorHeader + 'trapObj is undefined'); if (!trapField) throw new Error(_errorHeader + 'trapField is undefined'); trapObj[trapField] = true; var ret = cbToExec(); $timeout(function () { trapObj[trapField] = false; }, _watchTrapDelayMilliSec); return ret; }; return { watchTrapDelayMilliSec: _watchTrapDelayMilliSec, modelChangeInDirective: _modelChangeInDirective, camelCase: camelCase, directiveNormalize: directiveNormalize, copy: _copy, clone: _clone, errorHeader: _errorHeader, getObjectValue: _getObjectValue, get: _getObjectValue, getObjectArrayPath: _getObjectArrayPath, getObjectDotPath: _getObjectDotPath, defaultTo: function defaultTo(val, _default) { return _isDefined(val) ? val : _default; }, //mainly for checking attributes of directives lets keep this minimal (on what we accept) isTruthy: function isTruthy(val) { return val === 'true' || val === true; }, //Determine if a reference is {} isEmpty: function isEmpty(value) { return Object.keys(value).length === 0; }, //Determine if a reference is undefined or {} isUndefinedOrEmpty: function isUndefinedOrEmpty(value) { return angular.isUndefined(value) || value === null || Object.keys(value).length === 0; }, // Determine if a reference is defined isDefined: _isDefined, isUndefined: _isUndefined, isNumber: angular.isNumber, isString: angular.isString, isArray: angular.isArray, isObject: angular.isObject, isFunction: angular.isFunction, equals: angular.equals, isValidCenter: function isValidCenter(center) { return angular.isDefined(center) && angular.isNumber(center.lat) && angular.isNumber(center.lng) && angular.isNumber(center.zoom); }, isValidPoint: function isValidPoint(point) { if (!angular.isDefined(point)) { return false; } if (angular.isArray(point)) { return point.length === 2 && angular.isNumber(point[0]) && angular.isNumber(point[1]); } return angular.isNumber(point.lat) && angular.isNumber(point.lng); }, isSameCenterOnMap: function isSameCenterOnMap(centerModel, map) { var mapCenter = map.getCenter(); var zoom = map.getZoom(); if (centerModel.lat && centerModel.lng && mapCenter.lat.toFixed(4) === centerModel.lat.toFixed(4) && mapCenter.lng.toFixed(4) === centerModel.lng.toFixed(4) && zoom === centerModel.zoom) { return true; } return false; }, safeApply: function safeApply($scope, fn) { var phase = $scope.$root.$$phase; if (phase === '$apply' || phase === '$digest') { $scope.$eval(fn); } else { $scope.$evalAsync(fn); } }, obtainEffectiveMapId: _obtainEffectiveMapId, getDefer: function getDefer(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 setResolvedDefer(d, mapId) { var id = _obtainEffectiveMapId(d, mapId); d[id].resolvedDefer = true; }, rangeIsSupported: function rangeIsSupported() { var testrange = document.createElement('input'); testrange.setAttribute('type', 'range'); return testrange.type === 'range'; }, FullScreenControlPlugin: { isLoaded: function isLoaded() { return angular.isDefined(L.Control.Fullscreen); } }, MiniMapControlPlugin: { isLoaded: function isLoaded() { return angular.isDefined(L.Control.MiniMap); } }, AwesomeMarkersPlugin: { isLoaded: function isLoaded() { return angular.isDefined(L.AwesomeMarkers) && angular.isDefined(L.AwesomeMarkers.Icon); }, is: function is(icon) { if (this.isLoaded()) { return icon instanceof L.AwesomeMarkers.Icon; } else { return false; } }, equal: function equal(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } } }, VectorMarkersPlugin: { isLoaded: function isLoaded() { return angular.isDefined(L.VectorMarkers) && angular.isDefined(L.VectorMarkers.Icon); }, is: function is(icon) { if (this.isLoaded()) { return icon instanceof L.VectorMarkers.Icon; } else { return false; } }, equal: function equal(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } } }, DomMarkersPlugin: { isLoaded: function isLoaded() { if (angular.isDefined(L.DomMarkers) && angular.isDefined(L.DomMarkers.Icon)) { return true; } else { return false; } }, is: function is(icon) { if (this.isLoaded()) { return icon instanceof L.DomMarkers.Icon; } else { return false; } }, equal: function equal(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } } }, PolylineDecoratorPlugin: { isLoaded: function isLoaded() { if (angular.isDefined(L.PolylineDecorator)) { return true; } else { return false; } }, is: function is(decoration) { if (this.isLoaded()) { return decoration instanceof L.PolylineDecorator; } else { return false; } }, equal: function equal(decorationA, decorationB) { if (!this.isLoaded()) { return false; } if (this.is(decorationA)) { return angular.equals(decorationA, decorationB); } else { return false; } } }, MakiMarkersPlugin: { isLoaded: function isLoaded() { if (angular.isDefined(L.MakiMarkers) && angular.isDefined(L.MakiMarkers.Icon)) { return true; } else { return false; } }, is: function is(icon) { if (this.isLoaded()) { return icon instanceof L.MakiMarkers.Icon; } else { return false; } }, equal: function equal(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } } }, ExtraMarkersPlugin: { isLoaded: function isLoaded() { if (angular.isDefined(L.ExtraMarkers) && angular.isDefined(L.ExtraMarkers.Icon)) { return true; } else { return false; } }, is: function is(icon) { if (this.isLoaded()) { return icon instanceof L.ExtraMarkers.Icon; } else { return false; } }, equal: function equal(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } } }, LabelPlugin: { isLoaded: function isLoaded() { return angular.isDefined(L.Label); }, is: function is(layer) { if (this.isLoaded()) { return layer instanceof L.MarkerClusterGroup; } else { return false; } } }, MarkerClusterPlugin: { isLoaded: function isLoaded() { return angular.isDefined(L.MarkerClusterGroup); }, is: function is(layer) { if (this.isLoaded()) { return layer instanceof L.MarkerClusterGroup; } else { return false; } } }, GeoJSONPlugin: { isLoaded: function isLoaded() { return angular.isDefined(L.TileLayer.GeoJSON); }, is: function is(layer) { if (this.isLoaded()) { return layer instanceof L.TileLayer.GeoJSON; } else { return false; } } }, CartoDB: { isLoaded: function isLoaded() { return cartodb; }, is: function is() /*layer*/{ return true; /* if (this.isLoaded()) { return layer instanceof L.TileLayer.GeoJSON; } else { return false; }*/ } }, Leaflet: { DivIcon: { is: function is(icon) { return icon instanceof L.DivIcon; }, equal: function equal(iconA, iconB) { if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } } }, Icon: { is: function is(icon) { return icon instanceof L.Icon; }, equal: function equal(iconA, iconB) { if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } } } }, /* watchOptions - object to set deep nested watches and turn off watches all together (rely on control / functional updates) watchOptions - Object type: string. //One of ['watch', 'watchCollection', 'watchDeep', null] individual type: string */ //legacy defaults watchOptions: { type: 'watchDeep', individual: { type: 'watchDeep' } } }; }]); 'use strict'; angular.module('ui-leaflet').service('leafletIterators', ["leafletLogger", "leafletHelpers", function (leafletLogger, leafletHelpers) { var lHlp = leafletHelpers, errorHeader = leafletHelpers.errorHeader + 'leafletIterators: '; //BEGIN COPY from underscore var _keys = Object.keys; var _isFunction = lHlp.isFunction; var _isObject = lHlp.isObject; var $log = leafletLogger; // Helper for collection methods to determine whether a collection // should be iterated as an array or as an object // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var _isArrayLike = function _isArrayLike(collection) { var length = collection !== null && collection.length; return lHlp.isNumber(length) && length >= 0 && length <= MAX_ARRAY_INDEX; }; // Keep the identity function around for default iteratees. var _identity = function _identity(value) { return value; }; var _property = function _property(key) { return function (obj) { return obj === null ? void 0 : obj[key]; }; }; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. var optimizeCb = function optimizeCb(func, context, argCount) { if (context === void 0) return func; switch (argCount === null ? 3 : argCount) { case 1: return function (value) { return func.call(context, value); }; case 2: return function (value, other) { return func.call(context, value, other); }; case 3: return function (value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function (accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function () { return func.apply(context, arguments); }; }; // An internal function for creating assigner functions. var createAssigner = function createAssigner(keysFunc, undefinedOnly) { return function (obj) { var length = arguments.length; if (length < 2 || obj === null) return obj; for (var index = 1; index < length; index++) { var source = arguments[index], keys = keysFunc(source), l = keys.length; for (var i = 0; i < l; i++) { var key = keys[i]; if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; } } return obj; }; }; // Assigns a given object with all the own properties in the passed-in object(s) // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) var _extendOwn, _assign = null; _extendOwn = _assign = createAssigner(_keys); // Returns whether an object has a given set of `key:value` pairs. var _isMatch = function _isMatch(object, attrs) { var keys = _keys(attrs), length = keys.length; if (object === null) return !length; var obj = Object(object); for (var i = 0; i < length; i++) { var key = keys[i]; if (attrs[key] !== obj[key] || !(key in obj)) return false; } return true; }; // Returns a predicate for checking whether an object has a given set of // `key:value` pairs. var _matcher, _matches = null; _matcher = _matches = function _matches(attrs) { attrs = _extendOwn({}, attrs); return function (obj) { return _isMatch(obj, attrs); }; }; // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either // identity, an arbitrary callback, a property matcher, or a property accessor. var cb = function cb(value, context, argCount) { if (value === null) return _identity; if (_isFunction(value)) return optimizeCb(value, context, argCount); if (_isObject(value)) return _matcher(value); return _property(value); }; var _every, _all = null; _every = _all = function _all(obj, predicate, context) { predicate = cb(predicate, context); var keys = !_isArrayLike(obj) && _keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; }; //END COPY fron underscore var _hasErrors = function _hasErrors(collection, cb, ignoreCollection, cbName) { if (!ignoreCollection) { if (!lHlp.isDefined(collection) || !lHlp.isDefined(cb)) { return true; } } if (!lHlp.isFunction(cb)) { cbName = lHlp.defaultTo(cb, 'cb'); $log.error(errorHeader + cbName + ' is not a function'); return true; } return false; }; var _iterate = function _iterate(collection, externalCb, internalCb) { if (_hasErrors(undefined, internalCb, true, 'internalCb')) { return; } if (!_hasErrors(collection, externalCb)) { for (var key in collection) { if (collection.hasOwnProperty(key)) { internalCb(collection[key], key); } } } }; //see http://jsperf.com/iterators/3 //utilizing for in is way faster var _each = function _each(collection, cb) { _iterate(collection, cb, function (val, key) { cb(val, key); }); }; return { each: _each, forEach: _each, every: _every, all: _all }; }]); 'use strict'; angular.module('ui-leaflet').factory('leafletLayerHelpers', ["$rootScope", "$q", "leafletLogger", "leafletHelpers", "leafletIterators", function ($rootScope, $q, leafletLogger, leafletHelpers, leafletIterators) { var Helpers = leafletHelpers; var isString = leafletHelpers.isString; var isObject = leafletHelpers.isObject; var isArray = leafletHelpers.isArray; var isDefined = leafletHelpers.isDefined; var errorHeader = leafletHelpers.errorHeader; var $it = leafletIterators; var $log = leafletLogger; var utfGridCreateLayer = function utfGridCreateLayer(params) { if (!Helpers.UTFGridPlugin.isLoaded()) { $log.error('[AngularJS - Leaflet] The UTFGrid plugin is not loaded.'); return; } var utfgrid = new L.UtfGrid(params.url, params.pluginOptions); var toSend = { model: params.$parent }; // TODO Use event manager utfgrid.on('mouseover', function (e) { angular.extend(toSend, { leafletEvent: e, leafletObject: e.target }); $rootScope.$broadcast('leafletDirectiveMap.utfgridMouseover', toSend); }); utfgrid.on('mouseout', function (e) { angular.extend(toSend, { leafletEvent: e, leafletObject: e.target }); $rootScope.$broadcast('leafletDirectiveMap.utfgridMouseout', toSend); }); utfgrid.on('click', function (e) { angular.extend(toSend, { leafletEvent: e, leafletObject: e.target }); $rootScope.$broadcast('leafletDirectiveMap.utfgridClick', toSend); }); utfgrid.on('mousemove', function (e) { angular.extend(toSend, { leafletEvent: e, leafletObject: e.target }); $rootScope.$broadcast('leafletDirectiveMap.utfgridMousemove', toSend); }); return utfgrid; }; var layerTypes = { xyz: { mustHaveUrl: true, createLayer: function createLayer(params) { return L.tileLayer(params.url, params.options); } }, geoJSON: { mustHaveUrl: true, createLayer: function createLayer(params) { if (!Helpers.GeoJSONPlugin.isLoaded()) { return; } return new L.TileLayer.GeoJSON(params.url, params.pluginOptions, params.options); } }, geoJSONShape: { mustHaveUrl: false, createLayer: function createLayer(params) { return new L.GeoJSON(params.data, params.options); } }, geoJSONAwesomeMarker: { mustHaveUrl: false, createLayer: function createLayer(params) { return new L.geoJson(params.data, { pointToLayer: function pointToLayer(feature, latlng) { return L.marker(latlng, { icon: L.AwesomeMarkers.icon(params.icon) }); } }); } }, geoJSONVectorMarker: { mustHaveUrl: false, createLayer: function createLayer(params) { return new L.geoJson(params.data, { pointToLayer: function pointToLayer(feature, latlng) { return L.marker(latlng, { icon: L.VectorMarkers.icon(params.icon) }); } }); } }, cartodbTiles: { mustHaveKey: true, createLayer: function createLayer(params) { var url = isDefined(params.url) ? params.url + '/' + params.user : '//' + params.user + '.cartodb.com'; url += '/api/v1/map/' + params.key + '/{z}/{x}/{y}.png'; return L.tileLayer(url, params.options); } }, cartodbUTFGrid: { mustHaveKey: true, mustHaveLayer: true, createLayer: function createLayer(params) { var url = isDefined(params.url) ? params.url + '/' + params.user : '//' + params.user + '.cartodb.com'; params.url = url + '/api/v1/map/' + params.key + '/' + params.layer + '/{z}/{x}/{y}.grid.json'; return utfGridCreateLayer(params); } }, cartodbInteractive: { mustHaveKey: true, mustHaveLayer: true, createLayer: function createLayer(params) { var url = isDefined(params.url) ? params.url + '/' + params.user : '//' + params.user + '.cartodb.com'; var tilesURL = url + '/api/v1/map/' + params.key + '/{z}/{x}/{y}.png'; var tileLayer = L.tileLayer(tilesURL, params.options); var layers = [tileLayer]; var addUtfLayer = function addUtfLayer(parent, params, layer) { var paramsCopy = angular.copy(params); paramsCopy.url = url + '/api/v1/map/' + paramsCopy.key + '/' + layer + '/{z}/{x}/{y}.grid.json'; parent.push(utfGridCreateLayer(paramsCopy)); }; if (isArray(params.layer)) { for (var i = 0; i < params.layer.length; i++) { addUtfLayer(layers, params, params.layer[i]); } } else { addUtfLayer(layers, params, params.layer); } return L.layerGroup(layers); } }, wms: { mustHaveUrl: true, createLayer: function createLayer(params) { return L.tileLayer.wms(params.url, params.options); } }, wmts: { mustHaveUrl: true, createLayer: function createLayer(params) { return L.tileLayer.wmts(params.url, params.options); } }, group: { mustHaveUrl: false, createLayer: function createLayer(params) { var lyrs = []; $it.each(params.options.layers, function (l) { lyrs.push(_createLayer(l)); }); params.options.loadedDefer = function () { var defers = []; if (isDefined(params.options.layers)) { for (var i = 0; i < params.options.layers.length; i++) { var d = params.options.layers[i].layerOptions.loadedDefer; if (isDefined(d)) { defers.push(d); } } } return defers; }; return L.layerGroup(lyrs); } }, featureGroup: { mustHaveUrl: false, createLayer: function createLayer() { return L.featureGroup(); } }, markercluster: { mustHaveUrl: false, createLayer: function createLayer(params) { if (!Helpers.MarkerClusterPlugin.isLoaded()) { $log.warn(errorHeader + ' The markercluster plugin is not loaded.'); return; } return new L.MarkerClusterGroup(params.options); } }, imageOverlay: { mustHaveUrl: true, mustHaveBounds: true, createLayer: function createLayer(params) { return L.imageOverlay(params.url, params.bounds, params.options); } }, iip: { mustHaveUrl: true, createLayer: function createLayer(params) { return L.tileLayer.iip(params.url, params.options); } }, // This "custom" type is used to accept every layer that user want to define himself. // We can wrap these custom layers like heatmap or yandex, but it means a lot of work/code to wrap the world, // so we let user to define their own layer outside the directive, // and pass it on "createLayer" result for next processes custom: { createLayer: function createLayer(params) { if (params.layer instanceof L.Class) { return angular.copy(params.layer); } else { $log.error('[AngularJS - Leaflet] A custom layer must be a leaflet Class'); } } }, cartodb: { mustHaveUrl: true, createLayer: function createLayer(params) { return cartodb.createLayer(params.map, params.url); } } }; function isValidLayerType(layerDefinition) { // Check if the baselayer has a valid type if (!isString(layerDefinition.type)) { $log.error('[AngularJS - Leaflet] A layer must have a valid type defined.'); return false; } if (Object.keys(layerTypes).indexOf(layerDefinition.type) === -1) { $log.error('[AngularJS - Leaflet] A layer must have a valid type: ' + Object.keys(layerTypes)); return false; } // Check if the layer must have an URL if (layerTypes[layerDefinition.type].mustHaveUrl && !isString(layerDefinition.url)) { $log.error('[AngularJS - Leaflet] A base layer must have an url'); return false; } if (layerTypes[layerDefinition.type].mustHaveData && !isDefined(layerDefinition.data)) { $log.error('[AngularJS - Leaflet] The base layer must have a "data" array attribute'); return false; } if (layerTypes[layerDefinition.type].mustHaveLayer && !isDefined(layerDefinition.layer)) { $log.error('[AngularJS - Leaflet] The type of layer ' + layerDefinition.type + ' must have an layer defined'); return false; } if (layerTypes[layerDefinition.type].mustHaveBounds && !isDefined(layerDefinition.bounds)) { $log.error('[AngularJS - Leaflet] The type of layer ' + layerDefinition.type + ' must have bounds defined'); return false; } if (layerTypes[layerDefinition.type].mustHaveKey && !isDefined(layerDefinition.key)) { $log.error('[AngularJS - Leaflet] The type of layer ' + layerDefinition.type + ' must have key defined'); return false; } return true; } function _createLayer(layerDefinition) { if (!isValidLayerType(layerDefinition)) { return; } if (!isString(layerDefinition.name)) { $log.error('[AngularJS - Leaflet] A base layer must have a name'); return; } if (!isObject(layerDefinition.layerParams)) { layerDefinition.layerParams = {}; } if (!isObject(layerDefinition.layerOptions)) { layerDefinition.layerOptions = {}; } // Mix the layer specific parameters with the general Leaflet options. Although this is an overhead // the definition of a base layers is more 'clean' if the two types of parameters are differentiated for (var attrname in layerDefinition.layerParams) { layerDefinition.layerOptions[attrname] = layerDefinition.layerParams[attrname]; } var params = { url: layerDefinition.url, data: layerDefinition.data, options: layerDefinition.layerOptions, layer: layerDefinition.layer, icon: layerDefinition.icon, type: layerDefinition.layerType, bounds: layerDefinition.bounds, key: layerDefinition.key, apiKey: layerDefinition.apiKey, pluginOptions: layerDefinition.pluginOptions, user: layerDefinition.user, $parent: layerDefinition }; //TODO Add $watch to the layer properties return layerTypes[layerDefinition.type].createLayer(params); } function safeAddLayer(map, layer) { if (layer && typeof layer.addTo === 'function') { layer.addTo(map); } else { map.addLayer(layer); } } function safeRemoveLayer(map, layer, layerOptions) { if (isDefined(layerOptions) && isDefined(layerOptions.loadedDefer)) { if (angular.isFunction(layerOptions.loadedDefer)) { var defers = layerOptions.loadedDefer(); $log.debug('Loaded Deferred', defers); var count = defers.length; if (count > 0) { var resolve = function resolve() { count--; if (count === 0) { map.removeLayer(layer); } }; for (var i = 0; i < defers.length; i++) { defers[i].promise.then(resolve); } } else { map.removeLayer(layer); } } else { layerOptions.loadedDefer.promise.then(function () { map.removeLayer(layer); }); } } else { map.removeLayer(layer); } } var changeOpacityListener = function changeOpacityListener(op) { return function (ly) { if (isDefined(ly.setOpacity)) { ly.setOpacity(op); } }; }; return { createLayer: _createLayer, layerTypes: layerTypes, safeAddLayer: safeAddLayer, safeRemoveLayer: safeRemoveLayer, changeOpacityListener: changeOpacityListener }; }]); 'use strict'; angular.module("ui-leaflet").factory('leafletLegendHelpers', ["$http", "$q", "$log", "leafletHelpers", function ($http, $q, $log, leafletHelpers) { var requestQueue = {}, isDefined = leafletHelpers.isDefined; var _execNext = function _execNext(mapId) { var queue = requestQueue[mapId]; var task = queue[0]; $http(task.c).then(function (data) { queue.shift(); task.d.resolve(data); if (queue.length > 0) { _execNext(mapId); } }, function (err) { queue.shift(); task.d.reject(err); if (queue.length > 0) { _execNext(mapId); } }); }; var _updateLegend = function _updateLegend(div, legendData, type, url) { div.innerHTML = ''; if (legendData.error) { div.innerHTML += '
' + legendData.error.message + '
'; } else { if (type === 'arcgis') { for (var i = 0; i < legendData.layers.length; i++) { var layer = legendData.layers[i]; div.innerHTML += '
' + layer.layerName + '
'; for (var j = 0; j < layer.legend.length; j++) { var leg = layer.legend[j]; div.innerHTML += '
' + '
' + leg.label + '
'; } } } else if (type === 'image') { div.innerHTML = ''; } } }; var _getOnAddLegend = function _getOnAddLegend(legendData, legendClass, type, url) { return function () /*map*/{ var div = L.DomUtil.create('div', legendClass); if (!L.Browser.touch) { L.DomEvent.disableClickPropagation(div); L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation); } else { L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation); } _updateLegend(div, legendData, type, url); return div; }; }; var _getOnAddArrayLegend = function _getOnAddArrayLegend(legend, legendClass) { return function () /*map*/{ var div = L.DomUtil.create('div', legendClass); for (var i = 0; i < legend.colors.length; i++) { div.innerHTML += '
' + '
' + legend.labels[i] + '
'; } if (!L.Browser.touch) { L.DomEvent.disableClickPropagation(div); L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation); } else { L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation); } return div; }; }; return { getOnAddLegend: _getOnAddLegend, getOnAddArrayLegend: _getOnAddArrayLegend, updateLegend: _updateLegend, addLegendURL: function addLegendURL(mapId, config) { var d = $q.defer(); if (!isDefined(requestQueue[mapId])) { requestQueue[mapId] = []; } requestQueue[mapId].push({ c: config, d: d }); if (requestQueue[mapId].length === 1) { _execNext(mapId); } return d.promise; } }; }]); 'use strict'; angular.module('ui-leaflet').factory('leafletMapDefaults', ["$q", "leafletHelpers", function ($q, leafletHelpers) { function _getDefaults() { return { keyboard: true, dragging: true, worldCopyJump: false, doubleClickZoom: true, scrollWheelZoom: true, tap: true, touchZoom: true, zoomControl: true, zoomsliderControl: false, zoomControlPosition: 'topleft', attributionControl: true, controls: { layers: { visible: true, position: 'topright', collapsed: true } }, nominatim: { server: ' http://nominatim.openstreetmap.org/search' }, crs: L.CRS.EPSG3857, tileLayer: '//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', tileLayerOptions: { attribution: '© OpenStreetMap contributors' }, path: { weight: 10, opacity: 1, color: '#0000ff' }, center: { lat: 0, lng: 0, zoom: 1 }, trackResize: true }; } var isDefined = leafletHelpers.isDefined, isObject = leafletHelpers.isObject, obtainEffectiveMapId = leafletHelpers.obtainEffectiveMapId, defaults = {}; // Get the _defaults dictionary, and override the properties defined by the user return { reset: function reset() { defaults = {}; }, getDefaults: function getDefaults(scopeId) { var mapId = obtainEffectiveMapId(defaults, scopeId); return defaults[mapId]; }, getMapCreationDefaults: function getMapCreationDefaults(scopeId) { var mapId = obtainEffectiveMapId(defaults, scopeId); var d = defaults[mapId]; var mapDefaults = { maxZoom: d.maxZoom, keyboard: d.keyboard, dragging: d.dragging, zoomControl: d.zoomControl, doubleClickZoom: d.doubleClickZoom, scrollWheelZoom: d.scrollWheelZoom, tap: d.tap, touchZoom: d.touchZoom, attributionControl: d.attributionControl, worldCopyJump: d.worldCopyJump, crs: d.crs, trackResize: d.trackResize }; 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 setDefaults(userDefaults, scopeId) { var newDefaults = _getDefaults(); if (isDefined(userDefaults)) { newDefaults.doubleClickZoom = isDefined(userDefaults.doubleClickZoom) ? userDefaults.doubleClickZoom : newDefaults.doubleClickZoom; newDefaults.scrollWheelZoom = isDefined(userDefaults.scrollWheelZoom) ? userDefaults.scrollWheelZoom : newDefaults.doubleClickZoom; newDefaults.tap = isDefined(userDefaults.tap) ? userDefaults.tap : newDefaults.tap; newDefaults.touchZoom = isDefined(userDefaults.touchZoom) ? userDefaults.touchZoom : newDefaults.doubleClickZoom; newDefaults.zoomControl = isDefined(userDefaults.zoomControl) ? userDefaults.zoomControl : newDefaults.zoomControl; newDefaults.zoomsliderControl = isDefined(userDefaults.zoomsliderControl) ? userDefaults.zoomsliderControl : newDefaults.zoomsliderControl; newDefaults.attributionControl = isDefined(userDefaults.attributionControl) ? userDefaults.attributionControl : newDefaults.attributionControl; newDefaults.tileLayer = isDefined(userDefaults.tileLayer) ? userDefaults.tileLayer : newDefaults.tileLayer; newDefaults.zoomControlPosition = isDefined(userDefaults.zoomControlPosition) ? userDefaults.zoomControlPosition : newDefaults.zoomControlPosition; newDefaults.keyboard = isDefined(userDefaults.keyboard) ? userDefaults.keyboard : newDefaults.keyboard; newDefaults.dragging = isDefined(userDefaults.dragging) ? userDefaults.dragging : newDefaults.dragging; newDefaults.trackResize = isDefined(userDefaults.trackResize) ? userDefaults.trackResize : newDefaults.trackResize; if (isDefined(userDefaults.controls)) { angular.extend(newDefaults.controls, userDefaults.controls); } if (isObject(userDefaults.crs)) { newDefaults.crs = userDefaults.crs; } else if (isDefined(L.CRS[userDefaults.crs])) { newDefaults.crs = L.CRS[userDefaults.crs]; } if (isDefined(userDefaults.center)) { angular.copy(userDefaults.center, newDefaults.center); } if (isDefined(userDefaults.tileLayerOptions)) { angular.copy(userDefaults.tileLayerOptions, newDefaults.tileLayerOptions); } if (isDefined(userDefaults.maxZoom)) { newDefaults.maxZoom = userDefaults.maxZoom; } if (isDefined(userDefaults.minZoom)) { newDefaults.minZoom = userDefaults.minZoom; } if (isDefined(userDefaults.zoomAnimation)) { newDefaults.zoomAnimation = userDefaults.zoomAnimation; } if (isDefined(userDefaults.fadeAnimation)) { newDefaults.fadeAnimation = userDefaults.fadeAnimation; } if (isDefined(userDefaults.markerZoomAnimation)) { newDefaults.markerZoomAnimation = userDefaults.markerZoomAnimation; } if (isDefined(userDefaults.worldCopyJump)) { newDefaults.worldCopyJump = userDefaults.worldCopyJump; } if (isDefined(userDefaults.map)) { newDefaults.map = userDefaults.map; } if (isDefined(userDefaults.path)) { newDefaults.path = userDefaults.path; } } var mapId = obtainEffectiveMapId(defaults, scopeId); defaults[mapId] = newDefaults; return newDefaults; } }; }]); 'use strict'; angular.module('ui-leaflet').service('leafletMarkersHelpers', ["$rootScope", "$timeout", "leafletHelpers", "leafletLogger", "$compile", "leafletGeoJsonHelpers", "leafletWatchHelpers", function ($rootScope, $timeout, leafletHelpers, leafletLogger, $compile, leafletGeoJsonHelpers, leafletWatchHelpers) { var isDefined = leafletHelpers.isDefined, defaultTo = leafletHelpers.defaultTo, MarkerClusterPlugin = leafletHelpers.MarkerClusterPlugin, AwesomeMarkersPlugin = leafletHelpers.AwesomeMarkersPlugin, VectorMarkersPlugin = leafletHelpers.VectorMarkersPlugin, MakiMarkersPlugin = leafletHelpers.MakiMarkersPlugin, ExtraMarkersPlugin = leafletHelpers.ExtraMarkersPlugin, DomMarkersPlugin = leafletHelpers.DomMarkersPlugin, safeApply = leafletHelpers.safeApply, Helpers = leafletHelpers, isString = leafletHelpers.isString, isNumber = leafletHelpers.isNumber, isObject = leafletHelpers.isObject, groups = {}, geoHlp = leafletGeoJsonHelpers, errorHeader = leafletHelpers.errorHeader, maybeWatch = leafletWatchHelpers.maybeWatch, $log = leafletLogger; var _string = function _string(marker) { //this exists since JSON.stringify barfs on cyclic var retStr = ''; ['_icon', '_latlng', '_leaflet_id', '_map', '_shadow'].forEach(function (prop) { retStr += prop + ': ' + defaultTo(marker[prop], 'undefined') + ' \n'; }); return '[leafletMarker] : \n' + retStr; }; var _log = function _log(marker, useConsole) { var logger = useConsole ? console : $log; logger.debug(_string(marker)); }; var existDomContainer = function existDomContainer(groupName) { return angular.element(groups[groupName]._map._container).parent().length > 0; }; var createLeafletIcon = function createLeafletIcon(iconData) { if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'awesomeMarker') { if (!AwesomeMarkersPlugin.isLoaded()) { $log.error(errorHeader + ' The AwesomeMarkers Plugin is not loaded.'); } return new L.AwesomeMarkers.icon(iconData); } if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'vectorMarker') { if (!VectorMarkersPlugin.isLoaded()) { $log.error(errorHeader + ' The VectorMarkers Plugin is not loaded.'); } return new L.VectorMarkers.icon(iconData); } if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'makiMarker') { if (!MakiMarkersPlugin.isLoaded()) { $log.error(errorHeader + 'The MakiMarkers Plugin is not loaded.'); } return new L.MakiMarkers.icon(iconData); } if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'extraMarker') { if (!ExtraMarkersPlugin.isLoaded()) { $log.error(errorHeader + 'The ExtraMarkers Plugin is not loaded.'); } return new L.ExtraMarkers.icon(iconData); } if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'div') { return new L.divIcon(iconData); } if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'dom') { if (!DomMarkersPlugin.isLoaded()) { $log.error(errorHeader + 'The DomMarkers Plugin is not loaded.'); } var markerScope = angular.isFunction(iconData.getMarkerScope) ? iconData.getMarkerScope().$new() : $rootScope, template = $compile(iconData.template)(markerScope), iconDataCopy = angular.copy(iconData); iconDataCopy.ngElement = template; iconDataCopy.element = template[0]; if (angular.isFunction(iconData.getMarkerScope)) iconDataCopy.scope = markerScope; return new L.DomMarkers.icon(iconDataCopy); } // allow for any custom icon to be used... assumes the icon has already been initialized if (isDefined(iconData) && isDefined(iconData.type) && iconData.type === 'icon') { return iconData.icon; } var base64icon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAApCAYAAADAk4LOAAAGmklEQVRYw7VXeUyTZxjvNnfELFuyIzOabermMZEeQC/OclkO49CpOHXOLJl/CAURuYbQi3KLgEhbrhZ1aDwmaoGqKII6odATmH/scDFbdC7LvFqOCc+e95s2VG50X/LLm/f4/Z7neY/ne18aANCmAr5E/xZf1uDOkTcGcWR6hl9247tT5U7Y6SNvWsKT63P58qbfeLJG8M5qcgTknrvvrdDbsT7Ml+tv82X6vVxJE33aRmgSyYtcWVMqX97Yv2JvW39UhRE2HuyBL+t+gK1116ly06EeWFNlAmHxlQE0OMiV6mQCScusKRlhS3QLeVJdl1+23h5dY4FNB3thrbYboqptEFlphTC1hSpJnbRvxP4NWgsE5Jyz86QNNi/5qSUTGuFk1gu54tN9wuK2wc3o+Wc13RCmsoBwEqzGcZsxsvCSy/9wJKf7UWf1mEY8JWfewc67UUoDbDjQC+FqK4QqLVMGGR9d2wurKzqBk3nqIT/9zLxRRjgZ9bqQgub+DdoeCC03Q8j+0QhFhBHR/eP3U/zCln7Uu+hihJ1+bBNffLIvmkyP0gpBZWYXhKussK6mBz5HT6M1Nqpcp+mBCPXosYQfrekGvrjewd59/GvKCE7TbK/04/ZV5QZYVWmDwH1mF3xa2Q3ra3DBC5vBT1oP7PTj4C0+CcL8c7C2CtejqhuCnuIQHaKHzvcRfZpnylFfXsYJx3pNLwhKzRAwAhEqG0SpusBHfAKkxw3w4627MPhoCH798z7s0ZnBJ/MEJbZSbXPhER2ih7p2ok/zSj2cEJDd4CAe+5WYnBCgR2uruyEw6zRoW6/DWJ/OeAP8pd/BGtzOZKpG8oke0SX6GMmRk6GFlyAc59K32OTEinILRJRchah8HQwND8N435Z9Z0FY1EqtxUg+0SO6RJ/mmXz4VuS+DpxXC3gXmZwIL7dBSH4zKE50wESf8qwVgrP1EIlTO5JP9Igu0aexdh28F1lmAEGJGfh7jE6ElyM5Rw/FDcYJjWhbeiBYoYNIpc2FT/SILivp0F1ipDWk4BIEo2VuodEJUifhbiltnNBIXPUFCMpthtAyqws/BPlEF/VbaIxErdxPphsU7rcCp8DohC+GvBIPJS/tW2jtvTmmAeuNO8BNOYQeG8G/2OzCJ3q+soYB5i6NhMaKr17FSal7GIHheuV3uSCY8qYVuEm1cOzqdWr7ku/R0BDoTT+DT+ohCM6/CCvKLKO4RI+dXPeAuaMqksaKrZ7L3FE5FIFbkIceeOZ2OcHO6wIhTkNo0ffgjRGxEqogXHYUPHfWAC/lADpwGcLRY3aeK4/oRGCKYcZXPVoeX/kelVYY8dUGf8V5EBRbgJXT5QIPhP9ePJi428JKOiEYhYXFBqou2Guh+p/mEB1/RfMw6rY7cxcjTrneI1FrDyuzUSRm9miwEJx8E/gUmqlyvHGkneiwErR21F3tNOK5Tf0yXaT+O7DgCvALTUBXdM4YhC/IawPU+2PduqMvuaR6eoxSwUk75ggqsYJ7VicsnwGIkZBSXKOUww73WGXyqP+J2/b9c+gi1YAg/xpwck3gJuucNrh5JvDPvQr0WFXf0piyt8f8/WI0hV4pRxxkQZdJDfDJNOAmM0Ag8jyT6hz0WGXWuP94Yh2jcfjmXAGvHCMslRimDHYuHuDsy2QtHuIavznhbYURq5R57KpzBBRZKPJi8eQg48h4j8SDdowifdIrEVdU+gbO6QNvRRt4ZBthUaZhUnjlYObNagV3keoeru3rU7rcuceqU1mJBxy+BWZYlNEBH+0eH4vRiB+OYybU2hnblYlTvkHinM4m54YnxSyaZYSF6R3jwgP7udKLGIX6r/lbNa9N6y5MFynjWDtrHd75ZvTYAPO/6RgF0k76mQla3FGq7dO+cH8sKn0Vo7nDllwAhqwLPkxrHwWmHJOo+AKJ4rab5OgrM7rVu8eWb2Pu0Dh4eDgXoOfvp7Y7QeqknRmvcTBEyq9m/HQQSCSz6LHq3z0yzsNySRfMS253wl2KyRDbcZPcfJKjZmSEOjcxyi+Y8dUOtsIEH6R2wNykdqrkYJ0RV92H0W58pkfQk7cKevsLK10Py8SdMGfXNXATY+pPbyJR/ET6n9nIfztNtZYRV9XniQu9IA2vOVgy4ir7GCLVmmd+zjkH0eAF9Po6K61pmCXHxU5rHMYd1ftc3owjwRSVRzLjKvqZEty6cRUD7jGqiOdu5HG6MdHjNcNYGqfDm5YRzLBBCCDl/2bk8a8gdbqcfwECu62Fg/HrggAAAABJRU5ErkJggg=="; var base64shadow = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAApCAYAAACoYAD2AAAC5ElEQVRYw+2YW4/TMBCF45S0S1luXZCABy5CgLQgwf//S4BYBLTdJLax0fFqmB07nnQfEGqkIydpVH85M+NLjPe++dcPc4Q8Qh4hj5D/AaQJx6H/4TMwB0PeBNwU7EGQAmAtsNfAzoZkgIa0ZgLMa4Aj6CxIAsjhjOCoL5z7Glg1JAOkaicgvQBXuncwJAWjksLtBTWZe04CnYRktUGdilALppZBOgHGZcBzL6OClABvMSVIzyBjazOgrvACf1ydC5mguqAVg6RhdkSWQFj2uxfaq/BrIZOLEWgZdALIDvcMcZLD8ZbLC9de4yR1sYMi4G20S4Q/PWeJYxTOZn5zJXANZHIxAd4JWhPIloTJZhzMQduM89WQ3MUVAE/RnhAXpTycqys3NZALOBbB7kFrgLesQl2h45Fcj8L1tTSohUwuxhy8H/Qg6K7gIs+3kkaigQCOcyEXCHN07wyQazhrmIulvKMQAwMcmLNqyCVyMAI+BuxSMeTk3OPikLY2J1uE+VHQk6ANrhds+tNARqBeaGc72cK550FP4WhXmFmcMGhTwAR1ifOe3EvPqIegFmF+C8gVy0OfAaWQPMR7gF1OQKqGoBjq90HPMP01BUjPOqGFksC4emE48tWQAH0YmvOgF3DST6xieJgHAWxPAHMuNhrImIdvoNOKNWIOcE+UXE0pYAnkX6uhWsgVXDxHdTfCmrEEmMB2zMFimLVOtiiajxiGWrbU52EeCdyOwPEQD8LqyPH9Ti2kgYMf4OhSKB7qYILbBv3CuVTJ11Y80oaseiMWOONc/Y7kJYe0xL2f0BaiFTxknHO5HaMGMublKwxFGzYdWsBF174H/QDknhTHmHHN39iWFnkZx8lPyM8WHfYELmlLKtgWNmFNzQcC1b47gJ4hL19i7o65dhH0Negbca8vONZoP7doIeOC9zXm8RjuL0Gf4d4OYaU5ljo3GYiqzrWQHfJxA6ALhDpVKv9qYeZA8eM3EhfPSCmpuD0AAAAASUVORK5CYII="; if (!isDefined(iconData) || !isDefined(iconData.iconUrl)) { return new L.Icon.Default({ iconUrl: base64icon, shadowUrl: base64shadow, iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41] }); } return new L.Icon(iconData); }; var _resetMarkerGroup = function _resetMarkerGroup(groupName) { if (isDefined(groups[groupName])) { delete groups[groupName]; } }; var _resetMarkerGroups = function _resetMarkerGroups() { groups = {}; }; var _resetUnusedMarkerGroups = function _resetUnusedMarkerGroups() { for (var groupName in groups) { if (!existDomContainer(groupName)) { _resetMarkerGroup(groupName); } } }; var _cleanDomIcon = function _cleanDomIcon(marker) { if (marker.options.icon.options.ngElement) { marker.options.icon.options.ngElement.remove(); } if (marker.options.icon.options.scope) { marker.options.icon.options.scope.$destroy(); } }; var _deleteMarker = function _deleteMarker(marker, map, layers) { marker.closePopup(); // if it's a dom icon, clean it if (marker.options.icon && marker.options.icon.options && marker.options.icon.options.type === 'dom') { _cleanDomIcon(marker); } // There is no easy way to know if a marker is added to a layer, so we search for it // if there are overlays if (isDefined(layers) && isDefined(layers.overlays)) { for (var key in layers.overlays) { if (layers.overlays[key] instanceof L.LayerGroup || layers.overlays[key] instanceof L.FeatureGroup) { if (layers.overlays[key].hasLayer(marker)) { layers.overlays[key].removeLayer(marker); return; } } } } if (isDefined(groups)) { for (var groupKey in groups) { if (groups[groupKey].hasLayer(marker)) { groups[groupKey].removeLayer(marker); } } } if (map.hasLayer(marker)) { map.removeLayer(marker); } }; var adjustPopupPan = function adjustPopupPan(marker, map) { var containerHeight = marker._popup._container.offsetHeight, layerPos = new L.Point(marker._popup._containerLeft, -containerHeight - marker._popup._containerBottom), containerPos = map.layerPointToContainerPoint(layerPos); if (containerPos !== null) { marker._popup._adjustPan(); } }; var compilePopup = function compilePopup(marker, markerScope) { $compile(marker._popup._contentNode)(markerScope); }; var updatePopup = function updatePopup(marker, markerScope, map) { //The innerText should be more than 1 once angular has compiled. //We need to keep trying until angular has compiled before we _updateLayout and _updatePosition //This should take care of any scenario , eg ngincludes, whatever. //Is there a better way to check for this? var innerText = marker._popup._contentNode.innerText || marker._popup._contentNode.textContent; if (innerText.length < 1) { $timeout(function () { updatePopup(marker, markerScope, map); }); } //cause a reflow - this is also very important - if we don't do this then the widths are from before $compile var reflow = marker._popup._contentNode.offsetWidth; marker._popup._updateLayout(); marker._popup._updatePosition(); if (marker._popup.options.autoPan) { adjustPopupPan(marker, map); } //using / returning reflow so jshint doesn't moan return reflow; }; var _manageOpenPopup = function _manageOpenPopup(marker, markerData, map) { // The marker may provide a scope returning function used to compile the message // default to $rootScope otherwise var markerScope = angular.isFunction(markerData.getMessageScope) ? markerData.getMessageScope() : $rootScope, compileMessage = isDefined(markerData.compileMessage) ? markerData.compileMessage : true; if (compileMessage) { if (!isDefined(marker._popup) || !isDefined(marker._popup._contentNode)) { $log.error(errorHeader + 'Popup is invalid or does not have any content.'); return false; } compilePopup(marker, markerScope); updatePopup(marker, markerData, map); } }; var _manageOpenLabel = function _manageOpenLabel(marker, markerData) { var markerScope = angular.isFunction(markerData.getMessageScope) ? markerData.getMessageScope() : $rootScope, labelScope = angular.isFunction(markerData.getLabelScope) ? markerData.getLabelScope() : markerScope, compileMessage = isDefined(markerData.compileMessage) ? markerData.compileMessage : true; if (Helpers.LabelPlugin.isLoaded() && isDefined(markerData.label)) { if (isDefined(markerData.label.options) && markerData.label.options.noHide === true) { marker.showLabel(); } if (compileMessage && isDefined(marker.label)) { $compile(marker.label._container)(labelScope); } } }; var _updateMarker = function _updateMarker(markerData, oldMarkerData, marker, name, leafletScope, layers, map) { if (!isDefined(oldMarkerData)) { return; } // Update the lat-lng property (always present in marker properties) if (!geoHlp.validateCoords(markerData)) { $log.warn('There are problems with lat-lng data, please verify your marker model'); _deleteMarker(marker, map, layers); return; } // watch is being initialized if old and new object is the same var isInitializing = markerData === oldMarkerData; // Update marker rotation if (isDefined(markerData.iconAngle) && oldMarkerData.iconAngle !== markerData.iconAngle) { marker.setIconAngle(markerData.iconAngle); } // It is possible that the layer has been removed or the layer marker does not exist // Update the layer group if present or move it to the map if not if (!isString(markerData.layer)) { // There is no layer information, we move the marker to the map if it was in a layer group if (isString(oldMarkerData.layer)) { // Remove from the layer group that is supposed to be if (isDefined(layers.overlays[oldMarkerData.layer]) && layers.overlays[oldMarkerData.layer].hasLayer(marker)) { layers.overlays[oldMarkerData.layer].removeLayer(marker); marker.closePopup(); } // Test if it is not on the map and add it if (!map.hasLayer(marker)) { map.addLayer(marker); } } } if ((isNumber(markerData.opacity) || isNumber(parseFloat(markerData.opacity))) && markerData.opacity !== oldMarkerData.opacity) { // There was a different opacity so we update it marker.setOpacity(markerData.opacity); } if (isString(markerData.layer) && oldMarkerData.layer !== markerData.layer) { // If it was on a layer group we have to remove it if (isString(oldMarkerData.layer) && isDefined(layers.overlays[oldMarkerData.layer]) && layers.overlays[oldMarkerData.layer].hasLayer(marker)) { layers.overlays[oldMarkerData.layer].removeLayer(marker); } marker.closePopup(); // Remove it from the map in case the new layer is hidden or there is an error in the new layer if (map.hasLayer(marker)) { map.removeLayer(marker); } // The markerData.layer is defined so we add the marker to the layer if it is different from the old data if (!isDefined(layers.overlays[markerData.layer])) { $log.error(errorHeader + 'You must use a name of an existing layer'); return; } // Is a group layer? var layerGroup = layers.overlays[markerData.layer]; if (!(layerGroup instanceof L.LayerGroup || layerGroup instanceof L.FeatureGroup)) { $log.error(errorHeader + 'A marker can only be added to a layer of type "group" or "featureGroup"'); return; } // The marker goes to a correct layer group, so first of all we add it layerGroup.addLayer(marker); // The marker is automatically added to the map depending on the visibility // of the layer, so we only have to open the popup if the marker is in the map if (map.hasLayer(marker) && markerData.focus === true) { marker.openPopup(); } } // Update the draggable property if (markerData.draggable !== true && oldMarkerData.draggable === true && isDefined(marker.dragging)) { marker.dragging.disable(); } if (markerData.draggable === true && oldMarkerData.draggable !== true) { // The markerData.draggable property must be true so we update if there wasn't a previous value or it wasn't true if (marker.dragging) { marker.dragging.enable(); } else { if (L.Handler.MarkerDrag) { marker.dragging = new L.Handler.MarkerDrag(marker); marker.options.draggable = true; marker.dragging.enable(); } } } // Update the icon property if (!isObject(markerData.icon)) { // If there is no icon property or it's not an object if (isObject(oldMarkerData.icon)) { if (oldMarkerData.icon.type === 'dom') { // clean previous icon if it's a dom one _cleanDomIcon(marker); } // If there was an icon before restore to the default marker.setIcon(createLeafletIcon()); marker.closePopup(); marker.unbindPopup(); if (isString(markerData.message)) { marker.bindPopup(markerData.message, markerData.popupOptions); } } } if (isObject(markerData.icon) && isObject(oldMarkerData.icon) && !angular.equals(markerData.icon, oldMarkerData.icon)) { var dragG = false; if (marker.dragging) { dragG = marker.dragging.enabled(); } if (oldMarkerData.icon.type === 'dom') { // clean previous icon if it's a dom one _cleanDomIcon(marker); } marker.setIcon(createLeafletIcon(markerData.icon)); if (dragG) { marker.dragging.enable(); } marker.closePopup(); marker.unbindPopup(); if (isString(markerData.message)) { marker.bindPopup(markerData.message, markerData.popupOptions); // if marker has been already focused, reopen popup if (map.hasLayer(marker) && markerData.focus === true) { marker.openPopup(); } } } // Update the Popup message property if (!isString(markerData.message) && isString(oldMarkerData.message)) { marker.closePopup(); marker.unbindPopup(); } // Update the label content or bind a new label if the old one has been removed. if (Helpers.LabelPlugin.isLoaded()) { if (isDefined(markerData.label) && isDefined(markerData.label.message)) { if ('label' in oldMarkerData && 'message' in oldMarkerData.label && !angular.equals(markerData.label.message, oldMarkerData.label.message)) { marker.updateLabelContent(markerData.label.message); } else if (!angular.isFunction(marker.getLabel) || angular.isFunction(marker.getLabel) && !isDefined(marker.getLabel())) { marker.bindLabel(markerData.label.message, markerData.label.options); _manageOpenLabel(marker, markerData); } else { _manageOpenLabel(marker, markerData); } } else if (!('label' in markerData && !('message' in markerData.label))) { if (angular.isFunction(marker.unbindLabel)) { marker.unbindLabel(); } } } // There is some text in the popup, so we must show the text or update existing if (isString(markerData.message) && !isString(oldMarkerData.message)) { // There was no message before so we create it marker.bindPopup(markerData.message, markerData.popupOptions); } if (isString(markerData.message) && isString(oldMarkerData.message) && markerData.message !== oldMarkerData.message) { // There was a different previous message so we update it marker.setPopupContent(markerData.message); } // Update the focus property var updatedFocus = false; if (markerData.focus !== true && oldMarkerData.focus === true) { // If there was a focus property and was true we turn it off marker.closePopup(); updatedFocus = true; } // The markerData.focus property must be true so we update if there wasn't a previous value or it wasn't true if (markerData.focus === true && (!isDefined(oldMarkerData.focus) || oldMarkerData.focus === false) || isInitializing && markerData.focus === true) { // Reopen the popup when focus is still true marker.openPopup(); updatedFocus = true; } // zIndexOffset adjustment if (oldMarkerData.zIndexOffset !== markerData.zIndexOffset) { marker.setZIndexOffset(markerData.zIndexOffset); } var markerLatLng = marker.getLatLng(); var isCluster = isString(markerData.layer) && Helpers.MarkerClusterPlugin.is(layers.overlays[markerData.layer]); // If the marker is in a cluster it has to be removed and added to the layer when the location is changed if (isCluster) { // The focus has changed even by a user click or programatically if (updatedFocus) { // We only have to update the location if it was changed programatically, because it was // changed by a user drag the marker data has already been updated by the internal event // listened by the directive if (markerData.lat !== oldMarkerData.lat || markerData.lng !== oldMarkerData.lng) { layers.overlays[markerData.layer].removeLayer(marker); marker.setLatLng([markerData.lat, markerData.lng]); layers.overlays[markerData.layer].addLayer(marker); } } else { // The marker has possibly moved. It can be moved by a user drag (marker location and data are equal but old // data is diferent) or programatically (marker location and data are diferent) if (markerLatLng.lat !== markerData.lat || markerLatLng.lng !== markerData.lng) { // The marker was moved by a user drag layers.overlays[markerData.layer].removeLayer(marker); marker.setLatLng([markerData.lat, markerData.lng]); layers.overlays[markerData.layer].addLayer(marker); } else if (markerData.lat !== oldMarkerData.lat || markerData.lng !== oldMarkerData.lng) { // The marker was moved programatically layers.overlays[markerData.layer].removeLayer(marker); marker.setLatLng([markerData.lat, markerData.lng]); layers.overlays[markerData.layer].addLayer(marker); } else if (isObject(markerData.icon) && isObject(oldMarkerData.icon) && !angular.equals(markerData.icon, oldMarkerData.icon)) { layers.overlays[markerData.layer].removeLayer(marker); layers.overlays[markerData.layer].addLayer(marker); } } } else if (markerLatLng.lat !== markerData.lat || markerLatLng.lng !== markerData.lng) { marker.setLatLng([markerData.lat, markerData.lng]); } }; var _getLayerModels = function _getLayerModels(models, layerName) { if (!isDefined(models)) return; if (layerName) return models[layerName]; return models; }; var _getModelFromModels = function _getModelFromModels(models, id, layerName) { if (!isDefined(models)) return; if (!id) { $log.error(errorHeader + 'marker id missing in getMarker'); return; } if (layerName) return models[layerName][id]; return models[id]; }; return { resetMarkerGroup: _resetMarkerGroup, resetMarkerGroups: _resetMarkerGroups, resetUnusedMarkerGroups: _resetUnusedMarkerGroups, deleteMarker: _deleteMarker, manageOpenPopup: _manageOpenPopup, manageOpenLabel: _manageOpenLabel, createMarker: function createMarker(markerData) { if (!isDefined(markerData) || !geoHlp.validateCoords(markerData)) { $log.error(errorHeader + 'The marker definition is not valid.'); return; } var coords = geoHlp.getCoords(markerData); if (!isDefined(coords)) { $log.error(errorHeader + 'Unable to get coordinates from markerData.'); return; } var markerOptions = { icon: createLeafletIcon(markerData.icon), title: isDefined(markerData.title) ? markerData.title : '', draggable: isDefined(markerData.draggable) ? markerData.draggable : false, clickable: isDefined(markerData.clickable) ? markerData.clickable : true, riseOnHover: isDefined(markerData.riseOnHover) ? markerData.riseOnHover : false, zIndexOffset: isDefined(markerData.zIndexOffset) ? markerData.zIndexOffset : 0, iconAngle: isDefined(markerData.iconAngle) ? markerData.iconAngle : 0 }; // Add any other options not added above to markerOptions for (var markerDatum in markerData) { if (markerData.hasOwnProperty(markerDatum) && !markerOptions.hasOwnProperty(markerDatum)) { markerOptions[markerDatum] = markerData[markerDatum]; } } var marker = new L.marker(coords, markerOptions); if (!isString(markerData.message)) { marker.unbindPopup(); } return marker; }, addMarkerToGroup: function addMarkerToGroup(marker, groupName, groupOptions, map) { if (!isString(groupName)) { $log.error(errorHeader + 'The marker group you have specified is invalid.'); return; } if (!MarkerClusterPlugin.isLoaded()) { $log.error(errorHeader + "The MarkerCluster plugin is not loaded."); return; } if (!isDefined(groups[groupName])) { groups[groupName] = new L.MarkerClusterGroup(groupOptions); map.addLayer(groups[groupName]); } groups[groupName].addLayer(marker); }, listenMarkerEvents: function listenMarkerEvents(marker, markerData, leafletScope, watchType, map) { marker.on("popupopen", function () /* event */{ safeApply(leafletScope, function () { if (isDefined(marker._popup) || isDefined(marker._popup._contentNode)) { markerData.focus = true; _manageOpenPopup(marker, markerData, map); //needed since markerData is now a copy } }); }); marker.on("popupclose", function () /* event */{ safeApply(leafletScope, function () { markerData.focus = false; }); }); marker.on("add", function () /* event */{ safeApply(leafletScope, function () { if ('label' in markerData) _manageOpenLabel(marker, markerData); }); }); }, updateMarker: _updateMarker, addMarkerWatcher: function addMarkerWatcher(marker, name, leafletScope, layers, map, watchOptions) { var markerWatchPath = Helpers.getObjectArrayPath("markers." + name); maybeWatch(leafletScope, markerWatchPath, watchOptions, function (markerData, oldMarkerData, clearWatch) { if (!isDefined(markerData)) { _deleteMarker(marker, map, layers); clearWatch(); return; } _updateMarker(markerData, oldMarkerData, marker, name, leafletScope, layers, map); }); }, string: _string, log: _log, getModelFromModels: _getModelFromModels, getLayerModels: _getLayerModels }; }]); 'use strict'; angular.module('ui-leaflet').factory('leafletPathsHelpers', ["$rootScope", "leafletLogger", "leafletHelpers", function ($rootScope, leafletLogger, leafletHelpers) { var isDefined = leafletHelpers.isDefined, isArray = leafletHelpers.isArray, isNumber = leafletHelpers.isNumber, isValidPoint = leafletHelpers.isValidPoint, $log = leafletLogger; var availableOptions = [ // Path options 'stroke', 'weight', 'color', 'opacity', 'fill', 'fillColor', 'fillOpacity', 'dashArray', 'lineCap', 'lineJoin', 'clickable', 'pointerEvents', 'className', // Polyline options 'smoothFactor', 'noClip']; function _convertToLeafletLatLngs(latlngs) { return latlngs.filter(function (latlng) { return isValidPoint(latlng); }).map(function (latlng) { return _convertToLeafletLatLng(latlng); }); } function _convertToLeafletLatLng(latlng) { if (isArray(latlng)) { return new L.LatLng(latlng[0], latlng[1]); } else { return new L.LatLng(latlng.lat, latlng.lng); } } function _convertToLeafletMultiLatLngs(paths) { return paths.map(function (latlngs) { return _convertToLeafletLatLngs(latlngs); }); } function _getOptions(path, defaults) { var options = {}; for (var i = 0; i < availableOptions.length; i++) { var optionName = availableOptions[i]; if (isDefined(path[optionName])) { options[optionName] = path[optionName]; } else if (isDefined(defaults.path[optionName])) { options[optionName] = defaults.path[optionName]; } } return options; } var _updatePathOptions = function _updatePathOptions(path, data) { var updatedStyle = {}; for (var i = 0; i < availableOptions.length; i++) { var optionName = availableOptions[i]; if (isDefined(data[optionName])) { updatedStyle[optionName] = data[optionName]; } } path.setStyle(data); }; var _isValidPolyline = function _isValidPolyline(latlngs) { if (!isArray(latlngs)) { return false; } for (var i = 0; i < latlngs.length; i++) { var point = latlngs[i]; if (!isValidPoint(point)) { return false; } } return true; }; var pathTypes = { polyline: { isValid: function isValid(pathData) { var latlngs = pathData.latlngs; return _isValidPolyline(latlngs); }, createPath: function createPath(options) { return new L.Polyline([], options); }, setPath: function setPath(path, data) { path.setLatLngs(_convertToLeafletLatLngs(data.latlngs)); _updatePathOptions(path, data); return; } }, multiPolyline: { isValid: function isValid(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 createPath(options) { return new L.multiPolyline([[[0, 0], [1, 1]]], options); }, setPath: function setPath(path, data) { path.setLatLngs(_convertToLeafletMultiLatLngs(data.latlngs)); _updatePathOptions(path, data); return; } }, polygon: { isValid: function isValid(pathData) { var latlngs = pathData.latlngs; return _isValidPolyline(latlngs); }, createPath: function createPath(options) { return new L.Polygon([], options); }, setPath: function setPath(path, data) { path.setLatLngs(_convertToLeafletLatLngs(data.latlngs)); _updatePathOptions(path, data); return; } }, multiPolygon: { isValid: function isValid(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 createPath(options) { return new L.MultiPolygon([[[0, 0], [1, 1], [0, 1]]], options); }, setPath: function setPath(path, data) { path.setLatLngs(_convertToLeafletMultiLatLngs(data.latlngs)); _updatePathOptions(path, data); return; } }, rectangle: { isValid: function isValid(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 createPath(options) { return new L.Rectangle([[0, 0], [1, 1]], options); }, setPath: function setPath(path, data) { path.setBounds(new L.LatLngBounds(_convertToLeafletLatLngs(data.latlngs))); _updatePathOptions(path, data); } }, circle: { isValid: function isValid(pathData) { var point = pathData.latlngs; return isValidPoint(point) && isNumber(pathData.radius); }, createPath: function createPath(options) { return new L.Circle([0, 0], 1, options); }, setPath: function setPath(path, data) { path.setLatLng(_convertToLeafletLatLng(data.latlngs)); if (isDefined(data.radius)) { path.setRadius(data.radius); } _updatePathOptions(path, data); } }, circleMarker: { isValid: function isValid(pathData) { var point = pathData.latlngs; return isValidPoint(point) && isNumber(pathData.radius); }, createPath: function createPath(options) { return new L.CircleMarker([0, 0], options); }, setPath: function setPath(path, data) { path.setLatLng(_convertToLeafletLatLng(data.latlngs)); if (isDefined(data.radius)) { path.setRadius(data.radius); } _updatePathOptions(path, data); } } }; var _getPathData = function _getPathData(path) { var pathData = {}; if (path.latlngs) { pathData.latlngs = path.latlngs; } if (path.radius) { pathData.radius = path.radius; } return pathData; }; return { setPathOptions: function setPathOptions(leafletPath, pathType, data) { if (!isDefined(pathType)) { pathType = "polyline"; } pathTypes[pathType].setPath(leafletPath, data); }, createPath: function createPath(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); } }; }]); 'use strict'; angular.module('ui-leaflet').service('leafletWatchHelpers', function () { var _maybe = function _maybe(scope, watchFunctionName, thingToWatchStr, watchOptions, initCb) { var unWatch = scope[watchFunctionName](thingToWatchStr, function (newValue, oldValue) { //make the unWatch function available to the callback as well. initCb(newValue, oldValue, unWatch); if (watchOptions.type === null) unWatch(); }, watchOptions.type === 'watchDeep'); return unWatch; }; /* @name: maybeWatch @description: Utility to watch something once or forever. @returns unWatch function @param watchOptions - This object is used to determine the type of watch used. */ var _maybeWatch = function _maybeWatch(scope, thingToWatchStr, watchOptions, initCb) { var watchMethod; if (watchOptions.type === 'watchCollection') { watchMethod = '$watchCollection'; } else { watchMethod = '$watch'; } return _maybe(scope, watchMethod, thingToWatchStr, watchOptions, initCb); }; return { maybeWatch: _maybeWatch }; }); 'use strict'; angular.module('ui-leaflet').service('leafletLogger', ["nemSimpleLogger", function (nemSimpleLogger) { return nemSimpleLogger.spawn(); }]); "use strict"; angular.module("ui-leaflet").factory("nominatimService", ["$q", "$http", "leafletHelpers", "leafletMapDefaults", function ($q, $http, leafletHelpers, leafletMapDefaults) { return { query: function query(address, mapId) { var defaults = leafletMapDefaults.getDefaults(mapId); var url = defaults.nominatim.server; var df = $q.defer(); if (angular.version.major === 1 && angular.version.minor < 6) { $http.get(url, { params: { format: "json", limit: 1, q: address } }).success(function (data) { resolvePromise(data, df); }); } else { $http.get(url, { params: { format: "json", limit: 1, q: address } }).then(function (response) { resolvePromise(response.data, df); }); } return df.promise; } }; function resolvePromise(data, promise) { var isDefined = leafletHelpers.isDefined; if (data.length > 0 && isDefined(data[0].boundingbox)) { promise.resolve(data[0]); } else { promise.reject("[Nominatim] Invalid address"); } } }]); 'use strict'; angular.module('ui-leaflet').directive('bounds', ["leafletLogger", "$timeout", "$http", "leafletHelpers", "nominatimService", "leafletBoundsHelpers", function (leafletLogger, $timeout, $http, leafletHelpers, nominatimService, leafletBoundsHelpers) { var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: ['leaflet'], link: function link(scope, element, attrs, controller) { var isDefined = leafletHelpers.isDefined; var createLeafletBounds = leafletBoundsHelpers.createLeafletBounds; var leafletScope = controller[0].getLeafletScope(); var mapController = controller[0]; var errorHeader = leafletHelpers.errorHeader + ' [Bounds] '; var emptyBounds = function emptyBounds(bounds) { return bounds._southWest.lat === 0 && bounds._southWest.lng === 0 && bounds._northEast.lat === 0 && bounds._northEast.lng === 0; }; mapController.getMap().then(function (map) { leafletScope.$on('boundsChanged', function (event) { var scope = event.currentScope; var bounds = map.getBounds(); if (emptyBounds(bounds) || scope.settingBoundsFromScope) { return; } scope.settingBoundsFromLeaflet = true; var newScopeBounds = { northEast: { lat: bounds._northEast.lat, lng: bounds._northEast.lng }, southWest: { lat: bounds._southWest.lat, lng: bounds._southWest.lng }, options: bounds.options }; if (!angular.equals(scope.bounds, newScopeBounds)) { scope.bounds = newScopeBounds; } $timeout(function () { scope.settingBoundsFromLeaflet = false; }); }); var lastNominatimQuery; leafletScope.$watch('bounds', function (bounds) { if (scope.settingBoundsFromLeaflet) return; if (isDefined(bounds.address) && bounds.address !== lastNominatimQuery) { scope.settingBoundsFromScope = true; nominatimService.query(bounds.address, attrs.id).then(function (data) { var b = data.boundingbox; var newBounds = [[b[0], b[2]], [b[1], b[3]]]; map.fitBounds(newBounds); }, function (errMsg) { $log.error(errorHeader + ' ' + errMsg + '.'); }); lastNominatimQuery = bounds.address; $timeout(function () { scope.settingBoundsFromScope = false; }); return; } var leafletBounds = createLeafletBounds(bounds); if (leafletBounds && !map.getBounds().equals(leafletBounds)) { scope.settingBoundsFromScope = true; map.fitBounds(leafletBounds, bounds.options); $timeout(function () { scope.settingBoundsFromScope = false; }); } }, true); }); } }; }]); 'use strict'; var centerDirectiveTypes = ['center', 'lfCenter'], centerDirectives = {}; centerDirectiveTypes.forEach(function (directiveName) { centerDirectives[directiveName] = ['leafletLogger', '$q', '$location', '$timeout', 'leafletMapDefaults', 'leafletHelpers', 'leafletBoundsHelpers', 'leafletMapEvents', function (leafletLogger, $q, $location, $timeout, leafletMapDefaults, leafletHelpers, leafletBoundsHelpers, leafletMapEvents) { var isDefined = leafletHelpers.isDefined, isNumber = leafletHelpers.isNumber, isSameCenterOnMap = leafletHelpers.isSameCenterOnMap, safeApply = leafletHelpers.safeApply, isValidCenter = leafletHelpers.isValidCenter, isValidBounds = leafletBoundsHelpers.isValidBounds, isUndefinedOrEmpty = leafletHelpers.isUndefinedOrEmpty, errorHeader = leafletHelpers.errorHeader, $log = leafletLogger; var shouldInitializeMapWithBounds = function shouldInitializeMapWithBounds(bounds, center) { return isDefined(bounds) && isValidBounds(bounds) && isUndefinedOrEmpty(center); }; var _leafletCenter; return { restrict: "A", scope: false, replace: false, require: 'leaflet', controller: function controller() { _leafletCenter = $q.defer(); this.getCenter = function () { return _leafletCenter.promise; }; }, link: function link(scope, element, attrs, controller) { var leafletScope = controller.getLeafletScope(), centerModel = leafletScope[directiveName]; controller.getMap().then(function (map) { var defaults = leafletMapDefaults.getDefaults(attrs.id); if (attrs[directiveName].search("-") !== -1) { $log.error(errorHeader + ' The "center" variable can\'t use a "-" on its key name: "' + attrs[directiveName] + '".'); map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom); return; } else if (shouldInitializeMapWithBounds(leafletScope.bounds, centerModel)) { map.fitBounds(leafletBoundsHelpers.createLeafletBounds(leafletScope.bounds), leafletScope.bounds.options); centerModel = map.getCenter(); safeApply(leafletScope, function (scope) { angular.extend(scope[directiveName], { lat: map.getCenter().lat, lng: map.getCenter().lng, zoom: map.getZoom(), autoDiscover: false }); }); safeApply(leafletScope, function (scope) { var mapBounds = map.getBounds(); scope.bounds = { northEast: { lat: mapBounds._northEast.lat, lng: mapBounds._northEast.lng }, southWest: { lat: mapBounds._southWest.lat, lng: mapBounds._southWest.lng } }; }); } else if (!isDefined(centerModel)) { $log.error(errorHeader + ' The "center" property is not defined in the main scope'); map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom); return; } else if (!(isDefined(centerModel.lat) && isDefined(centerModel.lng)) && !isDefined(centerModel.autoDiscover)) { angular.copy(defaults.center, centerModel); } var urlCenterHash, mapReady; if (attrs.urlHashCenter === "yes") { var extractCenterFromUrl = function extractCenterFromUrl() { var search = $location.search(); var centerParam; var centerKey = attrs.urlHashParam ? attrs.urlHashParam : 'c'; if (isDefined(search[centerKey])) { var cParam = search[centerKey].split(":"); if (cParam.length === 3) { centerParam = { lat: parseFloat(cParam[0]), lng: parseFloat(cParam[1]), zoom: parseInt(cParam[2], 10) }; } } return centerParam; }; urlCenterHash = extractCenterFromUrl(); leafletScope.$on('$locationChangeSuccess', function (event) { var scope = event.currentScope; //$log.debug("updated location..."); var urlCenter = extractCenterFromUrl(); if (isDefined(urlCenter) && !isSameCenterOnMap(urlCenter, map)) { //$log.debug("updating center model...", urlCenter); angular.extend(scope[directiveName], { lat: urlCenter.lat, lng: urlCenter.lng, zoom: urlCenter.zoom }); } }); } leafletScope.$watch(directiveName, function (center) { if (leafletScope.settingCenterFromLeaflet) return; //$log.debug("updated center model..."); // The center from the URL has priority if (isDefined(urlCenterHash)) { angular.copy(urlCenterHash, center); urlCenterHash = undefined; } if (!isValidCenter(center) && center.autoDiscover !== true) { $log.warn(errorHeader + " invalid 'center'"); //map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom); return; } if (center.autoDiscover === true) { if (!isNumber(center.zoom)) { map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom); } if (isNumber(center.zoom) && center.zoom > defaults.center.zoom) { map.locate({ setView: true, maxZoom: center.zoom }); } else if (isDefined(defaults.maxZoom)) { map.locate({ setView: true, maxZoom: defaults.maxZoom }); } else { map.locate({ setView: true }); } return; } if (mapReady && isSameCenterOnMap(center, map)) { //$log.debug("no need to update map again."); return; } //$log.debug("updating map center...", center); leafletScope.settingCenterFromScope = true; map.setView([center.lat, center.lng], center.zoom); leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map); $timeout(function () { leafletScope.settingCenterFromScope = false; //$log.debug("allow center scope updates"); }); }, true); map.whenReady(function () { mapReady = true; }); map.on('moveend', function () /* event */{ // Resolve the center after the first map position _leafletCenter.resolve(); leafletMapEvents.notifyCenterUrlHashChanged(leafletScope, map, attrs, $location.search()); //$log.debug("updated center on map..."); if (isSameCenterOnMap(centerModel, map) || leafletScope.settingCenterFromScope) { //$log.debug("same center in model, no need to update again."); return; } leafletScope.settingCenterFromLeaflet = true; safeApply(leafletScope, function (scope) { if (!leafletScope.settingCenterFromScope) { //$log.debug("updating center model...", map.getCenter(), map.getZoom()); angular.extend(scope[directiveName], { lat: map.getCenter().lat, lng: map.getCenter().lng, zoom: map.getZoom(), autoDiscover: false }); } leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map); $timeout(function () { leafletScope.settingCenterFromLeaflet = false; }); }); }); if (centerModel.autoDiscover === true) { map.on('locationerror', function () { $log.warn(errorHeader + " The Geolocation API is unauthorized on this page."); if (isValidCenter(centerModel)) { map.setView([centerModel.lat, centerModel.lng], centerModel.zoom); leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map); } else { map.setView([defaults.center.lat, defaults.center.lng], defaults.center.zoom); leafletMapEvents.notifyCenterChangedToBounds(leafletScope, map); } }); } }); } }; }]; }); centerDirectiveTypes.forEach(function (dirType) { angular.module('ui-leaflet').directive(dirType, centerDirectives[dirType]); }); 'use strict'; angular.module('ui-leaflet').directive('controls', ["leafletLogger", "leafletHelpers", "leafletControlHelpers", function (leafletLogger, leafletHelpers, leafletControlHelpers) { var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: '?^leaflet', link: function link(scope, element, attrs, controller) { if (!controller) { return; } var createControl = leafletControlHelpers.createControl; var isValidControlType = leafletControlHelpers.isValidControlType; var leafletScope = controller.getLeafletScope(); var isDefined = leafletHelpers.isDefined; var isArray = leafletHelpers.isArray; var leafletControls = {}; var errorHeader = leafletHelpers.errorHeader + ' [Controls] '; scope.$on('$destroy', function () { leafletControlHelpers.destroyMapLayersControl(scope.mapId); }); controller.getMap().then(function (map) { leafletScope.$watchCollection('controls', function (newControls) { // Delete controls from the array for (var name in leafletControls) { if (!isDefined(newControls[name])) { if (map.hasControl(leafletControls[name])) { map.removeControl(leafletControls[name]); } delete leafletControls[name]; } } for (var newName in newControls) { var control; var controlType = isDefined(newControls[newName].type) ? newControls[newName].type : newName; if (!isValidControlType(controlType)) { $log.error(errorHeader + ' Invalid control type: ' + controlType + '.'); return; } if (controlType !== 'custom') { control = createControl(controlType, newControls[newName]); map.addControl(control); leafletControls[newName] = control; } else { var customControlValue = newControls[newName]; if (isArray(customControlValue)) { for (var i = 0; i < customControlValue.length; i++) { var customControl = customControlValue[i]; map.addControl(customControl); leafletControls[newName] = !isDefined(leafletControls[newName]) ? [customControl] : leafletControls[newName].concat([customControl]); } } else { map.addControl(customControlValue); leafletControls[newName] = customControlValue; } } } }); }); } }; }]); "use strict"; angular.module('ui-leaflet').directive("decorations", ["leafletLogger", "leafletHelpers", function (leafletLogger, leafletHelpers) { var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: 'leaflet', link: function link(scope, element, attrs, controller) { var leafletScope = controller.getLeafletScope(), PolylineDecoratorPlugin = leafletHelpers.PolylineDecoratorPlugin, isDefined = leafletHelpers.isDefined, leafletDecorations = {}; /* Creates an "empty" decoration with a set of coordinates, but no pattern. */ function createDecoration(options) { if (isDefined(options) && isDefined(options.coordinates)) { if (!PolylineDecoratorPlugin.isLoaded()) { $log.error('[AngularJS - Leaflet] The PolylineDecorator Plugin is not loaded.'); } } return L.polylineDecorator(options.coordinates); } /* Updates the path and the patterns for the provided decoration, and returns the decoration. */ function setDecorationOptions(decoration, options) { if (isDefined(decoration) && isDefined(options)) { if (isDefined(options.coordinates) && isDefined(options.patterns)) { decoration.setPaths(options.coordinates); decoration.setPatterns(options.patterns); return decoration; } } } controller.getMap().then(function (map) { leafletScope.$watch("decorations", function (newDecorations) { for (var name in leafletDecorations) { if (!isDefined(newDecorations[name]) || !angular.equals(newDecorations[name], leafletDecorations)) { map.removeLayer(leafletDecorations[name]); delete leafletDecorations[name]; } } for (var newName in newDecorations) { var decorationData = newDecorations[newName], newDecoration = createDecoration(decorationData); if (isDefined(newDecoration)) { leafletDecorations[newName] = newDecoration; map.addLayer(newDecoration); setDecorationOptions(newDecoration, decorationData); } } }, true); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('eventBroadcast', ["leafletLogger", "$rootScope", "leafletHelpers", "leafletMapEvents", "leafletIterators", function (leafletLogger, $rootScope, leafletHelpers, leafletMapEvents, leafletIterators) { var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: 'leaflet', link: function link(scope, element, attrs, controller) { var isObject = leafletHelpers.isObject, isDefined = leafletHelpers.isDefined, leafletScope = controller.getLeafletScope(), eventBroadcast = leafletScope.eventBroadcast, availableMapEvents = leafletMapEvents.getAvailableMapEvents(), addEvents = leafletMapEvents.addEvents; controller.getMap().then(function (map) { var mapEvents = [], logic = "broadcast"; // We have a possible valid object if (!isDefined(eventBroadcast.map)) { // We do not have events enable/disable do we do nothing (all enabled by default) mapEvents = availableMapEvents; } else if (!isObject(eventBroadcast.map)) { // Not a valid object $log.warn("[AngularJS - Leaflet] event-broadcast.map must be an object check your model."); } else { // We have a possible valid map object // Event propadation logic if (eventBroadcast.map.logic !== "emit" && eventBroadcast.map.logic !== "broadcast") { // This is an error $log.warn("[AngularJS - Leaflet] Available event propagation logic are: 'emit' or 'broadcast'."); } else { logic = eventBroadcast.map.logic; } if (!(isObject(eventBroadcast.map.enable) && eventBroadcast.map.enable.length >= 0)) { $log.warn("[AngularJS - Leaflet] event-broadcast.map.enable must be an object check your model."); } else { // Enable events leafletIterators.each(eventBroadcast.map.enable, function (eventName) { // Do we have already the event enabled? if (mapEvents.indexOf(eventName) === -1 && availableMapEvents.indexOf(eventName) !== -1) { mapEvents.push(eventName); } }); } } // as long as the map is removed in the root leaflet directive we // do not need ot clean up the events as leaflet does it itself addEvents(map, attrs.id, mapEvents, "eventName", leafletScope, logic); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('geojson', ["$timeout", "leafletLogger", "leafletData", "leafletHelpers", "leafletWatchHelpers", "leafletDirectiveControlsHelpers", "leafletIterators", "leafletGeoJsonEvents", function ($timeout, leafletLogger, leafletData, leafletHelpers, leafletWatchHelpers, leafletDirectiveControlsHelpers, leafletIterators, leafletGeoJsonEvents) { var _maybeWatch = leafletWatchHelpers.maybeWatch, _defaultWatchOptions = leafletHelpers.watchOptions, _extendDirectiveControls = leafletDirectiveControlsHelpers.extend, hlp = leafletHelpers, $it = leafletIterators, watchTrap = { changeFromDirective: false }; // $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: 'leaflet', link: function link(scope, element, attrs, controller) { var isDefined = leafletHelpers.isDefined, leafletScope = controller.getLeafletScope(), leafletGeoJSON = {}, _hasSetLeafletData = false; controller.getMap().then(function (map) { var watchOptions; if (leafletScope.watchOptions && leafletScope.watchOptions.geojson) { watchOptions = leafletScope.watchOptions.geojson; } else { watchOptions = _defaultWatchOptions; } var _hookUpEvents = function _hookUpEvents(geojson, maybeName) { var onEachFeature; if (angular.isFunction(geojson.onEachFeature)) { onEachFeature = geojson.onEachFeature; } else { onEachFeature = function onEachFeature(feature, layer) { if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(feature.properties.description)) { layer.bindLabel(feature.properties.description); } leafletGeoJsonEvents.bindEvents(attrs.id, layer, null, feature, leafletScope, maybeName, { resetStyleOnMouseout: geojson.resetStyleOnMouseout, mapId: attrs.id }); }; } return onEachFeature; }; var isNested = hlp.isDefined(attrs.geojsonNested) && hlp.isTruthy(attrs.geojsonNested); var _clean = function _clean() { if (!leafletGeoJSON) return; var _remove = function _remove(lObject) { if (isDefined(lObject) && map.hasLayer(lObject)) { map.removeLayer(lObject); } }; if (isNested) { $it.each(leafletGeoJSON, function (lObject) { _remove(lObject); }); return; } _remove(leafletGeoJSON); }; var _addGeojson = function _addGeojson(geojson, maybeName) { if (!(isDefined(geojson) && isDefined(geojson.data))) { return; } var onEachFeature = _hookUpEvents(geojson, maybeName); if (!isDefined(geojson.options)) { hlp.modelChangeInDirective(watchTrap, "changeFromDirective", function () { geojson.options = { style: geojson.style, filter: geojson.filter, onEachFeature: onEachFeature, pointToLayer: geojson.pointToLayer }; }); } var lObject = L.geoJson(geojson.data, geojson.options); if (maybeName && hlp.isString(maybeName)) { leafletGeoJSON[maybeName] = lObject; } else { leafletGeoJSON = lObject; } lObject.addTo(map); if (!_hasSetLeafletData) { //only do this once and play with the same ref forever _hasSetLeafletData = true; leafletData.setGeoJSON(leafletGeoJSON, attrs.id); } }; var _create = function _create(model) { _clean(); if (isNested) { if (!model || !Object.keys(model).length) return; $it.each(model, function (m, name) { //name could be layerName and or groupName //for now it is not tied to a layer _addGeojson(m, name); }); return; } _addGeojson(model); }; _extendDirectiveControls(attrs.id, 'geojson', _create, _clean); _maybeWatch(leafletScope, 'geojson', watchOptions, function (geojson) { if (watchTrap.changeFromDirective) return; _create(geojson); }); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('layercontrol', ["$filter", "leafletLogger", "leafletData", "leafletHelpers", function ($filter, leafletLogger, leafletData, leafletHelpers) { var $log = leafletLogger; return { restrict: "E", scope: { icons: '=?', autoHideOpacity: '=?', // Hide other opacity controls when one is activated. showGroups: '=?', // Hide other opacity controls when one is activated. title: '@', baseTitle: '@', overlaysTitle: '@' }, replace: true, transclude: false, require: '^leaflet', controller: ["$scope", "$element", "$sce", function controller($scope, $element, $sce) { $log.debug('[Angular Directive - Layers] layers', $scope, $element); var safeApply = leafletHelpers.safeApply, isDefined = leafletHelpers.isDefined; angular.extend($scope, { baselayer: '', oldGroup: '', layerProperties: {}, groupProperties: {}, rangeIsSupported: leafletHelpers.rangeIsSupported(), changeBaseLayer: function changeBaseLayer(key, e) { leafletHelpers.safeApply($scope, function (scp) { scp.baselayer = key; leafletData.getMap().then(function (map) { leafletData.getLayers().then(function (leafletLayers) { if (map.hasLayer(leafletLayers.baselayers[key])) { return; } for (var i in scp.layers.baselayers) { scp.layers.baselayers[i].icon = scp.icons.unradio; if (map.hasLayer(leafletLayers.baselayers[i])) { map.removeLayer(leafletLayers.baselayers[i]); } } map.addLayer(leafletLayers.baselayers[key]); scp.layers.baselayers[key].icon = $scope.icons.radio; }); }); }); e.preventDefault(); }, moveLayer: function moveLayer(ly, newIndex, e) { var delta = Object.keys($scope.layers.baselayers).length; if (newIndex >= 1 + delta && newIndex <= $scope.overlaysArray.length + delta) { var oldLy; for (var key in $scope.layers.overlays) { if ($scope.layers.overlays[key].index === newIndex) { oldLy = $scope.layers.overlays[key]; break; } } if (oldLy) { safeApply($scope, function () { oldLy.index = ly.index; ly.index = newIndex; }); } } e.stopPropagation(); e.preventDefault(); }, initIndex: function initIndex(layer, idx) { var delta = Object.keys($scope.layers.baselayers).length; layer.index = isDefined(layer.index) ? layer.index : idx + delta + 1; }, initGroup: function initGroup(groupName) { $scope.groupProperties[groupName] = $scope.groupProperties[groupName] ? $scope.groupProperties[groupName] : {}; }, toggleOpacity: function toggleOpacity(e, layer) { if (layer.visible) { if ($scope.autoHideOpacity && !$scope.layerProperties[layer.name].opacityControl) { for (var k in $scope.layerProperties) { $scope.layerProperties[k].opacityControl = false; } } $scope.layerProperties[layer.name].opacityControl = !$scope.layerProperties[layer.name].opacityControl; } e.stopPropagation(); e.preventDefault(); }, toggleLegend: function toggleLegend(layer) { $scope.layerProperties[layer.name].showLegend = !$scope.layerProperties[layer.name].showLegend; }, showLegend: function showLegend(layer) { return layer.legend && $scope.layerProperties[layer.name].showLegend; }, unsafeHTML: function unsafeHTML(html) { return $sce.trustAsHtml(html); }, getOpacityIcon: function getOpacityIcon(layer) { return layer.visible && $scope.layerProperties[layer.name].opacityControl ? $scope.icons.close : $scope.icons.open; }, getGroupIcon: function getGroupIcon(group) { return group.visible ? $scope.icons.check : $scope.icons.uncheck; }, changeGroupVisibility: function changeGroupVisibility(groupName) { if (!isDefined($scope.groupProperties[groupName])) { return; } var visible = $scope.groupProperties[groupName].visible; for (var k in $scope.layers.overlays) { var layer = $scope.layers.overlays[k]; if (layer.group === groupName) { layer.visible = visible; } } } }); var div = $element.get(0); if (!L.Browser.touch) { L.DomEvent.disableClickPropagation(div); L.DomEvent.on(div, 'mousewheel', L.DomEvent.stopPropagation); } else { L.DomEvent.on(div, 'click', L.DomEvent.stopPropagation); } }], template: '
' + '

{{ title }}

' + '
' + '
{{ baseTitle }}
' + '
' + '' + '
' + '
' + '
' + '
{{ overlaysTitle }}
' + '
' + '
' + '' + '' + '
' + ' ' + ' ' + ' ' + '' + '
' + '
' + '
' + '' + '' + '' + '
Range is not supported in this browser
' + '
' + '
' + '
' + '
' + '
', link: function link(scope, element, attrs, controller) { var isDefined = leafletHelpers.isDefined, leafletScope = controller.getLeafletScope(), layers = leafletScope.layers; scope.$watch('icons', function () { var defaultIcons = { uncheck: 'fa fa-square-o', check: 'fa fa-check-square-o', radio: 'fa fa-dot-circle-o', unradio: 'fa fa-circle-o', up: 'fa fa-angle-up', down: 'fa fa-angle-down', open: 'fa fa-angle-double-down', close: 'fa fa-angle-double-up', toggleLegend: 'fa fa-pencil-square-o' }; if (isDefined(scope.icons)) { angular.extend(defaultIcons, scope.icons); angular.extend(scope.icons, defaultIcons); } else { scope.icons = defaultIcons; } }); // Setting layer stack order. attrs.order = isDefined(attrs.order) && (attrs.order === 'normal' || attrs.order === 'reverse') ? attrs.order : 'normal'; scope.order = attrs.order === 'normal'; scope.orderNumber = attrs.order === 'normal' ? -1 : 1; scope.layers = layers; controller.getMap().then(function (map) { leafletScope.$watch('layers.baselayers', function (newBaseLayers) { var baselayersArray = {}; leafletData.getLayers().then(function (leafletLayers) { var key; for (key in newBaseLayers) { var layer = newBaseLayers[key]; layer.icon = scope.icons[map.hasLayer(leafletLayers.baselayers[key]) ? 'radio' : 'unradio']; baselayersArray[key] = layer; } scope.baselayersArray = baselayersArray; }); }); leafletScope.$watch('layers.overlays', function (newOverlayLayers) { var overlaysArray = []; var groupVisibleCount = {}; leafletData.getLayers().then(function () { var key; for (key in newOverlayLayers) { var layer = newOverlayLayers[key]; layer.icon = scope.icons[layer.visible ? 'check' : 'uncheck']; overlaysArray.push(layer); if (!isDefined(scope.layerProperties[layer.name])) { if (isDefined(layer.layerOptions.opacity)) { layer.layerOptions.opacity = 1; } scope.layerProperties[layer.name] = { opacityControl: false, showLegend: true, layerOptions: layer.layerOptions }; } if (isDefined(layer.group)) { if (!isDefined(scope.groupProperties[layer.group])) { scope.groupProperties[layer.group] = { visible: false }; } groupVisibleCount[layer.group] = isDefined(groupVisibleCount[layer.group]) ? groupVisibleCount[layer.group] : { count: 0, visibles: 0 }; groupVisibleCount[layer.group].count++; if (layer.visible) { groupVisibleCount[layer.group].visibles++; } } /* if(isDefined(layer.index) && leafletLayers.overlays[key].setZIndex) { leafletLayers.overlays[key].setZIndex(newOverlayLayers[key].index); } */ } for (key in groupVisibleCount) { scope.groupProperties[key].visible = groupVisibleCount[key].visibles === groupVisibleCount[key].count; } scope.overlaysArray = overlaysArray; }); }, true); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('layers', ["leafletLogger", "$q", "leafletData", "leafletHelpers", "leafletLayerHelpers", "leafletControlHelpers", function (leafletLogger, $q, leafletData, leafletHelpers, leafletLayerHelpers, leafletControlHelpers) { // var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: 'leaflet', controller: ["$scope", function controller($scope) { $scope._leafletLayers = $q.defer(); this.getLayers = function () { return $scope._leafletLayers.promise; }; }], link: function link(scope, element, attrs, controller) { var isDefined = leafletHelpers.isDefined, leafletLayers = {}, leafletScope = controller.getLeafletScope(), layers = leafletScope.layers, createLayer = leafletLayerHelpers.createLayer, safeAddLayer = leafletLayerHelpers.safeAddLayer, safeRemoveLayer = leafletLayerHelpers.safeRemoveLayer, changeOpacityListener = leafletLayerHelpers.changeOpacityListener, updateLayersControl = leafletControlHelpers.updateLayersControl, isLayersControlVisible = false; scope.$on('$destroy', function () { leafletControlHelpers.destroyMapLayersControl(scope.mapId); }); controller.getMap().then(function (map) { // We have baselayers to add to the map scope._leafletLayers.resolve(leafletLayers); leafletData.setLayers(leafletLayers, attrs.id); leafletLayers.baselayers = {}; leafletLayers.overlays = {}; var mapId = attrs.id; // Setup all baselayers definitions var oneVisibleLayer = false; for (var layerName in layers.baselayers) { var newBaseLayer = createLayer(layers.baselayers[layerName]); if (!isDefined(newBaseLayer)) { delete layers.baselayers[layerName]; continue; } leafletLayers.baselayers[layerName] = newBaseLayer; // Only add the visible layer to the map, layer control manages the addition to the map // of layers in its control if (layers.baselayers[layerName].top === true) { safeAddLayer(map, leafletLayers.baselayers[layerName]); oneVisibleLayer = true; } } // If there is no visible layer add first to the map if (!oneVisibleLayer && Object.keys(leafletLayers.baselayers).length > 0) { safeAddLayer(map, leafletLayers.baselayers[Object.keys(layers.baselayers)[0]]); } // Setup the Overlays for (layerName in layers.overlays) { if (layers.overlays[layerName].type === 'cartodb') {} var newOverlayLayer = createLayer(layers.overlays[layerName]); if (!isDefined(newOverlayLayer)) { delete layers.overlays[layerName]; continue; } leafletLayers.overlays[layerName] = newOverlayLayer; // Only add the visible overlays to the map if (layers.overlays[layerName].visible === true) { safeAddLayer(map, leafletLayers.overlays[layerName]); } } // Watch for the base layers leafletScope.$watch('layers.baselayers', function (newBaseLayers, oldBaseLayers) { if (angular.equals(newBaseLayers, oldBaseLayers)) { isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, newBaseLayers, layers.overlays, leafletLayers); return true; } // Delete layers from the array for (var name in leafletLayers.baselayers) { if (!isDefined(newBaseLayers[name]) || newBaseLayers[name].doRefresh) { // Remove from the map if it's on it if (map.hasLayer(leafletLayers.baselayers[name])) { map.removeLayer(leafletLayers.baselayers[name]); } delete leafletLayers.baselayers[name]; if (newBaseLayers[name] && newBaseLayers[name].doRefresh) { newBaseLayers[name].doRefresh = false; } } } // add new layers for (var newName in newBaseLayers) { if (!isDefined(leafletLayers.baselayers[newName])) { var testBaseLayer = createLayer(newBaseLayers[newName]); if (isDefined(testBaseLayer)) { leafletLayers.baselayers[newName] = testBaseLayer; // Only add the visible layer to the map if (newBaseLayers[newName].top === true) { safeAddLayer(map, leafletLayers.baselayers[newName]); } } } else { if (newBaseLayers[newName].top === true && !map.hasLayer(leafletLayers.baselayers[newName])) { safeAddLayer(map, leafletLayers.baselayers[newName]); } else if (newBaseLayers[newName].top === false && map.hasLayer(leafletLayers.baselayers[newName])) { map.removeLayer(leafletLayers.baselayers[newName]); } } } //we have layers, so we need to make, at least, one active var found = false; // search for an active layer for (var key in leafletLayers.baselayers) { if (map.hasLayer(leafletLayers.baselayers[key])) { found = true; break; } } // If there is no active layer make one active if (!found && Object.keys(leafletLayers.baselayers).length > 0) { safeAddLayer(map, leafletLayers.baselayers[Object.keys(leafletLayers.baselayers)[0]]); } // Only show the layers switch selector control if we have more than one baselayer + overlay isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, newBaseLayers, layers.overlays, leafletLayers); }, true); // Watch for the overlay layers leafletScope.$watch('layers.overlays', function (newOverlayLayers, oldOverlayLayers) { if (angular.equals(newOverlayLayers, oldOverlayLayers)) { isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, layers.baselayers, newOverlayLayers, leafletLayers); return true; } // Delete layers from the array for (var name in leafletLayers.overlays) { if (!isDefined(newOverlayLayers[name]) || newOverlayLayers[name].doRefresh) { // Remove from the map if it's on it if (map.hasLayer(leafletLayers.overlays[name])) { // Safe remove when ArcGIS layers is loading. var options = isDefined(newOverlayLayers[name]) ? newOverlayLayers[name].layerOptions : null; safeRemoveLayer(map, leafletLayers.overlays[name], options); } // TODO: Depending on the layer type we will have to delete what's included on it var oldLayer = oldOverlayLayers[name]; if (isDefined(oldLayer) && oldLayer.hasOwnProperty("cleanupAction")) { oldLayer.cleanupAction(leafletLayers.overlays[name]); } delete leafletLayers.overlays[name]; if (newOverlayLayers[name] && newOverlayLayers[name].doRefresh) { newOverlayLayers[name].doRefresh = false; } } } // add new overlays for (var newName in newOverlayLayers) { if (!isDefined(leafletLayers.overlays[newName])) { var testOverlayLayer = createLayer(newOverlayLayers[newName]); if (!isDefined(testOverlayLayer)) { // If the layer creation fails, continue to the next overlay continue; } leafletLayers.overlays[newName] = testOverlayLayer; if (newOverlayLayers[newName].visible === true) { safeAddLayer(map, leafletLayers.overlays[newName]); } if (isDefined(newOverlayLayers[newName].index) && leafletLayers.overlays[newName].setZIndex) { leafletLayers.overlays[newName].setZIndex(newOverlayLayers[newName].index); } } else { // check for the .visible property to hide/show overLayers if (newOverlayLayers[newName].visible && !map.hasLayer(leafletLayers.overlays[newName])) { safeAddLayer(map, leafletLayers.overlays[newName]); } else if (newOverlayLayers[newName].visible === false && map.hasLayer(leafletLayers.overlays[newName])) { // Safe remove when ArcGIS layers is loading. safeRemoveLayer(map, leafletLayers.overlays[newName], newOverlayLayers[newName].layerOptions); } // check for the .layerOptions.opacity property has changed. var ly = leafletLayers.overlays[newName]; if (map.hasLayer(leafletLayers.overlays[newName])) { if (newOverlayLayers[newName].layerOptions.opacity !== oldOverlayLayers[newName].layerOptions.opacity) { if (isDefined(ly.setOpacity)) { ly.setOpacity(newOverlayLayers[newName].layerOptions.opacity); } if (isDefined(ly.getLayers) && isDefined(ly.eachLayer)) { ly.eachLayer(changeOpacityListener(newOverlayLayers[newName].layerOptions.opacity)); } } if (isDefined(newOverlayLayers[newName].index) && ly.setZIndex && newOverlayLayers[newName].index !== oldOverlayLayers[newName].index) { ly.setZIndex(newOverlayLayers[newName].index); } } } //refresh heatmap data if present if (newOverlayLayers[newName].visible && map._loaded && newOverlayLayers[newName].data && newOverlayLayers[newName].type === "heatmap") { leafletLayers.overlays[newName].setData(newOverlayLayers[newName].data); leafletLayers.overlays[newName].update(); } } // Only add the layers switch selector control if we have more than one baselayer + overlay isLayersControlVisible = updateLayersControl(map, mapId, isLayersControlVisible, layers.baselayers, newOverlayLayers, leafletLayers); }, true); }); } }; }]); 'use strict'; angular.module("ui-leaflet").directive('legend', ["leafletLogger", "$http", "$timeout", "leafletHelpers", "leafletLegendHelpers", function (leafletLogger, $http, $timeout, leafletHelpers, leafletLegendHelpers) { var $log = leafletLogger, errorHeader = leafletHelpers.errorHeader + ' [Legend] '; return { restrict: "A", scope: false, replace: false, require: 'leaflet', transclude: false, link: function link(scope, element, attrs, controller) { var isArray = leafletHelpers.isArray, isString = leafletHelpers.isString, isDefined = leafletHelpers.isDefined, isFunction = leafletHelpers.isFunction, leafletScope = controller.getLeafletScope(), legend = leafletScope.legend; var legendClass; var position; var leafletLegend; var type; leafletScope.$watch('legend', function (newLegend) { if (isDefined(newLegend)) { legendClass = newLegend.legendClass ? newLegend.legendClass : "legend"; position = newLegend.position || 'bottomright'; // default to arcgis type = newLegend.type || 'arcgis'; } }, true); var createLegend = function createLegend(map, legendData, newURL) { if (legendData && legendData.layers && legendData.layers.length > 0) { if (isDefined(leafletLegend)) { leafletLegendHelpers.updateLegend(leafletLegend.getContainer(), legendData, type, newURL); } else { leafletLegend = L.control({ position: position }); leafletLegend.onAdd = leafletLegendHelpers.getOnAddLegend(legendData, legendClass, type, newURL); leafletLegend.addTo(map); } if (isDefined(legend.loadedData) && isFunction(legend.loadedData)) { legend.loadedData(); } } }; controller.getMap().then(function (map) { leafletScope.$watch('legend', function (newLegend) { if (!isDefined(newLegend)) { if (isDefined(leafletLegend)) { leafletLegend.removeFrom(map); leafletLegend = null; } return; } if (!isDefined(newLegend.url) && type === 'arcgis' && (!isArray(newLegend.colors) || !isArray(newLegend.labels) || newLegend.colors.length !== newLegend.labels.length)) { $log.warn(errorHeader + " legend.colors and legend.labels must be set."); return; } if (isDefined(newLegend.url)) { $log.info(errorHeader + " loading legend service."); return; } if (isDefined(leafletLegend)) { leafletLegend.removeFrom(map); leafletLegend = null; } leafletLegend = L.control({ position: position }); if (type === 'arcgis') { leafletLegend.onAdd = leafletLegendHelpers.getOnAddArrayLegend(newLegend, legendClass); } leafletLegend.addTo(map); }); leafletScope.$watch('legend.url', function (newURL) { if (!isDefined(newURL)) { return; } if (!isArray(newURL) && !isString(newURL)) { $log.warn(errorHeader + " legend.url must be an array or string."); return; } var urls = isString(newURL) ? [newURL] : newURL; var legendData; var onResult = function onResult(idx, url) { return function (ld) { if (isDefined(ld.data.error)) { $log.warn(errorHeader + 'Error loadin legend from: ' + url, ld.data.error.message); } else { if (legendData && legendData.layers && legendData.layers.length > 0) { legendData.layers = legendData.layers.concat(ld.data.layers); } else { legendData = ld.data; } } if (idx === urls.length - 1) { createLegend(map, legendData, newURL); } }; }; var onError = function onError(err) { $log.warn(errorHeader + ' legend.url not loaded.', err); }; for (var i = 0; i < urls.length; i++) { leafletLegendHelpers.addLegendURL(attrs.id, { url: urls[i], method: 'GET' }).then(onResult(i)).catch(onError); } }); leafletScope.$watch('legend.legendData', function (legendData) { $log.debug('legendData', legendData); if (isDefined(leafletScope.legend.url) || !isDefined(legendData)) { return; } createLegend(map, legendData); }, true); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('markers', ["leafletLogger", "$rootScope", "$q", "leafletData", "leafletHelpers", "leafletMapDefaults", "leafletMarkersHelpers", "leafletMarkerEvents", "leafletIterators", "leafletWatchHelpers", "leafletDirectiveControlsHelpers", function (leafletLogger, $rootScope, $q, leafletData, leafletHelpers, leafletMapDefaults, leafletMarkersHelpers, leafletMarkerEvents, leafletIterators, leafletWatchHelpers, leafletDirectiveControlsHelpers) { //less terse vars to helpers var isDefined = leafletHelpers.isDefined, errorHeader = leafletHelpers.errorHeader, Helpers = leafletHelpers, isString = leafletHelpers.isString, addMarkerWatcher = leafletMarkersHelpers.addMarkerWatcher, updateMarker = leafletMarkersHelpers.updateMarker, listenMarkerEvents = leafletMarkersHelpers.listenMarkerEvents, addMarkerToGroup = leafletMarkersHelpers.addMarkerToGroup, createMarker = leafletMarkersHelpers.createMarker, deleteMarker = leafletMarkersHelpers.deleteMarker, getModelFromModels = leafletMarkersHelpers.getModelFromModels, getLayerModels = leafletMarkersHelpers.getLayerModels, resetUnusedMarkerGroups = leafletMarkersHelpers.resetUnusedMarkerGroups, $it = leafletIterators, _defaultWatchOptions = leafletHelpers.watchOptions, maybeWatch = leafletWatchHelpers.maybeWatch, extendDirectiveControls = leafletDirectiveControlsHelpers.extend, $log = leafletLogger, watchTrap = { changeFromDirective: false }; var _getLMarker = function _getLMarker(leafletMarkers, name, maybeLayerName) { if (!Object.keys(leafletMarkers).length) return; if (maybeLayerName && isString(maybeLayerName)) { if (!leafletMarkers[maybeLayerName] || !Object.keys(leafletMarkers[maybeLayerName]).length) return; return leafletMarkers[maybeLayerName][name]; } return leafletMarkers[name]; }; var _setLMarker = function _setLMarker(lObject, leafletMarkers, name, maybeLayerName) { if (maybeLayerName && isString(maybeLayerName)) { if (!isDefined(leafletMarkers[maybeLayerName])) leafletMarkers[maybeLayerName] = {}; leafletMarkers[maybeLayerName][name] = lObject; } else leafletMarkers[name] = lObject; return lObject; }; var _maybeAddMarkerToLayer = function _maybeAddMarkerToLayer(layerName, layers, model, marker, watchType, map) { if (!isString(layerName)) { $log.error(errorHeader + ' A layername must be a string'); return false; } if (!isDefined(layers)) { $log.error(errorHeader + ' You must add layers to the directive if the markers are going to use this functionality.'); return false; } if (!isDefined(layers.overlays) || !isDefined(layers.overlays[layerName])) { $log.error(errorHeader + ' A marker can only be added to a layer of type "group"'); return false; } var layerGroup = layers.overlays[layerName]; if (!(layerGroup instanceof L.LayerGroup || layerGroup instanceof L.FeatureGroup)) { $log.error(errorHeader + ' Adding a marker to an overlay needs a overlay of the type "group" or "featureGroup"'); return false; } // The marker goes to a correct layer group, so first of all we add it layerGroup.addLayer(marker); // The marker is automatically added to the map depending on the visibility // of the layer, so we only have to open the popup if the marker is in the map if (watchType === null && map.hasLayer(marker) && model.focus === true) { marker.openPopup(); } return true; }; //TODO: move to leafletMarkersHelpers??? or make a new class/function file (leafletMarkersHelpers is large already) var _addMarkers = function _addMarkers(mapId, markersToRender, oldModels, map, layers, leafletMarkers, leafletScope, watchOptions, maybeLayerName, skips) { $it.each(markersToRender, function (model, newName) { if (skips[newName]) return; if (newName.search("-") !== -1) { $log.error('The marker can\'t use a "-" on his key name: "' + newName + '".'); return; } var pathToMarker = Helpers.getObjectDotPath(maybeLayerName ? [maybeLayerName, newName] : [newName]); var maybeLMarker = _getLMarker(leafletMarkers, newName, maybeLayerName); Helpers.modelChangeInDirective(watchTrap, "changeFromDirective", function () { if (!isDefined(maybeLMarker)) { var marker = createMarker(model); var layerName = (model ? model.layer : undefined) || maybeLayerName; //original way takes pref if (!isDefined(marker)) { $log.error(errorHeader + ' Received invalid data on the marker ' + newName + '.'); return; } _setLMarker(marker, leafletMarkers, newName, maybeLayerName); // Bind message if (isDefined(model.message)) { marker.bindPopup(model.message, model.popupOptions); } // Add the marker to a cluster group if needed if (isDefined(model.group)) { var groupOptions = isDefined(model.groupOption) ? model.groupOption : null; addMarkerToGroup(marker, model.group, groupOptions, map); } // Show label if defined if (Helpers.LabelPlugin.isLoaded() && isDefined(model.label) && isDefined(model.label.message)) { marker.bindLabel(model.label.message, model.label.options); } // Check if the marker should be added to a layer if (isDefined(model) && (isDefined(model.layer) || isDefined(maybeLayerName))) { var pass = _maybeAddMarkerToLayer(layerName, layers, model, marker, watchOptions.individual.type, map); if (!pass) return; //something went wrong move on in the loop } else if (!isDefined(model.group)) { // We do not have a layer attr, so the marker goes to the map layer map.addLayer(marker); if (watchOptions.individual.type === null && model.focus === true) { marker.openPopup(); } } if (watchOptions.individual.type !== null) { addMarkerWatcher(marker, pathToMarker, leafletScope, layers, map, watchOptions.individual); } listenMarkerEvents(marker, model, leafletScope, watchOptions.individual.type, map); leafletMarkerEvents.bindEvents(mapId, marker, pathToMarker, model, leafletScope, layerName); } else { var oldModel = getModelFromModels(oldModels, newName, maybeLayerName); updateMarker(model, oldModel, maybeLMarker, pathToMarker, leafletScope, layers, map); } }); }); }; var _seeWhatWeAlreadyHave = function _seeWhatWeAlreadyHave(markerModels, oldMarkerModels, lMarkers, isEqual, cb) { var hasLogged = false, equals = false, oldMarker, newMarker; var doCheckOldModel = isDefined(oldMarkerModels); for (var name in lMarkers) { if (!hasLogged) { $log.debug(errorHeader + "[markers] destroy: "); hasLogged = true; } if (doCheckOldModel) { //might want to make the option (in watch options) to disable deep checking //ie the options to only check !== (reference check) instead of angular.equals (slow) newMarker = markerModels[name]; oldMarker = oldMarkerModels[name]; equals = isEqual && angular.equals(newMarker, oldMarker); } if (!isDefined(markerModels) || !Object.keys(markerModels).length || !isDefined(markerModels[name]) || !Object.keys(markerModels[name]).length || equals) { if (cb && Helpers.isFunction(cb)) cb(newMarker, oldMarker, name); } } }; var _destroy = function _destroy(markerModels, oldMarkerModels, lMarkers, map, layers) { _seeWhatWeAlreadyHave(markerModels, oldMarkerModels, lMarkers, false, function (newMarker, oldMarker, lMarkerName) { $log.debug(errorHeader + '[marker] is deleting marker: ' + lMarkerName); deleteMarker(lMarkers[lMarkerName], map, layers); delete lMarkers[lMarkerName]; }); }; var _getNewModelsToSkipp = function _getNewModelsToSkipp(newModels, oldModels, lMarkers) { var skips = {}; _seeWhatWeAlreadyHave(newModels, oldModels, lMarkers, true, function (newMarker, oldMarker, lMarkerName) { $log.debug(errorHeader + '[marker] is already rendered, marker: ' + lMarkerName); skips[lMarkerName] = newMarker; }); return skips; }; return { restrict: "A", scope: false, replace: false, require: ['leaflet', '?layers'], link: function link(scope, element, attrs, controller) { var mapController = controller[0], leafletScope = mapController.getLeafletScope(); mapController.getMap().then(function (map) { var leafletMarkers = {}, getLayers; // If the layers attribute is used, we must wait until the layers are created if (isDefined(controller[1])) { getLayers = controller[1].getLayers; } else { getLayers = function getLayers() { var deferred = $q.defer(); deferred.resolve(); return deferred.promise; }; } var watchOptions; if (leafletScope.watchOptions && leafletScope.watchOptions.markers) { watchOptions = leafletScope.watchOptions.markers; } else { watchOptions = _defaultWatchOptions; } var isNested = isDefined(attrs.markersNested) && Helpers.isTruthy(attrs.markersNested); getLayers().then(function (layers) { var _clean = function _clean(models, oldModels) { resetUnusedMarkerGroups(); if (isNested) { $it.each(models, function (markerToMaybeDel, layerName) { var oldLayerModels = getLayerModels(oldModels, layerName); _destroy(markerToMaybeDel, oldLayerModels, leafletMarkers[layerName], map, layers); }); return; } _destroy(models, oldModels, leafletMarkers, map, layers); }; var _create = function _create(models, oldModels) { _clean(models, oldModels); var skips = null; if (isNested) { $it.each(models, function (markersToAdd, layerName) { var oldLayerModels = getLayerModels(oldModels, layerName); var newlayerModels = getLayerModels(models, layerName); skips = _getNewModelsToSkipp(newlayerModels, oldLayerModels, leafletMarkers[layerName]); _addMarkers(attrs.id, markersToAdd, oldModels, map, layers, leafletMarkers, leafletScope, watchOptions, layerName, skips); }); return; } skips = _getNewModelsToSkipp(models, oldModels, leafletMarkers); _addMarkers(attrs.id, models, oldModels, map, layers, leafletMarkers, leafletScope, watchOptions, undefined, skips); }; extendDirectiveControls(attrs.id, 'markers', _create, _clean); leafletData.setMarkers(leafletMarkers, attrs.id); maybeWatch(leafletScope, 'markers', watchOptions, function (newMarkers, oldMarkers) { if (watchTrap.changeFromDirective) return; _create(newMarkers, oldMarkers); }); scope.$on('$destroy', function () { _destroy(leafletScope.markers, {}, leafletMarkers, map, layers); }); }); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('maxbounds', ["leafletLogger", "leafletMapDefaults", "leafletBoundsHelpers", "leafletHelpers", function (leafletLogger, leafletMapDefaults, leafletBoundsHelpers, leafletHelpers) { // var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: 'leaflet', link: function link(scope, element, attrs, controller) { var leafletScope = controller.getLeafletScope(), isValidBounds = leafletBoundsHelpers.isValidBounds, isNumber = leafletHelpers.isNumber; controller.getMap().then(function (map) { leafletScope.$watch("maxbounds", function (maxbounds) { if (!isValidBounds(maxbounds)) { // Unset any previous maxbounds map.setMaxBounds(); return; } var leafletBounds = leafletBoundsHelpers.createLeafletBounds(maxbounds); if (isNumber(maxbounds.pad)) { leafletBounds = leafletBounds.pad(maxbounds.pad); } map.setMaxBounds(leafletBounds); if (!attrs.center && !attrs.lfCenter) { map.fitBounds(leafletBounds); } }); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('paths', ["leafletLogger", "$q", "leafletData", "leafletMapDefaults", "leafletHelpers", "leafletPathsHelpers", "leafletPathEvents", "leafletWatchHelpers", function (leafletLogger, $q, leafletData, leafletMapDefaults, leafletHelpers, leafletPathsHelpers, leafletPathEvents, leafletWatchHelpers) { var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: ['leaflet', '?layers'], link: function link(scope, element, attrs, controller) { var mapController = controller[0], isDefined = leafletHelpers.isDefined, isString = leafletHelpers.isString, leafletScope = mapController.getLeafletScope(), paths = leafletScope.paths, createPath = leafletPathsHelpers.createPath, bindPathEvents = leafletPathEvents.bindPathEvents, setPathOptions = leafletPathsHelpers.setPathOptions, maybeWatch = leafletWatchHelpers.maybeWatch; mapController.getMap().then(function (map) { var defaults = leafletMapDefaults.getDefaults(attrs.id), getLayers; // If the layers attribute is used, we must wait until the layers are created if (isDefined(controller[1])) { getLayers = controller[1].getLayers; } else { getLayers = function getLayers() { var deferred = $q.defer(); deferred.resolve(); return deferred.promise; }; } if (!isDefined(paths)) { return; } //legacy behaviour does a watch collection on the paths var _legacyWatchOptions = { type: 'watchCollection', individual: { type: 'watchDeep' } }; var watchOptions; if (leafletScope.watchOptions && leafletScope.watchOptions.paths) { watchOptions = leafletScope.watchOptions.paths; } else { watchOptions = _legacyWatchOptions; } getLayers().then(function (layers) { var leafletPaths = {}; leafletData.setPaths(leafletPaths, attrs.id); // Function for listening every single path once created var watchPathFn = function watchPathFn(leafletPath, name, watchOptions) { var pathWatchPath = "paths[\"" + name + "\"]"; maybeWatch(leafletScope, pathWatchPath, watchOptions, function (pathData, old, clearWatch) { if (!isDefined(pathData)) { if (isDefined(old.layer)) { for (var i in layers.overlays) { var overlay = layers.overlays[i]; overlay.removeLayer(leafletPath); } } map.removeLayer(leafletPath); clearWatch(); return; } setPathOptions(leafletPath, pathData.type, pathData); }); }; var _clean = function _clean(newPaths) { // Delete paths (by name) from the array for (var name in leafletPaths) { if (!isDefined(newPaths[name])) { map.removeLayer(leafletPaths[name]); delete leafletPaths[name]; } } }; var _create = function _create(newPaths) { _clean(newPaths); // Create the new paths for (var newName in newPaths) { if (newName.search('\\$') === 0) { continue; } if (newName.search("-") !== -1) { $log.error('[AngularJS - Leaflet] The path name "' + newName + '" is not valid. It must not include "-" and a number.'); continue; } if (!isDefined(leafletPaths[newName])) { var pathData = newPaths[newName]; var newPath = createPath(newName, newPaths[newName], defaults); // bind popup if defined if (isDefined(newPath) && isDefined(pathData.message)) { newPath.bindPopup(pathData.message, pathData.popupOptions); } // Show label if defined if (leafletHelpers.LabelPlugin.isLoaded() && isDefined(pathData.label) && isDefined(pathData.label.message)) { newPath.bindLabel(pathData.label.message, pathData.label.options); } // Check if the marker should be added to a layer if (isDefined(pathData) && isDefined(pathData.layer)) { if (!isString(pathData.layer)) { $log.error('[AngularJS - Leaflet] A layername must be a string'); continue; } if (!isDefined(layers)) { $log.error('[AngularJS - Leaflet] You must add layers to the directive if the markers are going to use this functionality.'); continue; } if (!isDefined(layers.overlays) || !isDefined(layers.overlays[pathData.layer])) { $log.error('[AngularJS - Leaflet] A path can only be added to a layer of type "group"'); continue; } var layerGroup = layers.overlays[pathData.layer]; if (!(layerGroup instanceof L.LayerGroup || layerGroup instanceof L.FeatureGroup)) { $log.error('[AngularJS - Leaflet] Adding a path to an overlay needs a overlay of the type "group" or "featureGroup"'); continue; } // Listen for changes on the new path leafletPaths[newName] = newPath; // The path goes to a correct layer group, so first of all we add it layerGroup.addLayer(newPath); if (watchOptions.individual.type !== null) { watchPathFn(newPath, newName, watchOptions.individual); } else { setPathOptions(newPath, pathData.type, pathData); } } else if (isDefined(newPath)) { // Listen for changes on the new path leafletPaths[newName] = newPath; map.addLayer(newPath); if (watchOptions.individual.type !== null) { watchPathFn(newPath, newName, watchOptions.individual); } else { setPathOptions(newPath, pathData.type, pathData); } } bindPathEvents(attrs.id, newPath, newName, pathData, leafletScope); } } }; maybeWatch(leafletScope, 'paths', watchOptions, function (newPaths) { _create(newPaths); }); }); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('tiles', ["leafletLogger", "leafletData", "leafletMapDefaults", "leafletHelpers", function (leafletLogger, leafletData, leafletMapDefaults, leafletHelpers) { var $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: 'leaflet', link: function link(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); }); } }; }]); 'use strict'; angular.module('ui-leaflet').directive('watchOptions', ['$log', '$rootScope', '$q', 'leafletData', 'leafletHelpers', function (leafletLogger, $rootScope, $q, leafletData, leafletHelpers) { var isDefined = leafletHelpers.isDefined, errorHeader = leafletHelpers.errorHeader, isObject = leafletHelpers.isObject, $log = leafletLogger; return { restrict: "A", scope: false, replace: false, require: ['leaflet'], link: function link(scope, element, attrs, controller) { var mapController = controller[0], leafletScope = mapController.getLeafletScope(); var _isValidWatchType = function _isValidWatchType(type) { return type === 'watch' || type === 'watchCollection' || type === 'watchDeep' || type === null; }; if (isDefined(leafletScope.watchOptions) && isObject(leafletScope.watchOptions)) { angular.forEach(['markers', 'geojson', 'paths'], function (name) { if (isDefined(leafletScope.watchOptions[name])) { if (!_isValidWatchType(leafletScope.watchOptions[name].type)) { $log.error(errorHeader + ' watchOptions.' + name + '.type is not a valid type.'); } if (isDefined(leafletScope.watchOptions[name].individual)) { if (!_isValidWatchType(leafletScope.watchOptions[name].individual.type)) { $log.error(errorHeader + ' watchOptions.' + name + '.individual.type is not a valid type.'); } } else { $log.error(errorHeader + ' watchOptions.' + name + '.type.individual must be defined.'); } } }); } } }; }]); 'use strict'; angular.module('ui-leaflet').factory('leafletEventsHelpersFactory', ["$rootScope", "$q", "leafletLogger", "leafletHelpers", function ($rootScope, $q, leafletLogger, leafletHelpers) { var safeApply = leafletHelpers.safeApply, isDefined = leafletHelpers.isDefined, isObject = leafletHelpers.isObject, isArray = leafletHelpers.isArray, errorHeader = leafletHelpers.errorHeader, $log = leafletLogger; var EventsHelper = function EventsHelper(rootBroadcastName, lObjectType) { this.rootBroadcastName = rootBroadcastName; $log.debug("leafletEventsHelpersFactory: lObjectType: " + lObjectType + "rootBroadcastName: " + rootBroadcastName); //used to path/key out certain properties based on the type , "markers", "geojson" this.lObjectType = lObjectType; }; EventsHelper.prototype.getAvailableEvents = function () { return []; }; /* argument: name: Note this can be a single string or dot notation Example: markerModel : { m1: { lat:_, lon: _} } //would yield name of name = "m1" If nested: markerModel : { cars: { m1: { lat:_, lon: _} } } //would yield name of name = "cars.m1" */ EventsHelper.prototype.genDispatchEvent = function (maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName, extra) { var _this = this; maybeMapId = maybeMapId || ''; if (maybeMapId) maybeMapId = '.' + maybeMapId; return function (e) { var broadcastName = _this.rootBroadcastName + maybeMapId + '.' + eventName; $log.debug(broadcastName); _this.fire(leafletScope, broadcastName, logic, e, e.target || lObject, model, name, layerName, extra); }; }; EventsHelper.prototype.fire = function (scope, broadcastName, logic, event, lObject, model, modelName, layerName, extra) { // Safely broadcast the event safeApply(scope, function () { var toSend = { leafletEvent: event, leafletObject: lObject, modelName: modelName, model: model }; if (isDefined(layerName)) angular.extend(toSend, { layerName: layerName }); if (logic === "emit") { scope.$emit(broadcastName, toSend); } else { $rootScope.$broadcast(broadcastName, toSend); } }); }; EventsHelper.prototype.bindEvents = function (maybeMapId, lObject, name, model, leafletScope, layerName, extra) { var events = []; var logic = 'emit'; var _this = this; if (!isDefined(leafletScope.eventBroadcast)) { // Backward compatibility, if no event-broadcast attribute, all events are broadcasted events = this.getAvailableEvents(); } else if (!isObject(leafletScope.eventBroadcast)) { // Not a valid object $log.error(errorHeader + "event-broadcast must be an object check your model."); } else { // We have a possible valid object if (!isDefined(leafletScope.eventBroadcast[_this.lObjectType])) { // We do not have events enable/disable do we do nothing (all enabled by default) events = this.getAvailableEvents(); } else if (!isObject(leafletScope.eventBroadcast[_this.lObjectType])) { // Not a valid object $log.warn(errorHeader + 'event-broadcast.' + [_this.lObjectType] + ' must be an object check your model.'); } else { // We have a possible valid map object // Event propadation logic if (isDefined(leafletScope.eventBroadcast[this.lObjectType].logic)) { // We take care of possible propagation logic var configuredLogic = leafletScope.eventBroadcast[_this.lObjectType].logic; if (configuredLogic !== "emit" && configuredLogic !== "broadcast") { $log.warn(errorHeader + "Available event propagation logic are: 'emit' or 'broadcast'."); } else { logic = configuredLogic; } } // Enable / Disable var eventsEnable = false, eventsDisable = false; if (isDefined(leafletScope.eventBroadcast[_this.lObjectType].enable) && isArray(leafletScope.eventBroadcast[_this.lObjectType].enable)) eventsEnable = true; if (isDefined(leafletScope.eventBroadcast[_this.lObjectType].disable) && isArray(leafletScope.eventBroadcast[_this.lObjectType].disable)) eventsDisable = true; if (eventsEnable && eventsDisable) { // Both are active, this is an error $log.warn(errorHeader + "can not enable and disable events at the same time"); } else if (!eventsEnable && !eventsDisable) { // Both are inactive, this is an error $log.warn(errorHeader + "must enable or disable events"); } else { // At this point the object is OK, lets enable or disable events if (eventsEnable) { // Enable events leafletScope.eventBroadcast[this.lObjectType].enable.forEach(function (eventName) { // Do we have already the event enabled? if (events.indexOf(eventName) !== -1) { // Repeated event, this is an error $log.warn(errorHeader + "This event " + eventName + " is already enabled"); } else { // Does the event exists? if (_this.getAvailableEvents().indexOf(eventName) === -1) { // The event does not exists, this is an error $log.warn(errorHeader + "This event " + eventName + " does not exist"); } else { // All ok enable the event events.push(eventName); } } }); } else { // Disable events events = this.getAvailableEvents(); leafletScope.eventBroadcast[_this.lObjectType].disable.forEach(function (eventName) { var index = events.indexOf(eventName); if (index === -1) { // The event does not exist $log.warn(errorHeader + "This event " + eventName + " does not exist or has been already disabled"); } else { events.splice(index, 1); } }); } } } } events.forEach(function (eventName) { lObject.on(eventName, _this.genDispatchEvent(maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName, extra)); }); return logic; }; return EventsHelper; }]).service('leafletEventsHelpers', ["leafletEventsHelpersFactory", function (leafletEventsHelpersFactory) { return new leafletEventsHelpersFactory(); }]); 'use strict'; angular.module('ui-leaflet').factory('leafletGeoJsonEvents', ["$rootScope", "$q", "leafletLogger", "leafletHelpers", "leafletEventsHelpersFactory", "leafletData", function ($rootScope, $q, leafletLogger, leafletHelpers, leafletEventsHelpersFactory, leafletData) { var safeApply = leafletHelpers.safeApply, EventsHelper = leafletEventsHelpersFactory; // $log = leafletLogger; var GeoJsonEvents = function GeoJsonEvents() { EventsHelper.call(this, 'leafletDirectiveGeoJson', 'geojson'); }; GeoJsonEvents.prototype = new EventsHelper(); GeoJsonEvents.prototype.genDispatchEvent = function (maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName, extra) { var base = EventsHelper.prototype.genDispatchEvent.call(this, maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName), _this = this; return function (e) { if (eventName === 'mouseout') { if (extra.resetStyleOnMouseout) { leafletData.getGeoJSON(extra.mapId).then(function (leafletGeoJSON) { //this is broken on nested needs to traverse or user layerName (nested) var lobj = layerName ? leafletGeoJSON[layerName] : leafletGeoJSON; lobj.resetStyle(e.target); }); } safeApply(leafletScope, function () { $rootScope.$broadcast(_this.rootBroadcastName + '.mouseout', e); }); } base(e); //common }; }; GeoJsonEvents.prototype.getAvailableEvents = function () { return ['click', 'dblclick', 'mouseover', 'mouseout']; }; return new GeoJsonEvents(); }]); 'use strict'; angular.module('ui-leaflet').factory('leafletLabelEvents', ["$rootScope", "$q", "leafletLogger", "leafletHelpers", "leafletEventsHelpersFactory", function ($rootScope, $q, leafletLogger, leafletHelpers, leafletEventsHelpersFactory) { var Helpers = leafletHelpers, EventsHelper = leafletEventsHelpersFactory; //$log = leafletLogger; var LabelEvents = function LabelEvents() { EventsHelper.call(this, 'leafletDirectiveLabel', 'markers'); }; LabelEvents.prototype = new EventsHelper(); LabelEvents.prototype.genDispatchEvent = function (maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) { var markerName = name.replace('markers.', ''); return EventsHelper.prototype.genDispatchEvent.call(this, maybeMapId, eventName, logic, leafletScope, lObject, markerName, model, layerName); }; LabelEvents.prototype.getAvailableEvents = function () { return ['click', 'dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; }; LabelEvents.prototype.genEvents = function (maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) { var _this = this; var labelEvents = this.getAvailableEvents(); var scopeWatchName = Helpers.getObjectArrayPath("markers." + name); labelEvents.forEach(function (eventName) { lObject.label.on(eventName, _this.genDispatchEvent(maybeMapId, eventName, logic, leafletScope, lObject.label, scopeWatchName, model, layerName)); }); }; LabelEvents.prototype.bindEvents = function (maybeMapId, lObject, name, model, leafletScope, layerName) {}; return new LabelEvents(); }]); 'use strict'; angular.module('ui-leaflet').factory('leafletMapEvents', ["$rootScope", "$q", "leafletLogger", "leafletHelpers", "leafletEventsHelpers", "leafletIterators", function ($rootScope, $q, leafletLogger, leafletHelpers, leafletEventsHelpers, leafletIterators) { var isDefined = leafletHelpers.isDefined, fire = leafletEventsHelpers.fire; var _getAvailableMapEvents = function _getAvailableMapEvents() { return ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'contextmenu', 'focus', 'blur', 'preclick', 'load', 'unload', 'viewreset', 'movestart', 'move', 'moveend', 'dragstart', 'drag', 'dragend', 'zoomstart', 'zoomanim', 'zoomend', 'zoomlevelschange', 'resize', 'autopanstart', 'layeradd', 'layerremove', 'baselayerchange', 'overlayadd', 'overlayremove', 'locationfound', 'locationerror', 'popupopen', 'popupclose', 'draw:created', 'draw:edited', 'draw:deleted', 'draw:drawstart', 'draw:drawstop', 'draw:editstart', 'draw:editstop', 'draw:deletestart', 'draw:deletestop']; }; var _genDispatchMapEvent = function _genDispatchMapEvent(scope, eventName, logic, maybeMapId) { if (maybeMapId) { maybeMapId = maybeMapId + '.'; } return function (e) { // Put together broadcast name var broadcastName = 'leafletDirectiveMap.' + maybeMapId + eventName; leafletLogger.debug(broadcastName); // Safely broadcast the event fire(scope, broadcastName, logic, e, e.target, scope); }; }; var _notifyCenterChangedToBounds = function _notifyCenterChangedToBounds(scope) { scope.$broadcast("boundsChanged"); }; var _notifyCenterUrlHashChanged = function _notifyCenterUrlHashChanged(scope, map, attrs, search) { if (!isDefined(attrs.urlHashCenter)) { return; } var center = map.getCenter(); var centerUrlHash = center.lat.toFixed(4) + ":" + center.lng.toFixed(4) + ":" + map.getZoom(); if (!isDefined(search.c) || search.c !== centerUrlHash) { //$log.debug("notified new center..."); scope.$emit("centerUrlHash", centerUrlHash); } }; var _addEvents = function _addEvents(map, mapId, mapEvents, contextName, scope, logic) { leafletIterators.each(mapEvents, function (eventName) { var context = {}; context[contextName] = eventName; if (!mapId) { mapId = map._container.id || ''; } map.on(eventName, _genDispatchMapEvent(scope, eventName, logic, mapId), context); }); }; return { getAvailableMapEvents: _getAvailableMapEvents, genDispatchMapEvent: _genDispatchMapEvent, notifyCenterChangedToBounds: _notifyCenterChangedToBounds, notifyCenterUrlHashChanged: _notifyCenterUrlHashChanged, addEvents: _addEvents }; }]); 'use strict'; angular.module('ui-leaflet').factory('leafletMarkerEvents', ["$rootScope", "$q", "leafletLogger", "leafletHelpers", "leafletEventsHelpersFactory", "leafletLabelEvents", function ($rootScope, $q, leafletLogger, leafletHelpers, leafletEventsHelpersFactory, leafletLabelEvents) { var safeApply = leafletHelpers.safeApply, isDefined = leafletHelpers.isDefined, Helpers = leafletHelpers, lblHelp = leafletLabelEvents, EventsHelper = leafletEventsHelpersFactory, $log = leafletLogger; var MarkerEvents = function MarkerEvents() { EventsHelper.call(this, 'leafletDirectiveMarker', 'markers'); }; MarkerEvents.prototype = new EventsHelper(); MarkerEvents.prototype.genDispatchEvent = function (maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) { var handle = EventsHelper.prototype.genDispatchEvent.call(this, maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName); return function (e) { // Broadcast old marker click name for backwards compatibility if (eventName === "click") { safeApply(leafletScope, function () { $rootScope.$broadcast('leafletDirectiveMarkersClick', name); }); } else if (eventName === 'dragend') { safeApply(leafletScope, function () { model.lat = lObject.getLatLng().lat; model.lng = lObject.getLatLng().lng; }); if (model.message && model.focus === true) { lObject.openPopup(); } } handle(e); //common }; }; MarkerEvents.prototype.getAvailableEvents = function () { return ['click', 'dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu', 'dragstart', 'drag', 'dragend', 'move', 'remove', 'popupopen', 'popupclose', 'touchend', 'touchstart', 'touchmove', 'touchcancel', 'touchleave']; }; MarkerEvents.prototype.bindEvents = function (maybeMapId, lObject, name, model, leafletScope, layerName) { var logic = EventsHelper.prototype.bindEvents.call(this, maybeMapId, lObject, name, model, leafletScope, layerName); if (Helpers.LabelPlugin.isLoaded() && isDefined(lObject.label)) { lblHelp.genEvents(maybeMapId, name, logic, leafletScope, lObject, model, layerName); } }; return new MarkerEvents(); }]); 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; angular.module('ui-leaflet').factory('leafletPathEvents', ["$rootScope", "$q", "leafletLogger", "leafletHelpers", "leafletLabelEvents", "leafletEventsHelpers", function ($rootScope, $q, leafletLogger, leafletHelpers, leafletLabelEvents, leafletEventsHelpers) { var isDefined = leafletHelpers.isDefined, isObject = leafletHelpers.isObject, Helpers = leafletHelpers, errorHeader = leafletHelpers.errorHeader, lblHelp = leafletLabelEvents, fire = leafletEventsHelpers.fire, $log = leafletLogger; /* TODO (nmccready) This EventsHelper needs to be derrived from leafletEventsHelpers to elminate copy and paste code. */ var _genDispatchPathEvent = function _genDispatchPathEvent(maybeMapId, eventName, logic, leafletScope, lObject, name, model, layerName) { maybeMapId = maybeMapId || ''; if (maybeMapId) maybeMapId = '.' + maybeMapId; return function (e) { var broadcastName = 'leafletDirectivePath' + maybeMapId + '.' + eventName; $log.debug(broadcastName); fire(leafletScope, broadcastName, logic, e, e.target || lObject, model, name, layerName); }; }; var _bindPathEvents = function _bindPathEvents(maybeMapId, lObject, name, model, leafletScope) { var pathEvents = [], i, eventName, logic = "broadcast"; if (!isDefined(leafletScope.eventBroadcast)) { // Backward compatibility, if no event-broadcast attribute, all events are broadcasted pathEvents = _getAvailablePathEvents(); } else if (!isObject(leafletScope.eventBroadcast)) { // Not a valid object $log.error(errorHeader + "event-broadcast must be an object check your model."); } else { // We have a possible valid object if (!isDefined(leafletScope.eventBroadcast.path)) { // We do not have events enable/disable do we do nothing (all enabled by default) pathEvents = _getAvailablePathEvents(); } else if (isObject(leafletScope.eventBroadcast.paths)) { // Not a valid object $log.warn(errorHeader + "event-broadcast.path must be an object check your model."); } else { // We have a possible valid map object // Event propadation logic if (leafletScope.eventBroadcast.path.logic !== undefined && leafletScope.eventBroadcast.path.logic !== null) { // We take care of possible propagation logic if (leafletScope.eventBroadcast.path.logic !== "emit" && leafletScope.eventBroadcast.path.logic !== "broadcast") { // This is an error $log.warn(errorHeader + "Available event propagation logic are: 'emit' or 'broadcast'."); } else if (leafletScope.eventBroadcast.path.logic === "emit") { logic = "emit"; } } // Enable / Disable var pathEventsEnable = false, pathEventsDisable = false; if (leafletScope.eventBroadcast.path.enable !== undefined && leafletScope.eventBroadcast.path.enable !== null) { if (_typeof(leafletScope.eventBroadcast.path.enable) === 'object') { pathEventsEnable = true; } } if (leafletScope.eventBroadcast.path.disable !== undefined && leafletScope.eventBroadcast.path.disable !== null) { if (_typeof(leafletScope.eventBroadcast.path.disable) === 'object') { pathEventsDisable = true; } } if (pathEventsEnable && pathEventsDisable) { // Both are active, this is an error $log.warn(errorHeader + "can not enable and disable events at the same time"); } else if (!pathEventsEnable && !pathEventsDisable) { // Both are inactive, this is an error $log.warn(errorHeader + "must enable or disable events"); } else { // At this point the path object is OK, lets enable or disable events if (pathEventsEnable) { // Enable events for (i = 0; i < leafletScope.eventBroadcast.path.enable.length; i++) { eventName = leafletScope.eventBroadcast.path.enable[i]; // Do we have already the event enabled? if (pathEvents.indexOf(eventName) !== -1) { // Repeated event, this is an error $log.warn(errorHeader + "This event " + eventName + " is already enabled"); } else { // Does the event exists? if (_getAvailablePathEvents().indexOf(eventName) === -1) { // The event does not exists, this is an error $log.warn(errorHeader + "This event " + eventName + " does not exist"); } else { // All ok enable the event pathEvents.push(eventName); } } } } else { // Disable events pathEvents = _getAvailablePathEvents(); for (i = 0; i < leafletScope.eventBroadcast.path.disable.length; i++) { eventName = leafletScope.eventBroadcast.path.disable[i]; var index = pathEvents.indexOf(eventName); if (index === -1) { // The event does not exist $log.warn(errorHeader + "This event " + eventName + " does not exist or has been already disabled"); } else { pathEvents.splice(index, 1); } } } } } } for (i = 0; i < pathEvents.length; i++) { eventName = pathEvents[i]; lObject.on(eventName, _genDispatchPathEvent(maybeMapId, eventName, logic, leafletScope, pathEvents, name)); } if (Helpers.LabelPlugin.isLoaded() && isDefined(lObject.label)) { lblHelp.genEvents(maybeMapId, name, logic, leafletScope, lObject, model); } }; var _getAvailablePathEvents = function _getAvailablePathEvents() { return ['click', 'dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu', 'add', 'remove', 'popupopen', 'popupclose']; }; return { getAvailablePathEvents: _getAvailablePathEvents, bindPathEvents: _bindPathEvents }; }]); }(angular));