AlpacaTravel / react-map-gl-alt

React Mapbox-GL-JS binding with improved performance, event and API exposure
Other
42 stars 2 forks source link

Geolocate #5

Open seddonm1 opened 7 years ago

seddonm1 commented 7 years ago

Hi, Have you worked out a method for listening to geolocate events so a GPS tracking dot could be added to the maps?

cammanderson commented 7 years ago

We are going to be working on this internally over the next fortnight. Hopefully I can post some progress as examples. :-) Initially, I imagine you'd have to listen to the geolocation events separately and update your map style source, and have a corresponding layer style to match.

seddonm1 commented 7 years ago

Great. In Sydney so on same timezone if you need testing help. Good work on library and your site looks good 👍

cammanderson commented 7 years ago

Hi @seddonm1,

Just sharing a quick gist for binding a map center to the geolocation of a user on mount. https://gist.github.com/cammanderson/5e4a32d4b19cf2413aeee5d88a3b3f58

This is just early on for you in case you want to progress something quickly.

Depending on your use case, you could have it so that onClick the geolocation event is called once off to update the map position; https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition

I am assuming that you have a custom map style where you are in control of the source and layers? Instead of supplying your map a mapStyle URL? You'll need that to be able to modify the style dynamically. You can load it in using Axios or similar in a middleware or using webpack/json-loader approach that gives you access to the style as an object where you could possibly mutate it.

The next step to have it show up as a dot would be to have a map style source that is a type 'geoJson' with a feature point that is updated with the lnglat position obtained by the watch position.

To setup a geoJSON source, there is this documentation available https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson

To watch the users position as it updates, you can use watchPosition and then have it update your above geoJSON source https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition

You could then style the source with a layer that has a symbols for the dot as you want; https://www.mapbox.com/mapbox-gl-style-spec/#layers-symbol

I'll do my best to share more example code to implement your original request.

Cheers Cameron

seddonm1 commented 7 years ago

Thanks Cameron. Sorry for the delay I have been building backend stuff too.

Yes we will use a combination of layers so I will try your code. I was thinking a geolocate overlay would be very nice. I just need to get my head around the mapboxgl js api as I have done a lot of leafletjs.

seddonm1 commented 7 years ago

Great! works well.

Can you please bump the mapboxgl version to 0.29 so we get access to a few extra 'circle' rendering options (circle-stroke-width and circle-stroke-color). I will see if i can work out how to convert the geolocate accuracy attribute to circle-radius to do the confidence ring like other maps.

{
  'id': 'geolocate_point',
  'type': 'circle',
  'source': "geolocate",
  'paint': {
    "circle-radius": 6.5,
    "circle-color": "#4285F4",
    "circle-stroke-width": 1,
    "circle-stroke-color": "#fff"
  }
}
cammanderson commented 7 years ago

OK yes, I will do a version bump. Give me until Monday if that is alright.

cammanderson commented 7 years ago

Hi @seddonm1,

I've pushed the changes to version as a bump to master, but our tests aren't passing. We are getting a failed to invert matrix error. I will need to investigate that. From a browser based test though, it appears to be working fine. Maybe the test needs to be updated.

I'll continue to investigate on the weekend.

Speak soon Cam

cammanderson commented 7 years ago

I'm going to move this into a branch and revert in master; that was a wrong to push to master. I'll create the branch for the next bump.

cammanderson commented 7 years ago

OK PR ready for pull and version bump.

cammanderson commented 7 years ago

OK @seddonm1 - released v0.4.0 with mapbox-gl-js 0.31.0 support. Let me know if there are any issues.

seddonm1 commented 7 years ago

great. will test.

also, you should probably add the accuracy attribute to your state so we can use it to show the confidence circle (once i get the maths sorted):

  _updatePosition(position) {
    this.setState({
      target: {
        ...this.state.target,
        center: [position.coords.longitude, position.coords.latitude],
        accuracy: position.coords.accuracy
      },
    });
  }
seddonm1 commented 7 years ago

Hi, Just an update. My conversion from geolocate accuracy to meters (to show the geolocate confidence circle) is using this formula. I think it's correct but a bit hard to find much clarity. It is working nicely:

let accuracy = viewport ? 1/(40075016.686 * Math.abs(Math.cos(target.center[1] * 180/Math.PI)) / Math.pow(2, viewport.zoom+8)) * target.accuracy : 0
cammanderson commented 7 years ago

Great progress @seddonm1! We are progressing slowly on our release, and hopefully will have something ready in the next couple of days. I'm keen to try out your accuracy formula! Did you by chance have a gif of your progress in action? (they always create a bit of wow!)

seddonm1 commented 7 years ago

Hi Cam. Nothing very exciting so no git at the moment. But here are my layer defs. Basically the flow is:

  1. enable geolocate and register _updatePosition callback
  2. _updatePosition sets target.accuracy = position.coords.accuracy
  3. calculate pixels: let accuracy = viewport ? 1/(40075016.686 * Math.abs(Math.cos(target.center[1] * 180/Math.PI)) / Math.pow(2, viewport.zoom+8)) * target.accuracy : 0
  4. render block includes a style.layers[style.layers.findIndex(layer => layer.id === 'geolocate_confidence')].paint["circle-radius"] = accuracy

Sources:

      "geolocate": {
        "type": "geojson",
        "data": {
          "type": "Feature",
          "geometry": {
            "type": "Point",
            "coordinates": [0,0]
          },
        }
      }

Layers:

    {
        "id": "geolocate_confidence",
        "type": "circle",
        "source": "geolocate",
        "layout": {
            "visibility": "none",
        },
        "paint": {
          "circle-radius": 0,
          "circle-color": "#4285F4",
          "circle-opacity": 0.3
        }
    },
    {
        "id": "geolocate",
        "type": "circle",
        "source": "geolocate",
        "layout": {
            "visibility": "none",
        },
        "paint": {
          "circle-radius": 7.5,
          "circle-color": "#4285F4",
          "circle-opacity": 1,
          "circle-stroke-width": 2.5,
          "circle-stroke-color": "#FFFFFF",
          "circle-stroke-opacity": 1,
        }
    },
seddonm1 commented 7 years ago

I have also had great success with the custom d3 overlays (which I thought was going to be difficult but with Uber's viewport-mercator-project project they have done all the hard work!

https://github.com/uber/react-map-gl/blob/master/src/overlays/svg.react.js