4

I'm using Mapbox GL JS to load in GeoJSON from an external URL on some pages. I would like to automatically fit the map to the boundaries of the polygon I'm loading.

I understand that turf.js's bbox method can help with this, but I'm not sure how to get the GeoJSON into the turf.bbox call.

This is my code right now:

map.addSource('mylayer', {
    type: 'geojson',
    data: '/boundaries.geojson'
});
map.addLayer({
    "id": "mylayer",
    "type": "fill",
    "source": "mylayer",
    'paint': {
        'fill-color': '#088',
        'fill-opacity': 0.6
    }
});
var bbox = turf.bbox('mylayer');
map.fitBounds(bbox, {padding: 20});

But it fails with turf.min.js:1 Uncaught Error: Unknown Geometry Type. The docs say that bbox wants "any GeoJSON object".

How do I do this correctly? I'd obviously rather not load the external file twice.

Richard
  • 62,943
  • 126
  • 334
  • 542

3 Answers3

6
  1. Loading data from a remote source is asynchronous. That is, you are trying to analyze the data before it was loaded.

So you need handle the sourcedata event.

  1. The input parameter of the bbox function is a valid GeoJson object.

  2. As already noted, the Turf.js does not know anything about the Mapbox, so you need to read the loaded data from the source in addition.

  3. And for an example:

    map.addSource('mylayer', {
        type: 'geojson',
        data: '/boundaries.geojson'
    });
    map.addLayer({
        "id": "mylayer",
        "type": "fill",
        "source": "mylayer",
        'paint': {
            'fill-color': '#088',
            'fill-opacity': 0.6
        }
    });

    map.on('sourcedata', function (e) {
      if (e.sourceId !== 'mylayer' || !e.isSourceLoaded) return
      var f = map.querySourceFeatures('mylayer')
      if (f.length === 0) return
      var bbox = turf.bbox({
        type: 'FeatureCollection',
        features: f
      });
      map.fitBounds(bbox, {padding: 20});    
    })

Rohan Khude
  • 4,455
  • 5
  • 49
  • 47
stdob--
  • 28,222
  • 5
  • 58
  • 73
  • 1
    This worked for me but note that you have both the source name and layer name identical. In the 'source data' event listener, 'mylayer' refers to the source name. – CrnlWes Mar 10 '20 at 16:46
0

The turf.bbox function accepts the GeoJSON as an Object, it doesn't know about Mapbox.

A Mapbox GeoJSON Source has the full GeoJSON object stored privately at ._data when you created the Source by passing the full GeoJSON (rather than passing a URL), so you could do:

turf.bbox(map.getSource('mylayer')._data);

However ._data could change at any time in any new release without it being detailed in the release notes since it's a private API.

There is a public API map.querySourceFeatures:

map.querySourceFeatures('mylayer');

but it will only return the features in the current view and after being tiled by geojson-vt meaning even when viewing the whole world it's still not guaranteed to return all features.

AndrewHarvey
  • 2,867
  • 11
  • 21
  • 1
    Thanks! Actually it seems my GeoJSON source isn't storing the full GeoJSON object at `._data` - what I have there is the URL, i.e. `'/boundaries.geojson'`. – Richard Mar 19 '18 at 09:40
  • And `map.querySourceFeatures('ward')` returns `[]`. – Richard Mar 19 '18 at 09:42
  • You're right, _data won't work when you passed a URL to create the Source. You'll need to download the GeoJSON yourself first and pass in the full Object when creating a new Source, that way you'll have access to the original GeoJSON data for turf. querySourceFeatures will return the tiled data by geojson-vt so if you're zoomed out and it decides to drop the feature as it's too small or outside the viewport, it won't be included in the results. – AndrewHarvey Mar 19 '18 at 23:03
0

Instead of passing the URL to addSource, download it first and then pass the full GeoJSON Object.

$.getJSON('/boundaries.geojson', (geojson) => {
   map.addSource('mylayer', {
       type: 'geojson',
       data: geojson
   });
   map.fitBounds(turf.bbox(geojson), {padding: 20});
}

Subsitite jQuery with any other request library.

AndrewHarvey
  • 2,867
  • 11
  • 21