Using QGIS to create a custom map

I’ve always loved maps. I guess it goes well with liking drawing and maths, there’s a very deep connection (the pun is intended) between the subjects. As a map lover, when we decided to relocate to a somewhat more countryside town, I wanted better-looking maps to wander around the area. I checked government-issued maps, but they were either too large (scale 1:25000) or didn’t show the area I was interested (for the 1:10000 maps.) Thus I did what any other nerd would do: roll my own map as I wanted it.

The end result looks like this (downscaled to 20%)

The real map is 1.4 × 1.2 metres

Getting the data

My first idea was to follow the steps of Dominik Schwarz in his quest for printing a huge map of the world, and harvest images from Google Maps and get away with it. But one of the fundamental parts of my map was supposed to be having all walking routes visible and available. Google Maps won’t show that, or at least not everything. Specially among vines or forests. What’s the point of living close to the Pantà de Foix (the Foix Dam) and not being able to walk there?

Castellet, just beside Pantà de Foix
Hence, I turned to our trusty government, and in particular to the Catalan Cartographic and Geologic Institute (from now on the ICC, which is the Catalan acronym.) They have a powerful online map application, with good quality satellite imagery, elevation data, toponymy… All is available online. And for free download! You only need to register, and seem to be limited to one download at a time. No big issue, although I needed to get 16 satellite tiles.

There is a huge amount of data available for download. Since my knowledge of technical map stuff ended at knowing that the technical term for what I wanted to do is GIS , I wasn’t really sure what files I needed and what I could do with them on a Mac without having to pay for a dedicated app.

The first step was finding a free app that could “do maps.” After some Googling it was pretty clear it had to be QGIS (formerly known as Quantum GIS) which is free, open-source and available cross-platform. It was quite straightforward to install on Mac, just needing some extra libraries to work with the satellite image files I was using. Once I had it, the real “nightmare” started, where I had to decide what kind of files I needed and what they were good for. This is where ICC helped, since they have documentation for all their file types, not only explaining what they have but also what they are useful for.

For a starter I downloaded all satellite imagery with a scale of 1:2500, generated on 2014. Recent enough. This was the easy one:
  • Download each file
  • Unzip it
  • In QGIS, Layer→Add Layer→Add Raster Layer (or Shift-Cmd-R)
Since the files are already geolocated data (the format is SID, which is why I needed a special plugin to handle it) in just a few minutes I had a satellite view of the area, automatically placed and multi-scale. Not bad for a few minutes of downloading and goofing around.

Then the real work started: I wanted street names, road names and features. How? I’m pretty sure there are many ways, but the one I found most straightforward was:
  • Download Topography-base maps, with a scale of 1:2500 in SHP (Shapefile) format
  • In QGIS, Layer→Add Layer→Add Vector Layer (or Shift-Cmd-V)
  • Select full folder, Arc/Info binary format and browse to it
  • Select the files you really want
ICC provides a lot of files for each map tile at that resolution. Lots of stuff I didn’t really want. I chose only toponymy, elevation lines, hydrography and roads. Why?
  • I needed toponymy layers to set labels to features
  • Elevation looks very cool (even if I disabled it for the final generated map)
  • With hydrography I could change the colour from the satellite imagery to a more bluish tone in water areas (since the Foix dam is quite greenish)
  • Roads was the main reason I was doing this

Adding some text

Once I had everything in place I wondered: Where are the labels?
  • Double-click a toponymy layer
  • Labels→Label this layer with…
Here some of the huge power of QGIS started to appear. Most “variables” in the map can be controlled via either fields in the data layer OR functions applied to these fields. So, I could easily label by a data column, like TEXT_TOP (which had the common name of the feature, like Pantà de Foix.) But I also could set the size of the font to the one defined in the datafile. And I could even choose the font according to the feature type (or in my case, the font defined in the data file: the data provided by the ICC had font size and font family for the features) with a function!

  WHEN "FONTNAME" LIKE 'Times New Roman' THEN 'Georgia'
  WHEN "FONTNAME" LIKE 'Arial Narrow' THEN 'Futura'
  WHEN "FONTNAME" LIKE 'Courier New' THEN 'Inconsolata'

The language seems to be QGIS specific, but has SQL-like LIKE. Single quotes denote strings, double quotes denote fields. Important to remember, since it’s very easy to mix one with the other without realising. I set all labels to show, even colliding labels. I didn't want to miss a castle or church because it was too close to a large street. I'd rather have overlapping labels in this case.

Then I decided I wanted something more: I wanted icons to show what each feature was. Icons like what you have in a normal map, where churches are indicated by a cross and castles by a tower. The layer I was working with (the toponymy layer) only had type codes for its features, so I couldn’t really tell what a 23 was. Church? Castle? Farm?

Symbols…? Labels...?

To do this, I needed to download an auxiliary file (which the ICC already told me about when I clicked Shapefile as type of download) having the list of code together with human-readable definitions. It is loaded as a single-file Shapefile layer, but has no “visible data.” Instead you need some magic.
  • Open a toponymy layer
  • Select Joins
  • Add a join layer with codis_topo (the source, in this case the layer I just added) and selecting as join field the type code (in this case, CODI_TOP)
  • I disabled caching and added a custom name to the newly created column, the join

