Creating the Metropolis-2 style

There is already a nice tutorial on how to use maputnik for custom styles. It has an online version that can sync with your local style.json. The benefit is that if you just create a vanilla mbtiles file with OpenMapTiles then you can design your style without adding your local mbtiles to maputnik becuase maputnik can serve tiles from MapTiler without an api key. However, when serving with TileServer GL you need to replace the tile source, sprites, and glyphs paths with a local path or get a MapTiler api key.

Note

OpenMapTiles provides several open source styles that are a good starting point for beginners. These can be loaded directly into maputnik.

You can use any tiles in maputnik as long as they adhere to the mbtiles convention. I alternate between mapbox and MapTiler. However a MapTiler style is not compatible with a mapbox style even if they both use OpenStreetMap as they have different layers.

For Metropolis-2, it is not possible to design the style this way because it does not use the same layer schema as OpenMapTiles or Maptiler. Since, we modified the data considerably we need to add them as a source.

Adding our local tiles to maputnik

There is no way to directly add m2.mbtiles as a source to maputnik. Among other things, it accepts GeoJSON and TileJSON but not mbtiles. Luckily, TileServer GL can serve an mbtiles file as a TileJSON. If you look at the image of the TileServer GL homepage above you can see that there is a TileJSON button. If you click that and get the url it looks like this:

http://localhost:8080/data/v3.json

So ideally, we can now add this url to maputnik and see our data. However, to do this we need the local maputnik CLI because the online editer only works with Hypertext Transfer Protocol Secure (https). So unless you serve the tiles to a public url you will not be able to see the data.

Serve a TileJSON

Now we are ready to serve the Metropolis-2 tiles to start designing our style.

So at this point we can make changes to openmaptiles/data before starting the server.

Step 1

Place m2.mbtiles in openmaptiles/data

Step 2

Place the following config.json in openmaptiles/data

{
    "options": {
      "paths": {
        "root": "",
        "fonts": "fonts",
        "styles": "styles",
        "mbtiles": ""
      }
    },
    "data": {
      "v3": {
        "mbtiles": "m2.mbtiles"
      }
    }
  }

Note how that by setting "root": "" the root directory will be inside /data/ in the docker container.

Step 3

Make a directory called fonts in openmaptiles/data and copy the fonts created with OpenMaptiles fonts.

Step 4

Make a directory called styles in openmaptiles/data. It can remain empty for the time being.

At this point your openmaptiles/data directory should look like this:

_images/styleserver.png

Step 5

Start the server by running make start-tileserver in openmaptiles. Now visit http://localhost:8080 and you should only see your data. If you click “inspect” you should see something like this

_images/data.png

Note the 4 available layers in the top right and how you can hover over the geometry to get more information.

Step 6

In the home page of the server you can now find the link to the TileJSON:

http://localhost:8080/data/v3.json

Note how it is an http url.

Add http url as a source in maputnik

Now that the server is running with the Metropolis-2 tiles, we can add it as a source to maputnik. The Metropolis-2 style will be inspired by the dark matter style from OpenMaptiles.

Step 1

Download the dark matter style.json from github

https://github.com/openmaptiles/dark-matter-gl-style/blob/master/style.json

then place it inside openmaptiles/data/styles.

Step 2

Change line 2 to "name": "m2",

Step 3

Assuming you have installed the Maputnik-CLI correctly run maputnik --watch --file style.json from openmaptiles/data/styles. This will make sure any changes made in maputnik are saved to your file.

If it went well, you can now visit

http://localhost:8000

and you will see the maputnik editor with the dark matter style.

Step 4

Click Data Sources at the top of the page and add the the source in the pop-up window.

_images/sources.png

If you click View at the top and select Inspect and then navigate to Vienna you will see our Data overlayed with the MapTiler data.

_images/maputnik.png

Note

We could already remove the openmaptiles source. However, because all the style layers reference it, removing it would give us a big error and make it difficult to style.

For the time being it is better to modify the style layers.

Make style layers

Now we can start making our styling layers which will reference the mbtiles layers

  • airspace_border

  • buildings

  • geofences

  • streets

There are already some good tutorials for styling with maputnik on their github so I will not spend too much time describing it.

Here I will go over the main general steps for the Metropolis-2 style.

Streets layer

For this layer I just slightly modified two style layers

  • highway_major_casing

  • highway_major_casing

This involved:

  1. changing the source to m2 and the source layer to streets.

  2. removing the class filter.

  3. Makinf the backgrounda little lighter.

  4. Change the minimum zoom to 9.

I then deleted the rest of the layers. Now everything is referencing m2 so I can remove the OpenMaptiles source.

_images/streets.png

Airspace border layer

The airspace border is just a simple line. The main thing is that I want it to be available at all zoom levels.

_images/airspace.png

Geofence layer

This will require two style layers. One for the geometry and one for the label of the geofence.

For the geometry, all we need to do is create a fill layer add an outline color and make it available to a minimum of zoom 8.

For the label, we use the id property and make it available only when zoomed in to at least a zoom level of 15.

_images/geofences.png

The font, Open Sans Regular, is located at openmaptiles/data/fonts.

Building layer

