Mapbox-gl-js: Capacitor Geolocation Support?

Created on 12 May 2020  路  5Comments  路  Source: mapbox/mapbox-gl-js

mapbox-gl-js version: 1.9.0

Question

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
            }
          );
        }
      });

Links to related documentation

https://docs.mapbox.com/mapbox-gl-js/api/#geolocatecontrol

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.

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;
  }
}

All 5 comments

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!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stevage picture stevage  路  3Comments

samanpwbb picture samanpwbb  路  3Comments

stevage picture stevage  路  3Comments

aendrew picture aendrew  路  3Comments

muesliq picture muesliq  路  3Comments