2 Copyright (c) 2013 Dominik Moritz
4 This file is part of the leaflet locate control. It is licensed under the MIT license.
5 You can find the project at: https://github.com/domoritz/leaflet-locatecontrol
7 L.Control.Locate = L.Control.extend({
11 follow: false, // follow with zoom and pan the user's location
12 stopFollowingOnDrag: false, // if follow is true, stop following when map is dragged (deprecated)
30 // changes to range circle and inner marker while following
31 // it is only necessary to provide the things that should change
32 followCircleStyle: {},
35 //fillColor: '#FFB000'
37 circlePadding: [0, 0],
39 onLocationError: function(err) {
40 // this event is called in case of any location error
41 // that is not a time out error.
44 onLocationOutsideMapBounds: function(context) {
45 // this event is repeatedly called when the location changes
46 alert(context.options.strings.outsideMapBoundsMsg);
48 setView: true, // automatically sets the map view to the user's location
50 title: "Show me where I am",
51 popup: "You are within {distance} {unit} from this point",
52 outsideMapBoundsMsg: "You seem located outside the boundaries of the map"
57 onAdd: function (map) {
58 var container = L.DomUtil.create('div', 'control-locate');
61 this._layer = new L.LayerGroup();
62 this._layer.addTo(map);
63 this._event = undefined;
65 this._locateOptions = {
66 watch: true // if you overwrite this, visualization cannot be updated
68 L.extend(this._locateOptions, this.options.locateOptions);
69 L.extend(this._locateOptions, {
70 setView: false // have to set this to false because we have to
71 // do setView manually
74 // extend the follow marker style and circle from the normal style
76 L.extend(tmp, this.options.markerStyle, this.options.followMarkerStyle);
77 this.options.followMarkerStyle = tmp;
79 L.extend(tmp, this.options.circleStyle, this.options.followCircleStyle);
80 this.options.followCircleStyle = tmp;
82 var link = L.DomUtil.create('a', 'control-button', container);
83 link.innerHTML = "<span class='icon geolocate'></span>";
85 link.title = this.options.strings.title;
88 .on(link, 'click', L.DomEvent.stopPropagation)
89 .on(link, 'click', L.DomEvent.preventDefault)
90 .on(link, 'click', function() {
91 if (self._active && (map.getBounds().contains(self._event.latlng) || !self.options.setView ||
92 isOutsideMapBounds())) {
98 .on(link, 'dblclick', L.DomEvent.stopPropagation);
100 var locate = function () {
101 if (self.options.setView) {
102 self._locateOnNextLocationFound = true;
105 map.locate(self._locateOptions);
108 if (self.options.follow) {
112 L.DomUtil.addClass(self._container, "requesting");
113 L.DomUtil.removeClass(self._container, "active");
114 L.DomUtil.removeClass(self._container, "following");
120 var onLocationFound = function (e) {
121 // no need to do anything if the location has not changed
123 (self._event.latlng.lat === e.latlng.lat &&
124 self._event.latlng.lng === e.latlng.lng &&
125 self._event.accuracy === e.accuracy)) {
135 if (self.options.follow && self._following) {
136 self._locateOnNextLocationFound = true;
142 var startFollowing = function() {
143 map.fire('startfollowing');
144 self._following = true;
145 if (self.options.stopFollowingOnDrag) {
146 map.on('dragstart', stopFollowing);
150 var stopFollowing = function() {
151 map.fire('stopfollowing');
152 self._following = false;
153 if (self.options.stopFollowingOnDrag) {
154 map.off('dragstart', stopFollowing);
159 var isOutsideMapBounds = function () {
160 if (self._event === undefined)
162 return map.options.maxBounds &&
163 !map.options.maxBounds.contains(self._event.latlng);
166 var visualizeLocation = function() {
167 if (self._event.accuracy === undefined)
168 self._event.accuracy = 0;
170 var radius = self._event.accuracy;
171 if (self._locateOnNextLocationFound) {
172 if (isOutsideMapBounds()) {
173 self.options.onLocationOutsideMapBounds(self);
175 map.fitBounds(self._event.bounds, { padding: self.options.circlePadding });
177 self._locateOnNextLocationFound = false;
180 // circle with the radius of the location's accuracy
182 if (self.options.drawCircle) {
183 if (self._following) {
184 style = self.options.followCircleStyle;
186 style = self.options.circleStyle;
190 self._circle = L.circle(self._event.latlng, radius, style)
193 self._circle.setLatLng(self._event.latlng).setRadius(radius);
195 self._circle.options[o] = style[o];
201 if (self.options.metric) {
202 distance = radius.toFixed(0);
205 distance = (radius * 3.2808399).toFixed(0);
209 // small inner marker
211 if (self._following) {
212 mStyle = self.options.followMarkerStyle;
214 mStyle = self.options.markerStyle;
217 var t = self.options.strings.popup;
218 if (!self._circleMarker) {
219 self._circleMarker = L.circleMarker(self._event.latlng, mStyle)
220 .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
223 self._circleMarker.setLatLng(self._event.latlng)
224 .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
225 ._popup.setLatLng(self._event.latlng);
227 self._circleMarker.options[o] = mStyle[o];
231 if (!self._container)
233 if (self._following) {
234 L.DomUtil.removeClass(self._container, "requesting");
235 L.DomUtil.addClass(self._container, "active");
236 L.DomUtil.addClass(self._container, "following");
238 L.DomUtil.removeClass(self._container, "requesting");
239 L.DomUtil.addClass(self._container, "active");
240 L.DomUtil.removeClass(self._container, "following");
244 var resetVariables = function() {
245 self._active = false;
246 self._locateOnNextLocationFound = self.options.setView;
247 self._following = false;
252 var stopLocate = function() {
254 map.off('dragstart', stopFollowing);
256 L.DomUtil.removeClass(self._container, "requesting");
257 L.DomUtil.removeClass(self._container, "active");
258 L.DomUtil.removeClass(self._container, "following");
261 self._layer.clearLayers();
262 self._circleMarker = undefined;
263 self._circle = undefined;
266 var onLocationError = function (err) {
267 // ignore time out error if the location is watched
268 if (err.code == 3 && this._locateOptions.watch) {
273 self.options.onLocationError(err);
277 map.on('locationfound', onLocationFound, self);
278 map.on('locationerror', onLocationError, self);
280 // make locate functions available to outside world
281 this.locate = locate;
282 this.stopLocate = stopLocate;
283 this.stopFollowing = stopFollowing;
289 L.Map.addInitHook(function () {
290 if (this.options.locateControl) {
291 this.locateControl = L.control.locate();
292 this.addControl(this.locateControl);
296 L.control.locate = function (options) {
297 return new L.Control.Locate(options);