Get features from Mapbox Vector Tiles from a lng/lat query point
npm install @mapbox/vtquery


npm install @mapbox/vtquery
Get the closest features from a longitude/latitude in a set of vector tile buffers. Made possible with node-cpp-skel, vtzero, geometry.hpp, spatial-algorithms, and cheap-ruler-cpp.
The two major use cases for this library are:
1. To get a list of the features closest to a query point
2. Point in polygon checks (radius=0)
- vtquery
- Parameters
- Examples
- tiles Array<Object> an array of tile objects with buffer, z, x, and y values
- LngLat Array<Number> a query point of longitude and latitude to query, [lng, lat]
- options Object?
- options.radius Number the radius to query for features. If your radius is larger than
the extent of an individual tile, include multiple nearby buffers to collect a realistic list of features (optional, default 0)
- options.limit Number limit the number of results/features returned from the query. Minimum is 1, maximum is 1000 (to avoid pre allocating large amounts of memory) (optional, default 5)
- options.layers Array<String>? an array of layer string names to query from. Default is all layers.
- options.geometry String? only return features of a particular geometry type. Can be point, linestring, or polygon.
Defaults to all geometry types.
- options.dedupe String perform deduplication of features based on shared layers, geometry, IDs and matching
properties. (optional, default true)
- options.basic-filters Array<String, Array>? an expression-like filter to include features with Numeric or Boolean properties
that match the filters based on the following conditions: =, !=, <, <=, >, >=. The first item must be the value "any" or "all" whether
any or all filters must evaluate to true.
- options.direct_hit_polygon Boolean When true, the query will exlcude any polygons that do not contain the query point regardless of the radius value. (Optional, defaults to false)
``javascript
const vtquery = require('@mapbox/vtquery');
const fs = require('fs');
const tiles = [
{ buffer: fs.readFileSync('./path/to/tile.mvt'), z: 15, x: 5238, y: 12666 }
];
const options = {
radius: 0,
limit: 5,
geometry: 'polygon',
layers: ['building', 'parks'],
dedupe: true,
'basic-filters': ['all', [['population', '>', 10], ['population', '<', 1000]]]
};
vtquery(tiles, [-122.4477, 37.7665], options, function(err, result) {
if (err) throw err;
console.log(result); // geojson FeatureCollection
});
`
The response object is a GeoJSON FeatureCollection with Point features containing the following in formation:
- Geometry - always a Point. This library does not return full geometries of features. It only returns the closest point (longitude/latitude) of a feature. If the original feature is a point, the result is the actual location of that point. If the original feature is a linestring or polygon, the result is the interpolated closest latitude/longitude point of that feature. This could be _along_ a line of a polygon and not an actual node of a polygon. If the query point is _within_ a polygon, the result will be the query point location.tilequery.geometry_type
- Properties of the feature
- Extra properties including:
- - either "Point", "Linestring", or "Polygon"tilequery.distance
- in meters - if distance is 0.0, the query point is _within_ the geometry (point in polygon)tilequery.layer
- which layer the feature was a part of in the vector tile bufferid
- An if it existed in the vector tile feature
Here's an example response
`JSON`
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-122.42901,
37.80633
]
},
"properties": {
"property_one": "hello",
"property_two": 15,
"tilequery": {
"distance": 11.3,
"geometry": "Point",
"layer": "parks"
}
}
},
{ ... },
{ ... },
]
}
To perform a "point in polygon" query, set your radius value to 0. This will only return polygons that your query point is _within_.
GOTCHA 1: Be aware of the number of results you are returning - there may be overlapping polygons in a tile, especially if you are querying multiple layers. If a query point exists within multiple polygons there is no way to sort them so they come back in the order they were queried. If there are _more_ results than your numResults value specifies, they will just be cut off once the query hits the maximum number of results.
GOTCHA 2: Any query point that exists _directly_ along an edge of a polygon will _not_ return.
When querying across multiple tiles (or even within a single tile) it's likely source geometries have been split by the tile boundaries into multiple, seemingly unique geometries. This can result in duplicate results in a response for edges of tile boundaries, rather than actual edges of source data. Vtquery assumes features are duplicates if all of the following are true:
- The features are from the same layer
- The features are the same geometry type
- The features have the same id AND same properties
- The features' properties are the same (if no ids are present)
`bash
git clone git@github.com:mapbox/vtquery.git
cd vtquery
To install and test on a linux instance, you can use the Dockerfile provided.
`shell
build the image
docker build -t vtquery .run it - this will put you inside the image
docker run -it vtqueryrun tests
make testedit files - helpful for debugging
vim src/vtquery.cpp
`Benchmarks
Benchmarks can be run with the bench/vtquery.bench.js script to test vtquery against common real-world fixtures (provided by mvt-fixtures). When making changes in a pull request, please provide the benchmarks from the master branch and the HEAD of your current branch. You can control the
concurrency and iterations of the benchmarks with the following command: node bench/vtquery.bench.js --iterations 1000 --concurrency 5
And the output will show how many times the library was able to execute per second, per fixture:
1: pip: many building polygons ... 954 runs/s (1048ms)
2: pip: many building polygons, single layer ... 1012 runs/s (988ms)
3: query: many building polygons, single layer ... 773 runs/s (1294ms)
4: query: linestrings, mapbox streets roads ... 1664 runs/s (601ms)
5: query: polygons, mapbox streets buildings ... 745 runs/s (1343ms)
6: query: all things - dense single tile ... 400 runs/s (2497ms)
7: query: all things - dense nine tiles ... 51 runs/s (19544ms)
8: elevation: terrain tile nepal ... 801 runs/s (1249ms)
9: geometry: 2000 points in a single tile, no properties ... 2083 runs/s (480ms)
10: geometry: 2000 points in a single tile, with properties ... 1047 runs/s (955ms)
11: geometry: 2000 linestrings in a single tile, no properties ... 978 runs/s (1022ms)
12: geometry: 2000 linestrings in a single tile, with properties ... 689 runs/s (1452ms)
13: geometry: 2000 polygons in a single tile, no properties ... 661 runs/s (1513ms)
14: geometry: 2000 polygons in a single tile, with properties ... 485 runs/s (2062ms)
Viz
The viz/ directory contains a small node application that is helpful for visual QA of vtquery results. It requests Mapbox Streets tiles and adds results as points to the map. In order to request tiles, you'll need a
MapboxAccessToken environment variable.`shell
cd viz
npm install
MapboxAccessToken={token} node app.js
localhost:5000
``