This article describes how to use our vector tiles in your own mapstyle.
You can render maps with our vector tiles using mapping engines such as Maplibre or Mapbox. To style this data, tools like Maputnik let you create your own custom map styles. To get started, you’ll need a valid Mapcreator vector token. If you don’t already have one, please contact our team at info@mapcreator.io.
This guide introduces the basics of working with our data in your map styles. The examples include both raw JSON snippets and screenshots from Maputnik. For more advanced details on styling, we recommend consulting the official documentation for Maplibre or Mapbox, depending on your chosen rendering engine.
This article is organized into the following sections:
o Examples to Get You Started - Adding Disputed Area
- Highlight a Country
- Hide (Parts of) a Specific Label
o Overview of Our Vector Tile Sets and Their Source Layers
- Base
- Administrative
o Text Field Translations
Examples to Get You Started
Adding Layers Using Our Base Data
To use our tiles, you'll need to include our tiles as a source in the sources section of your mapstyle. Use the following URL: https://vapi.mc-cdn.io/dataset/base/json?access_token=YOUR_ACCESS_TOKEN. Don't forget to replace YOUR_ACCESS_TOKEN with a valid access token.
"mc-base": {
"type": "vector",
"url": "https://vapi.mc-cdn.io/dataset/base/json?access_token=YOUR_ACCESS_TOKEN"
}
In Maputnik, you'd do this by clicking Data Sources in the top and adding a new source in the bottom of the menu as follows:
Once the data source is added to Maputnik, you can toggle the View in the top bar from Map to Inspect. This will provide you with an interface showing all the layers and features in your data sources. You can inspect the properties of features by hovering over them.
Next up, you'll need to add layers that use this data source. Let's create a background layer first, showing a blue oceanic color.
{
"id": "Landuse-background",
"type": "background",
"layout": {"visibility": "visible"},
"paint": {
"background-color": "#85CBFA"
}
}
In Maptunik, add a new layer using the Add Layer button in the top-left. Set the type to Background and give it an informative ID (for example, Landuse-background):
Now, let's add a layer to show all land.
{
"id": "Landuse-land",
"type": "fill",
"source": "mc-base",
"source-layer": "polygons",
"filter": ["==", ["get", "categ"], "land"],
"layout": {"visibility": "visible"},
"paint": {
"fill-color": "rgba(255, 255, 229, 1)",
"fill-antialias": false
}
}
This looks as follows in Maputnik. To add the layer:
And then to edit the filter and color:
By default, Maputnik shows the function builder, which is a legacy way to define mapstyle filters in Mapbox/Maplibre. We recommend using expressions instead. To convert a field into an expression, click the fx button. This will allow you to type your expressions as shown in the previous screenshot.
Let's add a line layer next that shows us the boundaries of all countries. The filter will ensure we only show administrative boundaries with admin level 2, and that they are not maritime. This makes sure we do not draw other features in the lines source layer, like roads, railways, waterways, etc. We also adapt the line width depending on the zoom level.
{
"id": "Boundary-country",
"type": "line",
"source": "mc-base",
"source-layer": "lines",
"minzoom": 0,
"maxzoom": 24,
"filter": [
"all",
["==", ["get", "boundarylevel"], 2],
["!", ["has", "is_maritime"]]
],
"layout": {"visibility": "visible"},
"paint": {
"line-color": "rgba(154, 154, 154, 1)",
"line-width": [
"interpolate",
["linear"],
["zoom"],
0,
0.5,
4,
1,
6,
1.3,
8,
2,
12,
2.5,
16,
3,
23,
25
]
}
}
Next up, let's add a label layer to show all country names.
{
"id": "World-country-name",
"type": "symbol",
"source": "mc-base",
"source-layer": "labelcentroid",
"minzoom": 0,
"maxzoom": 8,
"filter": [
"==",
["get", "categ"],
"admin_level_2"
],
"layout": {
"text-field": [
"coalesce",
["get", "name:en"],
["get", "name"]
],
"symbol-sort-key": [
"-",
["get", "area"]
],
"text-font": ["Open Sans Regular"],
"text-size": [
"interpolate",
["linear"],
["zoom"],
6,
15,
8,
18
],
"visibility": "visible"
},
"paint": {
"text-color": "rgba(110, 110, 110, 1)",
"text-halo-color": "rgba(255, 255, 255, 1)",
"text-halo-width": 0.5
}
}
At the time of writing, there is no symbol-sort-key option in the Maputnik interface, so the expression needs to be added manually in the layout section of the JSON. See lines 29 to 32 in the image below:
More information about the various source layers within our base data can be found below, as well as recommendations for the expression to be used with the text field property.
Adding Disputed Areas
In the example above, we added all country boundaries. However, if you need more flexibility, you can further filter them to show either disputed or undisputed boundaries. In this case, the setup remains the same as before, but you add one of the following filters.
For disputed boundaries:
["has", "is_disputed"]
For undisputed boundaries:
["!", ["has", "is_disputed"]]
Additionally, you can apply different styling based on the boundary type. For example, add a dashed line to disputed boundaries:
{
"id": "Boundary-country-disputed",
"type": "line",
"source": "mc-base",
"source-layer": "lines",
"minzoom": 0,
"maxzoom": 24,
"filter": [
"all",
["==", ["get", "boundarylevel"], 2],
["!", ["has", "is_maritime"]],
["has", "is_disputed"]
],
"layout": {
"line-join": "round",
"visibility": "visible",
"line-cap": "butt"
},
"paint": {
"line-color": "rgba(154, 154, 154, 1)",
"line-width": [
"interpolate",
["linear"],
["zoom"],
0,
0.5,
4,
1,
6,
1.3,
8,
2,
12,
2.5,
16,
3,
23,
25
],
"line-dasharray": [3, 2]
}
}
Highlight a Country
To highlight a country, we will first add mc-countries
source, with URL https://vapi.mc-cdn.io/dataset/saga-countries/json?access_token=YOUR_ACCESS_TOKEN. Don't forget to replace YOUR_ACCESS_TOKEN with your personal access token.
"mc-countries": {
"type": "vector",
"url": "https://vapi.mc-cdn.io/dataset/saga-countries/json?access_token=YOUR_ACCESS_TOKEN&include_token=true"
}
Next up, we add a new polygon layer using this data source and source layer data
. We add a filter on the ISO-code property iso
. Make sure to add the layer above the background, landuse, and other polygons, but below our lines and labels.
{
"id": "Italy",
"type": "fill",
"source": "mc-countries",
"source-layer": "data",
"filter": ["==", ["get", "iso"], "IT"],
"paint": {"fill-color": "rgba(20, 80, 79, 1)"}
}
Hide (Parts of) a Specific Label
Let's say we have a layer with labels for Iraqi governorates. This might look as follows:
{
"id": "Iraq-governorates-boundary",
"type": "line",
"source": "mc-base",
"source-layer": "lines",
"filter": [
"all",
[
"in",
["get", "boundarylevel"],
["literal", [3, 4]]
],
["!", ["has", "is_maritime"]],
["==", ["get", "isocode"], "IQ"]
],
"layout": {"visibility": "visible"},
"paint": {
"line-color": "rgba(208, 208, 208, 1)",
"line-width": [
"interpolate",
["linear"],
["zoom"],
0,
0.5,
4,
1,
6,
1.3,
8,
2,
12,
2.5,
16,
3,
23,
25
]
}
},
{
"id": "Iraq-governorates-labels",
"type": "symbol",
"source": "mc-base",
"source-layer": "labelcentroid",
"layout": {
"text-field": ["get", "name:en"],
"visibility": "visible",
"text-font": ["Arial-BoldMT"]
},
"filter": [
"all",
[
"==",
["get", "categ"],
"admin_level_4"
],
["==", ["get", "isocode"], "IQ"]
]
}
Even though the word "governorate" is officially part of the administrative divisions, we might want to hide those. We can use an expression for the text field as follows to display the names differently:
[
"slice",
["get", "name:en"],
0,
[
"-",
["length", ["get", "name:en"]],
12
]
]
Let's say we want to change a single name on the map. We can use an expression like this:
[
"match",
["get", "name:en"],
["Karbala"],
"Karbala Governorate",
["get", "name:en"]
]
Overview of Our Vector Tile Sets and Their Source Layers
Base Data
URL: https://vapi.mc-cdn.io/dataset/base/json?access_token=YOUR_ACCESS_TOKEN
The base data consists of 5 source layers, split by the type of features: polygons, lines, points, places, and labelcentroid. Each of them has properties derived from OpenStreetMap. These properties can be used to filter the data. For your convenience, a list of these properties per source layer is provided below.
polygons
Only the categ
property is present for each polygon. Depending on the category, additional properties may be present. All categories and their additional properties can be seen in the table below.
The amin_level_X categories denote the administrative boundaries. Usually, level 2 is the country level, level 4 the first level below that (states in the USA, bundesländer in Germany, etc). For more information, see the OSM wiki.
Polygons with categ
building have a few additional properties. The properties render_min_height
and render_height
can be used as the extrusion base and height, respectively. This is useful when rendering buildings in 3D. When hide_3d
is present, the building should not be rendered. This is usually the outline of a building that should not be part of the 3D model. Finally, there's a colour
property.
Category | Extra property | Value |
admin_level_2 |
||
admin_level_3 |
||
admin_level_4 |
||
admin_level_5 |
||
admin_level_6 |
||
admin_level_7 |
||
admin_level_8 |
||
admin_level_9 |
||
admin_level_10 |
||
airport |
||
athletics |
||
bridge |
||
brownfield |
||
building |
colour |
The colour of the building (#RGB or textual) |
hide_3d |
Present for building outlines that should not be rendered in 3D, has no value |
|
render_height |
The building height in meters | |
render_min_height |
The start rendering height in meters (for building parts which do not start on the ground) |
|
canal |
is_intermittent |
Present only when the body of water is marked as intermittent, has no value |
cape |
||
reef |
||
river |
||
strait |
||
lake |
||
rank |
0 till 5 where 0 denotes the most important lakes and 5 the lest important ones based on the area of the polygons in pixels |
|
construction |
||
education |
||
forest |
||
golf_course |
||
grasland |
||
greenhouse |
||
heath |
||
horse_race |
||
hospital |
||
ice |
||
industrial |
||
island |
||
nationalpark |
||
park |
||
parking |
||
piste |
||
pitch |
||
playground |
||
protectedarea |
||
recreation |
||
residential |
||
retail |
||
sand |
||
sports_centre |
||
square |
||
stadium |
||
transport |
||
university |
||
winter_sports |
lines
The main classifier for lines is the road_type
property. The possible values for each road_type are shown in the diagram below (orange ellipses), plus any optional additional properties (blue boxes). Below the diagram, additional properties are explained in more detail. The road_type
property is not always present. Administrative boundaries and rails are determined by properties on any lines, with or without road_type
.
On top of this, all lines will have name
, name:en
, ...
for all languages we support, if the translation is present. Additionally, name_is_latin
to indicate the name
property contains only Latin characters. For more information about this, see the Text Field Translations section below. Finally, there's name:abbr
, an abbreviated version of the name
property. This is only present if the name
contained only Latin characters.
To distinguish different administrative levels, there are boolean properties boundarylevel 2
, boundarylevel 3
, boundarylevel 4
, boundarylevel 5
, boundarylevel 6
, boundarylevel 7
, boundarylevel 8
, boundarylevel 9
, boundarylevel 10
. To be able to hide administrative boundaries that lie in the ocean, the is_maritime
property is present on such lines.
Property | Explanation/value |
bridgelevel | A whole number indicating the level of elevation. Using this value, bridges and tunnels can be rendered in the correct order |
is_tunnel | If present, indicates that the line is a tunnel |
is_construction | If present, indicates that the road or track is under construction |
is_private | If present, indicates that the road or track is not publicly accessible |
oneway | If present, indicates this is a unidirectional road |
is_indoor | If present, indicates that the road is inside a building. |
isocode | The iso code of the country this road is located in. This is mainly useful to render road shields and road numbers conditionally. |
roadnumber* indicates the presence of the following properties:
|
|
is_connector | If present, indicates that the road is a connector |
is_ramp | If present, indicates that the road is a ramp |
is_rail |
If present, indicates the nature of the type of rail |
colour |
The colour of the track is commonly used with metros/subways |
is_intermittent |
If present, indicates that a body of water can regularly fall dry |
length |
The length of the river or stream in meters (in Web Mercator) |
area |
The area of the lake in square meters (in Web Mercator) |
difficulty (on pistes) | Values like easy, novice, intermediate, advanced, expert, freeride, and more (see OSM) |
points
All points have a categ
property, and the various name
, name:en
, ...
, and name_is_latin
properties as explained below in the Text Field Translations section.
An overview of all categories and optional special properties can be found in the table below:
Categ | Extra Property | Value |
pharmacy | ||
clinics | ||
doctors | ||
dentist | ||
veterinary | ||
horse_riding | ||
wildflife_hide | ||
bird_hide | ||
beach_resort | ||
surfing | ||
scuba_dive | ||
sport_centre | sport | The sport being played there |
fitness | ||
camping | ||
playground | ||
swimming_pool | ||
dog_park | ||
skiing | ||
community_centre | ||
minigolf | ||
casino | ||
arcade | ||
cinema | ||
fountain | ||
theatre | ||
place_of_worship | religion | The religion being practised there |
horse_race | ||
monument | ||
memorial | ||
castle | type | The type of the castle. Among other values, this could be "stately", "manor", "defensive", "palace", "fortress", "shiro", "castrum", "citadel", or "kremlin" |
museum | ||
artwork | ||
attraction | ||
zoo | ||
theme_park | ||
monastery | ||
archaeological_site | ||
water_park | ||
information | ||
viewpoint | ||
mountainpeak | elevation | The height in meters |
mountainpass | ||
volcano | ||
graveyard | ||
marina | ||
marketplace | ||
lighthouse | ||
aquarium | ||
picnic_site | ||
fastfood | brand | The brand of the fast-food company/chain located here |
restaurant | ||
pub | ||
cafe | ||
bar | ||
apartment | ||
chalet | ||
guesthouse | ||
hostel | ||
hotel | ||
motel | ||
thermal | ||
alpine_hut | ||
wilderness_hut | ||
shelter | shop | The brand or shop that is located at this premise |
library | ||
caravan | ||
shop | ||
airport | importance | The type/importance of the airport. Among others: "regional", "international", "private", "military" |
subway_station |
isocode
sub
station_pre |
The iso code Whether the station is part of a group of stations with the same name. Particularly useful for metro stations with multiple entrances. A central station with no sub property can be drawn on lower zoom levels, while all individual entrances can be displayed when zoomed in. Only present in Berlin, it contains the letter U or S to differentiate U-bahns from S-bahns. |
lightrail_station | ||
monorail_station | ||
railway_station | ||
tram_station | ||
bus | ||
ferry | ||
parking | ||
bicycle_parking | ||
park_ride | ||
traffic_lights | ||
charging_station | ||
helipad | ||
motorway_junction | junction_number | The number of the junction/exit/ramp |
hospital | ||
house_number | ||
dam | ||
recycling | ||
post_office | ||
police | ||
firedepartment | ||
bank | ||
atm | ||
kindergarten | ||
dog_toilet | ||
gas_station | ||
ranger_station | ||
diplomatic | country_code | The country code of the country the diplomatic location associates with/belongs to |
government | ||
ngo | ||
townhall | ||
courthouse | ||
mines | resource | The resources mined here |
power | source | The type of plant. For example: "coal", "solar", "nuclear", "wind" |
windturbine | ||
crematorium | ||
watermill | ||
windmill | ||
fort |
places
Each place has a place
property that is either city, town, village, hamlet, borough, suburb, quarter, or neighbourhood. Each one has the name
, name:en
, ...
, and optional name_is_latin
properties as explained in Text Field Translations.
To order place labels properly, there are a few properties that can help you: sortorder
, scalerank
, and population
. The sortorder
is a value between 0 and 400 giving the most important places in each tile. The lower the sortorder
, the more cartographically relevant the place. To influence how busy the map looks without leaving desolate areas completely empty, we suggest setting a filter on the sortorder. For example: "filter": ["<=", ["get", "sortorder"], 60]
. It is advisable to use the same threshold on every layer with placenames. More importantly, do not use a less strict sortorder
on a layer with less important places. The scalerank
property comes from Natural Earth and rates the importance of places from 0 to 10. Lastly, the population
property can be used. It is recommended to use this as a symbol sort key: "symbol-sort-key": ["-", ["get", "population"]]
.
Property | Value |
place | One of (from large to small) city, town, village, hamlet, borough, suburb, quarter, or neighbourhood |
population | This value should be taken with a grain of salt. It varies in quality and is not always up to date. It can be useful to sort places. |
capital_level | The capital level of the place. Corresponds to the admin_level values used by OSM. For example, capital_level = 2 usually means it's a country capital, level 4 means it's a state capital in the US and a Bundesländ capital in Germany. |
sortorder | Value between 0 (most important) and 400 (least important) within each tile. |
scalerank | Sorting value from natural earth |
isocode | Only present for places that also have capital_level 2. Contains the iso code of the country the capital belongs to. |
labelcentroid
This layer contains labels originating from polygons. The categ
property is present for each labelcentroid. This can be either of the following values: building, airport, ice, sand, grasland, recreation, park, forest, heath, university, education, hospital, transport, cemetery, residential, retail, industrial, pitch, playground, stadium, golf_course, sports_centre, nationalpark, protectedarea, river, canal, bay, lake, strait, reef, cape, island, parking, greenhouse, building, square, bridge, piste, winter_sports, horse_race, athletics, brownfield, construction, admin_level_2, admin_level_3, admin_level_4, admin_level_5, admin_level_6, admin_level_7, admin_level_8, admin_level_9, admin_level_10.
Each one has the name
, name:en
, ...
, and optional name_is_latin
properties as explained in Text Field Translations.
Administrative labels (admin_level_X) also have a name:abbr
property, which is an abbreviated version of the native name. On top of that, country labels have name:abbr:en
, name:abbr:de
, ...
for each language it had a name for. On top of this, there is an isocode
property to identify the country the administrative label is part of.
The area
property denotes the area of the source polygon in square meters (in Web Mercator).
Labelcentroids with either of the categories bay, cape, lake, reef, river, or strait have the optional is_intermittent
property if the body of water can be dry during parts of the year.
Administrative Data
The Landuse-land
layer we added in the first example is a single polygon, which means it can only be styled with one color. If you want to assign different colors to individual countries, you can use the following source: https://vapi.mc-cdn.io/dataset/saga-countries/json?access_token=YOUR_ACCESS_TOKEN.
"mc-countries": {
"type": "vector",
"url": "https://vapi.mc-cdn.io/dataset/saga-countries/json"
}
The mc-countries
source contains separate country polygons that can be filtered by name
or iso
. For example, you can add one country and assign it a different color from the Landuse-land
layer to make it stand out. Let’s assign a unique color to Brazil:
{
"id": "Brazil",
"type": "fill",
"source": "mc-countries",
"source-layer": "data",
"filter": ["==", ["get", "iso"], "BR"],
"layout": {"visibility": "none"},
"paint": {"fill-color": "rgba(255, 255, 189, 1)"}
}
This layer can be used on top of the Landuse-land
layer or independently.
Hillshading
You can enrich your map with additional data by including extra data sources. To create a relief effect, you can add a raster data source: https://vapi.mc-cdn.io/dataset/jaxa_terrainrgb/{z}/{x}/{y}?access_token=YOUR_ACCESS_TOKEN.
"dem": {
"type": "raster-dem",
"tiles": ["https://vapi.mc-cdn.io/dataset/jaxa_terrainrgb/{z}/{x}/{y}?access_token=YOUR_ACCESS_TOKEN"],
"minzoom": 0,
"maxzoom": 12,
"attribution": "<a href=\"https://earth.jaxa.jp/en/data/policy\" target=\"_blank\">© AW3D30 (JAXA)</a>"
}
You can adjust the shades by specifying color values for shadow
and accent
.
The exaggeration
parameter controls the layer’s visibility, with values ranging from 0 (fully transparent) to 1 (fully opaque). If you want the hillshading to gradually fade as you zoom in on the map, you can add an interpolation to the value.
{
"id": "Hillshading",
"type": "hillshade",
"source": "dem",
"maxzoom": 24,
"paint": {
"hillshade-illumination-anchor": "map",
"hillshade-exaggeration": [
"interpolate",
["linear"],
["zoom"],
4,
0.3,
8,
0.15
],
"hillshade-shadow-color": "rgba(70, 70, 70, 1)",
"hillshade-accent-color": "rgba(70, 70, 70, 1)"
}
Landcover
To create a natural cover, add the following data source: https://vapi.mc-cdn.io/dataset/landcover/json?access_token=YOUR_ACCESS_TOKEN.
"landcover": {
"type": "vector",
"url": "https://vapi.mc-cdn.io/dataset/landcover/json?access_token=YOUR_ACCESS_TOKEN&include_token=true""
}
Landcover allows you to create layers with different land_type
values such as snow
, barren
, crop
, grass
, shrub
, forest
, moss
, mangrove
, wetland
, and urban
.
As an example, let’s create a layer with the forest.
{
"id": "Landuse-cover-forest",
"type": "fill",
"source": "landcover",
"source-layer": "data",
"minzoom": 0,
"maxzoom": 24,
"filter": ["==", ["get", "land_type"], "forest"],
"layout": {"visibility": "visible"},
"paint": {
"fill-color": "rgba(230, 248, 223, 1)",
"fill-antialias": false,
"fill-opacity": 1
}
As you can see, the only required parameters are land_type
and color
. To create smooth transitions between different types of landcover on the map, we recommend using similar colors for forest
, shrub
, and grass
, with slight variations in brightness or lightness.
Pro tip: The mc-base source also includes similar categories such as ice
, sand
, grassland
, park
, forest
, and nationalpark
. For better results, you can combine these data sources.
Text Field Translations
Many of our data sources have labels in various translations. We generally support these languages (with the abbreviated ISO-639 language codes in brackets) on top of the native name:
o English (en)
o Danish (da)
o Dutch (nl)
o Finish (fi)
o French (fr)
o German (de)
o Italian (it)
o Norse (no)
o Polish (pl)
o Portuguese (pt)
o Spanish (es)
o Swedish (sv)
o Japanese (ja)
o Chinese (zh)
o Russian (ru)
o Ukrainian (uk)
o Latvian (lv)
o Kazakh (kk)
o Arabic (ar)
Most of our source layers that have a name
property (containing the native name) also have various other properties in the format name:language_code
where the language code is either of the language codes listed above. In our base data, there is an additional property name_is_latin
that is only present if the native name
property contains only Latin characters.
We recommend using this to your advantage in the following way. For each symbol layer with labels, use a coalesce expression as follows. If your desired language uses the Latin script:
"text-field": [
'coalesce',
['get', 'name:language_code'],
['case', ['has', 'name_is_latin'], ['get', 'name'], ['get', 'name:en']],
]
This will make sure that the label will be shown in your desired language, if available. If not, it will fall back to the native name if that uses the Latin script, or English otherwise.
Alternatively, you can simply specify the label order manually like this:
['coalesce', ['get', 'name:language_code'], ['get', 'name:en'], ['get', 'name']]