Tomorrow I will be all the day at the polling station, since I've been chosen as a member.
The last two weeks I was playing with the amazing Kartograph software, so it was a good moment to experiment with electoral maps (the first time for me).
In this example, I will explain step by step how to create the map above. It's quite similar to this tutorial, but I want to continue in a new post, going interactive.
Kartograph creates vector SVG images, which you can edit later with Inkscape or Illustrator, so gives much more flexibility than systems generating PNG like files, much more difficult to modify.
As in all the posts, you can get all the files used in the example.
So I've had to join the two files to get one file with the geometries and the results. The process is quite long and dirty (why didn't they use an id? I had to join with the names), so I won't explain how to do it, but put the result at the data files. You can find this file here.
Then, to decorate the map, I used the following files
ogr2ogr -clipsrc -3 37 4 44 Trees2.shp Trees.shp
and to simplify:
ogr2ogr -simplify 10 munis.shp bm50mv33sh1fpm1r170.shp
Doing so, the time to generate the map is divided by five or more.
The last two weeks I was playing with the amazing Kartograph software, so it was a good moment to experiment with electoral maps (the first time for me).
In this example, I will explain step by step how to create the map above. It's quite similar to this tutorial, but I want to continue in a new post, going interactive.
Kartograph creates vector SVG images, which you can edit later with Inkscape or Illustrator, so gives much more flexibility than systems generating PNG like files, much more difficult to modify.
As in all the posts, you can get all the files used in the example.
This example has two continuation posts:
Getting the data
As usual, getting the data is not so easy. The Catalan government has a good open data web site, where I found:
- A file with a really descriptive name, bm50mv33sh1fc1r170.zip, with all the administrative boundaries (provinces, comarques, and municipalities).
- Lots of files with election results. I choose the 2010 elections, since they where to the Catalan parliament, like the ones tomorrow. As you can see on the map, the party CiU won with a very big majority, so the map is not as interesting as it could be.
So I've had to join the two files to get one file with the geometries and the results. The process is quite long and dirty (why didn't they use an id? I had to join with the names), so I won't explain how to do it, but put the result at the data files. You can find this file here.
Then, to decorate the map, I used the following files
- World boundaries from Natural Earth (ne_10m_admin_0_countries.zip), to draw the coast line outside Catalonia
- From VMAP, the layers Trees, Crops, and DepthContours, to decorate the map outside the electoral constituencies.
ogr2ogr -clipsrc -3 37 4 44 Trees2.shp Trees.shp
and to simplify:
ogr2ogr -simplify 10 munis.shp bm50mv33sh1fpm1r170.shp
Doing so, the time to generate the map is divided by five or more.
Installing Kartograph
Since we only need kartograph.py for this tutorial, first, download it from the github page clicking at the zip icon.
In a linux system, uncompress and execute
python setup install
as a super user.
That's all, if you have the GDAL python bindings installed.
Creating the map
To create a map with Kartograph, you will need a configuration file in JSON format, which will have three basic sections:
"proj": {
"id": "sinusoidal",
"lon0": 20
}
Projection
To set the projection, there used to be a web page named Visual map configurator, that doesn't work any more. But don't worry, you can use the Map Projections page. Just choose the projection that fits you more, change the parameters and click the gear icon:
A dialog will open, and the lines that are interesting in this case are, in the image example, like:
"proj": "sinusoidal",
"lon0": 20
This will be translated in our json file as:"lon0": 20
"proj": {
"id": "sinusoidal",
"lon0": 20
}
Bounds:
The part of the world we want to represent is set here. It's quite well explained at the documentation, but it can be a bit confusing, and not all the options work with all the projections.
In our example, I have used:
All the layers will be drawn in the order indicated at the json file, so this must be well chosen to select which layer hides what.
"bounds": {
"mode": "bbox",
"data": [-0, 40, 4, 43],
"crop": [-3, 37, 5, 44]
}
- mode: How the bounds are expressed. BBOX is the basic option, but you can also set it defining the points you want to enter in the map, or even the features in a layer. If the layers are in different projections, other modes can be a little tricky.
- data: In our case, the bounding box. In other modes, the layer name, the points, or whatever.
- crop: Is an optional label. Our svg will be clipped at the bounds set at data, but all the data in the files will be processed. If the files include all the world, this takes a long time, and generates much bigger SVG outputs. With crop, only the features inside the BBOX will be included.
Layers:
As the name suggests, the layers to include.
The shapefiles are added as:
"municipalities":{
"src": "./mun_out.shp"
}
There are also two special layer, graticule and sea. The first draws the meridians ans parallels, while the second does nothing more than giving a feature to draw the background: "background": {"special": "sea"},
"graticule":{ "special": "graticule", "latitudes": 1, "longitudes": 1}
All the layers will be drawn in the order indicated at the json file, so this must be well chosen to select which layer hides what.
Styling
This is the nice part. Without styling, the SVG can be used directly with Inkscape or Kartograph.js, but is possible to generate styled maps directly with kartograph.py.
You can give the style either in the json file or in a separate css file, which seems cleaner. The names given to the layer are the ones to be used in the css as the id. So to give a style to the municipalities layer, add
You can give the style either in the json file or in a separate css file, which seems cleaner. The names given to the layer are the ones to be used in the css as the id. So to give a style to the municipalities layer, add
#municipalities {
fill: #FFF;
stroke: #882222;
stroke-width: 0.5px;
stroke-opacity: 0.4;
}
The general options are at the documentation again. CSS for SVG is a little different from the one used in traditional html.
Since we want to paint the municipalities in a different color depending of the party who won the elections, we will use filters, like this one:
It would be nice to compare different fields i.e. CiU > PSOE, but this is not possible (at least, I haven't found how to do it), so I had to calculate the winner and put it in a field (called Winner, as you can see in the example)
elections.css
Since we want to paint the municipalities in a different color depending of the party who won the elections, we will use filters, like this one:
#municipalities[Winner=CiU]{
fill: #99edff;
}
It would be nice to compare different fields i.e. CiU > PSOE, but this is not possible (at least, I haven't found how to do it), so I had to calculate the winner and put it in a field (called Winner, as you can see in the example)
Drawing
There are two options to draw the map. A command line program is installed with the setup, called kartograph.
To draw the styled map, just type
kartograph elections.json --style elections.css -o elections.svg
But you can also include all this in a python program, so could generate the data and then the map. In our case, the code would be
from kartograph import Kartograph
from kartograph.options import read_map_descriptor
import sys
K = Kartograph()
css = open("elections.css").read()
cfg = read_map_descriptor(open("elections.json"))
K.generate(cfg, outfile='elections.svg', format='svg', stylesheet=css)
Finally, I edited the svg file with Inkscape to put the titles and legend. Is just to show that the idea is generating a base svg and from there, draw the pretty final map.
Configuration files
To draw the map in the example, I have used the following files:
elections.json
{
"proj": {
"id": "sinusoidal",
"lon0": 20
},
"layers": {
"background": {"special": "sea"},
"graticule":{ "special": "graticule", "latitudes": 1, "longitudes": 1, "styles": { "stroke-width": "0.3px" } },
"world":{
"src": "data/ne_10m_admin_0_countries2.shp"
},
"trees":{
"src": "data/Trees2.shp",
"simplify": true
},
"crops":{
"src": "data/Crops2.shp",
"simplify": true
},
"depth": {
"src": "data/DepthContours2.shp",
"simplify": true
},
"municipalities":{
"src": "./mun_out.shp"
}
},
"bounds": {
"mode": "bbox",
"data": [-0, 40, 4, 43],
"crop": [-3, 37, 5, 44]
}
}
elections.css
#background {
fill: #e8f9fb;
stroke: none;
},
#world {
fill: #f5f3f2;
stroke: none;
},
#graticule {
stroke-width: 0.3px;
},
#municipalities {
fill: #FFF;
stroke: #882222;
stroke-width: 0.5px;
stroke-opacity: 0.4;
},
#municipalities-label {
font-family: Arial;
font-size: 13px;
},
#municipalities[Winner=CiU]{
fill: #99edff;
},
#municipalities[Winner=PSC-PSOE]{
fill: #ff9999;
},
#municipalities[Winner=ERC]{
fill: #EDE61A;
},
#depth {
stroke: #223366;
stroke-width: 0.5px;
stroke-opacity: 0.4;
},
#trees {
fill: #d2f8c0;
stroke: none;
},
#crops {
fill: #fcf8d8;
stroke: none;
}
What's next
If I have time, I'll try my first Kartograph.js example. From the svg generated, is possible to create cool interactive maps.


