Census blocks - unioning census geographies
This example uses United States Census data for Connecticut to show how to union
features differently at different zoom levels. The example recipe uses the union filter to group census blocks into larger geographies as you zoom out, so the size of features is appropriate to each zoom level. The example recipe also sums the land area attribute available in the source data for any features that are unioned.
How to manage U.S. Census data on a map
-
Define the simplification value of features. The recipe starts from a tileset source of Census blocks and initially simplifies them outward (
outward_only: true
) with a small simplification distance (distance: 1
). For MTS, recipe simplification values range from 0 - 40, with a value 20 indicating moderate simplification. 4 is the default parameter if you do not specify a value in your recipe. The default simplification value simplifies your data so that is not distinguishable visually but will still make the tiles as small as possible under the constraints of tile boundaries. Setting simplification to a value of1
ensures the highest amount of precision possible of a feature, aiding theoutward_only: true
operation. Outward simplification is helpful because it ensures more precise block unioning by defaulting to the largest area possible for unioned polygons, although that is at the expense of a larger tile size. -
Define the hierarchy of features per zoom level. The recipe then uses
set
to create a new attribute,key
, whose value is equal to a combination of existing data attributes depending on the zoom level. At zoom levels >= 0, the value forkey
is only the state ID (STATEFP10
), then at zoom level >= 5, county ID (COUNTYFP10
) is added, at zoom levels >= 8 tract ID (TRACTCE10
) is added, and finally at zoom levels >= 11 block ID (BLOCKCE10
) is added.concat
is used within theset
expression to combine what are individual data attributes in the source data into a single attribute for the resulting feature in the tileset. For example, at zoom level 5, akey
will have a state level reference, such as09003
, but at zoom level 6, it will concatenate theSTATE
andCOUNTY
reference ids to a value such as09003520202
. -
Union features. The
union
field of the recipe then combines features based on the newly created key attribute usinggroup_by
. Because we are unioning based onkey
and the value ofkey
changes with zoom level, this also means the way features are unioned changes based on zoom level. We're also usingsum
in theaggregate
option of theunion
to sum the land area (ALAND10
) attribute of each feature that is being unioned. Once theunion
is complete, bidirectional (outward_only: false
) simplification is applied with a standard simplification distance (distance: 4
). Setting outward simplification tofalse
reduces the size of the tiles in the resulting tileset by defaulting to the smallest area possible when unioning polygon features. This means better map performance, but less precise unioning among polygons.
This is helpful because it ensures more precise block unioning, although that is at the expense of a larger tile size.
{
"version": 1,
"layers": {
"census": {
"minzoom": 0,
"maxzoom": 11,
"source": "mapbox://tileset-source/{username}/census-blocks",
"features": {
"simplification": {
"outward_only": true,
"distance": 1
},
"attributes": {
"set": {
"key": [ "concat",
[ "case", [ ">=", [ "zoom" ], 0 ], [ "get", "STATEFP10" ], "" ],
[ "case", [ ">=", [ "zoom" ], 5 ], [ "get", "COUNTYFP10" ], "" ],
[ "case", [ ">=", [ "zoom" ], 8 ], [ "get", "TRACTCE10" ], "" ],
[ "case", [ ">=", [ "zoom" ], 11 ], [ "get", "BLOCKCE10" ], "" ]
]
}
}
},
"tiles": {
"union": [
{
"group_by": [ "key" ],
"aggregate": {
"ALAND10": "sum"
},
"simplification": {
"distance": 4,
"outward_only": false
}
}
]
}
}
}
}
The tileset on the left was generated without using any recipe options. The source data contains too much information to be viewed at a world view; features are randomly dropped to meet the 1250 Kilobyte tile size limit. You see fragmented roads because there is no logic in the recipe to dictate prioritization of features at the city and street-level perspective.
The map on the right is a tileset generated with the sample recipe above. The map is rendered at a state-level view, where you can view STATE
census blocks. When you zoom in, the census blocks become increasingly smaller at the COUNTY
, TRACT
, and BLOCK
levels. If you hover over the features, you will see the generated key
values and the associated census block areas under ALAND10
.
Using the Tilesets CLI to generate a tileset
This section describes how to use the Tilesets CLI to generate a tileset.
- Download the data you'll use to create the tileset:
- Create a tileset source named
census-blocks
with the data you downloaded
tilesets upload-source username census-blocks ~/your/local/path/census.ldgeojson
- Create your recipe as a local JSON file (for example,
census-blocks-recipe.json
) with yourcensus-blocks
tileset source.
{
"version": 1,
"layers": {
"census": {
"minzoom": 0,
"maxzoom": 11,
"source": "mapbox://tileset-source/{username}/census-blocks",
"features": {
"simplification": {
"outward_only": true,
"distance": 1
},
"attributes": {
"set": {
"key": [ "concat",
[ "case", [ ">=", [ "zoom" ], 0 ], [ "get", "STATEFP10" ], "" ],
[ "case", [ ">=", [ "zoom" ], 5 ], [ "get", "COUNTYFP10" ], "" ],
[ "case", [ ">=", [ "zoom" ], 8 ], [ "get", "TRACTCE10" ], "" ],
[ "case", [ ">=", [ "zoom" ], 11 ], [ "get", "BLOCKCE10" ], "" ]
]
}
}
},
"tiles": {
"union": [
{
"group_by": [ "key" ],
"aggregate": {
"ALAND10": "sum"
},
"simplification": {
"distance": 4,
"outward_only": false
}
}
]
}
}
}
}
- Create a tileset with the id
username.census-blocks-unioned
using your recipe and name it"census blocks"
. This will be an empty tileset to start, until you publish it.
tilesets create username.census-blocks-unioned --recipe ~/your/local/path/census-blocks-recipe.json --name "census blocks"
- Now you're ready to publish your tileset and start processing your data.
tilesets publish username.census-blocks-unioned
Preview your tileset
- 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.census-blocks-unioned
tileset as a vector tile source to a Mapbox GL JS map style.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Census blocks - unioning census geographies</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: 5,
center: [-72.619628, 41.754922]
});
map.on('load', () => {
map.addSource('census', {
type: 'vector',
url: 'mapbox://examples.census-blocks-unioned'
});
map.addLayer({
'id': 'census-id',
'type': 'line',
'source': 'census',
'source-layer': 'census_block_layer',
'layout': {
'line-join': 'round',
'line-cap': 'round'
},
'paint': {
'line-color': '#ff69b4',
'line-width': 1
}
});
});
</script>
</body>
</html>
Recipe options used
Field | Description | Data 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 |
minzoom | Specify the minimum zoom at which your tileset will be available. A value of 0 here ensures that the census blocks will be visible at a global level. | Integer |
maxzoom | Specify the maximum zoom at which your tileset will be available. A value of 11 here ensures that the census blocks will be visible as the user zooms to block level, and will be magnified as they zoom deeper. | Integer |
features.simplification.outward_only | Specifies that features will be simplified only outward, so they will overlap slightly and union correctly rather than leaving gaps. | Boolean expression |
features.simplification.distance | Specifies that features will be simplified only by a single unit of distance, so even at low zoom levels small blocks will not be simplified away, so they will union correctly rather than leaving gaps. | Integer expression |
features.attributes.set | Creates a new feature attribute from the conditional concatenation of existing attributes | Object mapping String to Expression |
features.tiles.union.group_by | Specifies that features will be unioned if the key attribute created above matches | Array of String |
features.tiles.union.aggregate | Specifies that the land area (ALAND10 ) attribute of features will be summed as they are unioned | Object mapping String to String |
features.tiles.union.simplification.distance | Specifies that after unioning, the features will be simplified to the normal distance of 4 . | Integer Expression |
features.tiles.union.simplification.outward_only | Specifies that after unioning, the features will may simplified either inward or outward, allowing the smallest final tiles | Boolean Expression |