A join
With this at least I’d know, since the type was now human-readable. I still didn’t know how to add icons, but I could edit the name and add a letter to denote what it is (or I could even add some text.) The labelling function is then

  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%capella%' THEN CONCAT( '† ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%monestir%' THEN CONCAT( '† ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%monestir%' THEN CONCAT( '† ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%tedral%' THEN CONCAT( '† ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE 'Església' THEN CONCAT( '† ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE 'Convent' THEN CONCAT( '† ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE 'Temple' THEN CONCAT( '† ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE 'Arc' THEN CONCAT( 'fl ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%ducte' THEN CONCAT( 'fl ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE 'Font, bullidor, deu' THEN CONCAT( 'ƒ ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%toll,%' THEN CONCAT( '≈ ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%astell,%' THEN CONCAT( 'Ħ ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE   'Talaia, torre de defensa, torre de guaita'  THEN CONCAT( 'Ħ ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%rica (en%' THEN CONCAT( 'Ħ ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE  'Muralla'  THEN CONCAT( '= ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE 'GR (Sender de Gran Recorregut)' THEN CONCAT( 'GR: ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE   'PR (Sender de Petit Recorregut)'  THEN CONCAT( 'PR: ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE 'Pou%' THEN CONCAT( 'ů ', "TEXT_TOP" )
  WHEN  "coditopo_03ca_CONCEPTE"  LIKE '%pont%' THEN CONCAT( 'π ', "TEXT_TOP" )

This, basically adds a weird symbol to the text, so, for instance, bridges have a π symbol in front. Not great, but I actually printed it like this for a starter and it looks pretty decent. Here you can see a zoom in and the general view:

The general view

A zoom
In the zoom you can see the glyphs for a fountain (Font d’Horta and Font de Mas Carlús), a church (Sant Pere) an aqueduct (de les Aigües de Vilanova i la Geltrú) and a bridge (de la font d’Horta.) This was nice but non-optimal. I wanted symbols! So, how do you get symbols if you have a toponymy layer with lines, not feature points?


First, duplicate the layers holding the labels. It can be done without duplicating, but it introduces a layer of complication for the human… So, if your computer doesn’t mind the extra layers, it is easier for you as human to have a separate layer for the labels and a layer for the icons.

Once you have duplicated the layer, change its style to rule-based, and add marks following rules with the add sign. For instance, this is the rule I use for Castell (castle):

  WHEN "coditopo_03ca_CONCEPTE" LIKE 'Castell,%' THEN 1
  ELSE 0

with SVG marker svg/fortress2.svg

NOTE: if you are using only one condition, for rule-based markers you can just keep it at

"coditopo_03ca_CONCEPTE" LIKE 'Castell,%'

since it will just return 0 or 1 as needed. You'll only need CASE when you have several types going to the same marker. Since I just copy-pasted most of my code I didn't mind the extra cruft. Thanks to Nathan Woodrow, one of QGIS' developers for pointing it out on twitter.

To add this as image you need to select Marker, then SVG marker. I found very nice SVG icons here, so I use this opportunity as credit (since the printed map is just for my personal use.) And indeed, you need to add as many rules as icons you have. That’s life. A GIS professional probably knows other ways, I’m just a newbie. Once you have all the rules you want, you can copy-paste them in all the layers. Actually, I think all this map business would have been easier merging all vector layers before doing anything else, but since I didn’t do it from the beginning it was hard to do it now. You can find how to merge vector layers in this StackOverflow question.
This is how Style should look like with rule-based

And this is what a specific rule looks like
 After adding markers you’ll need to tweak them. I did one part in SVG (adding stroke to the shapes and changing colours directy in the file so I didn’t need to bother in QGIS) and some others relating to placement (middle point and then changing size and baseline of the icon.) I also did some changes editing the file directly in Emacs, since QGIS files are just XML and replacing is much faster without clicking anything, once you know how the fields are named. The end result? Here you can see the same area as above with icons.

A zoom in with markers in place

Nice, don’t you think?

So, how do you print it?

This is actually very very easy. To print you need a New print composer from the Project menu. This is a set of rules to print some map. So, you choose the paper size and then add pieces to the layout. The basics are a map, a scale and some text, or at least this is the bare minimum I wanted. 

Once you have the map area you can change scale, drag around and change many things, it’s awesome. I decided to use 1:10000 since fitting the whole map in the map area I chose was 1:9556 and I’d rather have a round number and lose some map around the borders. Ready to export to PDF and bring to a printing place. You can also generate a JPG file, but beware of trying to open a 176 Mb with an iPad (even if it is an Air.) It won’t zoom. On the other hand, for my Mac it was much easier to open and zoom/navigate a huge JPG rather than a huge PDF file. Also keep in mind large maps take a long time to export: if you are making tests disable as many layers as you can really manage.

The map composer reminds me of Scribus
Since I wanted a portable version of the map to have on my iPad Air, I decided to split it using ImageMagick in 9 tiles I could easily zoom in any app. IM has the nice “crop” feature that can generate tiles, like:

convert -crop 4x3 MEGAMAP.jpg megamap/tiles%03d.jpg

The problem is that this takes way, way longer than expected, because the image is too big. The fastest way is instead to use the stream command to generate raw RGB data and convert that to JPG. So, repeating 9 times stuff like:

stream -extract 5566x3937+11132+7874 MEGAMAP.jpg t9.rgb
convert -depth 8 -size 5566x3937 rgb:t9.rgb t9.jpeg

Since I only needed to do it this time I didn’t bother to code it as a bash for loop or anything. Whereas convert was taking more than 12 hours to do anything, stream took 3 minutes, counting the time I needed to compute the pixel shifts and the conversions. And probably even the time it took me to create a index.html with the pieces to zoom.

What’s the takeaway of this post? Doing maps is fun, and using QGIS is not that hard as it may seem. And yes, I have an awesome big map now.
Written by Ruben Berenguel