UNPKG

mohsen-angular-leaflet-directive

Version:

angular-leaflet-directive - An AngularJS directive to easily interact with Leaflet maps

1,834 lines (1,592 loc) 199 kB
/**! * The MIT License * * Copyright (c) 2013 the angular-leaflet-directive Team, http://tombatossals.github.io/angular-leaflet-directive * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * angular-leaflet-directive * https://github.com/tombatossals/angular-leaflet-directive * * @authors https://github.com/tombatossals/angular-leaflet-directive/graphs/contributors */ /*! * angular-leaflet-directive 0.10.1 2015-11-13 * angular-leaflet-directive - An AngularJS directive to easily interact with Leaflet maps * git: https://github.com/tombatossals/angular-leaflet-directive */ (function(angular){ 'use strict'; angular.module('leaflet-directive', []).directive('leaflet', ["$q", "leafletData", "leafletMapDefaults", "leafletHelpers", "leafletMapEvents", function($q, leafletData, leafletMapDefaults, leafletHelpers, leafletMapEvents) { return { restrict: 'EA', replace: true, scope: { center: '=lfCenter', defaults: '=lfDefaults', maxbounds: '=lfMaxbounds', bounds: '=lfBounds', markers: '=lfMarkers', geojson: '=lfGeojson', paths: '=lfPaths', tiles: '=lfTiles', layers: '=lfLayers', controls: '=lfControls', decorations: '=lfDecorations', events: '=lfEvents', markersWatchOptions: '=', geojsonWatchOptions: '=', }, transclude: true, template: '<div class="angular-leaflet-map"><div ng-transclude></div></div>', controller: ["$scope", function($scope) { this._leafletMap = $q.defer(); this.getMap = function() { return this._leafletMap.promise; }; this.getLeafletScope = function() { return $scope; }; }], link: function(scope, element, attrs, ctrl) { var isDefined = leafletHelpers.isDefined; var defaults = leafletMapDefaults.setDefaults(scope.defaults, attrs.id); var mapEvents = leafletMapEvents.getAvailableMapEvents(); var addEvents = leafletMapEvents.addEvents; scope.mapId = attrs.id; leafletData.setDirectiveControls({}, attrs.id); // Set width and height utility functions function updateWidth() { if (isNaN(attrs.width)) { element.css('width', attrs.width); } else { element.css('width', attrs.width + 'px'); } } function updateHeight() { if (isNaN(attrs.height)) { element.css('height', attrs.height); } else { element.css('height', attrs.height + 'px'); } } // If the width attribute defined update css // Then watch if bound property changes and update css if (isDefined(attrs.width)) { updateWidth(); scope.$watch( function() { return element[0].getAttribute('width'); }, function() { updateWidth(); map.invalidateSize(); }); } // If the height attribute defined update css // Then watch if bound property changes and update css if (isDefined(attrs.height)) { updateHeight(); scope.$watch( function() { return element[0].getAttribute('height'); }, function() { updateHeight(); map.invalidateSize(); }); } // Create the Leaflet Map Object with the options var map = new L.Map(element[0], leafletMapDefaults.getMapCreationDefaults(attrs.id)); ctrl._leafletMap.resolve(map); if (!isDefined(attrs.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.lfTiles) && (!isDefined(attrs.lfLayers))) { 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.lfEvents)) { var logic = 'broadcast'; addEvents(map, mapEvents, 'eventName', scope, logic); } // Resolve the map object to the promises map.whenReady(function() { leafletData.setMap(map, attrs.id); }); scope.$on('$destroy', function() { leafletMapDefaults.reset(); map.remove(); leafletData.unresolveMap(attrs.id); }); //Handle request to invalidate the map size //Up scope using $scope.$emit('invalidateSize') //Down scope using $scope.$broadcast('invalidateSize') scope.$on('invalidateSize', function() { map.invalidateSize(); }); }, }; }]); angular.module('leaflet-directive').factory('leafletBoundsHelpers', ["leafletLogger", "leafletHelpers", function(leafletLogger, leafletHelpers) { var isArray = leafletHelpers.isArray; var isNumber = leafletHelpers.isNumber; var isFunction = leafletHelpers.isFunction; var isDefined = leafletHelpers.isDefined; function _isValidBounds(bounds) { return angular.isDefined(bounds) && angular.isDefined(bounds.southWest) && angular.isDefined(bounds.northEast) && angular.isNumber(bounds.southWest.lat) && angular.isNumber(bounds.southWest.lng) && angular.isNumber(bounds.northEast.lat) && angular.isNumber(bounds.northEast.lng); } return { createLeafletBounds: function(bounds) { if (_isValidBounds(bounds)) { return L.latLngBounds([bounds.southWest.lat, bounds.southWest.lng], [bounds.northEast.lat, bounds.northEast.lng]); } }, isValidBounds: _isValidBounds, createBoundsFromArray: function(boundsArray) { if (!(isArray(boundsArray) && boundsArray.length === 2 && isArray(boundsArray[0]) && isArray(boundsArray[1]) && boundsArray[0].length === 2 && boundsArray[1].length === 2 && isNumber(boundsArray[0][0]) && isNumber(boundsArray[0][1]) && isNumber(boundsArray[1][0]) && isNumber(boundsArray[1][1]))) { leafletLogger.error('The bounds array is not valid.'); return; } return { northEast: { lat: boundsArray[0][0], lng: boundsArray[0][1], }, southWest: { lat: boundsArray[1][0], lng: boundsArray[1][1], }, }; }, createBoundsFromLeaflet: function(lfBounds) { if (!(isDefined(lfBounds) && isFunction(lfBounds.getNorthEast) && isFunction(lfBounds.getSouthWest))) { leafletLogger.error('The leaflet bounds is not valid object.'); return; } var northEast = lfBounds.getNorthEast(); var southWest = lfBounds.getSouthWest(); return { northEast: { lat: northEast.lat, lng: northEast.lng, }, southWest: { lat: southWest.lat, lng: southWest.lng, }, }; }, }; }]); angular.module('leaflet-directive').factory('leafletControlHelpers', ["$rootScope", "leafletLogger", "leafletHelpers", "leafletLayerHelpers", "leafletMapDefaults", function($rootScope, leafletLogger, leafletHelpers, leafletLayerHelpers, leafletMapDefaults) { var isDefined = leafletHelpers.isDefined; var isObject = leafletHelpers.isObject; var createLayer = leafletLayerHelpers.createLayer; var _controls = {}; var _controlLayersMustBeVisible = function(baselayers, overlays, mapId) { var defaults = leafletMapDefaults.getDefaults(mapId); if (!defaults.controls.layers.visible) { return false; } var atLeastOneControlItemMustBeShown = false; if (isObject(baselayers)) { Object.keys(baselayers).forEach(function(key) { var layer = baselayers[key]; if (!isDefined(layer.layerOptions) || layer.layerOptions.showOnSelector !== false) { atLeastOneControlItemMustBeShown = true; } }); } if (isObject(overlays)) { Object.keys(overlays).forEach(function(key) { var layer = overlays[key]; if (!isDefined(layer.layerParams) || layer.layerParams.showOnSelector !== false) { atLeastOneControlItemMustBeShown = true; } }); } return atLeastOneControlItemMustBeShown; }; var _createLayersControl = function(mapId) { var defaults = leafletMapDefaults.getDefaults(mapId); var controlOptions = { collapsed: defaults.controls.layers.collapsed, position: defaults.controls.layers.position, autoZIndex: false, }; angular.extend(controlOptions, defaults.controls.layers.options); var control; if (defaults.controls.layers && isDefined(defaults.controls.layers.control)) { control = defaults.controls.layers.control.apply(this, [[], [], controlOptions]); } else { control = new L.control.layers([], [], controlOptions); } return control; }; var controlTypes = { draw: { isPluginLoaded: function() { if (!angular.isDefined(L.Control.Draw)) { leafletLogger.error('Draw plugin is not loaded.'); return false; } return true; }, checkValidParams: function(/* params */) { return true; }, createControl: function(params) { return new L.Control.Draw(params); }, }, scale: { isPluginLoaded: function() { return true; }, checkValidParams: function(/* params */) { return true; }, createControl: function(params) { return new L.control.scale(params); }, }, fullscreen: { isPluginLoaded: function() { if (!angular.isDefined(L.Control.Fullscreen)) { leafletLogger.error('Fullscreen plugin is not loaded.'); return false; } return true; }, checkValidParams: function(/* params */) { return true; }, createControl: function(params) { return new L.Control.Fullscreen(params); }, }, search: { isPluginLoaded: function() { if (!angular.isDefined(L.Control.Search)) { leafletLogger.error(' Search plugin is not loaded.'); return false; } return true; }, checkValidParams: function(/* params */) { return true; }, createControl: function(params) { return new L.Control.Search(params); }, }, custom: {}, minimap: { isPluginLoaded: function() { if (!angular.isDefined(L.Control.MiniMap)) { leafletLogger.error('Minimap plugin is not loaded.'); return false; } return true; }, checkValidParams: function(params) { if (!isDefined(params.layer)) { leafletLogger.warn('minimap "layer" option should be defined.'); return false; } return true; }, createControl: function(params) { var layer = createLayer(params.layer); if (!isDefined(layer)) { leafletLogger.warn('minimap control "layer" could not be created.'); return; } return new L.Control.MiniMap(layer, params); }, }, }; return { layersControlMustBeVisible: _controlLayersMustBeVisible, isValidControlType: function(type) { return Object.keys(controlTypes).indexOf(type) !== -1; }, createControl: function(type, params) { if (!controlTypes[type].checkValidParams(params)) { return; } return controlTypes[type].createControl(params); }, updateLayersControl: function(map, mapId, loaded, baselayers, overlays, leafletLayers) { var i; var _layersControl = _controls[mapId]; var mustBeLoaded = _controlLayersMustBeVisible(baselayers, overlays, mapId); if (isDefined(_layersControl) && loaded) { for (i in leafletLayers.baselayers) { _layersControl.removeLayer(leafletLayers.baselayers[i]); } for (i in leafletLayers.overlays) { _layersControl.removeLayer(leafletLayers.overlays[i]); } map.removeControl(_layersControl); delete _controls[mapId]; } if (mustBeLoaded) { _layersControl = _createLayersControl(mapId); _controls[mapId] = _layersControl; for (i in baselayers) { var hideOnSelector = isDefined(baselayers[i].layerOptions) && baselayers[i].layerOptions.showOnSelector === false; if (!hideOnSelector && isDefined(leafletLayers.baselayers[i])) { _layersControl.addBaseLayer(leafletLayers.baselayers[i], baselayers[i].name); } } for (i in overlays) { var hideOverlayOnSelector = isDefined(overlays[i].layerParams) && overlays[i].layerParams.showOnSelector === false; if (!hideOverlayOnSelector && isDefined(leafletLayers.overlays[i])) { _layersControl.addOverlay(leafletLayers.overlays[i], overlays[i].name); } } map.addControl(_layersControl); } return mustBeLoaded; }, }; }]); angular.module('leaflet-directive').service('leafletData', ["leafletLogger", "$q", "leafletHelpers", function(leafletLogger, $q, leafletHelpers) { var getDefer = leafletHelpers.getDefer; var getUnresolvedDefer = leafletHelpers.getUnresolvedDefer; var setResolvedDefer = leafletHelpers.setResolvedDefer; var _private = {}; var _this = this; var upperFirst = function(string) { return string.charAt(0).toUpperCase() + string.slice(1); }; var _privateItems = [ 'map', 'tiles', 'layers', 'paths', 'markers', 'geoJSON', 'UTFGrid', //odd ball on naming convention keeping to not break 'decorations', 'directiveControls', ]; //init _privateItems.forEach(function(itemName) { _private[itemName] = {}; }); this.unresolveMap = function(scopeId) { var id = leafletHelpers.obtainEffectiveMapId(_private.map, scopeId); _privateItems.forEach(function(itemName) { _private[itemName][id] = undefined; }); }; //int repetitive stuff (get and sets) _privateItems.forEach(function(itemName) { var name = upperFirst(itemName); _this['set' + name] = function(lObject, scopeId) { var defer = getUnresolvedDefer(_private[itemName], scopeId); defer.resolve(lObject); setResolvedDefer(_private[itemName], scopeId); }; _this['get' + name] = function(scopeId) { var defer = getDefer(_private[itemName], scopeId); return defer.promise; }; }); }]); angular.module('leaflet-directive') .service('leafletDirectiveControlsHelpers', ["leafletLogger", "leafletData", "leafletHelpers", function(leafletLogger, leafletData, leafletHelpers) { var _isDefined = leafletHelpers.isDefined; var _isString = leafletHelpers.isString; var _isObject = leafletHelpers.isObject; var _extend = function(id, thingToAddName, createFn, cleanFn) { var extender = {}; if (!_isDefined(thingToAddName)) { leafletLogger.error('control name 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 { leafletLogger.error('incorrect arguments'); return; } //add external control to create / destroy markers without a watch leafletData.getDirectiveControls().then(function(controls) { angular.extend(controls, extender); leafletData.setDirectiveControls(controls, id); }); }; return { extend: _extend, }; }]); angular.module('leaflet-directive') .service('leafletGeoJsonHelpers', ["leafletHelpers", "leafletIterators", function(leafletHelpers, leafletIterators) { var lHlp = leafletHelpers; var lIt = leafletIterators; var Point = function(lat, lng) { this.lat = lat; this.lng = lng; return this; }; var _getLat = function(value) { if (Array.isArray(value) && value.length === 2) { return value[1]; } else if (lHlp.isDefined(value.type) && value.type === 'Point') { return +value.coordinates[1]; } else { return +value.lat; } }; var _getLng = function(value) { if (Array.isArray(value) && value.length === 2) { return value[0]; } else if (lHlp.isDefined(value.type) && value.type === 'Point') { return +value.coordinates[0]; } else { return +value.lng; } }; var _validateCoords = function(coords) { if (lHlp.isUndefined(coords)) { return false; } if (lHlp.isArray(coords)) { if (coords.length === 2 && lHlp.isNumber(coords[0]) && lHlp.isNumber(coords[1])) { return true; } } else if (lHlp.isDefined(coords.type)) { if ( coords.type === 'Point' && lHlp.isArray(coords.coordinates) && coords.coordinates.length === 2 && lHlp.isNumber(coords.coordinates[0]) && lHlp.isNumber(coords.coordinates[1])) { return true; } } var ret = lIt.all(['lat', 'lng'], function(pos) { return lHlp.isDefined(coords[pos]) && lHlp.isNumber(coords[pos]); }); return ret; }; var _getCoords = function(value) { if (!value || !_validateCoords(value)) { return; } var p = null; if (Array.isArray(value) && value.length === 2) { p = new Point(value[1], value[0]); } else if (lHlp.isDefined(value.type) && value.type === 'Point') { p = new Point(value.coordinates[1], value.coordinates[0]); } else { return value; } //note angular.merge is avail in angular 1.4.X we might want to fill it here return angular.extend(value, p);//tap on lat, lng if it doesnt exist }; return { getLat: _getLat, getLng: _getLng, validateCoords: _validateCoords, getCoords: _getCoords, }; }]); angular.module('leaflet-directive').service('leafletHelpers', ["$q", "leafletLogger", function($q, leafletLogger) { var _copy = angular.copy; var _clone = _copy; /* For parsing paths to a field in an object Example: var obj = { bike:{ 1: 'hi' 2: 'foo' } }; _getObjectValue(obj,"bike.1") returns 'hi' this is getPath in ui-gmap */ var _getObjectValue = function(object, pathStr) { var obj; if (!object || !angular.isObject(object)) { return; } //if the key is not a sting then we already have the value if ((pathStr === null) || !angular.isString(pathStr)) { return pathStr; } obj = object; pathStr.split('.').forEach(function(value) { if (obj) { obj = obj[value]; } }); return obj; }; /* Object Array Notation _getObjectArrayPath("bike.one.two") returns: 'bike["one"]["two"]' */ var _getObjectArrayPath = function(pathStr) { return pathStr.split('.').reduce(function(previous, current) { return previous + '["' + current + '"]'; }); }; /* Object Dot Notation _getObjectPath(["bike","one","two"]) returns: "bike.one.two" */ var _getObjectDotPath = function(arrayOfStrings) { return arrayOfStrings.reduce(function(previous, current) { return previous + '.' + current; }); }; function _obtainEffectiveMapId(d, mapId) { var id; var i; if (!angular.isDefined(mapId)) { if (Object.keys(d).length === 0) { id = 'main'; } else if (Object.keys(d).length >= 1) { for (i in d) { if (d.hasOwnProperty(i)) { id = i; } } } else { leafletLogger.error('You have more than 1 map on the DOM, you must provide the map ID to the leafletData.getXXX call'); } } else { id = mapId; } return id; } function _getUnresolvedDefer(d, mapId) { var id = _obtainEffectiveMapId(d, mapId); var defer; if (!angular.isDefined(d[id]) || d[id].resolvedDefer === true) { defer = $q.defer(); d[id] = { defer: defer, resolvedDefer: false, }; } else { defer = d[id].defer; } return defer; } var _isDefined = function(value) { return angular.isDefined(value) && value !== null; }; var _isUndefined = function(value) { return !_isDefined(value); }; // BEGIN DIRECT PORT FROM AngularJS code base var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; var MOZ_HACK_REGEXP = /^moz([A-Z])/; var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i; /** Converts snake_case to camelCase. Also there is special case for Moz prefix starting with upper case letter. @param name Name to normalize */ var camelCase = function(name) { return name.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { if (offset) { return letter.toUpperCase(); } else { return letter; } }).replace(MOZ_HACK_REGEXP, 'Moz$1'); }; /** Converts all accepted directives format into proper directive name. @param name Name to normalize */ var directiveNormalize = function(name) { return camelCase(name.replace(PREFIX_REGEXP, '')); }; // END AngularJS port return { camelCase: camelCase, directiveNormalize: directiveNormalize, copy:_copy, clone:_clone, getObjectValue: _getObjectValue, getObjectArrayPath:_getObjectArrayPath, getObjectDotPath: _getObjectDotPath, defaultTo: function(val, _default) { return _isDefined(val) ? val : _default; }, //mainly for checking attributes of directives lets keep this minimal (on what we accept) isTruthy: function(val) { return val === 'true' || val === true; }, //Determine if a reference is {} isEmpty: function(value) { return Object.keys(value).length === 0; }, //Determine if a reference is undefined or {} isUndefinedOrEmpty: function(value) { return (angular.isUndefined(value) || value === null) || Object.keys(value).length === 0; }, // Determine if a reference is defined isDefined: _isDefined, isUndefined:_isUndefined, isNumber: angular.isNumber, isString: angular.isString, isArray: angular.isArray, isObject: angular.isObject, isFunction: angular.isFunction, equals: angular.equals, isValidCenter: function(center) { return angular.isDefined(center) && angular.isNumber(center.lat) && angular.isNumber(center.lng) && angular.isNumber(center.zoom); }, isValidPoint: function(point) { if (!angular.isDefined(point)) { return false; } if (angular.isArray(point)) { return point.length === 2 && angular.isNumber(point[0]) && angular.isNumber(point[1]); } return angular.isNumber(point.lat) && angular.isNumber(point.lng); }, isSameCenterOnMap: function(centerModel, map) { var mapCenter = map.getCenter(); var zoom = map.getZoom(); if (centerModel.lat && centerModel.lng && mapCenter.lat.toFixed(4) === centerModel.lat.toFixed(4) && mapCenter.lng.toFixed(4) === centerModel.lng.toFixed(4) && zoom === centerModel.zoom) { return true; } return false; }, safeApply: function($scope, fn) { var phase = $scope.$root.$$phase; if (phase === '$apply' || phase === '$digest') { $scope.$eval(fn); } else { $scope.$evalAsync(fn); } }, obtainEffectiveMapId: _obtainEffectiveMapId, getDefer: function(d, mapId) { var id = _obtainEffectiveMapId(d, mapId); var defer; if (!angular.isDefined(d[id]) || d[id].resolvedDefer === false) { defer = _getUnresolvedDefer(d, mapId); } else { defer = d[id].defer; } return defer; }, getUnresolvedDefer: _getUnresolvedDefer, setResolvedDefer: function(d, mapId) { var id = _obtainEffectiveMapId(d, mapId); d[id].resolvedDefer = true; }, rangeIsSupported: function() { var testrange = document.createElement('input'); testrange.setAttribute('type', 'range'); return testrange.type === 'range'; }, FullScreenControlPlugin: { isLoaded: function() { return angular.isDefined(L.Control.Fullscreen); }, }, MiniMapControlPlugin: { isLoaded: function() { return angular.isDefined(L.Control.MiniMap); }, }, AwesomeMarkersPlugin: { isLoaded: function() { return angular.isDefined(L.AwesomeMarkers) && angular.isDefined(L.AwesomeMarkers.Icon); }, is: function(icon) { if (this.isLoaded()) { return icon instanceof L.AwesomeMarkers.Icon; } else { return false; } }, equal: function(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } }, }, VectorMarkersPlugin: { isLoaded: function() { return angular.isDefined(L.VectorMarkers) && angular.isDefined(L.VectorMarkers.Icon); }, is: function(icon) { if (this.isLoaded()) { return icon instanceof L.VectorMarkers.Icon; } else { return false; } }, equal: function(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } }, }, DomMarkersPlugin: { isLoaded: function() { if (angular.isDefined(L.DomMarkers) && angular.isDefined(L.DomMarkers.Icon)) { return true; } else { return false; } }, is: function(icon) { if (this.isLoaded()) { return icon instanceof L.DomMarkers.Icon; } else { return false; } }, equal: function(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } }, }, PolylineDecoratorPlugin: { isLoaded: function() { if (angular.isDefined(L.PolylineDecorator)) { return true; } else { return false; } }, is: function(decoration) { if (this.isLoaded()) { return decoration instanceof L.PolylineDecorator; } else { return false; } }, equal: function(decorationA, decorationB) { if (!this.isLoaded()) { return false; } if (this.is(decorationA)) { return angular.equals(decorationA, decorationB); } else { return false; } }, }, MakiMarkersPlugin: { isLoaded: function() { if (angular.isDefined(L.MakiMarkers) && angular.isDefined(L.MakiMarkers.Icon)) { return true; } else { return false; } }, is: function(icon) { if (this.isLoaded()) { return icon instanceof L.MakiMarkers.Icon; } else { return false; } }, equal: function(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } }, }, ExtraMarkersPlugin: { isLoaded: function() { if (angular.isDefined(L.ExtraMarkers) && angular.isDefined(L.ExtraMarkers.Icon)) { return true; } else { return false; } }, is: function(icon) { if (this.isLoaded()) { return icon instanceof L.ExtraMarkers.Icon; } else { return false; } }, equal: function(iconA, iconB) { if (!this.isLoaded()) { return false; } if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } }, }, LabelPlugin: { isLoaded: function() { return angular.isDefined(L.Label); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.MarkerClusterGroup; } else { return false; } }, }, MarkerClusterPlugin: { isLoaded: function() { return angular.isDefined(L.MarkerClusterGroup); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.MarkerClusterGroup; } else { return false; } }, }, GoogleLayerPlugin: { isLoaded: function() { return angular.isDefined(L.Google); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.Google; } else { return false; } }, }, LeafletProviderPlugin: { isLoaded: function() { return angular.isDefined(L.TileLayer.Provider); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.TileLayer.Provider; } else { return false; } }, }, ChinaLayerPlugin: { isLoaded: function() { return angular.isDefined(L.tileLayer.chinaProvider); }, }, HeatLayerPlugin: { isLoaded: function() { return angular.isDefined(L.heatLayer); }, }, WebGLHeatMapLayerPlugin: { isLoaded: function() { return angular.isDefined(L.TileLayer.WebGLHeatMap); }, }, BingLayerPlugin: { isLoaded: function() { return angular.isDefined(L.BingLayer); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.BingLayer; } else { return false; } }, }, WFSLayerPlugin: { isLoaded: function() { return L.GeoJSON.WFS !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.GeoJSON.WFS; } else { return false; } }, }, AGSBaseLayerPlugin: { isLoaded: function() { return L.esri !== undefined && L.esri.basemapLayer !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.esri.basemapLayer; } else { return false; } }, }, AGSLayerPlugin: { isLoaded: function() { return lvector !== undefined && lvector.AGS !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof lvector.AGS; } else { return false; } }, }, AGSFeatureLayerPlugin: { isLoaded: function() { return L.esri !== undefined && L.esri.featureLayer !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.esri.featureLayer; } else { return false; } }, }, AGSTiledMapLayerPlugin: { isLoaded: function() { return L.esri !== undefined && L.esri.tiledMapLayer !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.esri.tiledMapLayer; } else { return false; } }, }, AGSDynamicMapLayerPlugin: { isLoaded: function() { return L.esri !== undefined && L.esri.dynamicMapLayer !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.esri.dynamicMapLayer; } else { return false; } }, }, AGSImageMapLayerPlugin: { isLoaded: function() { return L.esri !== undefined && L.esri.imageMapLayer !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.esri.imageMapLayer; } else { return false; } }, }, AGSClusteredLayerPlugin: { isLoaded: function() { return L.esri !== undefined && L.esri.clusteredFeatureLayer !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.esri.clusteredFeatureLayer; } else { return false; } }, }, AGSHeatmapLayerPlugin: { isLoaded: function() { return L.esri !== undefined && L.esri.heatmapFeatureLayer !== undefined; }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.esri.heatmapFeatureLayer; } else { return false; } }, }, YandexLayerPlugin: { isLoaded: function() { return angular.isDefined(L.Yandex); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.Yandex; } else { return false; } }, }, GeoJSONPlugin: { isLoaded: function() { return angular.isDefined(L.TileLayer.GeoJSON); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.TileLayer.GeoJSON; } else { return false; } }, }, UTFGridPlugin: { isLoaded: function() { return angular.isDefined(L.UtfGrid); }, is: function(layer) { if (this.isLoaded()) { return layer instanceof L.UtfGrid; } else { leafletLogger.error('No UtfGrid plugin found.'); return false; } }, }, CartoDB: { isLoaded: function() { return cartodb; }, is: function(/*layer*/) { return true; /* if (this.isLoaded()) { return layer instanceof L.TileLayer.GeoJSON; } else { return false; }*/ }, }, Leaflet: { DivIcon: { is: function(icon) { return icon instanceof L.DivIcon; }, equal: function(iconA, iconB) { if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } }, }, Icon: { is: function(icon) { return icon instanceof L.Icon; }, equal: function(iconA, iconB) { if (this.is(iconA)) { return angular.equals(iconA, iconB); } else { return false; } }, }, }, /* watchOptions - object to set deep nested watches and turn off watches all together (rely on control / functional updates) watchOptions - Object doWatch:boolean isDeep:boolean (sets $watch(function,isDeep)) individual doWatch:boolean isDeep:boolean */ //legacy defaults watchOptions: { doWatch:true, isDeep: true, individual:{ doWatch:true, isDeep: true, }, }, }; }]); angular.module('leaflet-directive').service('leafletIterators', ["leafletLogger", "leafletHelpers", function(leafletLogger, leafletHelpers) { var lHlp = leafletHelpers; //BEGIN COPY from underscore var _keys = Object.keys; var _isFunction = lHlp.isFunction; var _isObject = lHlp.isObject; // Helper for collection methods to determine whether a collection // should be iterated as an array or as an object // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var _isArrayLike = function(collection) { var length = collection !== null && collection.length; return lHlp.isNumber(length) && length >= 0 && length <= MAX_ARRAY_INDEX; }; // Keep the identity function around for default iteratees. var _identity = function(value) { return value; }; var _property = function(key) { return function(obj) { return obj === null ? void 0 : obj[key]; }; }; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. var optimizeCb = function(func, context, argCount) { if (context === void 0) { return func; } switch (argCount === null ? 3 : argCount) { case 1: { return function(value) { return func.call(context, value); }; } case 2: { return function(value, other) { return func.call(context, value, other); }; } case 3: { return function(value, index, collection) { return func.call(context, value, index, collection); }; } case 4: { return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } } return function() { return func.apply(context, arguments); }; }; // An internal function for creating assigner functions. var createAssigner = function(keysFunc, undefinedOnly) { return function(obj) { var length = arguments.length; if (length < 2 || obj === null) { return obj; } for (var index = 1; index < length; index++) { var source = arguments[index]; var keys = keysFunc(source); var l = keys.length; for (var i = 0; i < l; i++) { var key = keys[i]; if (!undefinedOnly || obj[key] === void 0) { obj[key] = source[key]; } } } return obj; }; }; // Assigns a given object with all the own properties in the passed-in object(s) // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) var _extendOwn; var _assign = null; _extendOwn = _assign = createAssigner(_keys); // Returns whether an object has a given set of `key:value` pairs. var _isMatch = function(object, attrs) { var keys = _keys(attrs); var length = keys.length; if (object === null) { return !length; } var obj = Object(object); for (var i = 0; i < length; i++) { var key = keys[i]; if (attrs[key] !== obj[key] || !(key in obj)) { return false; } } return true; }; // Returns a predicate for checking whether an object has a given set of // `key:value` pairs. var _matcher; var _matches = null; _matcher = _matches = function(attrs) { attrs = _extendOwn({}, attrs); return function(obj) { return _isMatch(obj, attrs); }; }; // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either // identity, an arbitrary callback, a property matcher, or a property accessor. var cb = function(value, context, argCount) { if (value === null) { return _identity; } if (_isFunction(value)) { return optimizeCb(value, context, argCount); } if (_isObject(value)) { return _matcher(value); } return _property(value); }; var _every; var _all = null; _every = _all = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !_isArrayLike(obj) && _keys(obj); var length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) { return false; } } return true; }; //END COPY fron underscore var _hasErrors = function(collection, cb, ignoreCollection, cbName) { if (!ignoreCollection) { if (!lHlp.isDefined(collection) || !lHlp.isDefined(cb)) { return true; } } if (!lHlp.isFunction(cb)) { cbName = lHlp.defaultTo(cb, 'cb'); leafletLogger.error(cbName + ' is not a function', 'iterators'); return true; } return false; }; var _iterate = function(collection, externalCb, internalCb) { if (_hasErrors(undefined, internalCb, true, 'internalCb')) { return; } if (!_hasErrors(collection, externalCb)) { for (var key in collection) { if (collection.hasOwnProperty(key)) { internalCb(collection[key], key); } } } }; //see http://jsperf.com/iterators/3 //utilizing for in is way faster var _each = function(collection, cb) { _iterate(collection, cb, function(val, key) { cb(val, key); }); }; return { each:_each, forEach: _each, every: _every, all: _all, }; }]); angular.module('leaflet-directive') .factory('leafletLayerHelpers', ["$rootScope", "leafletLogger", "$q", "leafletHelpers", "leafletIterators", function($rootScope, leafletLogger, $q, leafletHelpers, leafletIterators) { var Helpers = leafletHelpers; var isString = leafletHelpers.isString; var isObject = leafletHelpers.isObject; var isArray = leafletHelpers.isArray; var isDefined = leafletHelpers.isDefined; var $it = leafletIterators; var utfGridCreateLayer = function(params) { if (!Helpers.UTFGridPlugin.isLoaded()) { leafletLogger.error('The UTFGrid plugin is not loaded'); return; } var utfgrid = new L.UtfGrid(params.url, params.pluginOptions); utfgrid.on('mouseover', function(e) { $rootScope.$broadcast('leafletDirectiveMap.utfgridMouseover', e); }); utfgrid.on('mouseout', function(e) { $rootScope.$broadcast('leafletDirectiveMap.utfgridMouseout', e); }); utfgrid.on('click', function(e) { $rootScope.$broadcast('leafletDirectiveMap.utfgridClick', e); }); utfgrid.on('mousemove', function(e) { $rootScope.$broadcast('leafletDirectiveMap.utfgridMousemove', e); }); return utfgrid; }; var layerTypes = { xyz: { mustHaveUrl: true, createLayer: function(params) { return L.tileLayer(params.url, params.options); }, }, mapbox: { mustHaveKey: true, createLayer: function(params) { var version = 3; if (isDefined(params.options.version) && params.options.version === 4) { version = params.options.version; } var url = version === 3 ? '//{s}.tiles.mapbox.com/v3/' + params.key + '/{z}/{x}/{y}.png' : '//api.tiles.mapbox.com/v4/' + params.key + '/{z}/{x}/{y}.png?access_token=' + params.apiKey; return L.tileLayer(url, params.options); }, }, geoJSON: { mustHaveUrl: true, createLayer: function(params) { if (!Helpers.GeoJSONPlugin.isLoaded()) { return; } return new L.TileLayer.GeoJSON(params.url, params.pluginOptions, params.options); }, }, geoJSONShape: { mustHaveUrl: false, createLayer: function(params) { return new L.GeoJSON(params.data, params.options); }, }, geoJSONAwesomeMarker: { mustHaveUrl: false, createLayer: function(params) { return new L.geoJson(params.data, { pointToLayer: function(feature, latlng) { return L.marker(latlng, {icon: L.AwesomeMarkers.icon(params.icon)}); }, }); }, }, geoJSONVectorMarker: { mustHaveUrl: false, createLayer: function(params) { return new L.geoJson(params.data, { pointToLayer: function(feature, latlng) { return L.marker(latlng, {icon: L.VectorMarkers.icon(params.icon)}); }, }); }, }, utfGrid: { mustHaveUrl: true, createLayer: utfGridCreateLayer, }, cartodbTiles: { mustHaveKey: true, createLayer: function(params) { var url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/{z}/{x}/{y}.png'; return L.tileLayer(url, params.options); }, }, cartodbUTFGrid: { mustHaveKey: true, mustHaveLayer: true, createLayer: function(params) { params.url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/' + params.layer + '/{z}/{x}/{y}.grid.json'; return utfGridCreateLayer(params); }, }, cartodbInteractive: { mustHaveKey: true, mustHaveLayer: true, createLayer: function(params) { var tilesURL = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/{z}/{x}/{y}.png'; var tileLayer = L.tileLayer(tilesURL, params.options); params.url = '//' + params.user + '.cartodb.com/api/v1/map/' + params.key + '/' + params.layer + '/{z}/{x}/{y}.grid.json'; var utfLayer = utfGridCreateLayer(params); return L.layerGroup([tileLayer, utfLayer]); }, }, wms: { mustHaveUrl: true, createLayer: function(params) { return L.tileLayer.wms(params.url, params.options); }, }, wmts: { mustHaveUrl: true, createLayer: function(params) { return L.tileLayer.wmts(params.url, params.options); }, }, wfs: { mustHaveUrl: true, mustHaveLayer: true, createLayer: function(params) { if (!Helpers.WFSLayerPlugin.isLoaded()) { return; } var options = angular.copy(params.options); if (options.crs && typeof options.crs === 'string') { /*jshint -W061 */ options.crs = eval(options.crs); } return new L.GeoJSON.WFS(params.url, params.layer, options); }, }, group: { mustHaveUrl: false, createLayer: function(params) { var lyrs = []; $it.each(params.options.layers, function(l) { lyrs.push(createLayer(l)); }); params.options.loadedDefer = function() { var defers = []; if (isDefined(params.options.layers)) { for (var i = 0; i < params.options.layers.length; i++) { var d = params.options.layers[i].layerOptions.loadedDefer; if (isDefined(d)) { defers.push(d); } } } return defers; }; return L.layerGroup(lyrs); }, }, featureGroup: { mustHaveUrl: false, createLayer: function() { return L.featureGroup(); }, }, google: { mustHaveUrl: false, createLayer: function(params) { var type = params.type || 'SATELLITE'; if (!Helpers.GoogleLayerPlugin.isLoaded()) { return; } return new L.Google(type, params.options); }, }, here: { mustHaveUrl: false, createLayer: function(params) { var provider = params.provider || 'HERE.terrainDay'; if (!Helpers.LeafletProviderPlugin.isLoaded()) { return; } return new L.TileLayer.Provider(provider, params.options); }, }, china:{ mustHaveUrl:false, createLayer:function(params) { var type = params.type || ''; if (!Helpers.ChinaLayerPlugin.isLoaded()) { return; } return L.tileLayer.chinaProvider(type, params.options); }, }, agsBase: { mustHaveLayer: true, createLayer: function(params) { if (!Helpers.AGSBaseLayerPlugin.isLoaded()) { return; } return L.esri.basemapLayer(params.layer, params.options); }, }, ags: { mustHaveUrl: true, createLayer: function(params) { if (!Helpers.AGSLayerPlugin.isLoaded()) { return; } var options = angular.copy(params.options); angular.extend(options, { url: params.url, }); var layer = new lvector.AGS(options); layer.onAdd = function(map) { this.setMap(map); }; layer.onRemove = function() { this.setMap(null); }; return layer; }, },