Search for content

Combine Vector Map and Raster Map

In this tutorial you will learn how to mash up different technologies in an interactive Vector Map with Raster Map Restriction Data. It will allow to display a map and visualize clickable truck restrictions like weight limits or truck-bans in inner cities. To concentrate on the basic principles, all other important features like error treatment are neglected. The aim of the code example is to illustrate how to combine Leaflet and MapLibre with the PTV Developer Vector Maps API and PTV Developer Raster Maps API

Try it! Download from GitHub


  • Basic knowledge of JavaScript.
  • helpful: Basic knowledge of Leaflet and MapLibre.

Getting started

  • Request an API key:
    • Register and login at
    • Activate PTV Developer
    • Create your API key
  • Download an editor like Visual Studio Code or Atom to work on HTML and JavaScript.

Create an HTML page

Create a new HTML file called index.html and include all libraries we will use: leaflet, maplibre-gl-leaflet and leaflet-ptv-developer.

        <meta charset='utf-8' />
        <title>PTV Vector Map Restrictions Tutorial</title>
        <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
        <link rel="stylesheet"  type="text/css" href="" />
        <link rel="stylesheet" type="text/css" href="" />

            body { margin:0; padding:0; }
            #map { position:absolute; top:0; bottom:0; width:100%; }

        <div id="map"></div>

        <script type="text/javascript" src="" ></script>
        <script type="text/javascript" src="" ></script>
        <script type="text/javascript" src="" ></script>
        <script type="text/javascript" src="" ></script>
        <script type="text/javascript">
            /* The JS code below goes here */


Initialize the map framework

First let's initialize the Leaflet map control and set the api key. Add the code below and replace the string "YOUR_API_KEY" with your PTV Developer API key.


var coordinates = L.latLng(49.012, 8.4044); // Karlsruhe, Germany
var map ='map', {
    fullscreenControl: true
}).setView(coordinates, 17);


The right-to-left text plugin makes sure that languages such as Arabic and Hebrew are displayed correctly.

Add the vector map as base

Now let's use the vector tiles with MapLibre as our base layer. After loading the index.html file into a web browser, you should see a panable and zoomable map.

var vectorLayer = L.maplibreGL({
    attribution: '&copy; ' + new Date().getFullYear() + ' PTV Group, HERE',
    maxZoom: 18,
    style: '',
    transformRequest: (url, resourceType) => {
        if (resourceType === 'Tile' && url.startsWith('')) {
            return {
                url: url + '?apiKey=' + API_KEY

The transformRequest function adds your apiKey to the request url automatically.

Add a Raster Map layer with restrictions

Now it's time to implement the visualization of truck restrictions. We create a new pane to add a clickable restriction layer using the leaflet extension leaflet-ptv-developer. To do so add the following JavaScript code below the already existing one.

map.getPane('clickableTiles').style.zIndex = 500;

var restrictionsLayer = L.tileLayer.ptvDeveloper(
    '{z}/{x}/{y}' +
    '?apiKey={token}&layers={layers}', {
        layers: 'restrictions',
        token: API_KEY,
        maxZoom: 18,
        opacity: 0.5,
        pane: 'clickableTiles'

Add a Raster Map layer with low emission zones

In addition to the restrictions layer, there is also a low emission zones layer, which is displayed as areas.

map.getPane('lowEmissionZones').style.zIndex = 400;
var lowEmissionZonesLayer = L.tileLayer.ptvDeveloper(
    rasterTileUrl + '?layers={layers}&apiKey=' + api_key, {
    layers: 'lowEmissionZones',
    maxZoom: 18,
    opacity: 0.5,
    pane: 'lowEmissionZones'

Add a layer control

Finally let's add a control to toggle the restrictions layer.

var layers = {
    "Vector Base Map":  vectorLayer,
    "Restrictions": restrictionsLayer,
    "Low Emission Zones": lowEmissionZonesLayer,
L.control.layers({}, layers, {
    position: 'bottomleft',
    autoZIndex: false