0

Currently working with Mapbox gl js. I know for OpenLayers there's a function called GetFeatureInfo to do this, but is there a similar one for Mapbox? What I want to do is whenever the user clicks on a point in the map, which is a Mapbox style, it will show the data from the currently added layers at that point. I used this example to add the layer from GeoServer to my Mapbox map: https://www.mapbox.com/mapbox-gl-js/example/wms/

  • 2
    While mapbox-gl might not have the exact getFeatureInfo function as OpenLayers, you can still make the api call to GeoServer to get data from the point clicked using a standard ajax call. docs.geoserver.org/stable/en/user/services/wms/… – Owen J Lamb Jan 2 at 18:16
  • Thanks for the response. I've tried that, but the data I'm getting is different from the actual data. For one thing, in mapbox the x/y coordinates on a clicked point are relative to the top left side of the map, but in geoserver I think it's based on the geographical region of the 256x256 portion of the raster layer. Is there a way to get the proper x/y coordinates and bounding box with mapbox? – supaaaRoboto Jan 3 at 0:45
  • I did something similar very recently. Here are the steps I took: 1) The mouse event from Mapbox also has a lngLat property which you can use the get long/lat coordinates; 2) Convert the long/lat to Web Mercator 3857 projection; 3) Construct a small bounding box around that point and use that in the WMS query. – Dave Hartnoll Jan 3 at 17:21
  • Thanks for the insight Dave. How would I go about converting the long/lat to Web Mercator 3857? When I add the layer I already pull the wms layer from geoserver as espg:3857. Also what should be put into the X/Y parameters of the wms get request? I tried using e.point before, but I learned that for mapbox e.point is the x, y coordinates of the mousemove event relative to the top-left corner of the map and as a result it was giving me wrong data, if not errors. – supaaaRoboto Jan 3 at 17:47
  • Firstly, use e.lngLat, not e.point. Both are available, but use the former to get access to the long/lat. You should able to find simple code for a long/lat->3857 conversion with an online search. I'm pushed for time right now but if you're still stuck I'll try to write up a full answer when I can. – Dave Hartnoll Jan 4 at 1:27
0

Normally, with WMS, you start by requesting a single raster-image map that represents an area bounded by some real-world coordinates. The coordinates will be in some specific system as identified by the CRS attribute of the GetMap request. The request also includes the required width and height of the returned raster image.

To use the GetFeatureInfo request, you would get the X,Y coordinates of some mouse click, relative to the top-left corner of the previously requested map and the WMS server will take care of converting that to real-world coordinates in order to find the requested feature info. The GetFeatureInfo request includes the same bounding box and width/height attributes as GetMap, and in addition, also has an x,y (or i,j) position to identify the point of interest.

With Mapbox, things are slightly different. A Mapbox setup similar to the one linked in the question splits the area of interest into multiple 256x256 tiles, and issues separate requests to the WMS server for each tile. Here's a typical URL as might be used to configure the Mapbox source, and that will be used to get the image for each tile: (attributes shown on separate lines for readability - this should all be one continuous line with no spaces)

http://your/geoserver/url?
    BBOX={bbox-epsg-3857}&
    SERVICE=WMS&
    REQUEST=GetMap&
    CRS=EPSG:3857&
    WIDTH=256&
    HEIGHT=256&
    LAYERS=your_layer_name&
    FORMAT=image/png

Unfortunately, when the user clicks on a point somewhere on one of the displayed tiles, we have no easy way of knowing which tile was clicked, and the offsets within that tile in order to construct an appropriate GetFeatureInfo query. We can get the X,Y relative to the top left of the map, but cannot easily convert that into an offset of any of the GetMap requests that Mapbox has issued. Fortunately, we don't have to...

The GetFeatureInfo will work with any imaginary map that may or may not have been returned by GetMap (or at least it does for GeoServer - I can't speak for other WMS servers). One way would be to work out the bounding box for the entire map display and then use the X,Y offset of the click in a GetFeatureInfo request. While this would work in principle, I think it would get far too tricky when the on-screen map is rotated or tilted.

My alternative solution was to imagine a very small map centred around the point clicked. I arbitrarily chose a distance of 50m in each NSEW direction, giving a 100mx100m area and then I construct a GetFeatureInfo request for the exact centre of this area. This is the GetFeatureInfo URL pattern we're aiming to construct:

http://your/geoserver/url?
    SERVICE=WMS&
    VERSION=1.3.0&
    REQUEST=GetFeatureInfo&
    BBOX={bbox-epsg-3857}&
    CRS=EPSG:3857&
    WIDTH=256&
    HEIGHT=256&
    LAYERS=your_layer_name&
    STYLES=&
    FORMAT=image/png&
    QUERY_LAYERS=your_layer_name&
    INFO_FORMAT=application/json&
    I=128&
    J=128

If you start with a similar pattern (but with actual values for the WMS server and the layer name), then the only thing still to replace in this pattern is the {bbox-epsg-3857} part to represent the bounding box of our small imaginary map surrounding the point of interest. Notice that we will abitrarily ask for the bounding box to represent a 256x256 pixel square, and our point of interest is at 128,128, exactly in the centre.

The mouse event's X,Y click position, e.point is not useful for our purposes, so we can use the e.lngLat position instead. The first step would be to convert this to 3857 projection so we can construct the bounding box for our imaginary 100mx100m map:

// e is the Mapbox mouse click event
var r = 6378137 * Math.PI * 2;
var x = (e.lngLat.lng / 360) * r;
var sin = Math.sin(e.lngLat.lat * Math.PI / 180);
var y = 0.25 * Math.log((1 + sin) / (1 - sin)) / Math.PI * r;
// x,y = the Web Mercator 3857 coordinates of the click position

Construct the bounding box:

var bbox = (x-50) + ',' + (y-50) + ',' + (x+50) + ',' + (y+50);

Now construct the final URL, starting from the pattern we specified above:

var url = 'http://your/geoserver/url?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetFeatureInfo&BBOX={bbox-epsg-3857}&CRS=EPSG:3857&WIDTH=256&HEIGHT=256&LAYERS=your_layer_name&STYLES=&FORMAT=image/png&QUERY_LAYERS=your_layer_name&INFO_FORMAT=application/json&I=128&J=128';
url = url.replace('{bbox-epsg-3857}', bbox);

// url is now ready to return a JSON representation of
// features at the clicked point

Caveats: 1) This solution may need more work if the clicked point was somewhere near the international dateline (in my own use case, that will never happen); 2) I've no idea whether GeoServer (or any other WMS server) actually constructs the 'imaginary' map during a GetFeatureInfo request but if it does, there may be a more efficient solution. This solution has worked well enough for me!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.