+++ /dev/null
-/*
-Copyright (c) 2014 Dominik Moritz
-
-This file is part of the leaflet locate control. It is licensed under the MIT license.
-You can find the project at: https://github.com/domoritz/leaflet-locatecontrol
-*/
-L.Control.Locate = L.Control.extend({
- options: {
- position: 'topleft',
- drawCircle: true,
- follow: false, // follow with zoom and pan the user's location
- stopFollowingOnDrag: false, // if follow is true, stop following when map is dragged (deprecated)
- // range circle
- circleStyle: {
- color: '#136AEC',
- fillColor: '#136AEC',
- fillOpacity: 0.15,
- weight: 2,
- opacity: 0.5
- },
- // inner marker
- markerStyle: {
- color: '#136AEC',
- fillColor: '#2A93EE',
- fillOpacity: 0.7,
- weight: 2,
- opacity: 0.9,
- radius: 5
- },
- // changes to range circle and inner marker while following
- // it is only necessary to provide the things that should change
- followCircleStyle: {},
- followMarkerStyle: {
- //color: '#FFA500',
- //fillColor: '#FFB000'
- },
- icon: 'icon-location', // icon-location or icon-direction
- iconLoading: 'icon-spinner animate-spin',
- circlePadding: [0, 0],
- metric: true,
- onLocationError: function(err) {
- // this event is called in case of any location error
- // that is not a time out error.
- alert(err.message);
- },
- onLocationOutsideMapBounds: function(control) {
- // this event is repeatedly called when the location changes
- control.stopLocate();
- alert(context.options.strings.outsideMapBoundsMsg);
- },
- setView: true, // automatically sets the map view to the user's location
- // keep the current map zoom level when displaying the user's location. (if 'false', use maxZoom)
- keepCurrentZoomLevel: false,
- strings: {
- title: "Show me where I am",
- popup: "You are within {distance} {unit} from this point",
- outsideMapBoundsMsg: "You seem located outside the boundaries of the map"
- },
- locateOptions: {
- maxZoom: Infinity,
- watch: true // if you overwrite this, visualization cannot be updated
- }
- },
-
- onAdd: function (map) {
- var container = L.DomUtil.create('div', 'control-locate');
-
- var self = this;
- this._layer = new L.LayerGroup();
- this._layer.addTo(map);
- this._event = undefined;
-
- this._locateOptions = this.options.locateOptions;
- L.extend(this._locateOptions, this.options.locateOptions);
- L.extend(this._locateOptions, {
- setView: false // have to set this to false because we have to
- // do setView manually
- });
-
- // extend the follow marker style and circle from the normal style
- var tmp = {};
- L.extend(tmp, this.options.markerStyle, this.options.followMarkerStyle);
- this.options.followMarkerStyle = tmp;
- tmp = {};
- L.extend(tmp, this.options.circleStyle, this.options.followCircleStyle);
- this.options.followCircleStyle = tmp;
-
- var link = L.DomUtil.create('a', 'control-button ' + this.options.icon, container);
- link.innerHTML = "<span class='icon geolocate'></span>";
- link.href = '#';
- link.title = this.options.strings.title;
-
- L.DomEvent
- .on(link, 'click', L.DomEvent.stopPropagation)
- .on(link, 'click', L.DomEvent.preventDefault)
- .on(link, 'click', function() {
- if (self._active && (self._event === undefined || map.getBounds().contains(self._event.latlng) || !self.options.setView ||
- isOutsideMapBounds())) {
- stopLocate();
- } else {
- locate();
- }
- })
- .on(link, 'dblclick', L.DomEvent.stopPropagation);
-
- var locate = function () {
- if (self.options.setView) {
- self._locateOnNextLocationFound = true;
- }
- if(!self._active) {
- map.locate(self._locateOptions);
- }
- self._active = true;
- if (self.options.follow) {
- startFollowing();
- }
- if (!self._event) {
- setClasses('requesting');
- } else {
- visualizeLocation();
- }
- };
-
- var onLocationFound = function (e) {
- // no need to do anything if the location has not changed
- if (self._event &&
- (self._event.latlng.lat === e.latlng.lat &&
- self._event.latlng.lng === e.latlng.lng &&
- self._event.accuracy === e.accuracy)) {
- return;
- }
-
- if (!self._active) {
- return;
- }
-
- self._event = e;
-
- if (self.options.follow && self._following) {
- self._locateOnNextLocationFound = true;
- }
-
- visualizeLocation();
- };
-
- var startFollowing = function() {
- map.fire('startfollowing', self);
- self._following = true;
- if (self.options.stopFollowingOnDrag) {
- map.on('dragstart', stopFollowing);
- }
- };
-
- var stopFollowing = function() {
- map.fire('stopfollowing', self);
- self._following = false;
- if (self.options.stopFollowingOnDrag) {
- map.off('dragstart', stopFollowing);
- }
- visualizeLocation();
- };
-
- var isOutsideMapBounds = function () {
- if (self._event === undefined)
- return false;
- return map.options.maxBounds &&
- !map.options.maxBounds.contains(self._event.latlng);
- };
-
- var visualizeLocation = function() {
- if (self._event.accuracy === undefined)
- self._event.accuracy = 0;
-
- var radius = self._event.accuracy;
- if (self._locateOnNextLocationFound) {
- if (isOutsideMapBounds()) {
- self.options.onLocationOutsideMapBounds(self);
- } else {
- map.fitBounds(self._event.bounds, {
- padding: self.options.circlePadding,
- maxZoom: self.options.keepCurrentZoomLevel ? map.getZoom() : self._locateOptions.maxZoom
- });
- }
- self._locateOnNextLocationFound = false;
- }
-
- // circle with the radius of the location's accuracy
- var style, o;
- if (self.options.drawCircle) {
- if (self._following) {
- style = self.options.followCircleStyle;
- } else {
- style = self.options.circleStyle;
- }
-
- if (!self._circle) {
- self._circle = L.circle(self._event.latlng, radius, style)
- .addTo(self._layer);
- } else {
- self._circle.setLatLng(self._event.latlng).setRadius(radius);
- for (o in style) {
- self._circle.options[o] = style[o];
- }
- }
- }
-
- var distance, unit;
- if (self.options.metric) {
- distance = radius.toFixed(0);
- unit = "meters";
- } else {
- distance = (radius * 3.2808399).toFixed(0);
- unit = "feet";
- }
-
- // small inner marker
- var mStyle;
- if (self._following) {
- mStyle = self.options.followMarkerStyle;
- } else {
- mStyle = self.options.markerStyle;
- }
-
- var t = self.options.strings.popup;
- if (!self._circleMarker) {
- self._circleMarker = L.circleMarker(self._event.latlng, mStyle)
- .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
- .addTo(self._layer);
- } else {
- self._circleMarker.setLatLng(self._event.latlng)
- .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
- ._popup.setLatLng(self._event.latlng);
- for (o in mStyle) {
- self._circleMarker.options[o] = mStyle[o];
- }
- }
-
- if (!self._container)
- return;
- if (self._following) {
- setClasses('following');
- } else {
- setClasses('active');
- }
- };
-
- var setClasses = function(state) {
- if (state == 'requesting') {
- L.DomUtil.removeClasses(self._container, "active following");
- L.DomUtil.addClasses(self._container, "requesting");
-
- L.DomUtil.removeClasses(link, self.options.icon);
- L.DomUtil.addClasses(link, self.options.iconLoading);
- } else if (state == 'active') {
- L.DomUtil.removeClasses(self._container, "requesting following");
- L.DomUtil.addClasses(self._container, "active");
-
- L.DomUtil.removeClasses(link, self.options.iconLoading);
- L.DomUtil.addClasses(link, self.options.icon);
- } else if (state == 'following') {
- L.DomUtil.removeClasses(self._container, "requesting");
- L.DomUtil.addClasses(self._container, "active following");
-
- L.DomUtil.removeClasses(link, self.options.iconLoading);
- L.DomUtil.addClasses(link, self.options.icon);
- }
- }
-
- var resetVariables = function() {
- self._active = false;
- self._locateOnNextLocationFound = self.options.setView;
- self._following = false;
- };
-
- resetVariables();
-
- var stopLocate = function() {
- map.stopLocate();
- map.off('dragstart', stopFollowing);
- if (self.options.follow && self._following) {
- stopFollowing();
- }
-
- L.DomUtil.removeClass(self._container, "requesting");
- L.DomUtil.removeClass(self._container, "active");
- L.DomUtil.removeClass(self._container, "following");
- resetVariables();
-
- self._layer.clearLayers();
- self._circleMarker = undefined;
- self._circle = undefined;
- };
-
- var onLocationError = function (err) {
- // ignore time out error if the location is watched
- if (err.code == 3 && self._locateOptions.watch) {
- return;
- }
-
- stopLocate();
- self.options.onLocationError(err);
- };
-
- // event hooks
- map.on('locationfound', onLocationFound, self);
- map.on('locationerror', onLocationError, self);
-
- // make locate functions available to outside world
- this.locate = locate;
- this.stopLocate = stopLocate;
- this.stopFollowing = stopFollowing;
-
- return container;
- }
-});
-
-L.Map.addInitHook(function () {
- if (this.options.locateControl) {
- this.locateControl = L.control.locate();
- this.addControl(this.locateControl);
- }
-});
-
-L.control.locate = function (options) {
- return new L.Control.Locate(options);
-};
-
-(function(){
- // leaflet.js raises bug when trying to addClass / removeClass multiple classes at once
- // Let's create a wrapper on it which fixes it.
- var LDomUtilApplyClassesMethod = function(method, element, classNames) {
- classNames = classNames.split(' ');
- classNames.forEach(function(className) {
- L.DomUtil[method].call(this, element, className);
- });
- };
-
- L.DomUtil.addClasses = function(el, names) { LDomUtilApplyClassesMethod('addClass', el, names); }
- L.DomUtil.removeClasses = function(el, names) { LDomUtilApplyClassesMethod('removeClass', el, names); }
-})();