> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/mapbox-tiling-service/ja/llms.txt)

# Census blocks - unioning census geographies

This example uses [United States Census](https://www2.census.gov/geo/tiger/TIGER2019/TABBLOCK/) 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

1.  **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 of `1` ensures the highest amount of precision possible of a feature, aiding the [`outward_only: true` operation](https://docs.mapbox.com/mapbox-tiling-service/ja/recipe-specification/vector/#outward-only-simplification). 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.
    
2.  **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 for `key` 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 the `set` 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, a `key` will have a state level reference, such as `09003`, but at zoom level 6, it will concatenate the `STATE` and `COUNTY` reference ids to a value such as `09003520202`.
    
3.  **Union features.** The `union` field of the recipe then combines features based on the newly created key attribute using `group_by`. Because you are unioning based on `key` and the value of `key` changes with zoom level, this also means the way features are unioned changes based on zoom level. You're also using `sum` in the `aggregate` option of the `union` to sum the land area (`ALAND10`) attribute of each feature that is unioned. Once the `union` is complete, [bidirectional (`outward_only: false`)](https://docs.mapbox.com/mapbox-tiling-service/ja/recipe-specification/vector/#outward-only-simplification) simplification is applied with a standard simplification distance (`distance: 4`). Setting outward simplification to `false` 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.

```json
{
    "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](https://docs.mapbox.com/mapbox-tiling-service/ja/guides/#tilesets-cli) to generate a tileset.

1.  Download the data you'll use to create the tileset:

[Download line-delimited GeoJSON](https://mts-recipe-example-data.s3.amazonaws.com/census.ldgeojson)

2.  Create a tileset source named `census-blocks` with the data you downloaded

```bash
tilesets upload-source username census-blocks ~/your/local/path/census.ldgeojson
```

1.  Create your recipe as a local JSON file (for example, `census-blocks-recipe.json`) with your `census-blocks` tileset source.

```json
{
    "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
                        }
                    }
                ]
            }
        }
    }
}
```

4.  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.

```bash
tilesets create username.census-blocks-unioned --recipe ~/your/local/path/census-blocks-recipe.json --name "census blocks"
```

5.  Now you're ready to publish your tileset and start processing your data.

```bash
tilesets publish username.census-blocks-unioned
```

### Preview your tileset

6.  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](https://docs.mapbox.com/mapbox-gl-js/overview/).

```html
<!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.25.0/mapbox-gl.css" rel="stylesheet">
<script src="https://api.mapbox.com/mapbox-gl-js/v3.25.0/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>
  const map = new mapboxgl.Map({
    // TO MAKE THE MAP APPEAR YOU MUST
    // ADD YOUR ACCESS TOKEN FROM
    // https://account.mapbox.com
    accessToken: 'YOUR_MAPBOX_ACCESS_TOKEN',
    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](https://docs.mapbox.com/api/maps/mapbox-tiling-service/#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` |