5
votes

I have an application that serves vector tiles. The features in the tiles are clickable. When a user clicks the map, I pass mapbox-gl's queryRenderedFeatures a 5 x 5px bounding box around the clicked point.

Is there a way to ascertain the lat-lon bounding box that mapbox uses to query its cached tiles? I would like to have this bounding box so I can query the database for the features around the clicked point. I can use the ids of the features in the vector tiles, but this becomes cumbersome/untenable when there are 1000s of features.

Here's how I am getting features near point, where:

  • map is the mapbox map object
  • mapboxLayers are the names of the layers I want to query
  • point is point property of the click event
export const getMapFeaturesNearPoint = ({ map, mapboxLayers, point }) => {
  const { x, y } = point;

  const halfPixels = 5;
  // set bbox as 5px rectangle around clicked point
  const bbox = [
    [x - halfPixels, y - halfPixels],
    [x + halfPixels, y + halfPixels],
  ];
  const features = map.queryRenderedFeatures(bbox, { layers: [...mapboxLayers] })
  return features;
};

Note: I have tried doing the following with the bbox defined above: bbox.map(pt => map.unproject(pt)) to get the lat lon bounding box. From my examination of the mapboxgl source code, it seems the process to unproject queryRenderedFeatures coordinates is a bit more complex than that.

1
Would you be able to provide an example of how you are passing queryRenderedFeatures "1 10 X 10px bounding box around the clicked point"? It sounds like you are trying to figure out how to convert this pixel bounding box into a bounding box defined by geographic coordinates -- is that correct? - Adriana Babakanian
@AdrianaBabakanian thanks for the comment! Yes, I am trying to figure out how to convert the pixel bounding box to geographic coordinate bounding box. I have expanded on my question with some source code, and a (hopefully) more coherent description of how I have tried to use map.unproject - Avocado
@AdrianaBabakanian any ideas here? - Avocado

1 Answers

2
votes

Not very clear to me what you are trying to get, but conceptually speaking, what any queryRenderedFeatures is doing with no filters, is basically find all the tiles in the cache (the ones cached are in the viewport), then per tile, get all the features on them, and merge them all into the result. So, if you use queryRenderedFeatures with no arguments or with only a options argument is equivalent to passing a bounding box encompassing the entire map viewport.

You can check the source code of query_features.js file in Mapbox github repo

With those features in the viewport, it would be easy to reduce them to a lnglat bounding box through a .reduce function that you can use, for instance, as a bounding box to use map.fitBounds.

var coordinates = points;

var bounds = coordinates.reduce(function(bounds, coord) {
  return bounds.extend(coord);
}, new mapboxgl.LngLatBounds(coordinates[0], coordinates[0]));

map.fitBounds(bounds, {
  padding: { top: 50, bottom: 50, left: 50, right: 50 },
  easing(t) {
      return t * (2 - t);
  }
});

But as said, not sure if that's what you are looking for.

Other option would be to use the canvas width and height and unproject them... I found this code while ago,

const canvas = map.getCanvas()
const w = canvas.width
const h = canvas.height
const cUL = map.unproject ([0,0]).toArray()
const cUR = map.unproject ([w,0]).toArray()
const cLR = map.unproject ([w,h]).toArray()
const cLL = map.unproject ([0,h]).toArray()
const coordinates = [cUL,cUR,cLR,cLL,cUL]