Skip to main content

NYC points of interest - reducing feature density

Points of Interest (POIs) must be visible and not overcrowded to allow users to quickly distinguish each point. They are likely going to be labels on a map or points or circles with labels. Below is a recipe suitable to visualize POI data at low to medium zooms (zoomed out).

Drop features randomly

In this recipe, we drop features to provide a visually digestible amount of POIs. Since there isn't a property that ranks features' importance (such as rating, seating capacity, etc...), we pass in a "null" value to limit.lowest_where_in_distance, which drops features in each tile. 256 sets the limit for the number of features in a tile - when a tile exceeds more than 256 features, features are randomly removed. This value allows space for labels of POIs if desired - the higher the threshold, the greater the density of points in your dataset.

{
"version": 1,
"layers": {
"nyc_points_layer": {
"source": "mapbox://tileset-source/{username}/nyc-pois",
"minzoom": 0,
"maxzoom": 10,
"tiles": {
"limit": [
[ "lowest_where_in_distance", true, 256, "null" ]
]
}
}
}
}

The tileset on the left was created with a recipe that doesn't drop any features and the tileset on the right was created with the recipe used above in the "Drop features randomly" section. As you can see, the tileset on the right is less dense as a result of dropping features.

Using the Tilesets CLI to generate a tileset

This section describes how to use the Tilesets CLI to generate a tileset.

  1. Download the data you'll use to create the tileset:
arrow-downDownload line-delimited GeoJSON
  1. Create a tileset source named nyc-pois with the data you downloaded
tilesets upload-source username nyc-pois ~/your/local/path/nyc_pois.ldgeojson
  1. Create your recipe as a local JSON file (for example nyc-pois-recipe.json) with your nyc-pois tileset source
{
"version": 1,
"layers": {
"nyc_points_layer": {
"source": "mapbox://tileset-source/{username}/nyc-pois",
"minzoom": 0,
"maxzoom": 10,
"tiles": {
"limit": [
[ "lowest_where_in_distance", true, 256, "null" ]
]
}
}
}
}
  1. Create a tileset with the id username.nyc-pois-filter using your recipe and name it "nyc pois"

This will be an empty tileset to start, until you publish it.

tilesets create username.nyc-pois-filter --recipe ~/your/local/path/nyc-pois-recipe.json --name "nyc pois"
  1. Now you're ready to publish your tileset and start processing your data
tilesets publish username.nyc-pois-filter

Preview your tileset

  1. You can now add your new tileset to a map style to visualize its data. Below is an example of how to add the username.nyc-pois-filter tileset as a vector tile source to a Mapbox GL JS map style.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>NYC points of interest - reducing feature density</title>
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="https://api.mapbox.com/mapbox-gl-js/v3.5.1/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.5.1/mapbox-gl.js"></script>
<style>
body { margin: 0; padding: 0; }
#map { position: absolute; top: 0; bottom: 0; width: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script>
// TO MAKE THE MAP APPEAR YOU MUST
// ADD YOUR ACCESS TOKEN FROM
// https://account.mapbox.com
mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';
const map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/light-v10',
zoom: 10,
center: [-73.926378, 40.82048]
});

map.on('load', () => {
map.addSource('nyc-pois-src', {
type: 'vector',
url: 'mapbox://examples.nyc-pois-filter'
});
map.addLayer({
'id': 'nyc-pois-id',
'type': 'circle',
'source': 'nyc-pois-src',
'source-layer': 'nyc_points_layer',
'paint': {
'circle-radius': 4,
'circle-color': '#ff69b4'
}
});
});
</script>

</body>
</html>

Recipe options used

FieldDescriptionData type
** source**The source data to use for this layer. Tileset sources are created with the Create a tileset source endpoint of Mapbox Tiling Service (MTS).String
minzoomSpecify the minimum zoom at which the data in your tileset will be available.Integer
maxzoomSpecify the maximum zoom at which the tileset will be available. A value of 10 provides up to 10 meters of precision, which is enough for relative locations of features like POIs. Your map will still zoom past the max zoom in your recipe because of overzoomingInteger
tiles.limit.lowest_where_in_distanceThis property is used to keep the number of points in each tile to a minimum, reducing the density of point data. 256 features per tile ensures space for labels. This value must be a power of 4 to match the tile grid. The accepted values are: 4, 16, 64, 256, 1024, 4096, or 16384.Array<Expression>
Was this example helpful?