How can I use Mapcreator vector tiles in my own Mapstyle?

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:

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
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]
      }
    }

disputed

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"
}

add source mc-countries
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)"}
}

add layer mc-countries

edit layer mc-countries

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.

Source-layer Lines.drawio-1

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:

  • roadcardinal
  • roadchar
  • roadnr_is_non_latin
  • roadnumber
  • roadsep
  • introadcardinal
  • introadchar
  • introadnr_is_non_latin
  • introadnumber
  • introadsep
 
is_connector If present, indicates that the road is a connector
is_ramp If present, indicates that the road is a ramp

is_rail
is_tram
is_subway
is_monorail
is_siderail
is_narrowrail

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
of the country the station belongs to. Allows for a different styling per country.

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)"}
  }

highlight brazil
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\">&copy 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)"
      }

hillshading

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
      }

landcover

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']]