# Maps à la carte

Those who follow my Mastodon account will know that I have a thing for bicycle trips. The bicycle is a technology that gave humans immense freedom back when it appears, and it remains one of my most favourite ways to meet the world on my terms.

Sometimes unrestricted freedom is the best, and sometimes we need to settle on a direction. And the best thing balancing freedom and directions on a bike is a map.

A plain map using only 4 colors: black for roads and labels, green for greenery, blue for water, and red for tow name

Maps are typically made for a group of people. Thanks to OpenStreetMap you can take a look at a map drawn for everyone, for the cyclist, or even for a biker and hiker. There's a service for generating maps ready for printing, too. However close they come, there are no easy tools to make a map for *me*, according to *my* preferences, showing things *I* need to plan my cycling.

> Yes, I'd like a map made, please. For cycling. Make the asphalt local roads thickest. Motorways? Skip them. Actually, draw them the same way as fences. And canals. Mark footpaths too, please. And information posts, and tourist information points. While we're at it, mark monuments in a different color. Oh, and don't forget about bike repair shops, public water sources and grocery stores. Make them red! Mountain springs? Well, only if there aren't any wells nearby...

– me, ordering a cycling map in my imagination

Thankfully, I'm a problem solver, and I know a solution must exist.

## Playing with crayons

If only I could draw the map on the computer like I drew fictional maps with crayons as a kid. A stroke there, a pictogram there, each takes two seconds total.

The maps I mentioned before use some sort of styles to generate the differently colored pictures out of the same data. The services are made for serving maps, rather than editing them, though. Tools like Mapnik require lots of set up, and even after it's done, editing the styles presumably isn't so comfortable.

What tools do the serious style designers use? Such a tool would put the ease of changing the looks at the forefront, even sacrificing speed…

*A while later*

Well, I found it. The project at the core of it is understaffed, and written in JavaScript, but works amazingly well. It's called TileMill, it's open source, and the maintainers are looking for fresh contributors!

Here's how to set up everything and render a map of your region.

## TileMill

As a first step, I recommend creating a separate user on your machine. The project uses NPM, and that wil leave lots of cruft in your home directory. In addition, we use the dreaded curl-to-shell pattern, which better be contained away from valuable data.

I'm using Fedora 37 as the base system. Fedora doesn't ship with nvm, and the required version of npm doesn't work, so let's fix this:

```
curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash
source ~/.bashrc
```

Now, install some needed packages:

```
sudo dnf install git wget osm2pgsql postgis postgresql-server postgresql-contrib vim
```

Then follow the installation instructions with some changes:

```
git clone https://github.com/tilemill-project/tilemill
cd tilemill
nvm install lts/carbon
nvm use v8.17.0
npm install # this will take a while
npm start
```

Ignore the rest of the installation instructions. Now we have an empty instance of TileMill running on http://localhost:20009. Sadly, have no idea how to change the ports :( Anyway, navigate there with your browser and start a new project. Uncheck "default data", we'll use our own.

A Web browser showing localhost:20009. on the left a list with "Projects" selected, central a "New project" button above a project tile "Open Streets, DC"

You'll see an empty project. Now that we have the renderer running, let's change tracks and prepare some data.

A panel with multiple options on the left, showing "Editor" selected. Left half occupied with a blue expanse and map zoom controls. Right half is an editor with style.mss file open. Inside, there's a Map {background-color: blue}

## OpenStreetMap Data

You won't draw a map if you don't know anything about your area. Thankfully, collecting geographical data is what the OpenStreetMap project is best at. Don't be fooled, the map is only a side thing. It should really be called OpenStreetData.

Let's get the data of region that interests us from Geofabrik. I'll choose Münster for demonstration purposes: Germany is *dense* with all kinds of detailed data added by volunteers, and Münster in particular is famous for its bike-friendliness. Now download the .osm.pbf file:

```
https://download.geofabrik.de/europe/germany/nordrhein-westfalen/muenster-regbez-latest.osm.pbf
```

TileMill does not support loading it directly, but it supports something better:connecting directly to a geographic database. The cost of that is that we need to set up the database ourselves.

A while back, we installed the necessary packages: Postgresql with Postgis. Now it's time to configure them.

```
sudo postgresql-setup --initdb --unit postgresql
```

**CAUTION**: it's possible that I messed up the access controls here. An attacker on the same network might be able to get in your (data)base and kill your landmarks.

Edit the file `/var/lib/pgsql/data/pg_hba.conf` to widen permissions. Afterwards, the relevant part should look more like this:

```
# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            trust
# IPv6 local connections:
host    all             all             ::1/128                 trust
```

Create the database "osm", to keep our geographical data.

```
systemctl restart postgresql
psql -U postgres -c "create database osm;"
psql -U postgres -d osm -c 'CREATE EXTENSION postgis;'
osm2pgsql -c -G -U postgres -d osm ./muenster-regbez-latest.osm.pbf # you can list additional files here!
```

This will take a moment, but if it succeeds, your data is now safely stored. Test your network access:

```
psql -h 127.0.0.1 -p 5432 -U postgres -d osm
```

If this succeeds, then TileMill will be able to access the database, too.

## My own map

Go back to the browser, and find the "layers" icon. It's shown on the picture:

Four vertically stacked icons. A triangle tip ends on the last one, looking like a stack of papers.

