openstreetmap-website/vendor/assets/leaflet/leaflet.locate.js
2013-12-06 19:40:54 +00:00

298 lines
10 KiB
JavaScript

/*
Copyright (c) 2013 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'
},
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(context) {
// this event is repeatedly called when the location changes
alert(context.options.strings.outsideMapBoundsMsg);
},
setView: true, // automatically sets the map view to the user's location
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: {}
},
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 = {
watch: true // if you overwrite this, visualization cannot be updated
};
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', 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 && (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) {
L.DomUtil.addClass(self._container, "requesting");
L.DomUtil.removeClass(self._container, "active");
L.DomUtil.removeClass(self._container, "following");
} 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._following = true;
if (self.options.stopFollowingOnDrag) {
map.on('dragstart', stopFollowing);
}
};
var stopFollowing = function() {
map.fire('stopfollowing');
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 });
}
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) {
L.DomUtil.removeClass(self._container, "requesting");
L.DomUtil.addClass(self._container, "active");
L.DomUtil.addClass(self._container, "following");
} else {
L.DomUtil.removeClass(self._container, "requesting");
L.DomUtil.addClass(self._container, "active");
L.DomUtil.removeClass(self._container, "following");
}
};
var resetVariables = function() {
self._active = false;
self._locateOnNextLocationFound = self.options.setView;
self._following = false;
};
resetVariables();
var stopLocate = function() {
map.stopLocate();
map.off('dragstart', 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 && this._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);
};