Monday, November 13, 2017

QGIS: Adding Bounding Box Ordinates as Shapefile Attributes

I mentioned last week, that for the purpose of zooming to a hydrology region when it's selected, we added some attributes to a CSV of metadata, which we were already loading for other UI and statistical purposes.

These attributes were called bbox_s, bbox_w, bbox_n, and bbox_e and are simply floats in WGS84 lat-long. But I thought I'd share how I generated 4 fields representing the bounding box, for these regions (and then later for a few hundred individual watersheds) quickly and easily.

To back up a bit:

  • The CSV was generated from the DBF component of that hydrology regions shapefile. Excel can load up the DBF just fine, then you use Save As and export as a CSV. Easy.
  • The shapefile has attributes of statistics which were generated by other means. The stats were miles of river, number of gauges, that sort of deal where GIS was the answer.
  • The only missing attributes now are the bounding box fields, preferably 4 floats in WGS84 lat-long.
This was done with QGIS's Field Calculator, using built-in functionality. The Field Calculator supports geometry operations including xmin and transform. So it's simple:

Take note of what SRS you are using, particularly its EPSG code.

Create a new field called bbox_s of type float. Don't forget to give it a Precision for after the decimal place! If you leave Precision at 0, the fields will still calculate but will then be truncated to whole numbers after you save.

Populate it with this formula, being sure to replace the first SRS with your dataset's own SRS:


y_min(transform($geometry, 'EPSG:3310', 'EPSG:4326'))

And there you are! You now have an attribute with the southern ordinate of the bbox. Repeat this 3 more times:

bbox_w
x_min(transform($geometry, 'EPSG:3310', 'EPSG:4326'))
 
bbox_e
x_max(transform($geometry, 'EPSG:3310', 'EPSG:4326'))
 
bbox_s
y_min(transform($geometry, 'EPSG:3310', 'EPSG:4326'))
bbox_n
y_max(transform($geometry, 'EPSG:3310', 'EPSG:4326'))

Save your edits, and you're nearly done. Load up the DBF in Excel, maybe tidy up some other fields and their formatting, and export as CSV.


Monday, November 6, 2017

More Mapbox! Using querySourceFeatures() ... or not

The project where I've been exploring Mapbox GL API continues along, and everyone is quite pleased with how it's coming along. Here's another technical posting, this time about something we needed which did not work out.

The need: One of the map layers is hydrology regions. When one is selected, it would be great to highlight it and also zoom the map to it.

Highlighting is already done and working just fine. I went with a filtering technique to swap between a choropleth of all regions, and a highlight of the one region. That's great.

Zooming though, implies that we have the bounding box coordinates handy for the selected region. Can we get these from the API, since it has the records? Answer: No.

querySourceFeatures Does Not Query


Mapbox API does provide a method that sounds promising: querySourceFeatures()  But it doesn't do what you're thinking. It queries only the vector tiles which are currently visible in the map viewport. As such, it can only query features that are already on the map and is not a search/query mechanism for your dataset.

Example code:
// specify the URL of the tileset when declaring the Source
// and ALSO its suffixed name when connecting it to a Layer
map.addSource('myplaces', {
    "type": "vector",
    "url": "mapbox://mapbox.abcde12345"
});
map.addLayer({
    "id": "places",
    "source": "myplaces",
    "source-layer": "MyPlaces-abc123",
    "type": "fill",
    "paint": {
        "fill-outline-color": "rgba(0,0,0,0)",
        "fill-color": "rgba(0,0,0,0)"
    }
});
var results = map.querySourceFeatures('myplaces', {
   sourceLayer: 'places',
   filter: [ '==', 'name', 'Montana' ],
});
console.log(results);

Limitations and expectations are:

  • The setup is a bit goofy, that you must program in both the URL of your Mapbox tileset (when creating the Source) and also the readable suffixed name of it (when adding the Layer).
  • The Source isn't actually useful unless there is also a Layer and the Layer is on the map. You can render with 0 alpha to make it invisible, but it must be on the map.
  • The query only searches within the vector tiles currently visible on the map. As you pan and zoom, you'll see that results changes even if your query does not.
The use case demonstrated by the Mapbox demos, is basically limited to querying what's under your mouse. And sadly, this does seem to be all that it's good for. querySourceFeatures() is basically the same thing as queryRenderedFeatures()except that you can specify the "click filtering" without changing the filters used for rendering.


So, What Now?


Mapbox offers a fairly new service called Datasets. When you upload your dataset, unlike Tilesets which are digested into vector tiles, this one is digested into a database and exposed as a GeoJSON structure. This can then be queried and downloaded, and there's mention of an editing and authoring environment as well.

Of course, this means uploading a second copy of these datasets just for querying purposes, which isn't so great for maintainability.

In our case, we were already loading a CSV of metadata used for constructing other UI elements and populating some statistical tables. As such, it was no problem to add to that CSV some new bbox_s, bbox_e, bbox_n, bbox_e fields to facilitate the zooming behavior. As such, I did not get an opportunity to work with Datasets this time.