mapbox / vtquery

Query some gosh darn vector tiles
BSD 2-Clause "Simplified" License
44 stars 15 forks source link

vtquery

Build Status codecov node-cpp-skel

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)

API

Table of Contents

vtquery

Parameters

Examples

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
});

Response object

The response object is a GeoJSON FeatureCollection with Point features containing the following in formation:

Here's an example response

{
  "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"
        }
      }
    },
    { ... },
    { ... },
  ]
}

Point in polygon queries

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.

Deduplicating results

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:

Develop

git clone git@github.com:mapbox/vtquery.git
cd vtquery

# Build binaries. This looks to see if there were changes in the C++ code. This does not reinstall deps.
make

# Run tests
make test

# Cleans your current builds and removes potential cache
make clean

# Cleans everything, including the things you download from the network in order to compile (ex: npm packages).
# This is useful if you want to nuke everything and start from scratch.
# For example, it's super useful for making sure everything works for Travis, production, someone else's machine, etc
make distclean

# Generate API docs from index.js
# Requires documentation.js to be installed globally
npm install -g documentation
npm run docs

To install and test on a linux instance, you can use the Dockerfile provided.

# build the image
docker build -t vtquery .

# run it - this will put you inside the image
docker run -it vtquery

# run tests
make test

# edit 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.

cd viz
npm install
MapboxAccessToken={token} node app.js
# localhost:5000