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
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'
38 onLocationError: function(err) {
39 // this event is called in case of any location error
40 // that is not a time out error.
43 onLocationOutsideMapBounds: function(context) {
44 // this event is repeatedly called when the location changes
45 alert(context.options.strings.outsideMapBoundsMsg);
47 setView: true, // automatically sets the map view to the user's location
49 title: "Show me where I am",
50 popup: "You are within {distance} {unit} from this point",
51 outsideMapBoundsMsg: "You seem located outside the boundaries of the map"
56 onAdd: function (map) {
57 var container = L.DomUtil.create('div', 'control-locate');
60 this._layer = new L.LayerGroup();
61 this._layer.addTo(map);
62 this._event = undefined;
64 this._locateOptions = {
65 watch: true // if you overwrite this, visualization cannot be updated
67 L.extend(this._locateOptions, this.options.locateOptions);
68 L.extend(this._locateOptions, {
69 setView: false // have to set this to false because we have to
70 // do setView manually
73 // extend the follow marker style and circle from the normal style
75 L.extend(tmp, this.options.markerStyle, this.options.followMarkerStyle);
76 this.options.followMarkerStyle = tmp;
78 L.extend(tmp, this.options.circleStyle, this.options.followCircleStyle);
79 this.options.followCircleStyle = tmp;
81 var link = L.DomUtil.create('a', 'control-button', container);
82 link.innerHTML = "<span class='icon geolocate'></span>";
84 link.title = this.options.strings.title;
87 .on(link, 'click', L.DomEvent.stopPropagation)
88 .on(link, 'click', L.DomEvent.preventDefault)
89 .on(link, 'click', function() {
90 if (self._active && (map.getBounds().contains(self._event.latlng) || !self.options.setView ||
91 isOutsideMapBounds())) {
94 if (self.options.setView) {
95 self._locateOnNextLocationFound = true;
98 map.locate(self._locateOptions);
101 if (self.options.follow) {
105 L.DomUtil.addClass(self._container, "requesting");
106 L.DomUtil.removeClass(self._container, "active");
107 L.DomUtil.removeClass(self._container, "following");
113 .on(link, 'dblclick', L.DomEvent.stopPropagation);
115 var onLocationFound = function (e) {
116 // no need to do anything if the location has not changed
118 (self._event.latlng.lat == e.latlng.lat &&
119 self._event.latlng.lng == e.latlng.lng)) {
129 if (self.options.follow && self._following) {
130 self._locateOnNextLocationFound = true;
136 var startFollowing = function() {
137 self._following = true;
138 if (self.options.stopFollowingOnDrag) {
139 map.on('dragstart', stopFollowing);
143 var stopFollowing = function() {
144 self._following = false;
145 if (self.options.stopFollowingOnDrag) {
146 map.off('dragstart', stopFollowing);
151 var isOutsideMapBounds = function () {
152 if (self._event === undefined)
154 return map.options.maxBounds &&
155 !map.options.maxBounds.contains(self._event.latlng);
158 var visualizeLocation = function() {
159 if (self._event.accuracy === undefined)
160 self._event.accuracy = 0;
162 var radius = self._event.accuracy;
163 if (self._locateOnNextLocationFound) {
164 if (isOutsideMapBounds()) {
165 self.options.onLocationOutsideMapBounds(self);
167 map.fitBounds(self._event.bounds);
169 self._locateOnNextLocationFound = false;
172 // circle with the radius of the location's accuracy
174 if (self.options.drawCircle) {
175 if (self._following) {
176 style = self.options.followCircleStyle;
178 style = self.options.circleStyle;
182 self._circle = L.circle(self._event.latlng, radius, style)
185 self._circle.setLatLng(self._event.latlng).setRadius(radius);
190 if (self.options.metric) {
191 distance = radius.toFixed(0);
194 distance = (radius * 3.2808399).toFixed(0);
198 // small inner marker
200 if (self._following) {
201 m = self.options.followMarkerStyle;
203 m = self.options.markerStyle;
206 var t = self.options.strings.popup;
207 if (!self._circleMarker) {
208 self._circleMarker = L.circleMarker(self._event.latlng, m)
209 .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
212 self._circleMarker.setLatLng(self._event.latlng)
213 .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
214 ._popup.setLatLng(self._event.latlng);
217 if (!self._container)
219 if (self._following) {
220 L.DomUtil.removeClass(self._container, "requesting");
221 L.DomUtil.addClass(self._container, "active");
222 L.DomUtil.addClass(self._container, "following");
224 L.DomUtil.removeClass(self._container, "requesting");
225 L.DomUtil.addClass(self._container, "active");
226 L.DomUtil.removeClass(self._container, "following");
230 var resetVariables = function() {
231 self._active = false;
232 self._locateOnNextLocationFound = self.options.setView;
233 self._following = false;
238 var stopLocate = function() {
240 map.off('dragstart', stopFollowing);
242 L.DomUtil.removeClass(self._container, "requesting");
243 L.DomUtil.removeClass(self._container, "active");
244 L.DomUtil.removeClass(self._container, "following");
247 self._layer.clearLayers();
248 self._circleMarker = undefined;
249 self._circle = undefined;
252 var onLocationError = function (err) {
253 // ignore time out error if the location is watched
254 if (err.code == 3 && this._locateOptions.watch) {
259 self.options.onLocationError(err);
263 map.on('locationfound', onLocationFound, self);
264 map.on('locationerror', onLocationError, self);
270 L.Map.addInitHook(function () {
271 if (this.options.locateControl) {
272 this.locateControl = L.control.locate();
273 this.addControl(this.locateControl);
277 L.control.locate = function (options) {
278 return new L.Control.Locate(options);