Vector Recipe
Current version: 1
A vector recipe is a JSON document containing configuration options that tell Mapbox Tiling Service (MTS) how to turn tileset source data into vector tiles.
Recipes must be constructed according to the rules in this recipe reference.
Recipe top-level fields
A recipe is a JSON object that must contain the following top-level fields:
Required fields | Description | Data type |
---|---|---|
version | The version of the Mapbox Tiling Service recipe reference that the recipe uses. | Integer |
layers | The names of the layers and their configuration options. | Object<String, LayerObject> |
The recipe can contain the following top-level optional field:
Optional fields | Description | Data type |
---|---|---|
fillzoom | The zoom level from which to generate overzoomed tiles, if there are no tiles at the maximum zoom level. | Integer |
{
"version": 1,
"fillzoom": 7,
"layers": { layername: LayerObject, ... }
}
Recipe version
The recipe version
indicates the version of the Mapbox Tiling Service recipe reference that the recipe uses. The current version is 1
. Using any version
other than the current version or a previous version will result in an error.
Fillzoom
Fillzoom defines a level from which to generate overzoomed tiles. Normally, MTS generates overzoomed tiles from tiles at the maximum zoom level. If you specify fillzoom
and MTS cannot find tiles at the maximum zoom level, it generates overzoomed tiles from the fillzoom
level.
For example, in a recipe with a maximum zoom level 10
and fillzoom level 7
specified, a request for a z11
tile will use z10
parent tiles to generate the new, overzoomed z11
tile. If there are no tiles at z10
, a request for a z11
tile will use the z7
tiles instead.
maxzoom
attribute in the tileset source determining the highest zoom level at which the feature can appear, it will not appear at zoom levels higher than the maxzoom
. But, if fillzoom
is specified in the recipe, the feature may still appear at higher zoom levels if the tile it belongs to contains no other features. This is a rare but expected case. For an example of using a maximum zoom filter, see the Feature filters section.Layers
The recipe layers
object contains key-value pairs associating the names of tile layers with their configuration. For each key-value pair, the key is the name of the layer, and the value is a LayerObject
containing the configuration and options for the layer.
Each layer must have a name (for example "trees", "parks", and "paths" in the example below). This is the unique identifier for the layer of data in your final tileset. In any tileset, each layer name must be unique. The layer name must be a string with only underscores (_
) and alphanumeric characters.
The corresponding LayerObject
for each layer name describes how that vector tile layer should be created. It specifies where to retrieve source data, the precision of the data through zoom levels, and how to transform features and render them into tiles.
Layer example
The simplest example tileset recipe includes a layer name, a tileset source ID, a minimum zoom value, and a maximum zoom.
{
"version": 1,
"layers": {
"trees": {
"source": "mapbox://tileset-source/{username}/trees-data",
"minzoom": 4,
"maxzoom": 8
}
}
}
Multilayer recipes
Recipes can also contain definitions for multiple layers. This format can be used to create multilayer tilesets. Recipes can have a maximum of 20 layers defined.
{
"version": 1,
"layers": {
"trees": { ... },
"parks": { ... },
"paths": { ... }
}
}
Although you can refer to multiple tilesets from the same style, a single multilayer tileset can contain more layers and can be accessed more quickly over the network than a composite of multiple single-layer tilesets.
Layer configuration
These are the required top-level fields for each layer:
Required fields | Description | Data type |
---|---|---|
source | The source data to use for this layer. For more information about this field, see the Tileset source section. | String |
minzoom | The lowest zoom level for the tileset. For more information about this field, see the Zoom levels section. | Integer |
maxzoom | The highest zoom level for the tileset. For more information about this field, see the Zoom levels section. | Integer |
Tileset source
The features that make up each layer come from a tileset source, which is a collection of geographic data stored as line-delimited GeoJSON on Mapbox.com. Tileset sources can be created using MTS's Create a tileset source endpoint.
Zoom levels
Each tileset layer contains tiles at one or more zoom levels that will be visible at different times as the user zooms in and out of the map.
The minzoom
and maxzoom
configurations control the zoom levels at which your data will be tiled. They are required for a recipe. These values must be integers. The minzoom
must be less than or equal to maxzoom
, and both must be between the values of 0
and 16
.
maxzoom
does not limit the zoom level your maps can be viewed at, as long as your layers all have the same maxzoom
. Your map will still zoom past the max zoom in your recipe because of overzooming. If you do have a different maxzoom
in different layers, only the layers with the highest maxzoom
will be overzoomed.The minimum and maximum zoom levels you choose directly impact the time it takes MTS to finish processing your data. Each additional zoom level increases the amount of data in the resulting tileset exponentially. So it’s important to first consider the use cases your map is trying to solve for when choosing your minzoom
and maxzoom
zoom levels so that you don't incur any performance costs unnecessarily.
The following table will help you decide the appropriate minzoom
and maxzoom
based on your data precision:
Zoom level | Precision in feet | Precision in meters | Use Case | Example |
---|---|---|---|---|
0 | 32000 ft | 10000 m | ||
1 | 16000 ft | 5000 m | ||
2 | 8000 ft | 2500 m | ||
3 | 4000 ft | 1250 m | ||
4 | 2000 ft | 600 m | ||
5 | 1000 ft | 300 m | ||
6 | 500 ft | 150 m | ||
7 | 250 ft | 80 m | ||
8 | 125 ft | 40 m | ||
9 | 64 ft | 20 m | ||
10 | 32 ft | 10 m | low-resolution administrative boundaries (for example, countries and state boundaries) | |
11 | 16 ft | 5 m | ||
12 | 8 ft | 2 m | ||
13 | 4 ft | 1 m | ||
14 | 2 ft | 0.5 m | high-resolution administrative boundaries (such as neighborhoods, census tracts, and towns), national park polygon data, national land use, and land cover polygon data | |
15 | 1 ft | 0.25 m | ||
16 | 0.5 ft | 0.125 m | building/parcel polygon data, road polygon or line data, points of interest (such as hotels, restaurants, and venues) |
Zoom levels greater than 16
, which are applicable for use-cases like lane-level navigation maps or high-definition indoor maps, are not possible by default. If you have a use case that requires a zoom level greater than 16
, reach out to us and let us know.
Additional layer options
You can further refine the resulting tileset with the following optional fields:
Optional fields | Description | Data type |
---|---|---|
features | Specifies the output on a per feature basis. For more information about this field, see the Feature configuration section. | Object |
tiles | Specifies the output on a per tile basis. For more information about this field, see the Tile configuration section. | Object |
Layer configuration summary
The following describes every available field for a LayerObject
and the JSON type for that field.
{
"source": String,
"minzoom": Integer,
"maxzoom": Integer,
"features": {
"id": Expression,
"bbox": [ Number, Number, Number, Number ],
"attributes": {
"zoom_element": Array<String>,
"set": Object<String, Expression>,
"allowed_output": Array<String>
},
"filter": Expression<Boolean>,
"simplification": Expression<Number>,
"simplification": {
"distance": Expression<Number>,
"outward_only": Expression<Boolean>
}
},
"tiles": {
"bbox": [ Number, Number, Number, Number ],
"extent": Expression<Number>,
"buffer_size": Expression<Number>,
"limit": [
[ String, Expression<Boolean>, Integer, String ], ...
],
"union": [
{
"where": Expression<Boolean>,
"group_by": Array<String>,
"aggregate": Object<String, String>,
"maintain_direction": Boolean,
"simplification": Expression<Number>,
"simplification": {
"distance": Expression<Number>,
"outward_only": Expression<Boolean>
}
}, ...
],
"filter": Expression<Boolean>,
"attributes": {
"set": Object<String, Expression>
},
"order": String,
"remove_filled": Expression<Boolean>,
"id": Expression,
"layer_size": Integer
}
}
Expressions
When writing rules in a recipe document, you can use "expressions" to base them on the properties in your data. Example rules that you might specify in a recipe document could include:
- Tile uploaded tornado data from zooms 5 through 16.
- Only tile the most populated cities within a dataset at zooms less than 7, then from zooms 8 through 16, only tile the top 20 most populated cities or towns per tile.
- Only tile the top 5 flood-affected regions by area from the uploaded dataset until zoom 10, at zoom levels greater than 10, only show flooded regions with an area greater than 8 pixels.
The expressions used to control recipe features are from the expression language described in the Expressions reference section, and are closely related to the expressions used in Mapbox GL styles.
Plain numbers, strings, true
, false
, and null
are all valid expressions, so you don't need to learn all the details of the expression language to start using MTS.
Feature configuration
The features
configuration object is used to describe how features are individually processed into vector tiles. This field contains the following elements, which are applied in the order indicated in the list below:
Optional fields | Description | Data type |
---|---|---|
id | Sets the ID of each feature. For more information about this field, see the ID expression section. | Expression |
bbox | Clips each feature to the specified bounding box. For more information about this field, see the Bounding box section. | Array<Number> |
attributes | Adds, removes, and modifies feature attributes. For more information about this field, see the Feature attributes section. | FeaturesAttributesObject |
filter | Removes features that do not match a pattern. For more information about this field, see the Feature filters section. | Expression<Boolean> |
simplification | Controls simplification of the feature geometry. For more information about this field, see the Feature simplification section. | Expression<Number> or SimplificationObject |
ID expression
Each feature that is tiled by MTS must have a numeric or string ID that uniquely identifies it, which by default comes from the id
field of the GeoJSON Feature object in the source. If no ID is present for a feature in the GeoJSON source, or if the provided ID is not a string or number, an ID will be randomly assigned by MTS.
If you want to use a value from a feature attribute (or some other calculation) for your feature's ID, you can use an expression to retrieve that attribute. For example, if you have an attribute called source_identifier
, you can use it for the feature ID by specifying:
{
"features": {
"id": [ "get", "source_identifier" ]
}
}
If you do not want your features to have an ID in the output tiles, or if you want to make additional changes to the ID, you can do this later in the recipe, in the Tiles section.
Read more about feature IDs and their custom operators in the "Identification" section.
Bounding box
If you want to clip your features to a bounding box instead of creating tiles for the full geographic extent of your source data, you can specify the bounding box with bbox
. It must contain an array of four numbers in this order: the minimum longitude, minimum latitude, maximum longitude, and maximum latitude. For example, if you wanted to clip all your features to the bounds of the state of Wyoming, you could specify:
{
"features": {
"bbox": [ -111, 41, -104, 45 ]
}
}
To experiment with bounding box values for your code, try our Location Helper.
Feature attributes
The attributes
configuration allows for manipulation of attribute data, generation of new attributes, and removal of attributes. The available options in the FeaturesAttributesObject
are described below, and performed in the order they are listed:
Optional field | Description | Data type |
---|---|---|
zoom_element | A list of attributes whose values vary by zoom level. For more information about this option, see the Zoom element section. | Array<String> |
set | Adds feature attributes or changes the value of attributes. For more information about this option, see the Set attributes section. | Object<String, Expression> |
allowed_output | A list of attributes that will appear in the final tileset. For more information about this option, see the Allowed output section. | Array<String> |
Zoom element
You may want certain attributes to vary by zoom level. For example, you might want to provide abbreviated street names at low zoom levels and the full names at high zoom levels. You can specify which attributes are provided per zoom level in the source data by providing a zoom_element
array in the attributes
object.
For each attribute listed in the zoom_element
, the final output attribute at zoom level N
will be the Nth
element in the array in the source data. If no zoom_element
is defined, no attributes are altered. If the zoom level is greater than or equal to the number of elements in the array, the last element is used.
For example, the following recipe would specify that name
should be treated as a zoom_element
attribute:
{
"features": {
"attributes": {
"zoom_element": [ "name" ]
}
}
}
The name
attribute in individual GeoJSON features would then be expected to contain an array of possible name
values by zoom level, like this:
{
"type": "Feature",
"geometry": { ... },
"properties": { "name": [ null, null, "Main", "Main St.", "Main Street" ] }
}
Using the attributes from the GeoJSON above, this feature would have no name
attribute at zoom levels 0
and 1
, and would have "Main" at zoom level 2
, "Main St." at zoom level 3
, and "Main Street" at zoom levels 4
and above.
Set attributes
You can add attributes or change the values of attributes by evaluating expressions in a set
specification.
The set
object contains key-value pairs where each key is the name of an attribute and each value is an expression that should be evaluated to provide the new value for that attribute.
Consider the following attributes for a single GeoJSON feature:
{
"type": "Feature",
"geometry": { ... },
"properties": { "area": 12345, "population": 123, "name": "Smallville" }
}
If you wanted to add a new attribute, density
, that is calculated from the population
per area
, and also to
change the value of the name
attribute to convert it to all capitals, you could use this recipe:
{
"features": {
"attributes": {
"set": {
"density": [ "/", [ "get", "population" ], [ "get", "area" ] ],
"name": [ "upcase", [ "get", "name" ] ]
}
}
}
}
If you specify multiple set
operations, the order in which they will happen is not defined.
Allowed output
If an allowed_output
array is provided, only the attributes specified in the array will be saved to the tileset. This does not prevent the attributes that are not listed in the allowed_output
from being used in filter expressions and other steps throughout the publish job; the other attributes are removed in the final step of processing.
In this example, features in the resulting tileset will only have two attributes, name_en
and name_es
:
{
"features": {
"attributes": {
"allowed_output": [ "name_en", "name_es" ]
}
}
}
Feature filters
The filter configuration is a single expression that results in a true
or false
evaluation. It is common to determine this result by considering the zoom level being created and an attribute of the feature. For example, if a feature has minzoom
and maxzoom
attributes to determine the zoom levels at which the feature appears, you can compare these attributes to the zoom level being created by specifying:
{
"features": {
"filter": [ "all",
[ ">=", [ "zoom" ], [ "get", "minzoom" ] ],
[ "<=", [ "zoom" ], [ "get", "maxzoom" ] ]
]
}
}
Or you could exclude residential roads from zoom levels below 12 by specifying:
{
"features": {
"filter": [ "any",
[ "!=", [ "get", "road" ], "residential" ],
[ ">=", [ "zoom" ], 12 ]
]
}
}
The filter
specification is the primary way to control which features are allowed into the final tileset on a per-feature basis. If no filter is provided, by default a value of true
is used for all features.
Feature simplification
MTS normally simplifies the geometry of features so that at each zoom level they correspond to the resolution of the display screen. If you want to preserve additional detail or if you want to reduce tile size by further simplifying the geometry, you can specify a simplification
to do that.
The simplification
expression or value describes the maximum distance that a point can be from the straight line that connects its two neighbors, and still be considered to be effectively on the line and thus safe to remove. Any point with a distance that is larger than the simplification
value is considered to be away from the line, and must be preserved.
As you increase the simplification
value, the number of vertices in each feature decreases. The MTS recipe validator will reject any simplification
value that is more than 4096
or less than 0
. If you do not specify a value for simplification
, MTS will use 4
, the default value.
The resulting shape depends on the original shape of your feature. For instance, if your original feature is circular and you increase simplification
, the result will look more like a polygon. If your original feature is a curved line and you increase simplification
, the result will look more like a straight line.
The simplification
may be a constant number, or it may be an expression that evaluates to a number. A typical use for an expression for the simplification
would be to use a minimal simplification at the layer's maximum zoom level, so it can be overzoomed with high precision, but to use a larger simplification at lower zoom levels that will never be magnified far, as in the example below.
Note: Even if the simplification
is specified as 0
, complex features will receive additional simplification so that they can be rendered correctly by Mapbox GL. Polygons or MultiPolygons with more than 65535 vertices and LineStrings and polygon rings with more than 6553 points will be simplified to reduce their complexity to these limits.
MTS uses the Ramer–Douglas–Peucker algorithm to simplify features.
This sample recipe uses the normal simplification of 4
at most zoom levels, but reduces the simplification to 1
at the highest zoom level so that overzoomed tiles will look better.
{
"minzoom": 0,
"maxzoom": 10,
"features": {
"simplification": [ "case",
[ "==", [ "zoom" ], 10 ], 1,
4
]
}
}
Simplification objects
If the simplification
is a SimplificationObject
, it can contain the following fields:
Optional field | Description | Data type |
---|---|---|
distance | Controls simplification of the feature geometry. For more information about this field, see the Simplification distance section. | Expression<Number> |
outward_only | Places limits on polygon simplification to improve union quality. For more information about this field, see the Outward-only simplification section. | Expression<Boolean> |
This sample recipe simplifies features where type
is rowhouse
outward, so that MTS can union each block of row houses into a single block feature instead of leaving them as separate features. MTS then performs normal simplification on the row house blocks after it unions them to further reduce the tile size. MTS simplifies other building types normally and does not union them together.
{
"features": {
"simplification": {
"outward_only": [ "==", [ "get", "type" ], "rowhouse" ]
}
},
"tiles": {
"union": [
{
"where": [ "==", [ "get", "type" ], "rowhouse" ],
"simplification": {
"outward_only": false
}
}
]
}
}
Simplification distance
The simplification distance
is a numeric expression, as described in the Feature simplification section.
It describes the maximum distance that a point can be from the straight line that connects its two neighbors, and still be considered to be effectively on the line and thus safe to remove. Any point with a distance that is larger than the simplification
value is considered to be away from the line, and must be preserved. Larger values cause the feature geometry to become increasingly coarse.
Outward-only simplification
If the outward_only
expression evaluates to true
, then Polygon
and MultiPolygon
features will be simplified only in ways that slightly increase their area, never in ways that slightly decrease their area, so the borders of adjacent polygons will often overlap slightly instead of sometimes being separated by small gaps. This will increase the quality of polygon unioning if the recipe contains a union
rule, at the cost of increased tile size because of reduced opportunity for simplification. (See the "Feature union" section.)
Tile configuration
The tiles
object contains the following elements, which are applied in this order as each tile is being assembled from its component features:
Optional field | Description | Data type |
---|---|---|
bbox | Clips each feature to the specified bounding box. For more information about this field, see the Tile bounding box section. | Array<Number> |
extent | Sets the precision of a vector tile. For more information about this field, see the Tile extent section. | Expression<Number> |
buffer_size | Keeps extra data from beyond the edge of each vector tile. For more information about this field, see the Buffer size section. | Expression<Number> |
limit | Limits the number of features of a specified type that can appear in the tile. For more information about this field, see the Limit section. | Array<LimitArray> |
union | Joins features based on whether a defined specified attribute matches. For more information about this field, see the Feature union section. | Array<UnionObject> |
filter | Removes features that do not match a specified pattern. For more information about this field, see the Post-union filter section. | Expression<Boolean> |
attributes | Adds attributes or changes the values of attributes. For more information about this field, see the Post-union attributes section. | TilesAttributesObject |
order | Specifies the order of a sequence of features in the final output tile. For more information about this field, see the Order section. | String |
remove_filled | Removes features that cover the entire tile. For more information about this field, see the Remove filled section. | Expression<Boolean> |
id | Controls the ID of features in the final output tileset. For more information about this field, see the Tiles ID section. | Expression |
layer_size | Sets the maximum allowed size (in KiB) of the tile layer. For more information about this field, see the Layer size section. | Integer |
Tile bounding box
If you want to generate tiles for only a geographic subset of your data,
so you can test a recipe without the time and expense of generating all
the tiles, you can specify a bounding box with bbox
. It must contain an array of four numbers in this order: the minimum longitude, minimum latitude, maximum longitude, and maximum latitude.
Only the tiles that intersect this bounding box will be generated.
For example, if you wanted to generate only those tiles that include part of the state of Wyoming, you could specify:
{
"tiles": {
"bbox": [ -111, 41, -104, 45 ]
}
}
Note that the tiles
bounding box is only useful for testing, not for final tilesets.
Because the sizes of tiles are different in each zoom level, different areas will come into view
as you zoom in and out. To clip features consistently in every zoom level, use the
features
bounding box.
The reason to use the tiles
bounding box instead of the features
bounding box for
testing is that with the tiles
bounding box, exactly the same set of features appear
in each tile as in the full tileset, so you your union
and limit
rules will have
the same set of features to work with as in the final tiles. With the features
bounding box, some of the features will already have been removed from consideration
before the union
and limit
happen, so you may see different results from these
than with the full tileset.
Tile extent
You can change the geometric precision within each vector tile by setting the extent
.
The extent
is a precision control for vector tiles, and by default is 4096
. In most situations, changing this value is not recommended. If you set this value, it must be a power of 2
between 256
and 8192
. You may also set this number using an expression.
Buffer size
Sets the size of the buffer that will be created around the edges of your vector tiles. buffer_size
is a percentage of the size of a tile. The default buffer size is 0.5
. Note that the value of this attribute cannot be larger than 100
. An expression can also be used in buffer_size
, but must still return a number between 0 and 100. Buffers are particularly helpful for label point layers to avoid cutting labels off at tile boundaries.
Limit
A limitation rule that reduces the number of features for the specified type to a specific number, choosing the lowest or highest-numbered features according to some attribute.
The limit
field must be an array of limitation rules, each of which is evaluated in sequence to potentially limit the total number of features to be included in the final tile. There are two ways in which data is limited using the limit
field, where
and where_in_distance
.
The where
style limits the total number of features in the tile and its buffer. Only the first number
of features that match a filter-expression
are kept and the rest are dropped from the tile. The sorting of these is based on attribute
which is the key to a feature's attribute and either sorted lowest
to highest or highest
to lowest.
The format for a where
style limit
are the following syntaxes:
[ "lowest_where", filter-expression, number, attribute ]
[ "highest_where", filter-expression, number, attribute ]
The where_in_distance
is the second style of limit
and subdivides a tile into a grid of regions. Within each region only one feature is kept from the features selected by the filter-expression
for the limit
. The number of regions contained within a single tile is controlled by region_count
. This should always be a value equal to 4 to a power in size (in other words, 1, 4, 16, 64, 256, 1024, 4096, or 16384). If the region_count
is not equal to one of these numbers it will be rounded to the nearest one of these numbers in processing. The feature that is selected in each region is the one that has the highest or lowest value for the attribute named in the attribute
field.
The format for a where_in_distance
style limit
are the following syntaxes:
[ "lowest_where_in_distance", filter-expression, region_count , attribute ]
[ "highest_where_in_distance", filter-expression, region_count , attribute ]
The only valid values for region_count
are 1
, 4
, 16
, 64
, 256
, 1024
, 4096
, or 16384
.
These where_in_distance
limit
forms also cause the tile buffer for the limited features
to be rounded down to a multiple of the buffer size corresponding to the region_count
:
region_count | associated buffer size |
---|---|
1 | 100 |
4 | 50 |
16 | 25 |
64 | 12.5 |
256 | 6.25 |
1024 | 3.125 |
4096 | 1.5625 |
16384 | 0.78125 |
Feature union
You can union features together if a specified set of their attributes match. The simplest case unions all features that have exactly the same attributes:
"tiles": { "union": [ { } ] }
The union
specification object UnionObject
can contain an expression to union only features that match the specified expression as well as having matching attributes:
Optional field | Description | Data type |
---|---|---|
where | Selects which features are considered for this union . | Expression<Boolean> |
group_by | Unions features where all the specified attributes are the same in each feature. | Array<String> |
aggregate | Specifies how to handle other attributes that are not specified in group_by when features are unioned. | Object<String, String> |
maintain_direction | Permits LineStrings to be reversed if it helps to connect them. | Boolean |
simplification | Specifies how features will be resimplified after unioning, as described in the Feature simplification section. | Number or SimplificationObject |
Union where
You can specify which features a union
rule applies to by providing a where
expression that selects a subset of features. For example, if your layer contains some building footprints that you want to union if they have the same height
, and some parks that you want to union if they have the same maintainer
, you could do this:
"tiles": {
"union": [
{
"where": [ "==", [ "get", "type" ], "building" ],
"group_by": [ "height" ]
},
{
"where": [ "==", [ "get", "type" ], "park" ],
"group_by": [ "maintainer" ]
}
]
}
Union group_by
Within a set of features that are union candidates, you can specify which
of them should become part of the same union by listing the attributes that must be the same in
all features of the union. For example, if you want to union all buildings that
have the same height
and construction_type
together, you could specify:
"tiles": {
"union": [
{
"group_by": [ "height", "construction_type" ]
}
]
}
All attributes that are not part of the group_by
and that do not have an
aggregate
rule specified will be removed at the end of unioning.
If you do not specify a group_by
, then all feature attributes must be
the same for features to be unioned together.
If you specify an empty group_by
list, then all features will be unioned
together regardless of their attributes.
Union aggregate
You can specify which attributes MTS should keep that are not part of a union's group_by
and how it should handle them by specifying aggregate
rules
for them.
Each key of the object is the name of an attribute. Each value is a string listed below that indicates how the named attribute should be aggregated from the source features. Attributes that are not specified in either group_by
or aggregate
will be removed.
These strings are the available aggregation methods:
sum
: add numbersproduct
: multiply numbersmin
: choose the lowest numbermax
: choose the highest numbermean
: take the average of numberscomma
: concatenate with a commaconcat
: concatenate the attribute data without a delimiterarbitrary
: take the attribute from one of the source featuresarbitrary-non-null
: take the attribute from one of the source features where the attribute is notnull
Union maintain_direction
Use maintain_direction: false
to make more compact unions of LineString
s for which directionality doesn't matter. This reverses some of the LineString
s if that helps to connect them.
Defaults to maintain_direction: true
Union simplification
For polygon unions, it is often useful to specify outward_only: true
in the main simplification
section, so the source features will slightly overlap and be merged into a single geometry. Then you can specify outward_only: false
in the union
's post-unioning simplification
section, so additional simplification will be applied to the polygon after unioning has already taken their overlap into account.
Union example
For example, you could specify the following to union only features where highway=motorway
, and keep the average of their speed_limit
attributes:
"tiles": {
"union": [
{
"where": [ "==", [ "get", "highway" ], "motorway" ],
"maintain_direction": true,
"aggregate": { "speed_limit": "mean" }
}
]
}
This example uses maintain_direction: true
(the default) because motorway
roads are generally mapped as pairs of roadways whose direction indicates their one-way direction.
For a more elaborate union
example, see the Census Blocks recipe example.
zoom-element
properties. If you are using union with a zoom-element
property, the union will occur after the feature has been assigned the value based on the zoom level.Post-union filter
You can further limit the set of features in a tile after unioning by specifying a filter
rule.
For instance, if your union
rule was unioning street segments together, summing their
length
attribute, you could use filter
here to remove the unioned streets whose
combined length
still did not reach a minimum visible threshold, as in this example:
"tiles": {
"union": [
{
"aggregate": { "length": "sum" }
}
],
"filter": [ ">=", [ "get", "length" ], 200 ]
}
Post-union attributes
You can add or change feature attributes after unioning by specifying an attributes.set
rule.
For instance, if your union
rule was unioning census blocks together, summing their
population
and area
attributes, you could calculate the density
of each unioned
feature afterward, as in this example:
"tiles": {
"union": [
{
"group_by": [ "tract" ]
"aggregate": { "population": "sum", "area": sum }
}
],
"attributes": {
"set": {
"density": [ "/", [ "get", "population" ], [ "get", area ] ]
}
}
}
Order
You can order the sequence of features in the final output tile by a specified attribute. The attribute values must be comparable (all strings or all numbers).
This example orders the features by the sequence
attribute:
"tiles": { "order": "sequence" }
Renderers, including Mapbox GL, draw or place features in the order they appear in the tile. LineString
and Polygon
features that are drawn early in the sequence may be covered by overlapping features drawn later in the sequence. Conversely, labels that are placed early in the sequence will prevent labels of features later in the sequence from being placed nearby.
You can follow these general guidelines:
- The most important LineStrings and Polygons should have the highest values for their
order
attribute, so they are not covered by overlapping features. - The most important labels should have the lowest values for their
order
attribute, so they are placed first.
Read more about how label visibility is affected by the order of features in the Optimize map label placement guide.
If no order
attribute is specified, the features will appear in an unpredictable order.
Remove filled
During tile creation it is possible for polygons to completely cover a tile and its surrounding buffer area. These are called "filled features". If all features within a tile are filled features, it may be useful in some tilesets not to create these tiles at all and to expect that clients will look to lower zoom levels instead.
The common situation that this applies to are large areas of water, where if the water polygon covers the entire tile, it is more efficient to overzoom the water from a lower zoom level rather than creating duplicate high-zoom tiles for an entire ocean.
To do this, you can use the remove_filled
recipe option. The syntax utilizes a filter-expression
to allow control over what features exactly should be removed.
"tiles": {
"remove_filled": expression
}
No tile will be created if:
- All features within the tile are considered filled features
- All features are matched by the filter-expression provided in
remove_filled
The following example removes all tiles that contain only filled features:
"tiles": {
"remove_filled": true
}
The following example removes all tiles that contain only filled features after zoom level 5:
"tiles": {
"remove_filled": [">", [ "zoom" ], 5]
}
Tiles ID
You can change the final output ID of features by specifying an id
expression.
For instance, you might want all the features that have the same road name to
have the same ID, so that if one of them is selected, they are all selected,
even though the source features had to have different IDs because in earlier
stages of processing, IDs are required to be distinct. So you could set a new
ID at the end of processing, like this:
"tiles": {
"id": [ "hash", [ "get", "road-name" ] ]
}
Within the id
expression you can refer to the ID assigned earlier with [ "id" ]
,
so you can make conditional expressions that leave the ID unchanged in some
circumstances or use it in calculations, like this example, which replaces
the ID if there is a special_id
attribute, or otherwise multiplies the
existing ID by 2:
"tiles": {
"id": [ "case",
[ "has", "special_id" ], [ "get", "special_id" ],
[ "*", [ "id" ], 2 ]
]
}
If you do not want your features to have an ID in the final output tiles,
set the ID to null
:
"tiles": {
"id": null
}
Allowed output
In the features.attributes
section of the recipe, you can specify a list of
attributes in allowed_output
that will appear in the final tile. The removal
of other attributes happens at this point, at the end of tiles
processing.
As a result, in limit
, union
, or id
processing you can continue to refer
to attributes that will not appear in the final output features because they do
not appear in allowed_output
.
Layer size
MTS limits the size (in bytes) of tile layers for the sake of network speed and
rendering time.
When the size of a layer's data in a single tile exceeds the layer_size
, the features that came last in the specified order
will be dropped from that tile to stay within the layer_size
limit.
The normal limit is 1250
, for 1250 kilobytes. You can further limit the layer size by specifying a lower layer_size
number, or raise the limit to as high as 2500
by specifying a higher layer_size
number.
If you are receiving warnings about dropped features due to exceeding layer_size
, you may want to consider a few options:
- Reduce the number of total attributes per feature. If you don't plan on using all your properties for styling or in your map, you can drop specific features using the attributes option.
- Reduce the size of attributes in your features. Some property types, especially strings, can take up a lot of unused space in a tile. Consider dropping long form text from your data.
- Filter your features at low zooms (z0 - z6) based on specific attributes. You can use the feature filter or the tile limit options to remove features at specific zoom levels, but keep the most important based on specific attributes.
- Lastly, you can consider spreading your data across different layers or tilesets to stay within the layer size limit. Be aware this can have cost implications by creating more than one layer or tileset.
If you want to exclude specific features, it is generally better to use a limit
specification instead. Setting layer_size
is more useful for holding yourself
to a performance budget than as a general feature limiting mechanism.
Identification
You can configure the identification of features in both "Features" and "Tiles". Both follow the same patterns and have the same options. In general, features.id
is used for defining or generating input IDs, while tiles.id
is used for defining or generating output IDs in the final vector tiles.
{
"features": {
"id": [ <Expression> ]
},
"tiles": {
"id": [ <Expression> ]
}
}
All IDs encoded in the final vector tiles will be integers, as defined by the vector tile specification. If you provide a string value to tiles.id
the value will be automatically hashed to an integer between 0
and 2^53 - 1
.
Order of operations
The following describes the order of operations and type conversions applied when using the features.id
and tiles.id
options. Remember the Mapbox Vector Tile specification requires all IDs to be encoded as positive integers between 0
and 2^53 - 1
.
- In general ID value in either
features.id
ortiles.id
will be converted to an Integer or Float if possible and will otherwise return a string or Null value. - If
features.id
is not defined, the top-levelid
of the GeoJSON feature will be used.- If there is no top-level
id
from the GeoJSON feature, arandom
integer will be generated. - If the value is set to
null
, no feature ID is set.
- If there is no top-level
- If
features.id
is defined, the result of the expression will be converted to an Integer or Float if possible and will otherwise return a string.- If the expression returns a null value, a random integer will be generated. If you are trying to prevent feature IDs in your final tile, set
tiles.id
to null.
- If the expression returns a null value, a random integer will be generated. If you are trying to prevent feature IDs in your final tile, set
- If
tiles.id
is not defined, the resulting value fromfeatures.id
will be used. - If
tiles.id
is defined, the value of the resulting expression replaces whatever value was defined infeatures.id
. - If the result of the
tiles.id
expression is a...- string, it is hashed to an integer.
- string representation of an integer, it will be converted to the absolute value of that number and fit within
0
to2^53 - 1
. - empty string, it will be treated as null and not encoded.
- negative number, the absolute value will be used.
- floating point number, it will be rounded down and fit within
0
to2^53 - 1
. - boolean, it will be treated as null and not encoded.
If you don't want to encode any IDs in your final vector tile you can pass null
to tiles.id
as your value instead of an expression array.
Duplicate IDs
While ID collision for unidentified features is unlikely, unique IDs aren't guaranteed since feature parsing occurs in a distributed environment. To guarantee unique identifiers, bring your own IDs. Duplicate feature IDs can cause unexpected behavior in tilesets and should be avoided.
Expressions reference
Instead of applying a static value in a recipe operation, some recipe operations allow you to set values dynamically on a per feature or per zoom level basis. This dynamic value setting is possible with Mapbox expressions.
An expression defines a formula for computing the value of a property using the operators described below. The set of expression operators provided by MTS includes:
- Mathematical operators for performing arithmetic and other operations on numeric values
- Logical operators for manipulating boolean values and making conditional decisions
- String operators for manipulating strings
- Data operators that provide access to the properties of features in your tileset source
Expressions are represented as JSON arrays. The first element of an expression array is a string naming the expression operator, for example "*"
or "case"
. Elements that follow (if any) are the arguments to the expression. Each argument is either a literal value (a string, number, boolean, or null
), or another expression array.
[expression_name, argument_0, argument_1, ...]
Expressions allow you to dynamically set values in the recipe and optimize your output tileset. The LayerObject houses all the available recipe options and denotes which fields accept expressions by listing a Expression
value.
Evaluating expressions
A data expression is any expression that accesses data in your tileset source — that is, any expression that uses one of the data operators: get
, has
, id
, geometry-type
, or properties
. The syntax of the expression and its arguments are checked when a recipe is validated, while the evaluation of the expression checked when a tileset is being processed. All expressions will be evaluated against a specific feature or zoom level, depending on logic of the recipe configurations.
Types
Mapbox expressions run on a series of primitive types.
- null: A null value.
- number: An integer or floating point number.
- string: Any series of UTF-8 characters.
- boolean: True or False.
- array: An array of values, optionally may specify the type of values in the array, for example,
array<boolean>
. - object: A map of key and value pairs.
It is also possible to see a value type in the documentation and error responses. This is a variable type that will not be a known primitive type until the expression is evaluated.
Errors
Errors in your expression can occur in two different ways, during recipe validation or during the evaluation of that recipe within MTS. If an expression in a recipe has a syntax error, such as an invalid input type, the recipe will fail validation and will not be allowed within MTS. If an expression experiences an error during the evaluation of an expression, the recipe operation determines the proper error handling and the processing a job or feature may continue. For example, if a filter
recipe operation experiences an evaluation error, the recipe expression will error and filter
will consider the feature to be false
. The default behavior of an expression evaluation error varies on the specific recipe operation.
Type expressions
The expressions in this section are for testing for and converting between different data types like strings, numbers, and boolean values.
Often, such tests and conversions are unnecessary, but they are helpful if a feature's data type is ambiguous or inconsistent. For example, you could use to-number
to make sure that values like "1.5"
(instead of 1.5
) are treated as numeric values. Another helpful use case is to convert values to their commonly represented types like using to-boolean
to convert a 1
to true
.
array
Asserts that the input is an array (optionally with a specific item type and length). If, when the input expression is evaluated, it is not of the asserted type, then this assertion will cause the whole expression to be aborted.
["array", value]: array
["array", type: "string" | "number" | "boolean", value]: array<type>
["array",
type: "string" | "number" | "boolean",
N: number (literal),
value
]: array<type, N>
Example
This example creates a tileset containing countries that have one or more official language. It uses the array
operator to assert that the value of the LANGUAGES
property pulled from the source data is a non-empty array.
It also uses the get
operator to pull the value of the LANGUAGES
property from the source data, the length
operator to get the length of the array, and the >=
operator to check if the resulting length is greater than or equal to 1
.
{
"version": 1,
"layers": {
"countries": {
"source": "mapbox://tileset-source/{username}/{id}",
"minzoom": 0,
"maxzoom": 10,
"features": {
"filter": [
"all",
[">=", ["length", ["array", ["get", "LANGUAGES"]]], 1]
],
"attributes": {
"set": {"num_official_languages": ["length", ["get", "LANGUAGES"]]}
}
}
}
}
}
boolean
Asserts that the input value is a boolean and returns it. If multiple values are provided, each one is evaluated in order until a boolean is obtained. If none of the inputs are booleans, the expression is an error. An example input of ["boolean", true]
would return True, ["boolean", "foo"]
would result in an error and ["boolean", 5, false]
would return False.
["boolean", value]: boolean
["boolean", value, fallback: value, fallback: value, ...]: boolean
Example
This example creates a tileset containing commercial buildings. It uses the boolean
operator to assert that the value of the commercial
property pulled from the source data is a boolean.
It also uses the get
operator to pull the value of the commercial
property from the source data, the ==
operator to check if the resulting string is equal to true
, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 12,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": [
"any",
["==", ["boolean", ["get", "commercial"], false], true]
]
}
}
}
}
literal
Provides a literal array or object value. If no input is provided, the output will be an error.
For example, the expression ["literal", {"foo": "bar"}}]
outputs {"foo": "bar"}
and the expression["literal", [1, 2, 3]]
outputs [1, 2, 3]
.
["literal", [...] (JSON array literal)]: array<T, N>
["literal", {...} (JSON object literal)]: object
Example
This example creates a tileset containing roads and adds a road_type
attribute to each feature. It uses get
in combination with literal
to set the road_type
attribute based on the value of the MTFCC
property in the source data. The ["get", "MTFCC"]
expression retrieves the value of the MTFCC
property from the current feature's properties.
If the value of MTFCC
matches one of the keys in the literal
expression, the road_type
will be set to the corresponding value. If the value of MTFCC
does not match any of the keys in the literal
expression, the road_type
will be null
. If MTFCC
does not exist in the current feature's properties, ["get", "MTFCC"]
will output null
and the outer get
expression will error because its argument is null
.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/roads",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {
"road_type": [
"get",
["get", "MTFCC"],
[
"literal",
{
"S1100": "Primary Road",
"S1200": "Secondary Road",
"S1400": "Local Street",
"S1720": "Stairway",
"S1730": "Alley",
"S1829": "Bike Path or Trail"
}
]
]
}
}
}
}
}
}
number
Asserts that the input value is a number and returns it. If multiple values are provided, each one is evaluated in order until a number is obtained. If none of the inputs are numbers, the expression is an error. An example input of ["number", 5]
returns 5, ["number", "foo"]
would result in an error and ["number", "foo", 5]
would also return 5.
["number", value]: number
["number", value, fallback: value, fallback: value, ...]: number
Example
This example creates a tileset containing buildings built before 1900. It uses the number
operator to assert that the value of the construction-date
property pulled from the source data is a number (unknown buildings are assumed to be built in 2020).
It also uses the get
operator to pull the value of the state
property from the source data, the <
operator to check if the resulting number is less than 1900
, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 12,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": [
"any",
["<", ["number", ["get", "construction-date"], 2020], 1900]
]
}
}
}
}
object
Returns the first object found in a list of arguments. If multiple values are provided, each one is evaluated in order until an object is obtained. The first object that is evaluated is returned.
- If none of the inputs are objects, the expression is an error.
- If there is no input, or the input is null, the expression is an error.
Examples:
["object", {}]
outputs{}
["object", "foo"]
would result in an error["object", 3, {}, {"foo": "bar"}}]
would return{}
["object", value]: object
["object", value, fallback: value, fallback: value, ...]: object
Example
This example creates a tileset containing roads and adds a project-lead
attribute to each feature. It uses the object
operator to validate the employees
field. It provides a default object {}
to prevent the expression from returning an error if ["get", "employees"]
does not output an object. If ["get", "employees"]
does output an object, and it has project-lead
as a property, then a new project-lead
attribute will be populated with the result.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/building-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"set": {
"attributes": {
"project-lead": [
"get",
"project-lead",
["object", ["get", "employees"], {}]
]
}
}
}
}
}
}
string
Asserts that the input value is a string and returns it. If multiple values are provided, each one is evaluated in order until a string is obtained. If none of the inputs are strings, the expression is an error. An example input of ["string", "foo"]
would return the string "foo", ["string", 5]
would result in an error and ["string", false, "bar"]
would return the string "bar".
["string", value]: string
["string", value, fallback: value, fallback: value, ...]: string
Example
This example creates a tileset containing places in the state of New York. It uses the string
operator to assert that the value of the state
property pulled from the source data is a string.
It also uses the get
operator to pull the value of the state
property from the source data, the ==
operator to check if the resulting string is equal to New York
, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 12,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/{id}",
"features": {
"filter": ["any", ["==", ["string", ["get", "state"]], "New York"]]
}
}
}
}
to-boolean
Converts the input value to a boolean. The result is false
when then input is an empty string, 0
, false
, null
, or NaN
; otherwise it is true
. The argument can be booleans, strings, numbers, arrays or objects; if during evaluation it is not, expression evaluation produces an error. Example input/output: ["to-boolean", 1] returns true
, ["to-boolean", ""] returns false
, ["to-boolean", "true"] returns true
.
["to-boolean", value]: boolean
Example
This example creates a tileset containing roads and adds a tollRequired
attribute to each feature. It uses the to-boolean
operator to change the value of the toll
property in the source data from a number to a boolean. The tollRequired
attribute will be true
if the value of toll
is 1
and false
if the value of toll is 0
.
This example also uses the get
operator to pull the value of the toll
property from the source data.
{
"version": 1,
"layers": {
"roads": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/natural-earth-roads",
"features": {
"attributes": {"set": {"tollRequired": ["to-boolean", ["get", "toll"]]}}
}
}
}
}
to-number
Converts the input value to a number, if possible. If the input is null
or false
, the result is 0. If the input is true
, the result is 1. If the input is a string, it is converted to a number as specified by the "ToNumber Applied to the String Type" algorithm of the ECMAScript Language Specification. If multiple values are provided, each one is evaluated in order until the first successful conversion is obtained. If none of the inputs can be converted, the expression is an error. Example input/output: ["to-number", "5"] returns 5
, ["to-number","false"] returns 0
, ["to-number", "foo"] throws an error
. The full expression result is a number.
["to-number", value, fallback: value, fallback: value, ...]: number
Example
This example creates a tileset containing buildings and adds a building_age
attribute to each feature set to the building's age calculated from the construction date. It uses the to-number
operator to set the type value of construction-date
from a string to a numeric value. If a value does not exist, it returns the default value 2021
.
This example also uses the get
operator to pull the value of the construction-date
property from the source data and the -
operator to get the difference between 2021
and the year of construction.
{
"version": 1,
"layers": {
"buildings": {
"source": "mapbox://tileset-source/{username}/building-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {
"building_age": [
"-",
2021,
["to-number", ["get", "construction-date"], 2021]
]
}
}
}
}
}
}
to-string
Converts the input value to a string. If the input is null
, the result is ""
. If the input is a boolean, the result is "true"
or "false"
. If the input is a number, it is converted to a string as specified by the "NumberToString" algorithm of the ECMAScript Language Specification. Otherwise, the input is converted to a string in the format specified by the JSON.stringify
function of the ECMAScript Language Specification. The arguments can be strings, numbers, booleans, arrays or objects. Example input/output: ["to-string",5] returns "5"
,["to-string",null] returns ""
, ["to-string",true] returns "true"
, ["to-string","foo"] returns "foo"
.
["to-string", value]: string
Example
This example creates a tileset containing buildings and adds an elevationDigitID
attribute to each feature containing a custom ID. It uses to-string
to change the value of the ELEVATION
property in the data source from a number to a string.
This example also uses the get
operator to pull the values of the ELEVATION
and NAME
properties from the source data, the length
operator to get the length of the array, and the concat
operator to combine the length of the ELEVATION
property with the NAME
.
{
"version": 1,
"layers": {
"buildings": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/populated-places",
"features": {
"attributes": {
"set": {
"elevationDigitID": [
"concat",
["length", ["to-string", ["get", "ELEVATION"]]],
["get", "NAME"]
]
}
}
}
}
}
}
typeof
Returns a string describing the type of the given value. The argument can be a string, number, boolean, array, object or null. Example input/output: ["typeof", ["get","foo"]] returns "string"
, ["typeof", 10] returns "number"
, ["typeof", true] returns "boolean"
, ["typeof", null] returns "null"
.
["typeof", value]: string
Example
This example creates a tileset containing places and adds a placeName
attribute to each feature with a value that is dependent on the data type of the NAME
property found in the source data. It uses typeof
to check the data type of the NAME
property. If it is of type number
, placeName
is set to INVALIDNUM
. If it is of type boolean
, placeName
is set to INVALIDBOOL
. In all other cases, placeName
is set to the value of NAME
.
This example also uses the case
operator to use the first output whose corresponding test condition evaluates to true
.
{
"version": 1,
"layers": {
"placeName": {
"source": "mapbox://tileset-source/{username}/populated-places",
"minzoom": 0,
"maxzoom": 10,
"features": {
"attributes": {
"set": {
"placeName": [
"case",
["==", ["typeof", ["get", "NAME"]], "number"],
"INVALIDNUM",
["==", ["typeof", ["get", "NAME"]], "boolean"],
"INVALIDBOOL",
["get", "NAME"]
]
}
}
}
}
}
}
Feature data
The feature
, geometry-type
, properties
, and id
operators are used to retrieve information about the properties contained in the GeoJSON specification. Feature expressions are often used as inputs to other expressions, enabling you to build chains of logic around your data. Common use cases for feature expressions include:
- Applying transformations to specific geometry types.
- Filtering out features that have (or lack) specific properties.
geometry-type
Gets the feature's geometry type: Point
, MultiPoint
, LineString
, MultiLineString
, Polygon
, MultiPolygon
. A common use for this expression is targeting a specific type of feature using a conditional operation, such as filter
expressions, union where
expressions, and case
expressions.
["geometry-type"]: string
Example
This example creates a tileset containing roads in which the simplification of features is based on the feature's geometry type. It uses the geometry-type
operator to check if the feature is a LineString
. If it is a LineString
, it will have a simplification value of 0
. If it is any other type, it will have a simplification value of 10
.
This example also uses the case
operator to use the first output whose corresponding test condition evaluates to true
.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/building-data",
"minzoom": 0,
"maxzoom": 10,
"features": {
"simplification": [
"case",
["==", ["geometry-type"], "LineString"],
0,
10
]
}
}
}
}
id
Gets the feature's ID, if it has one. See Identification for more detail on how IDs are used throughout the tiling process.
["id"]: value
Example
This example creates a tileset of points of interest in which features with even-numbered IDs are randomly selected to be included at zoom levels 0 to 4. This random-sampling approach can be used along with fine-tuned scale expressions to produce maps with gradually more features as you zoom in. It uses the id
operator to get the feature's unique ID.
This example also uses the zoom
operator to get the current zoom level, the %
operator to return the remainder after dividing the value of the id
by 2
, and the case
operator to use the first output whose corresponding test condition evaluates to true
.
{
"version": 1,
"layers": {
"poi": {
"source": "mapbox://{username}/places-of-interest",
"minzoom": 0,
"maxzoom": 10,
"features": {
"filter": [
"case",
["<=", ["zoom"], 4],
["==", ["%", ["id"], 2], 0],
true
]
}
}
}
}
properties
Gets the feature properties object. Note that in most cases, it is more efficient to use ["get", "property_name"]
directly.
["properties"]: object
Example
This example creates a tileset containing roads in which the properties from the source data is preserved in an old_attributes
attribute alongside a new streetname
attribute. It uses the properties
operator with the to-string
operator retrieve all properties from the source data, stringify them, and save them in the old_attributes
attribute. The old_attributes
can be used as a debugging tool as the attributes are transformed and modified later in the tiles
section of the recipe.
This example also uses the get
operator to pull the value of the streetname
property from the source data and the concat
operator to append the word "street" to the end of the street name.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://{username}/roads",
"minzoom": 0,
"maxzoom": 10,
"features": {
"attributes": {"set": {"old_attributes": ["to-string", ["properties"]]}}
},
"tiles": {
"attributes": {
"set": {"streetname": ["concat", ["get", "streetname"], " street"]}
}
}
}
}
}
Lookup
The at
, get
, has
, in
, index-of
, length
, and slice
operators are for retrieving information about feature properties contained in the GeoJSON specification. They access or test for properties, arrays, or strings, and the results can be used as inputs for other expressions. For example, the Census blocks - unioning census geographies example uses get
to retrieve existing properties and concatenates them into a single attribute based on zoom level. Another example is the Roads - unioning features based on shared attributes example, which uses get
to retrieve properties and filter them by comparing against other values.
at
Retrieves an item from an array using a 0-based index. The first argument, index, must be a positive integer within bounds of the array. Both arguments are required. The second argument, array, must be passed in by another expression that returns an array, for example literal
or get
(if the attribute is an array).
The following will work and return 5
: ["at", 1, ["literal", [4, 5, 6]]]
. The following will error: ["at", 1, [4, 5, 6]]
.
["at", number, array]: ItemType
Example
This example creates a tileset containing bakeries and adds a cheapest-menu-item
attribute to each feature. It uses the at
operator to select the first item in an array of prices.
It also uses the get
operator to pull the value of the sorted-item-prices
property from the data source.
{
"version": 1,
"layers": {
"bakeries": {
"source": "mapbox://tileset-source/{username}/dc-bakeries",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {
"cheapest-menu-item": ["at", 0, ["get", "sorted-item-prices"]]
}
}
}
}
}
}
get
Retrieves an attribute value from the current feature's attributes, or from another object if a second argument is provided. The first argument must be a string and the second (optional) argument must be an object. Evaluation will error if types are incorrect. Outputs null if the requested attribute is missing.
Examples:
["get", "name"]
will output the value of the feature'sname
attribute if it is set, else it will output null["get", "name", {"name": "Jasmine"}]
will outputJasmine
["get", "name", {"firstName": "Jasmine"}]
will output null
["get", string]: value
["get", string, object]: value
Example
This example creates a tileset containing roads and adds a road_type
attribute to each feature. It demonstrates both forms of get
. The outer get
sets a road_type
from an object produced by a literal
expression. The inner get
retrieves the MTFCC
property for a feature in the source data.
If MTFCC
does not exist in the feature's attributes, ["get", "MTFCC"]
will output null
, and the outer get
will error because its first argument is null
. If the value of MTFCC
matches one of the keys in the literal
expression, the road_type
will be set to the corresponding value. If the value of MTFCC
does not match any of the keys in the literal
expression, the road_type
will be null
.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/roads",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {
"road_type": [
"get",
["get", "MTFCC"],
[
"literal",
{
"S1100": "Primary Road",
"S1200": "Secondary Road",
"S1400": "Local Street",
"S1720": "Stairway",
"S1730": "Alley",
"S1829": "Bike Path or Trail"
}
]
]
}
}
}
}
}
}
has
If only one argument is provided, has
returns true
if the feature's attributes includes a key matching the first argument; returns false
otherwise. If two attributes are provided, has
returns true
if there is a key in the second argument that matches the first argument; returns false
otherwise.
["has", string]: boolean
["has", string, object]: boolean
Example
This example creates a tileset containing roads that have a quality
property in the source data. It uses has
to check for the existence of the quality
key in the properties in the source data.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/road-data",
"minzoom": 0,
"maxzoom": 5,
"features": {"filter": ["has", "quality"]}
}
}
}
in
Determines whether an item exists in an array or a substring exists in a string. Inputs should be a string and an array or a string; the output is a boolean. For example, ["in", "fun", "funky"]
evaluates to true
.
["in",
keyword: InputType (boolean, string, or number),
input: InputType (array or string)
]: boolean
Example
This example creates a tileset containing countries and adds a spanish_speaking
attribute to each feature set to true
if Spanish is among the languages spoken in that country. The in
operator is used to check whether the string Spanish
exists in the array of strings in the LANGUAGES
property in the source data.
It also uses the get
operator to pull the value of the LANGUAGES
property from the source data.
{
"version": 1,
"layers": {
"countries": {
"source": "mapbox://tileset-source/{username}/{id}",
"minzoom": 0,
"maxzoom": 10,
"features": {
"attributes": {
"set": {"spanish_speaking": ["in", "Spanish", ["get", "LANGUAGES"]]}
}
}
}
}
}
index-of
Returns the first position at which an item can be found in an array or a substring can be found in a string, or -1
if the input cannot be found. Accepts an optional index from where to begin the search.
The first argument must be the boolean, string, or number to search for, and the second argument must be either a string or an array containing the type of item being searched for. The optional third argument must be a number >= 0 indicating the element at which to begin the search. The return value is either the index of the item, if found, or -1 if it is not present.
Examples:
["index-of", "brown", "The quick brown fox"]
⇒10
["index-of", "jumps", "The quick brown fox"]
⇒-1
["index-of", "brown", ["literal", ["The", "quick", "brown", "fox"]]]
⇒2
["index-of",
keyword: InputType (boolean, string, or number),
input: InputType (array or string)
]: number
["index-of",
keyword: InputType (boolean, string, or number),
input: InputType (array or string),
index: number
]: number
Example
The recipe below uses index-of
to determine whether the street name (FULLNAME
) in the TIGER Roads example ends with " St"
, by comparing the index found to the length of the street name minus 3, and if so, uses concat
and slice
to replace the suffix with " Street"
.
{
"version": 1,
"layers": {
"streets": {
"minzoom": 0,
"maxzoom": 13,
"source": "mapbox://sample-source/tiger-roads",
"features": {
"attributes": {
"set": {
"FULLNAME": [
"let",
"name",
["coalesce", ["get", "FULLNAME"], ""],
[
"let",
"index_of_st",
["index-of", " St", ["var", "name"]],
"length_minus_3",
["-", ["length", ["var", "name"]], 3],
[
"case",
[
"all",
[">=", ["var", "index_of_st"], 0],
["==", ["var", "index_of_st"], ["var", "length_minus_3"]]
],
[
"concat",
["slice", ["var", "name"], 0, ["var", "index_of_st"]],
" Street"
],
["get", "FULLNAME"]
]
]
]
}
}
}
}
}
}
length
Gets the length of an array or string. Input should be an array or a string, and output is a number. For example, ["length", "hello"]
evaluates to 5.
["length", string | array | value]: number
Example
This example creates a tileset containing countries and adds a num_official_languages
attribute to each feature that reflects the number of languages spoken in the country. It uses the length
operator to get the length of the array of LANGUAGES
in the source data (like ["French", "German"]
).
This example also uses the get
operator to pull the value of the LANGUAGES
property from the source data.
{
"version": 1,
"layers": {
"countries": {
"source": "mapbox://tileset-source/{username}/{id}",
"minzoom": 0,
"maxzoom": 10,
"features": {
"attributes": {
"set": {"num_official_languages": ["length", ["get", "LANGUAGES"]]}
}
}
}
}
}
slice
Returns an item from an array or a substring from a string given a start index and optionally, an end index. The return value is inclusive of the start index but not of the end index. Negative indexing is supported.
- If no end index is specified,
slice
will return the array/substring fromstartIndex
to the end. - If
startIndex
is greater than the input length, this outputs an empty array/string. - If both
startIndex
andendIndex
are greater than the input length, this outputs an empty array/string. - If only
endIndex
is greater than the input length, this outputs a slice fromstartIndex
to the end of the input. - If no array/string input or no index is provided, the expression will error.
["slice",
input: InputType (array or string),
startIndex: number
]: OutputType (ItemType or string)
["slice",
input: InputType (array or string),
startIndex: number,
endIndex: number
]: OutputType (ItemType or string)
Example
This example creates a tileset containing bakeries and adds an abbreviated-title
attribute to each feature that contains only the first three characters of the full title
property found in the source data. It uses the slice
operator to include only the first three letters and assign the result to the new attribute.
It also uses the get
operator to pull the value of the title
property from the source data. If the title
property does not exist for the feature, ['get', 'title']
will evaluate to null
and this expression will error.
{
"version": 1,
"layers": {
"bakeries": {
"source": "mapbox://tileset-source/{username}/dc-bakeries",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {"abbreviated-title": ["slice", ["get", "title"], 0, 3]}
}
}
}
}
}
Decision
The expressions in this section can be used to add conditional logic to your recipe. For example, the 'case'
operator provides if/then/else logic, and the 'match'
operator allows you to map specific values of an input expression to different output expressions.
Decision logic could be used to filter a feature based on its property value compared against the current zoom. Decision logic could also be used to simplify a feature's geometry conditionally, based on a feature property value.
The Roads - ranking and simplifying linestrings example includes a few examples of decision logic, including the <=
and match
operators.
!
Logical negation. Returns true
if the input is false
, and false
if the input is true
. If the input is not a boolean, returns Error
. For example, ["!", true]
returns false.
["!", boolean]: boolean
Example
This example creates a tileset containing airport buildings that are not runways, taxiways, or aprons. It uses the !
operator to produce a filtered list of features. If the expression using !
returns true
, the feature will be included in the final list.
This example also uses the get
operator to pull the value of the type
property from the source data, the ==
operator to check if the input values are equal to one of the disallowed types (runway
, taxiway
, or apron
), and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"airport-buildings": {
"source": "mapbox://tileset-source/{username}/airport-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"filter": [
"!",
[
"any",
["==", ["get", "type"], "runway"],
["==", ["get", "type"], "taxiway"],
["==", ["get", "type"], "apron"]
]
]
}
}
}
}
!=
Returns true
if the input values are not equal, false
otherwise. The comparison is strictly typed: values of different runtime types are always considered unequal. Cases where the types are known to be different at parse time are considered invalid and will produce a parse error. For example, ["!=", "fee", "foo"]
returns true
; ["!=", 8.0, 8.0]
returns false
; ["!=", 8, "foo"]
returns Error
.
["!=", value, value]: boolean
Example
This example creates a tileset containing publicly accessible trails. It uses the !=
operator to produce a filtered list of features. If the expression using !=
returns true
, the feature will be included in the final list. In this example, the expression keeps all features in the trails layer with a right_of_way
value not equal to private
.
It also uses the get
operator to pull the value of the right_of_way
property from the source data.
{
"version": 1,
"layers": {
"trails": {
"source": "mapbox://tileset-source/{username}/trails-data",
"minzoom": 0,
"maxzoom": 5,
"features": {"filter": ["!=", ["get", "right_of_way"], "private"]}
}
}
}
<
Returns true
if the first input is strictly less than the second, false
otherwise. Strings are sorted in lexicographic order. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered invalid and will produce a parse error. For example, ["<", "fee", "foo"]
returns true
; ["<", 8, 8]
returns false
; ["<", 8, "foo"]
returns Error
.
["<", value, value]: boolean
Example
This example creates a tileset containing airports with fewer than three runways. It uses the <
operator to produce a filtered list of features. If the expression using <
returns 'true', the feature will be included in the final list. In this example, the expression keeps all features in the airport layer with a runways
value less than 3.
It also uses the get
operator to pull the value of the runways
property from the source data.
{
"version": 1,
"layers": {
"airport": {
"source": "mapbox://tileset-source/{username}/airport-data",
"minzoom": 0,
"maxzoom": 5,
"features": {"filter": ["<", ["get", "runways"], 3]}
}
}
}
<=
Returns true
if the first input is less than or equal to the second, false
otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Example input/output of the evaluation of each feature: ["<=", "fee", "foo"] returns True
, ["<=", 8, 10] returns True
, ["<=", 8, "foo"] returns Error
. The full expression result is a filtered list of features. If an expression fails, the feature is not included in the final list.
["<=", value, value]: boolean
Example
This example creates a tileset containing roads. It uses the <=
operator to filter out all features in the roads layer with a quality
integer property less than or equal to 10
.
It also uses the get
operator to pull the value of the quality
property from the source data.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/road-data",
"minzoom": 0,
"maxzoom": 5,
"features": {"filter": ["<=", ["get", "quality"], 10]}
}
}
}
==
Returns true
if the input values are equal, false
otherwise. The comparison is strictly typed: values of different runtime types are always considered unequal. Cases where the types are known to be different at parse time are considered invalid and will produce a parse error. For example, ["==", "fee", "foo"]
returns false
; ["==", 8.0, 8.0]
returns true
; ["==", 8, "foo"]
returns Error
.
["==", value, value]: boolean
Example
This example creates a tileset of publicly accessible trails. It uses the ==
operator to produce a filtered list of features. If the expression using ==
returns true
, the feature will be included in the final list. In this example, the expression keeps all features in the trails layer with a right_of_way
value of public
.
It also uses the get
operator to pull the value of the right_of_way
property from the source data.
{
"version": 1,
"layers": {
"trails": {
"source": "mapbox://tileset-source/{username}/trails-data",
"minzoom": 0,
"maxzoom": 5,
"features": {"filter": ["==", ["get", "right_of_way"], "public"]}
}
}
}
>
Returns true
if the first input is strictly greater than the second, false
otherwise. Strings are sorted in lexicographic order. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered invalid and will produce a parse error. For example, [">", "fee", "foo"]
returns false
; [">", 8, 8]
returns false
; [">", 8, "foo"]
returns Error
.
[">", value, value]: boolean
Example
This example creates a tileset containing routes that are more than 999 meters long. It uses the >
operator to produce a filtered list of features. If the >
expression returns true
, the feature will be included in the final list. In this example, the expression keeps all features in the trip layer with a length_m
integer property greater than 999.
It also uses the get
operator to pull the value of the length_m
property from the source data.
{
"version": 1,
"layers": {
"trip": {
"source": "mapbox://tileset-source/{username}/trip-data",
"minzoom": 0,
"maxzoom": 5,
"features": {"filter": [">", ["get", "length_m"], 999]}
}
}
}
>=
Returns true
if the first input is greater than or equal to the second, false
otherwise. Strings are sorted in lexicographic order. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered invalid and will produce a parse error. For example, [">=", "fee", "foo"]
returns false
; [">=", 8, 8]
returns true
; [">=", 8, "foo"]
returns Error
.
[">=", value, value]: boolean
Example
This example creates a tileset containing routes that are at least 1,000 meters long. It uses the >=
operator to produce a filtered list of features. If the expression using >=
returns 'true', the feature will be included in the final list. In this example, the expression keeps all features in the trip layer with a length_m
integer property greater than or equal to 1000
.
It also uses the get
operator to pull the value of the length_m
property from the source data.
{
"version": 1,
"layers": {
"trip": {
"source": "mapbox://tileset-source/{username}/trip-data",
"minzoom": 0,
"maxzoom": 5,
"features": {"filter": [">=", ["get", "length_m"], 1000]}
}
}
}
all
Returns true
if all the inputs are true
, false
if any is not, and Error
if any input is an error. The inputs are evaluated in order, and evaluation is short-circuiting: once an input expression evaluates to false
, the result is false
and no further input expressions are evaluated. For example, ["all", true, true]
returns true
; ["all", true, false]
returns false
.
["all", boolean, boolean]: boolean
["all", boolean, boolean, ...]: boolean
Example
This example creates a tileset containing roads that are of type Primary Highway
and have a length greater than or equal to 100
. It uses the all
operator to produce a filtered list of features. If the expression using all
returns true
, the feature will be included in the final list.
This example also uses the get
operator to pull the values of the length_km
and type
properties from the source data.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/road-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"filter": [
"all",
[">=", ["get", "length_km"], 100],
["==", ["get", "type"], "Primary Highway"]
]
}
}
}
}
any
Returns true
if any of the inputs are true
, false
if not, and Error
if any evaluated input is an error. The inputs are evaluated in order, and evaluation is short-circuiting: once an input expression evaluates to true
, the result is true
and no further input expressions are evaluated. For example, ["any", true, false]
returns true
; ["any", false, false]
returns false
.
["any", boolean, boolean]: boolean
["any", boolean, boolean, ...]: boolean
Example
This example creates a tileset containing airport surfaces that are runways, taxiways, and aprons. It uses the any
operator to produce a filtered list of features. If the any
expression returns true
, the feature will be included in the final list.
This example also uses the get
operator to pull the value of the type
property from the source data and the ==
operator to check if the input values are equal to one of the allowed types: runway
, taxiway
, or apron
.
{
"version": 1,
"layers": {
"surfaces": {
"source": "mapbox://tileset-source/{username}/airport-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"filter": [
"any",
["==", ["get", "type"], "runway"],
["==", ["get", "type"], "taxiway"],
["==", ["get", "type"], "apron"]
]
}
}
}
}
case
Selects the first output whose corresponding test condition evaluates to true, or the fallback value otherwise.
["case",
condition: boolean, output: OutputType,
condition: boolean, output: OutputType,
...,
fallback: OutputType
]: OutputType
Example
This example creates a tileset containing countries and adds a new num_official_languages
attribute to each feature that reflects the number of languages spoken in the country. The LANGUAGES
property in the source data has three possible types: some don't have a LANGUAGES
attribute at all (null
), some are set to a string (like "French"
), and some are set to an array of strings (like ["French", "German"]
). This example uses the case
operator to conditionally set the num_official_languages
attribute based on the type of data in the LANGUAGES
property.
In the first case, if a feature does not have a LANGUAGES
property, num_official_languages
is set to 0
. If the LANGUAGES
attribute is a string, num_official_languages
is set to 1
. In all other cases, num_official_languages
is set to the length of the LANGUAGES
array.
This example also uses the get
operator to pull the value of the LANGUAGES
property from the source data, the typeof
operator to check the data type, the ==
operator to check if the input values are equal, and length
operator to get the length of the array.
{
"version": 1,
"layers": {
"countries": {
"source": "mapbox://tileset-source/{username}/{id}",
"minzoom": 0,
"maxzoom": 10,
"features": {
"simplification": 10,
"attributes": {
"set": {
"num_official_languages": [
"case",
["==", ["get", "LANGUAGES"], null],
0,
["==", ["typeof", ["get", "LANGUAGES"]], "string"],
1,
["length", ["get", "LANGUAGES"]]
]
}
}
}
}
}
}
coalesce
Evaluates each expression in turn until the first non-null value is obtained, and returns that value.
The arguments to coalesce
may be of any type, and the return type is the type of its first non-null argument, or null
if it has no non-null arguments.
Example:
["coalesce", ["get", "no-such-attribute"], null, "fallback"]
⇒"fallback"
["coalesce", OutputType, OutputType, ...]: OutputType
Example
This example creates a tileset containing streets and adds a new name
attribute to each feature that is set to a default value when a street name is not available in the source data. It uses the coalesce
operator to use the FULLNAME
property if available, fall back to the zip code from the ZIPL
or ZIPR
property if necessary, or fall back to unknown
as a default value if none of these properties are present in the source data.
This example also uses the get
operator to pull the value of the FULLNAME
, ZIPL
, and ZIPR
properties from the source data and the concat
operator to prepend the name
attribute with "ZIP code "
if using a zip code instead of a street name.
See the TIGER Roads sample data.
{
"version": 1,
"layers": {
"streets": {
"minzoom": 0,
"maxzoom": 13,
"source": "mapbox://sample-source/tiger-roads",
"features": {
"attributes": {
"allowed_output": ["name"],
"set": {
"name": [
"coalesce",
["get", "FULLNAME"],
[
"concat",
"ZIP code ",
["coalesce", ["get", "ZIPL"], ["get", "ZIPR"], "unknown"]
]
]
}
}
}
}
}
}
match
Selects the output whose label value matches the input value, or the fallback value if no match is found. A match expression is made of a few parts:
- The input. The input can be any expression that returns a number or string (for example,
["get", "building_type"]
). - A label value with associated output. This may be repeated many times in the
match
expression. - A fallback output value.
The label values must be unique. Each label value must be either:
- A single literal value.
- An array of literal values, whose values must be all strings or all numbers (such as
[100, 101]
or["c", "b"]
). The input matches if any of the values in the array matches, like thein
operator.
The output types must all be of the same type. For example, if the fallback value is numeric, all output values must be numeric. If the input type does not match the type of the labels, the result will be the fallback value.
["match",
input: InputType (number or string),
label: InputType | [InputType, InputType, ...], output: OutputType,
label: InputType | [InputType, InputType, ...], output: OutputType,
...,
fallback: OutputType
]: OutputType
Example
This example creates a tileset containing roads and adds a quality
attribute to each feature with a value that is dependent on the road type defined in the source data. It uses the match
operator to map the type
property in the source data to a generated quality
attribute.
If the type
is Ferry Route
, the quality
will be 0
. If the type is Major Highway
or Primary Highway
, the quality
with be 1
. If the type is Secondary Highway
, the quality
will be 2
. In all other cases, the quality
with be 3
.
This example also uses the get
operator to pull the value of the type
property from the source data.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/road-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {
"quality": [
"match",
["get", "type"],
"Ferry Route",
0,
["Major Highway", "Primary Highway"],
1,
"Secondary Highway",
2,
3
]
}
}
}
}
}
}
Scales
The expressions in this section provide transformation operations between a numerical input and an output. Scale expressions are most commonly used to generate varying "simplification"
values by "zoom"
level, but they can also be used to generate new attributes based on existing properties of the source data.
interpolate
Produces continuous, smooth results by interpolating between pairs of input and output values ("stops"). The input
may be any numeric expression (e.g., ["get", "population"]
). Stop inputs must be numeric literals in strictly ascending order. The output type must be number
, or array<number>
.
Interpolation types:
["linear"]
: interpolates linearly from the pair of stops that the input falls between.["exponential", base]
: interpolates exponentially from the stops that the input falls between.base
controls the rate at which the output increases: higher values make the output increase more towards the high end of the range. With values close to 1 the output increases linearly.["cubic-bezier", x1, y1, x2, y2]
: interpolates using the cubic bezier curve defined by the given control points.
["interpolate",
interpolation: ["linear"] | ["exponential", base] | ["cubic-bezier", x1, y1, x2, y2],
input: number,
stop_input_1: number, stop_output_1: OutputType,
...,
stop_input_n: number, stop_output_n: OutputType
]: OutputType (number, array<number>)
Example
This example creates a tileset containing roads in which the simplification of features is based on zoom level using the interpolate
operator. This results in features that are more precise as you zoom in.
Each step value indicates the simplification value to be applied within a zoom range and linear
indicates that simplification values should be applied to zoom levels that fall between those steps linearly. In the resulting tileset zoom level 0
will have a simplification value of 10
, zoom level 10
will have a simplification value of 0
, and zoom levels between will be assigned linearly for example, zoom level 1
will have a simplification of 9
, zoom level 2
will have a simplification of 8
, and so on.
This example also uses the zoom
operator to get the current zoom level.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/building-data",
"minzoom": 0,
"maxzoom": 12,
"features": {
"simplification": ["interpolate", ["linear"], ["zoom"], 0, 10, 10, 0]
}
}
}
}
step
Produces discrete, stepped results by evaluating a function defined by pairs of input and output values ("stops"). The input
may be any numeric expression (e.g., ["get", "population"]
). Stop inputs must be numeric literals in strictly ascending order. Outputs may be any type, including numbers, booleans, strings or arrays. The value returned by the "step"
function is the output value associated with the stop less than the input, or the default output if the input is less than the first stop.
["step",
input: number,
stop_output_0: OutputType,
stop_input_1: number, stop_output_1: OutputType,
stop_input_n: number, stop_output_n: OutputType, ...
]: OutputType
Example
This example creates a tileset containing roads in which the simplification of features is based on zoom level using the step
operator. This results in features that are more precise as you zoom in.
The default simplification value is 10
and each step value indicates the simplification to be applied within a zoom range. Zoom levels less than the first step, 3
, will use the default value, 10
. Zoom levels from the first step, 3
, up to (but not including) the next step, 7
, will have a simplification value of 2
. Zoom levels 7
through 9
will have a simplification value of 1
. And, zoom levels 10
and higher will have a simplification value of 0
.
This example also uses the zoom
operator to get the current zoom level.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/building-data",
"minzoom": 0,
"maxzoom": 12,
"features": {"simplification": ["step", ["zoom"], 10, 3, 2, 7, 1, 10, 0]}
}
}
}
Variable binding
The let
and var
expressions allow you to calculate and give a name to one or more values that can then be used repeatedly within a single expression.
In most cases, recipes will instead use the set
recipe field to create a new feature attribute, but because the order of set
operations is undefined, a set
expression cannot refer to the output of a different set
expression. So if an expression needs to refer repeatedly to a previously-calculated value, it must instead use let
to calculate that value and var
to refer to the result of the calculation.
See let
below for a more detailed example of how let
and var
are used together.
let
Binds expressions to named variables, which can then be referenced in the result expression using ["var", "variable_name"]
. Note that let
only assigns a name within the current expression and does not save its calculation as an attribute. As a result, you must refer to these single-expression names with var
, not get
.
The local variable names for let
must be strings containing only alphanumeric characters and underscores. The expressions that are used to provide the value for each temporary variable, and the expression that uses them, may return any type.
For example, the following expression sets a variable named "a"
equal to 5
and "b"
equal to 10
, then it uses those variables with the +
expression to return the sum of 5
and 10
: ["let", "a", 5, "b", 10, ["+", ["var", "a"], ["var", "b"]]]
(returns 15
).
["let",
string (alphanumeric literal), any, string (alphanumeric literal), any, ...,
OutputType
]: OutputType
Example
This example creates a tileset containing cities and adds a new POP_OTHER
attribute to each feature that rounds the population of each city in the Populated Places sample data set by different divisors at different zoom levels. In this case, it rounds the population to the nearest 1,000 at high zoom levels, to the nearest 100,000 at the lowest zoom level, and to intermediate divisors at all other zoom levels between.
This example uses let
to calculate the appropriate divisor for the current zoom level and assigns it to the internal name divisor
. Then uses it for both division and multiplication by referring to the divisor
through var
.
This example also uses the case
and has
operators to leave the POP_OTHER
attribute unaltered if it is not present to avoid doing math on an undefined value, which will result in an error. If the POP_OTHER
attribute is not present, it still uses get
to retrieve the known-to-be-undefined attribute instead of using null
in the recipe because the case
expression can signal an error during recipe validation if different case
values have different types.
{
"version": 1,
"layers": {
"cities": {
"minzoom": 0,
"maxzoom": 7,
"source": "mapbox://tileset-source/{username}/populated-places",
"features": {
"attributes": {
"allowed_output": ["NAME", "POP_OTHER"],
"set": {
"POP_OTHER": [
"let",
"divisor",
[
"case",
["<", ["zoom"], 1],
100000,
["<", ["zoom"], 2],
50000,
["<", ["zoom"], 3],
25000,
["<", ["zoom"], 4],
10000,
["<", ["zoom"], 5],
5000,
["<", ["zoom"], 6],
2000,
1000
],
[
"case",
["has", "POP_OTHER"],
[
"*",
["var", "divisor"],
["round", ["/", ["get", "POP_OTHER"], ["var", "divisor"]]]
],
["get", "POP_OTHER"]
]
]
}
}
}
}
}
}
var
References variable bound using "let"
.
["var", previously bound variable name]: the type of the bound expression
String
The expressions in this section are for manipulating string values. Supported expressions are concat
, downcase
, and upcase
. The Census blocks - unioning census geographies example demonstrates the concat
string expression used in combination with the case
expression to conditionally concatenate a string to an attribute.
The slice
expression (in the Lookup section) can also be used to manipulate strings, although it can also be used for array slicing.
concat
Returns a string
consisting of the concatenation of the inputs. Each input is converted to a string as if by to-string
, so all data types are accepted. For example, ["concat", false, "2"]
outputs false2
.
["concat", value, value, ...]: string
Example
This example creates a tileset containing buildings and adds a building_description
attribute to each feature. It uses the string concat
operator to combine the string values of the construction-date
and building_group
properties of each feature in the source data. The building_description
attribute is then set to this string value.
This example also uses the get
operator to pull the value of the building_group
and construction-date
properties from the source data and to get the full description of the building_group
type from the object literal
.
{
"version": 1,
"layers": {
"buildings": {
"source": "mapbox://tileset-source/{username}/building-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {
"building_description": [
"concat",
["get", "construction-date"],
":",
[
"get",
["get", "building_group"],
[
"literal",
{"R": "residential property", "C": "commercial property"}
]
]
]
}
}
}
}
}
}
downcase
Returns the input string converted to lowercase. Follows the Unicode Default Case Conversion algorithm and the locale-insensitive case mappings in the Unicode Character Database. If the input is not provided, or it is not a string, the output is an error.
Examples:
["downcase", true]
outputs an error["downcase", "MapBox"]
outputsmapbox
["downcase", string]: string
Example
This example creates a tileset containing roads and adds a lowercase-name
attribute to each feature. It uses the downcase
operator to transform the value of the name
property in the source data to lowercase.
It also uses the get
operator to pull the value of the name
property from the source data.
{
"version": 1,
"layers": {
"roads": {
"source": "mapbox://tileset-source/{username}/roads",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {"set": {"lowercase-name": ["downcase", ["get", "name"]]}}
}
}
}
}
upcase
Returns the input string converted to uppercase. Follows the Unicode Default Case Conversion algorithm and the locale-insensitive case mappings in the Unicode Character Database.
There must be a single string argument, and the value returned is a string
Example:
["upcase", "Some Text"]
⇒"SOME TEXT"
["upcase", string]: string
Example
This example creates a tileset containing streets and adds a FULLNAME
attribute to each feature. It uses the upcase
operator to transform the value of the FULLNAME
property in the source data to uppercase to match a desired map appearance.
It also uses the get
operator to pull the value of the FULLNAME
property from the source data, the typeof
operator to determine if FULLNAME
is a string
, and the case
operator so it only attempts to convert FULLNAME
to uppercase if it is a string
.
{
"version": 1,
"layers": {
"streets": {
"minzoom": 0,
"maxzoom": 12,
"source": "mapbox://sample-source/tiger-roads",
"features": {
"attributes": {
"set": {
"FULLNAME": [
"case",
["==", ["typeof", ["get", "FULLNAME"]], "string"],
["upcase", ["get", "FULLNAME"]],
["get", "FULLNAME"]
]
}
}
}
}
}
}
Math
The -
, *
, /
, %
, ^
, +
, abs
, acos
, asin
, atan
, ceil
, cos
, e
, floor
, hash
, ln
, ln2
, log10
, log2
, max
, min
, pi
, random
, round
, sin
, sqrt
, and tan
operators are for applying mathematical operations.
-
For two inputs, returns the result of subtracting the second input from the first. For a single input, returns the result of subtracting it from 0. An example input of ["-", 5, 2]
returns 3, ["-", 3, -1]
returns 4, ["-", 3]
returns -3 and both ["-", 3, "foo"]
and ["-", false, 3]
would result in an error.
["-", number, number]: number
["-", number]: number
Example
This example creates a tileset containing buildings with a property value that is over $10,000 more than it was the year before. It uses the -
operator to find the difference between the value-last-year
and current-value
properties in the source data.
This example also uses the get
operator to pull the values of the value-last-year
and current-value
properties from the source data, the >
operator to check if the resulting difference is greater than 10000
, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": [
"any",
[
">",
["-", ["get", "current-value"], ["get", "value-last-year"]],
10000
]
]
}
}
}
}
*
Returns the product of the inputs. An example input of ["*", 3, 5, 2]
returns 30, ["*", 3, -1]
returns -3 and both ["*", 3, "foo"]
and ["*", false, 3]
would result in an error. An implied initial value of 1 is assumed so ["*"]
would return 1 and ["*", 3]
would return 3.
["*", number, number, ...]: number
Example
This example creates a tileset containing buildings with a calculated cost over $100,000. It uses the *
operator to calculate the total cost by multiplying the cost-per-sqft
and sqft
properties found in the source data.
This example also uses the get
operator to pull the value of the sqft
and cost-per-sqft
properties from the source data, the >
operator to check if the resulting product is greater than 100000
, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": [
"any",
[">", ["*", ["get", "sqft"], ["get", "cost-per-sqft"]], 100000]
]
}
}
}
}
/
Returns the result of floating point division of the first input by the second. An example input of ["/", 9, 3]
returns 3, ["/", 12.5, 2.5]
returns 5.0 and both ["/", 3, "foo"]
and ["/", false, 3]
would result in an error.
["/", number, number]: number
Example
Show buildings that have more than 10 dorm rooms (each sleeping 12), assuming capacity refers to the sleeping capacity of the entire building and each dorm room sleeps 12 people.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {"filter": ["any", [">", ["/", ["get", "capacity"], 12], 10]]}
}
}
}
%
Returns the remainder after integer division of the first input by the second. An example input of ["%", 10, 3]
returns 1, and both ["%", 3, "foo"]
and ["%", false, 3]
would result in an error.
["%", number, number]: number
Example
This example creates a tileset containing cities. Only some features are included at lower zoom levels and more features are included at higher zoom levels. This can be helpful if your source data contains many points, and it is not possible to display all data legibly at low zoom levels.
In this example, the features in the source data don't have an inherent hierarchy so it uses the random
operator to generate a random integer. Then it uses the %
operator to remove a systematic fraction of the features at lower zoom levels. As a result, zooming in will gradually reveal more points and fewer as you zoom out. The example below shows all the features at zoom level 5
, only half of them appear at zoom level 4
, and then the recipe filters away an additional half at each successively lower zoom level.
This example also uses the zoom
operator to get the current zoom level, the -
operator to get the difference, and the ^
operator to raise the first input to the power of the second.
{
"version": 1,
"layers": {
"cities": {
"minzoom": 0,
"maxzoom": 5,
"source": "mapbox://sample-source/populated-places",
"features": {
"filter": ["==", 0, ["%", ["random"], ["^", 2, ["-", 5, ["zoom"]]]]]
}
}
}
}
^
Returns the result of raising the first input to the power specified by the second. An example input of ["^", 2, 3]
returns 8, ["^", 10, -2]
returns 0.01 and both ["^", 3, "foo"]
and ["^", false, 3]
would result in an error.
["^", number, number]: number
Example
If your source data contains many points, it may not be possible to display all data legibly at low zoom levels. One possible solution is to hide some features at lower zoom levels and display more features as you zoom in.
If the features in your source data don't have an inherent hierarchy, you can assign a random
value to each feature and use filter
to remove a systematic fraction of the features at lower zoom levels. As a result, zooming in will gradually reveal more points and fewer as you zoom out. The example below shows all the features at zoom level 5
, only half of them appear at zoom level 4
, and then the recipe filters away an additional half at each successively lower zoom level.
This example also uses the zoom
operator to get the current zoom level, the -
operator to get the difference, and the ^
operator to raise the first input to the power of the second.
{
"version": 1,
"layers": {
"cities": {
"minzoom": 0,
"maxzoom": 5,
"source": "mapbox://sample-source/populated-places",
"features": {
"filter": ["==", 0, ["%", ["random"], ["^", 2, ["-", 5, ["zoom"]]]]]
}
}
}
}
+
Returns the sum of the inputs. An example input of ["+", 3, 5, 2]
returns 10, ["+", 3, -1]
returns 2 and ["+", 3]
returns 3. Both ["+", 3, "foo"]
and ["+", false, 3]
would result in an error. If no arguments are specified 0 is returned.
["+", number, number, ...]: number
Example
This example creates a tileset containing buildings whose altitude at its tallest point (the sum of the feature's altitude at ground level and the building height) is greater than 10,000 feet. It uses the +
operator to add the altitude
and building-height
properties from the source data.
This example also uses the get
operator to pull the values of the altitude
and building-height
properties from the source data, the >
operator to check if the resulting sum is greater than 10000
, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"buildings": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": [
"any",
[">", ["+", ["get", "altitude"], ["get", "building-height"]], 10000]
]
}
}
}
}
abs
Returns the absolute value of the input. An example input of ["abs", -3.5]
returns 3.5, ["abs", 3.5]
also returns 3.5 and ["abs", "foo"] would result in an error.
["abs", number]: number
Example
This example creates a tileset containing places between the Tropic of Cancer (at a latitude of 23.43652
) and the Tropic of Capricorn (at a latitude of -23.43652
). It uses the abs
operator to check against the latitude's absolute value.
It also uses the get
operator to pull the value of the latitude
property from the source data, the <
operator to check if the absolute value is less than 23.43652
, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": ["any", ["<", ["abs", ["get", "latitude"]], 23.43652]]
}
}
}
}
acos
Returns the arccosine of the input.
["acos", number]: number
asin
Returns the arcsine of the input.
["asin", number]: number
atan
Returns the arctangent of the input.
["atan", number]: number
ceil
Returns the smallest integer that is greater than or equal to the input.
["ceil", number]: number
cos
Returns the cosine of the input.
["cos", number]: number
e
Returns the mathematical constant e.
["e"]: number
floor
Returns the largest integer that is less than or equal to the input. The input is required to be a number; if during evaluation it is not, expression evaluation produces an error. Example input/output: ["floor", 5.5] returns 5.0
, ["floor", -5.5] returns -6.0
.
["floor", number]: number
Example
This example creates a tileset containing places that have a min_zoom
greater than or equal to 7
. It uses the floor
operator to get the largest integer that is less than or equal to the value of the min_zoom
property in the source data.
It also uses the get
operator to pull the value of the min_zoom
property from the source data.
{
"version": 1,
"layers": {
"places": {
"source": "mapbox://tileset-source/{username}/populated-places",
"minzoom": 0,
"maxzoom": 10,
"features": {"filter": ["all", [">=", ["floor", ["get", "min_zoom"]], 7]]}
}
}
}
hash
Generates a unique integer from a string value, or an expression that evaluates to a string. If no argument is passed, the hash
expression will throw an error. Particularly helpful for generating IDs from a combination of the feature attributes. See Identification for more details on the ID creation process.
["hash", string]: number
Example
This example creates a tileset containing traffic data and adds a unique identifier for each segment of the traffic data. It uses the hash
operator to combine the roadname
and hour
properties from the source data.
It also uses the get
operator to pull the value of the roadname
and hour
properties from the source data and the concat
operator to combine the contents of the two properties.
{
"version": 1,
"layers": {
"traffic": {
"source": "mapbox://{username}/traffic",
"minzoom": 0,
"maxzoom": 10,
"tiles": {
"id": ["hash", ["concat", ["get", "roadname"], ["get", "hour"]]]
}
}
}
}
ln
Returns the natural logarithm of the input.
["ln", number]: number
ln2
Returns mathematical constant ln(2).
["ln2"]: number
log10
Returns the base-ten logarithm of the input.
["log10", number]: number
log2
Returns the base-two logarithm of the input.
["log2", number]: number
max
Returns the maximum value of the inputs. An example input of ["max", 3, 5, 7]
returns 7, ["max", 3, -1]
returns 3 and both ["max", 3, "foo"]
, ["max", false, 3]
and ["max", [3, 5, 7]]
would result in an error.
["max", number, number, ...]: number
Example
This example creates a tileset containing buildings that were last built or sold (whichever is more recent) before 2010. It uses the max
operator to look at the values of both the year-built
and year-sold
properties in the source data and return whichever is larger.
This example also uses the get
operator to pull the values of the year-built
and year-sold
properties from the source data and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": [
"any",
["<", ["max", ["get", "year-build"], ["get", "year-sold"]], 2010]
]
}
}
}
}
min
Returns the minimum value of the inputs. An example input of ["min", 3, 5, 7]
returns 3, ["min", 3, -1]
returns -1 and both ["min", 3, "foo"]
, ["min", false, 3]
and ["min", [3, 5, 7]]
would result in an error.
["min", number, number, ...]: number
Example
This example creates a tileset containing counties that have had a population less than 1,000 people within the last 5 years. It uses the min
operator to retrieve the lowest population number from the last 5 years and then uses the <
check if it is less than 1000
.
This example also uses the get
operator to pull the value of the population-history
property from the source data, the at
operator to get the values at the first through fifth positions in the array, and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/counties",
"features": {
"filter": [
"any",
[
"<",
[
"min",
["at", 0, ["get", "population-history"]],
["at", 1, ["get", "population-history"]],
["at", 2, ["get", "population-history"]],
["at", 3, ["get", "population-history"]],
["at", 4, ["get", "population-history"]]
],
1000
]
]
}
}
}
}
pi
Returns the mathematical constant pi (3.141592653589793...). This does not take any additional arguments and always returns the same value.
["pi"]: number
Example
This example creates a tileset containing buildings that have an area of signal coverage greater than 500 m². It uses the pi
operator to calculate the area of a circle (πr²) around the feature using the value of the signal-range
property as the radius.
It also uses the get
operator to pull the value of the signal-range
property from the source data, the ^
operator to get the value of the radius to the second power, the *
operator to multiply the resulting radius² with pi, and the >
operator to check if the resulting area is greater than 500
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 15,
"source": "mapbox://tileset-source/{username}/building-data",
"features": {
"filter": [
"any",
[">", ["*", ["pi"], ["^", ["get", "signal-range"], 2]], 500]
]
}
}
}
}
random
Generates a random integer between 0
and 2^53
. This operator is used by default for any features that do not have an ID defined.
A random
expression takes no arguments and returns a number.
["random"]: number
Example
This example creates a tileset containing cities. Only some features are included at lower zoom levels and more features are included at higher zoom levels. This can be helpful if your source data contains many points, and it is not possible to display all data legibly at low zoom levels.
In this example, the features in the source data don't have an inherent hierarchy so it uses the random
operator to generate a random integer. Then it uses the %
operator to remove a systematic fraction of the features at lower zoom levels. As a result, zooming in will gradually reveal more points and fewer as you zoom out. The example below shows all the features at zoom level 5
, only half of them appear at zoom level 4
, and then the recipe filters away an additional half at each successively lower zoom level.
This example also uses the zoom
operator to get the current zoom level, the -
operator to get the difference, and the ^
operator to raise the first input to the power of the second.
{
"version": 1,
"layers": {
"cities": {
"minzoom": 0,
"maxzoom": 5,
"source": "mapbox://sample-source/populated-places",
"features": {
"filter": ["==", 0, ["%", ["random"], ["^", 2, ["-", 5, ["zoom"]]]]]
}
}
}
}
round
Rounds the input to the nearest integer. Halfway values are rounded away from zero. Input must be a number and the output will be a number. Any other input type will result in an Error. For example, ["round", -1.5]
evaluates to -2; ["round", "-1.5"]
evaluates to Error
.
["round", number]: number
Example
This example creates a tileset containing shops and adds an icecream_pints_per_capita
attribute to each feature. It uses the /
operator to calculate the pints per capita, then uses the round
operator to change the result into a whole number rather than a longer decimal number.
It also uses the get
operator to pull the values of the icecream_pints
and population
properties from the source data.
{
"version": 1,
"dessert": {
"shops": {
"source": "mapbox://tileset-source/{username}/dessert-data",
"minzoom": 0,
"maxzoom": 5,
"features": {
"attributes": {
"set": {
"icecream_pints_per_capita": [
"round",
["/", ["get", "icecream_pints"], ["get", "population"]]
]
}
}
}
}
}
}
sin
Returns the sine of the input.
["sin", number]: number
sqrt
Returns the square root of the input.
["sqrt", number]: number
tan
Returns the tangent of the input.
["tan", number]: number
Zoom
The expression in this section is for performing zoom-specific operations with your data. This is often used to simplify or filter data based on the tile's zoom level. For example, you can write an expression include less detailed information at lower zooms and include full detail at higher zooms. This provides a faster and more usable map. The Roads - ranking and simplifying linestrings example demonstrates this.
zoom
Gets the current zoom level. This will be a positive integer greater than or equal to zero.
["zoom"]: number
Example
This example creates a tileset containing roads and filters out residential roads below zoom level 12
. It uses the zoom
operator to get the current zoom level.
It also uses the get
operator to pull the value of the type
property from the source data and the any
operator to check if any of the inputs are true
.
{
"version": 1,
"layers": {
"places": {
"minzoom": 0,
"maxzoom": 14,
"source": "mapbox://tileset-source/{username}/{id}",
"features": {
"filter": [
"any",
[">=", ["zoom"], 12],
["!=", ["get", "type"], "residential"]
]
}
}
}
}
More resources
- To see the recipe for an existing tileset created with MTS, go to its Tileset explorer, click on Job history, then click on View recipe.
- For a list of example recipes that use some of the expression operators described above, see the Tileset recipe examples page.
- The Get started using Mapbox Tiling Service and the Tilesets CLI tutorial walks you through the process of creating a new tileset, including creating and using a tileset recipe, with the Tilesets CLI.
- Mapbox Tiling Service has several endpoints that can be used to validate, update, and read a tileset's recipe. Learn more in the Mapbox Tiling Service documentation.