Show accuracy circle around the browser-location fallback marker
Re-adds the gray radius MapLibre's own GeolocateControl used to draw, using the same element/class and pixel-radius calculation.
This commit is contained in:
1 parent
67585cd99a
commit
141b7a08ce
1 file changed
+38
-2
+38
-2
@@ -18,7 +18,10 @@ export class LocateControl {
|
|||||||
this._gnssControl = gnssControl;
|
this._gnssControl = gnssControl;
|
||||||
this._active = false;
|
this._active = false;
|
||||||
this._marker = null;
|
this._marker = null;
|
||||||
|
this._circleMarker = null;
|
||||||
|
this._accuracy = null;
|
||||||
this._watchId = null;
|
this._watchId = null;
|
||||||
|
this._onMapChange = () => this._updateCircleRadius();
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(map) {
|
onAdd(map) {
|
||||||
@@ -102,13 +105,15 @@ export class LocateControl {
|
|||||||
this._active = true;
|
this._active = true;
|
||||||
this._button.classList.add("maplibregl-ctrl-geolocate-active");
|
this._button.classList.add("maplibregl-ctrl-geolocate-active");
|
||||||
this._watchId = navigator.geolocation.watchPosition(
|
this._watchId = navigator.geolocation.watchPosition(
|
||||||
(position) => this._setPosition(position.coords.longitude, position.coords.latitude),
|
(position) => this._setPosition(position.coords.longitude, position.coords.latitude, position.coords.accuracy),
|
||||||
() => {
|
() => {
|
||||||
window.alert("Could not determine your location.");
|
window.alert("Could not determine your location.");
|
||||||
this._stop();
|
this._stop();
|
||||||
},
|
},
|
||||||
{ enableHighAccuracy: true },
|
{ enableHighAccuracy: true },
|
||||||
);
|
);
|
||||||
|
this._map.on("zoom", this._onMapChange);
|
||||||
|
this._map.on("move", this._onMapChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recenters on every fix, following the position the way GeolocateControl
|
// Recenters on every fix, following the position the way GeolocateControl
|
||||||
@@ -123,16 +128,40 @@ export class LocateControl {
|
|||||||
// .gnss-marker — reusing that green for a browser-estimated position too
|
// .gnss-marker — reusing that green for a browser-estimated position too
|
||||||
// looked like this control was somehow still using GNSS data even when
|
// looked like this control was somehow still using GNSS data even when
|
||||||
// it very much wasn't; distinct colors for distinct sources.
|
// it very much wasn't; distinct colors for distinct sources.
|
||||||
_setPosition(lon, lat) {
|
_setPosition(lon, lat, accuracy) {
|
||||||
|
this._accuracy = accuracy;
|
||||||
if (!this._marker) {
|
if (!this._marker) {
|
||||||
const el = document.createElement("div");
|
const el = document.createElement("div");
|
||||||
el.className = "maplibregl-user-location-dot";
|
el.className = "maplibregl-user-location-dot";
|
||||||
this._marker = new Marker({ element: el }).setLngLat([lon, lat]).addTo(this._map);
|
this._marker = new Marker({ element: el }).setLngLat([lon, lat]).addTo(this._map);
|
||||||
|
const circleEl = document.createElement("div");
|
||||||
|
circleEl.className = "maplibregl-user-location-accuracy-circle";
|
||||||
|
this._circleMarker = new Marker({ element: circleEl, pitchAlignment: "map" }).setLngLat([lon, lat]).addTo(this._map);
|
||||||
this._map.flyTo({ center: [lon, lat], zoom: Math.max(this._map.getZoom(), 15) });
|
this._map.flyTo({ center: [lon, lat], zoom: Math.max(this._map.getZoom(), 15) });
|
||||||
} else {
|
} else {
|
||||||
this._marker.setLngLat([lon, lat]);
|
this._marker.setLngLat([lon, lat]);
|
||||||
|
this._circleMarker.setLngLat([lon, lat]);
|
||||||
this._map.easeTo({ center: [lon, lat] });
|
this._map.easeTo({ center: [lon, lat] });
|
||||||
}
|
}
|
||||||
|
this._updateCircleRadius();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirrors MapLibre's own GeolocateControl._updateCircleRadiusIfNeeded:
|
||||||
|
// measures real-world meters per pixel at the marker's position (rather
|
||||||
|
// than assuming a flat conversion, which breaks away from the equator)
|
||||||
|
// by projecting a 100px offset back to lng/lat and measuring that
|
||||||
|
// distance, then sizes the circle element to the accuracy radius in
|
||||||
|
// that many pixels.
|
||||||
|
_updateCircleRadius() {
|
||||||
|
if (!this._circleMarker || !this._accuracy) return;
|
||||||
|
|
||||||
|
const lngLat = this._marker.getLngLat();
|
||||||
|
const point = this._map.project(lngLat);
|
||||||
|
const offsetLngLat = this._map.unproject([point.x + 100, point.y]);
|
||||||
|
const metersPerPixel = lngLat.distanceTo(offsetLngLat) / 100;
|
||||||
|
const diameter = (2 * this._accuracy) / metersPerPixel;
|
||||||
|
this._circleMarker.getElement().style.width = `${diameter.toFixed(2)}px`;
|
||||||
|
this._circleMarker.getElement().style.height = `${diameter.toFixed(2)}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stop() {
|
_stop() {
|
||||||
@@ -142,9 +171,16 @@ export class LocateControl {
|
|||||||
navigator.geolocation.clearWatch(this._watchId);
|
navigator.geolocation.clearWatch(this._watchId);
|
||||||
this._watchId = null;
|
this._watchId = null;
|
||||||
}
|
}
|
||||||
|
this._map.off("zoom", this._onMapChange);
|
||||||
|
this._map.off("move", this._onMapChange);
|
||||||
if (this._marker) {
|
if (this._marker) {
|
||||||
this._marker.remove();
|
this._marker.remove();
|
||||||
this._marker = null;
|
this._marker = null;
|
||||||
}
|
}
|
||||||
|
if (this._circleMarker) {
|
||||||
|
this._circleMarker.remove();
|
||||||
|
this._circleMarker = null;
|
||||||
|
}
|
||||||
|
this._accuracy = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user