If I am merging two shape files into one svg file. The resultant svg is getting cut.
ReplyDeleteDo you have any advice?
Steve, in the example up to five files are merged, but they stay as different layers.
DeleteYou want to do the same or merging two shapefiles into one single layer in the SVG?
If you want, send me the files and configuration file at my email rveciana (at) gmail (dot) com
Hi Roger. Thank you for posting this. I'm learning Kartograph and worked off your example. Out of curiosity, did the background color modification you made actually change the background---or is that coloration due to the ocean-depth layers you have? I tried something similar (http://gis.stackexchange.com/questions/45583/background-sea-layer-not-coloring-in-kartograph-map) and found I was unable to actually change the color of the "sea."
ReplyDeleteHi Adam,
ReplyDeleteI think that your problem is because of a known bug. I asked the author at GitHub and told me that in certain projection, this happens.
In my case, the color is defined at the background, not the ocean-depth. I define it with the special layer
"background": {"special": "sea"}
but if you change the projection, it stops working (in most of the options)
Hi Roger,
ReplyDeleteFirst thank for the posts, there is no much info about this library on the net. I am trying to work on spanish provinces svg maps, and i am not able to make it work. I am using this maps:
http://www.nbsp.es/2009/02/25/mapas-de-los-municipios-de-espana-en-vectorial/
An example -> http://upload.wikimedia.org/wikipedia/commons/2/2f/Alicante_-_Mapa_municipal.svg
I configure to load this map, and to add the following layer but it doesn't work.
Layers names must be id tags inside the svg file, right?
Hola Miguel!
ReplyDeleteHave you generated the file using kartograph.py?
The workflow is to take the GIS file (shapefile, GeoJSON, whatever you have) and run the kartograph.py to generate the SVG. You can't run it from an SVG file.
By the way, there is open data about the Spanish municipalitites in a proper format. Take a look at: http://centrodedescargas.cnig.es/CentroDescargas/equipamiento/lineas_limite.zip
I have a post explaining how to filter them by province: http://geoexamples.blogspot.com.es/2013/04/fast-tip-filtering-features-using-ogr.html
Use ogr2ogr with -where to get only the municipalities in Alicante.
Hey Roger,
ReplyDeleteThanks for your reply. No, i didn't try with a svg generated by kartograph.py. I did have lot of problems to make kartograph.py run, i wrote to the author, and post it in stackoverflow (http://stackoverflow.com/questions/16297476/install-of-gdal-failed-for-kartograph-py-dll-load-failed ). I think is due to all the dependencias, and that i install a pack with geo tools, and now it's a mess.
I'll take a look to your links, and write back, thanks a lot.
ogr2ogr is one of those tools i had to install i think...
i'll write you soon
Hi again,
ReplyDeleteI tried your suggestion of downloading the maps at cnig and when i ran your code, i got again the exception using GDAL.
What environment are you using? (OS, python version). I'll try to set it up in another computer, the one i'm trying is driving me nuts.
It definitely seems that your way it's the right one to get maps for all municipalities by provincies, what is my target here. But i'm dealing with this gdal-python installation in windows.
BTW, i'd be nice to share other geo python thoughts by email, and also we can switch to spanish, i couldn't find yours, so mine is m_fiandor_@_gmail_.com (without the _)