]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/leaflet/leaflet.locate.js
Update to latest leaflet-osm plugin
[rails.git] / vendor / assets / leaflet / leaflet.locate.js
1 /*
2 Copyright (c) 2013 Dominik Moritz
3
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
6 */
7 L.Control.Locate = L.Control.extend({
8     options: {
9         position: 'topleft',
10         drawCircle: true,
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
13         // range circle
14         circleStyle: {
15             color: '#136AEC',
16             fillColor: '#136AEC',
17             fillOpacity: 0.15,
18             weight: 2,
19             opacity: 0.5
20         },
21         // inner marker
22         markerStyle: {
23             color: '#136AEC',
24             fillColor: '#2A93EE',
25             fillOpacity: 0.7,
26             weight: 2,
27             opacity: 0.9,
28             radius: 5
29         },
30         // changes to range circle and inner marker while following
31         // it is only necessary to provide the things that should change
32         followCircleStyle: {},
33         followMarkerStyle: {
34             //color: '#FFA500',
35             //fillColor: '#FFB000'
36         },
37         metric: true,
38         onLocationError: function(err) {
39             // this event is called in case of any location error
40             // that is not a time out error.
41             alert(err.message);
42         },
43         onLocationOutsideMapBounds: function(context) {
44             // this event is repeatedly called when the location changes
45             alert(context.options.strings.outsideMapBoundsMsg);
46         },
47         setView: true, // automatically sets the map view to the user's location
48         strings: {
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"
52         },
53         locateOptions: {}
54     },
55
56     onAdd: function (map) {
57         var container = L.DomUtil.create('div', 'control-locate');
58
59         var self = this;
60         this._layer = new L.LayerGroup();
61         this._layer.addTo(map);
62         this._event = undefined;
63
64         this._locateOptions = {
65             watch: true  // if you overwrite this, visualization cannot be updated
66         };
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
71         });
72
73         // extend the follow marker style and circle from the normal style
74         var tmp = {};
75         L.extend(tmp, this.options.markerStyle, this.options.followMarkerStyle);
76         this.options.followMarkerStyle = tmp;
77         tmp = {};
78         L.extend(tmp, this.options.circleStyle, this.options.followCircleStyle);
79         this.options.followCircleStyle = tmp;
80
81         var link = L.DomUtil.create('a', 'control-button', container);
82         link.innerHTML = "<span class='icon geolocate'></span>";
83         link.href = '#';
84         link.title = this.options.strings.title;
85
86         L.DomEvent
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())) {
92                     stopLocate();
93                 } else {
94                     if (self.options.setView) {
95                         self._locateOnNextLocationFound = true;
96                     }
97                     if(!self._active) {
98                         map.locate(self._locateOptions);
99                     }
100                     self._active = true;
101                     if (self.options.follow) {
102                         startFollowing();
103                     }
104                     if (!self._event) {
105                         L.DomUtil.addClass(self._container, "requesting");
106                         L.DomUtil.removeClass(self._container, "active");
107                         L.DomUtil.removeClass(self._container, "following");
108                     } else {
109                         visualizeLocation();
110                     }
111                 }
112             })
113             .on(link, 'dblclick', L.DomEvent.stopPropagation);
114
115         var onLocationFound = function (e) {
116             // no need to do anything if the location has not changed
117             if (self._event &&
118                 (self._event.latlng.lat == e.latlng.lat &&
119                  self._event.latlng.lng == e.latlng.lng)) {
120                 return;
121             }
122
123             if (!self._active) {
124                 return;
125             }
126
127             self._event = e;
128
129             if (self.options.follow && self._following) {
130                 self._locateOnNextLocationFound = true;
131             }
132
133             visualizeLocation();
134         };
135
136         var startFollowing = function() {
137             self._following = true;
138             if (self.options.stopFollowingOnDrag) {
139                 map.on('dragstart', stopFollowing);
140             }
141         };
142
143         var stopFollowing = function() {
144             self._following = false;
145             if (self.options.stopFollowingOnDrag) {
146                 map.off('dragstart', stopFollowing);
147             }
148             visualizeLocation();
149         };
150
151         var isOutsideMapBounds = function () {
152             if (self._event === undefined)
153                 return false;
154             return map.options.maxBounds &&
155                 !map.options.maxBounds.contains(self._event.latlng);
156         };
157
158         var visualizeLocation = function() {
159             if (self._event.accuracy === undefined)
160                 self._event.accuracy = 0;
161
162             var radius = self._event.accuracy;
163             if (self._locateOnNextLocationFound) {
164                 if (isOutsideMapBounds()) {
165                     self.options.onLocationOutsideMapBounds(self);
166                 } else {
167                     map.fitBounds(self._event.bounds);
168                 }
169                 self._locateOnNextLocationFound = false;
170             }
171
172             // circle with the radius of the location's accuracy
173             var style;
174             if (self.options.drawCircle) {
175                 if (self._following) {
176                     style = self.options.followCircleStyle;
177                 } else {
178                     style = self.options.circleStyle;
179                 }
180
181                 if (!self._circle) {
182                     self._circle = L.circle(self._event.latlng, radius, style)
183                         .addTo(self._layer);
184                 } else {
185                     self._circle.setLatLng(self._event.latlng).setRadius(radius);
186                 }
187             }
188
189             var distance, unit;
190             if (self.options.metric) {
191                 distance = radius.toFixed(0);
192                 unit = "meters";
193             } else {
194                 distance = (radius * 3.2808399).toFixed(0);
195                 unit = "feet";
196             }
197
198             // small inner marker
199             var m;
200             if (self._following) {
201                 m = self.options.followMarkerStyle;
202             } else {
203                 m = self.options.markerStyle;
204             }
205
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}))
210                     .addTo(self._layer);
211             } else {
212                 self._circleMarker.setLatLng(self._event.latlng)
213                     .bindPopup(L.Util.template(t, {distance: distance, unit: unit}))
214                     ._popup.setLatLng(self._event.latlng);
215             }
216
217             if (!self._container)
218                 return;
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");
223             } else {
224                 L.DomUtil.removeClass(self._container, "requesting");
225                 L.DomUtil.addClass(self._container, "active");
226                 L.DomUtil.removeClass(self._container, "following");
227             }
228         };
229
230         var resetVariables = function() {
231             self._active = false;
232             self._locateOnNextLocationFound = self.options.setView;
233             self._following = false;
234         };
235
236         resetVariables();
237
238         var stopLocate = function() {
239             map.stopLocate();
240             map.off('dragstart', stopFollowing);
241
242             L.DomUtil.removeClass(self._container, "requesting");
243             L.DomUtil.removeClass(self._container, "active");
244             L.DomUtil.removeClass(self._container, "following");
245             resetVariables();
246
247             self._layer.clearLayers();
248             self._circleMarker = undefined;
249             self._circle = undefined;
250         };
251
252         var onLocationError = function (err) {
253             // ignore time out error if the location is watched
254             if (err.code == 3 && this._locateOptions.watch) {
255                 return;
256             }
257
258             stopLocate();
259             self.options.onLocationError(err);
260         };
261
262         // event hooks
263         map.on('locationfound', onLocationFound, self);
264         map.on('locationerror', onLocationError, self);
265
266         return container;
267     }
268 });
269
270 L.Map.addInitHook(function () {
271     if (this.options.locateControl) {
272         this.locateControl = L.control.locate();
273         this.addControl(this.locateControl);
274     }
275 });
276
277 L.control.locate = function (options) {
278     return new L.Control.Locate(options);
279 };