mapbox-gl-js version: 1.9.0
When implementing a MapboxGL.GeolocateControl in my Ionic 5 app, using the Capacitor Geolocation plugin, the button fails with the error:
[blocked] Access to geolocation was blocked over insecure connection to capacitor://localhost.
According to the Capacitor team, this is because Apple doesn't consider capacitor://localhost to be secure. I'm not sure if this is a permanent decision or a bug (Capacitor lists it as a "known apple bug").
Presumably this limitation is specific to navigator.geolocation in WKWebView because I have no issues getting user location data in native contexts. In the code below, I can recover from an error by instead using the native location data and flyTo(). Obviously, this wouldn't be a great production solution.
So the question is, how can I get MapboxGL.GeolocateControl to use the native device location provided by Capacitor? Is it possible to pass it default coordinates to use as a fallback?
let geolocate = new MapboxGL.GeolocateControl({
positionOptions: {
enableHighAccuracy: true
},
trackUserLocation: true
});
map.addControl(geolocate);
geolocate.on("error", () => {
// this.location.userLocation in this case is provided by Capacitor Geolocation
if (this.location.userLocation) {
this.map.flyTo(
{
center: [
this.location.userLocation.lon,
this.location.userLocation.lat
],
zoom: 15,
bearing: 0
}
);
}
});
Hi @ebellempire unfortunately I don't think there's much we can do about this as it's specific to the implementation of Capacitor/Ionic and Safari (assuming that's where this is happening). The Geolocation Control at its at heart is basically just a wrapper around the geolocation API and it doesn't have a way to provide coordinates. I think the solution you have in your post is probably the right way to go for this. I'm not familiar with Capacitor (perhaps it always uses the capacitor://localhost scheme) but this seems like it could/should only be an issue during development in the Safari browser. Either way, I think a solution has to be on the Capacitor side so I'm going to close this as not actionable for us.
Ok, thanks. After looking at this some more I ended up just rolling it into my custom controls (below in case someone finds it useful later). The only difference, for now, is that I just don't have the nice animated user location indicator.
import { Injectable } from "@angular/core";
import * as MapboxGL from "mapbox-gl";
@Injectable({
providedIn: "root"
})
export class CustomControlGroup {
constructor(
private _user_zoom: any,
private _userLocation: any = null,
private _map: MapboxGL.Map = null,
private _container: any = null,
private _custom_geolocation: any = null
) {}
insertControls() {
this._container = document.createElement("div");
this._container.classList.add("mapboxgl-ctrl");
this._container.classList.add("mapboxgl-ctrl-group");
this._container.classList.add("mapboxgl-ctrl-custom");
if (this._userLocation) {
this._custom_geolocation = document.createElement("button");
this._custom_geolocation.classList.add("mapboxgl-ctrl-icon");
this._custom_geolocation.classList.add(
"mapboxgl-ctrl-custom-geolocation"
);
this._custom_geolocation.setAttribute("aria-label", "Find My Location");
this._custom_geolocation.addEventListener("click", (e: any) => {
this._map.flyTo(
{
center: [this._userLocation.lon, this._userLocation.lat],
zoom: this._user_zoom,
bearing: 0
},
e
);
});
this._container.appendChild(this._custom_geolocation);
}
}
onAdd(map: any) {
this._map = map;
this._container = document.createElement("div");
this._container.id = "custom-control-group";
this._container.classList =
"mapboxgl-ctrl mapboxgl-ctrl-group custom-control-group";
this.insertControls();
return this._container;
}
onRemove() {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
}
}
@ebellempire Thank you for sharing the code! I have exactly the same problem. But I don't know how to call this to add to the map. Would you mind sharing the code for adding it to the map? Thank you so much.
Hi @mybluedog24 ,
In my map service, I import the code above (CustomControlGroup):
import { CustomControlGroup } from "./controls";
@Injectable({
providedIn: "root"
})
export class MapService {
customCtrl: CustomControlGroup;
... then I add the controls after the map has loaded:
.on("load", () => {
this.map.addControl(this.customCtrl, "top-right");
});
You'll probably also want to add some CSS so that the buttons look like they belong. For my case, I downloaded the SVG for the navigate-outline icon from Ionicons and added it to my CSS mapboxgl-ctrl-custom-geolocation class:
.mapboxgl-ctrl-icon.mapboxgl-ctrl-custom-geolocation {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 20 20' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10 4C9 4 9 5 9 5v.1A5 5 0 005.1 9H5s-1 0-1 1 1 1 1 1h.1A5 5 0 009 14.9v.1s0 1 1 1 1-1 1-1v-.1a5 5 0 003.9-3.9h.1s1 0 1-1-1-1-1-1h-.1A5 5 0 0011 5.1V5s0-1-1-1zm0 2.5a3.5 3.5 0 110 7 3.5 3.5 0 110-7z'/%3E%3Ccircle cx='10' cy='10' r='2'/%3E%3C/svg%3E");
background-size: 100%;
background-position: center center;
background-repeat: no-repeat;
}
This is all somewhat simplified, but it should put you on the right path.
Thank you so much!
Most helpful comment
Ok, thanks. After looking at this some more I ended up just rolling it into my custom controls (below in case someone finds it useful later). The only difference, for now, is that I just don't have the nice animated user location indicator.