The building layer is pretty much the same as the geofence layer. The main difference is that the geometry and labels are only visible at a minimum zoom level of 15.

In this case the label is called fid.

_images/buildings.png

Maputnik saves the style.json automatically so we can close a maputnik and our data tile server.

Serving the styled tiles

At this point we have a working style for the m2.mbtiles However, the config.json and style.json are still not ready for TileServer GL.

Final touches to style.json

After playing with the style schema in maputnik there are still some small changes needed to make sure the tile server works.

  1. Ensure the name is m2

  2. Change the sources url to "url": "mbtiles://{v3}"

  3. Change the glyphs to "glyphs": "{fontstack}/{range}.pbf"

  4. Delete the sprites entry.

  5. Change the id entry to "id": "m2"

  6. Delete the metadata entry (not required)

The style.json file ends up like this,

{
  "version": 8,
  "name": "m2",
  "sources": {
    "m2": {
      "type": "vector",
      "url": "mbtiles://{v3}"
    }
  },
  "glyphs": "{fontstack}/{range}.pbf",
  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": {
        "background-color": "rgba(92, 92, 92, 1)"
      }
    },
    {
      "id": "highway_major_casing",
      "type": "line",
      "metadata": {
        "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"
      },
      "source": "m2",
      "source-layer": "streets",
      "minzoom": 9,
      "filter": [
        "all",
        [
          "==",
          "$type",
          "LineString"
        ]
      ],
      "layout": {
        "line-cap": "butt",
        "line-join": "miter",
        "visibility": "visible"
      },
      "paint": {
        "line-color": "rgba(60,60,60,0.8)",
        "line-dasharray": [
          12,
          0
        ],
        "line-width": {
          "base": 1.3,
          "stops": [
            [
              10,
              3
            ],
            [
              20,
              23
            ]
          ]
        }
      }
    },
    {
      "id": "highway_major_inner",
      "type": "line",
      "metadata": {
        "mapbox:group": "b6371a3f2f5a9932464fa3867530a2e5"
      },
      "source": "m2",
      "source-layer": "streets",
      "minzoom": 9,
      "filter": [
        "all",
        [
          "==",
          "$type",
          "LineString"
        ]
      ],
      "layout": {
        "line-cap": "round",
        "line-join": "round",
        "visibility": "visible"
      },
      "paint": {
        "line-color": "hsl(0, 0%, 7%)",
        "line-width": {
          "base": 1.3,
          "stops": [
            [
              10,
              2
            ],
            [
              20,
              20
            ]
          ]
        }
      }
    },
    {
      "id": "airspace_border",
      "type": "line",
      "source": "m2",
      "source-layer": "airspace_border",
      "paint": {
        "line-color": "rgba(0, 0, 0, 1)",
        "line-width": 2
      }
    },
    {
      "id": "building",
      "type": "fill",
      "source": "m2",
      "source-layer": "buildings",
      "minzoom": 15,
      "paint": {
        "fill-color": "rgba(59, 59, 59, 1)",
        "fill-outline-color": "rgba(0, 0, 0, 1)",
        "fill-translate-anchor": "map"
      }
    },
    {
      "id": "building_name",
      "type": "symbol",
      "source": "m2",
      "source-layer": "buildings",
      "minzoom": 15,
      "layout": {
        "text-field": "{fid}",
        "symbol-placement": "point",
        "symbol-z-order": "auto",
        "text-size": 12,
        "text-font": [
          "Open Sans Regular"
        ]
      },
      "paint": {
        "text-color": "rgba(0, 0, 0, 1)"
      }
    },
    {
      "id": "geofence",
      "type": "fill",
      "source": "m2",
      "source-layer": "geofences",
      "minzoom": 8,
      "paint": {
        "fill-color": "rgba(59, 59, 59, 1)",
        "fill-outline-color": "rgba(0, 0, 0, 1)",
        "fill-translate-anchor": "map"
      }
    },
    {
      "id": "geofence_name",
      "type": "symbol",
      "source": "m2",
      "source-layer": "geofences",
      "minzoom": 15,
      "layout": {
        "text-field": "{id}",
        "symbol-placement": "point",
        "symbol-z-order": "auto",
        "text-size": 12,
        "text-font": [
          "Open Sans Regular"
        ]
      },
      "paint": {
        "text-color": "rgba(0, 0, 0, 1)"
      }
    }
  ],
  "id": "m2"
}

Final touches to config.json

Now we need to add a style entry so the tile server can use our m2 style.

This final config file looks like this,

{
    "options": {
      "paths": {
        "root": "",
        "fonts": "fonts",
        "styles": "styles",
        "mbtiles": ""
      }
    },
    "styles": {
      "m2": {
        "style": "style.json",
        "tilejson": {
          "bounds": [
            16.254755,
            48.133106,
            16.469847,
            48.276893
          ]
        }
      }
    },
    "data": {
      "v3": {
        "mbtiles": "m2.mbtiles"
      }
    }
  }

Serve the final tiles

After modifying the schema we can restart the tile server by running make start-tileserver from openmaptiles.

And if we go to the viewer we can see our scrollable Metropolis-2 map!

_images/final_tiles.png