Add a new layer, select PostGIS, and give it "dbname=osm host=localhost port=5432 user=postgres" in the "Connection" field. Write "highway" in "Class". Finally, enter the query in "Table or subquery":

```
(select * from planet_osm_line where highway!='') as lines
```

A form with 3 options above, PostGIS selected. It contains fields: ID, Class, Connection, Table or subquery.

We're almost there. Now try this style:

```
Map {
  background-color: #b8dee6;
}

#highway {
  line-color: #808080;
  line-width: 1.0;
}
```

Enter it in `map.mss`, and press "Save" (or Ctrl+S). Suddenly…

A crop of the empty project view, containing only the left half. There's now a small gray shape among the blue. Zoom level is at 3.

What is that? Zoom in, please.

The whole empty projct view again. The blue is criss-crossed with gray lines. Zoom level is at 10. The style.mss file now contains the #highway section from above.

Now, this looks like the communication network in the region!

## Come on, paint my world

I'm not going to give you a hand-holding here, but I'll leave you with a couple useful tips.

  1. TileMill's documentation is quite decent for educating you how to style things.

  2. Remember that your database holds 3 types of objects: points, lines, and polygons. Those don't have to be the same as in OSM, and, in fact, I don't know how relations are represented, if at all.

  3. You can filter objects using CSS styles, like this:

```
#highways[highway='path'][bicycle='permit'] { foo; }
```
  1. But dedicated layers are faster at filtering data:
```
(select * from planet_osm_line where highway='path' and bicycle='permit') as bikes
```
  1. Take a good look at your database with `psql -h 127.0.0.1 -p 5432 -U postgres -d osm`.

Tables:

```
osm=# select * from # I pressed tab here
geography_columns    pg_toast.            planet_osm_roads
geometry_columns     planet_osm_line      public.
information_schema.  planet_osm_point     spatial_ref_sys
pg_catalog.          planet_osm_polygon  
```

Tag values:

```
osm=# select distinct highway from planet_osm_line where highway!='';
    highway     
----------------
 trunk
 road
 disused
 footway
 cycleway
 services
 secondary
 traffic_island
 tertiary
 abandoned
```

Example data (caution, long lines):

```
select * from planet_osm_roads limit 5;
```
  1. Use more layers!

## Printing

This application is not perfect for printing. It doesn't have a built-in rose of winds, nor a scale, and you're stuck with the Web Mercator projection. QGIS is way better in that respect, but also creating styles in QGIS is a lot more painful.

In a pinch, however, it's sufficient, and exporting is easy, too. The export menu is on the top-right. Make sure to aim at 600pixels per 2.54cm, and to select a zoom level that makes things readable.

That's it! Have fun with your new set of crayons!

## Example

The picture in the first section is from a live demo I performed at the local hackspace. Here's the style for it:

```
Map {
  background-color: #fff;
}
 
#lines {
  line-color: #808080;
  line-width: 0.0;
  
  [waterway='stream'] {
    line-color: #aaf;
    line-width: 1.0;
  }
  
  [waterway='river'] {
    line-color: #aaf;
    line-width: 2.0;
  }

  [highway='path'] {
    [bicycle='permit'],
    [bicycle='yes'],
    [bicycle='designated'],
    [bicycle='official'],
    [bicycle='permissive'],
    [bicycle='use_sidepath'] {
      line-color: #444;
      line-width: 1.0;
    }
  }
  //[bicycle='permit'],
  //[bicycle='yes'],
  [bicycle='designated'],
  [bicycle='official'],
  //[bicycle='permissive'],
  [bicycle='use_sidepath'],
  [highway='cycleway']{
    line-color: #444;
    line-width: 2.0;
  }
  
  [highway='track'][surface='asphalt'][bicycle!='no'],
  [highway='service'],
  [highway='unclassified'],
  [highway='residential'],
  [highway='tertiary'] {
    line-width: 1.0;
  }
}

#lines {
  line-color: #808080;
  line-width: 0.0;

[surface='asphalt'] {
    //[bicycle='permit'],
    //[bicycle='yes'],
    [bicycle='designated'],
    [bicycle='official'],
    //[bicycle='permissive'],
    [bicycle='use_sidepath'],
    [highway='cycleway']{
      line-width: 2.0;
    }
  }
}
#roads {
  line-color: #808080;
  line-width: 0.0;/*
  [highway='secondary'] {
    line-width: 2.0;
  }
  */
  [highway='primary'],
  [highway='trunk'],
  [highway='motorway'] {
    line-color: #d8c1b1;
    line-width: 2.0;
  }
}

#toiletten {
  [amenity='toilets'] {
    marker-width: 6;
    marker-fill: #070;
    marker-line-width: 0;
  }
  [amenity='drinking_water'] {
    marker-width: 6;
    marker-fill: #700;
    marker-line-width: 0;
  }
}

#rathaus {
  [amenity='townhall'] {
    polygon-fill: #f00
  }
}

#towns {
  [place='village'] {
    text-name: [name];
    text-fill: #f00;
    text-face-name: 'Droid Sans Regular';
  }
  [place='town'],
  [place='city']{
    text-name: [name];
    text-fill: #f00;
    text-face-name: 'Droid Sans Regular';
    text-size: 20;
  }
}
```

Written on .

Comments

dcz's projects

Thoughts on software and society.

Atom feed