Skip to content

Query features from remote MVT tiles by location

License

Notifications You must be signed in to change notification settings

mmomtchev/query-mvt

Folders and files

NameName
Last commit message
Last commit date
Feb 28, 2025
Feb 20, 2023
Apr 11, 2024
Oct 3, 2024
Feb 24, 2023
Feb 23, 2023
Dec 12, 2023
Apr 11, 2024
Feb 22, 2023
Apr 11, 2024
Mar 28, 2025
Jan 21, 2025
Feb 24, 2023
Feb 23, 2023

Repository files navigation

query-mvt

Query map features nearest to a given point from a remote MVT vector tiles layer

License: ISC Node.js CIcodecov

query-mvt allows you query remote vector tile sets for features. Its main advantage is that it does not require any special access or server-side software as it mimics the behavior of a web browser displaying the map.

It works both in Node.js and in the browser. It supports all vector mapping services that use MVT/PBF files and can adapt to different projections and tileset extents.

Usage

npm i query-mvt

Vector tile sets created by GDAL and a few other tools come with a de-facto standard metadata.json that allows query-mvt to automatically acquire all the needed parameters (such as map projects, tile grid and size, world bounds):

with metadata.json

import * as queryMVT from 'query-mvt';

// Automatically imports the layer metadata
// (EPSG:4326 with world coverage in this case)
queryMVT.acquire('https://velivole.b-cdn.net/tiles/place/2/metadata.json')
  .then((metadata) => queryMVT.search({
    url: 'https://velivole.b-cdn.net/tiles/place/2/{z}/{x}/{y}.pbf',
    lon: 6.220432,
    lat: 45.779170,
    metadata
  }))
  .then((result) => {
    assert.strictEqual(result.feature.properties['n'], 'Doussard');
    assert.closeTo(result.distance, 0.34, 0.1);
  })

raw MVT tiles

Most commercial public mapping services such as Qwant do not have a metadata.json but tend to use the same EPSG:3857 projection (aka Web Mercator) and world bounds:

import * as queryMVT from 'query-mvt';

// Configure manually the layer metadata
// (EPSG:3857 with world coverage in this case)
queryMVT.search({
  url: 'https://tiles.qwant.com/default/{z}/{x}/{y}',
  lon: 2.348942,
  lat: 48.853289,
  // You can filter the results
  filter: (f) => f.properties['class'] === 'city',
  maxFeatures: 20,
  maxRadius: 10,
  metadata: queryMVT.constants.EPSG3857
})
  .then((results) => {
    assert.strictEqual(results[0].feature.properties['class'], 'city');
    assert.strictEqual(results[0].feature.properties['name'], 'Paris');
    assert.closeTo(results[0].distance, 0.04, 0.1);
  })

CLI

A stand-alone CLI version exists as well:

# Query the nearest village near 45.779° N : 6.22° E
query-mvt 45.779 6.22 -f class=village

API

Table of Contents

Result

Type: {distance: number, feature: turf.Feature}

Properties

  • distance number
  • feature turf.Feature

distance

Distance in km

Type: number

feature

Feature (turf.js and GeoJSON compatible)

Type: turf.Feature

acquire

Parameters

  • url string URL of a GDAL-style metadata.json
  • fetchOpts RequestInit? optional fetch options (AbortController, authorization headers...)

Returns MVTMetadata

search

Parameters

  • opts Record<string, any> options

    • opts.url string Openlayers-style URL template for requesting tiles, must contain {x}, {y} and {z}
    • opts.metadata MVTMetadata? optional GDAL-style metadata.json, may be empty for world-wide EPSG:3857 tilesets
    • opts.lon number longitude
    • opts.lat number latitude
    • opts.queue Queue? optional shared Queue to be used for limiting concurrency, @default Queue(8,0)
    • opts.maxFeatures number? optional number of features to return, @default 1
    • opts.maxRadius number? optional maximum radius in km to search in, @default 10
    • opts.fetchOpts RequestInit? optional fetch options (AbortController, authorization headers...)
    • opts.dedupe boolean? dedupe the returned features (as the text will usually stretch across several tiles)

Returns Promise<Array<Result>>