Helsinki Population Trends

Already a while ago, I had to come up with illustrations for a case study on Helsinki’s public transit and its possible future developments. For one reason or another, the whole study was never finished or published and everything ended up in a stray folder on my computer’s hard disk. Cleaning it up I recently stumbled upon the maps and chart and was positively surprised of my 2014 self: I really still like them!

From a dataviz perspective I especially like how I implemented the legends. For the chart I even managed to eliminate it altogether by sharing a legend’s duty between the title and the data itself. Making the chart literally self-explaining.

But now the exhibits:

First, there is this chart which was to illustrate a short chapter on a new overspill development in the east of the city’s territory. It does nothing but show the city government’s projection of population numbers (plus a bit of interpolation in between).


Then, here’s the first map: It shows the population density in the Helsinki metropolitan region: the three cities of Helsinki, Vantaa and Espoo, having grown together so tight over the years you couldn’t tell where one ends and the other one starts.


Another map, and that’s the only illustration in this set which I am not really content with, tries to tell two stories at once: a district’s population growth AND its absolute number. The main message the map transports – and it does well so I think – is that the fastest growing districts are the ones with both the largest area and the smallest population. Helsinki is sub-urbanising?


Finally, another map demonstrates how quickly population density declines outside of the very centre of the city. The neighbourhoods of Kallio and Tölö, both hardly 2 km from the centre, still have fairly high density housing, but even in the core of neighbouring Espoo, itself boasting a quarter-million inhabitants, the density is not even half.


All data used is either Open Government Data of the Helsinki Region, data from the Open Street Map project, or from the Natural Earth Data initiative. I realise in the maps’ legends I refer to the wonderful Stamen as a source, but now I can’t see their products anywhere: They definitely remain an important influence.

An Interactive School Atlas


I am happy to present to you a project I completed last week: is the accompanying website for a middle school atlas issued by map publisher Freytag Berndt & Artaria (F&B) and the former Federal Publishing House of Austria ÖBV, nowadays one of the leading producers of school books in the country.

The atlas is now issued in its second revision to be used in classrooms from autumn 2016. A cartography student and part-timer at F&B at that time, I had collaborated in the first edition’s website: the interactive web maps were devised and programmed by me. It had become obvious quickly what geography teachers loved the most about the atlas: the feature-rich website offering a multitude of additional classroom material, and especially the interactive maps.
Now, more than a few years later, I was approached and asked to realise the website for the new atlas.

In a project of this size, naturally, many things are set in stone from the very beginning, and many stakeholders are involved in seemingly small decisions. Nonetheless, the atlas website came out in a surprisingly consistent form, and I was able to bring in a few constraints I had set myself for any web project I would henceforth pursue.

The key characteristics of the page include:

  • The entire atlas, i.e. every single page of the printed product, can be previewed from the website in high quality without jeopardising the printed product’s sales
  • The page sticks to the visual language of the atlas, also retaining its thematic structure, and is intuitive to the novice visitor. Also: pretty URLs representing the book’s structure
  • The principal maps (21) are devised in an interactive manner. Besides zooming and panning, they allow the user to add annotations and download a PDF document which includes the map at the chosen extent, the annotations, an appropriate legend and other map decorations.
  • A total of 24 maps are available as a Google Earth KML-file.
  • Over 100 work sheets supplementing the atlas’ use in the classroom available as non-DRM-ed PDF.
  • Blank maps (german: „stumme Karte“, “mute map”) of 20 different regions can be downloaded as a PDF in six variants each.
  • All content is accessible from mobile, tablet and desktop devices the like, with most of it following the principle of “progressive enhancement” allowing older devices at least basic access.

See for yourself:

cross-platform python font installer

For a QGIS python plugin for the advanced visualising of map data I need to make sure a certain font is available to the user. Amongst the users there will be non-technical staff, which is why adding a plugin repository URL is about as much as I can ask for. Thus, I searched for a way to install fonts from within the plugin. Not at all that difficult, as I quickly found out:

To use it in your python script simple from installfont import installfont and call installfont(path-to-font-file)

You can find the source code – released under the GPL – in my Bitbucket

QGIS, PostGIS and ambiguous geometry column types

I am currently preparing data for Ramon. The idea is to have an easy-to-use QGIS template file with all geographical data he might want to use to analyse, visualise, and present his well-filled treasure trove of demographic data.

My concept foresees a server-side read-only repository of vector datasets, served by WFS and/or PostGIS, which feature the necessary columns to join to the demographic datasets which would remain in the local spread sheet files Ramon is used to keep them in. Using the marvellous PostgreSQL feature MATERIALIZED VIEWS, I stride to keep redundancy at a minimum by disolving the higher-order statistical unit polygons out of the smallest ones used.

While the primary use-pattern would be for Ramon to open one of the supplied template files, discarding any unused layers and starting work from there, I still want him to be able to (re-)add additional layers from the server-side database. What I found a merely cosmetic thing until now started to bother me more than I liked: the QGIS dialog for adding PostGIS layers lists many layers twice, once with geometry column, geometry type and reference system pre-selected, once with input fields for manually specifying them. Ramon is quite tech-savy himself, but still I felt like this is nothing you want to have an end-user having to deal with.

PostGIS layers tend to show up twice in QGIS

A look at the table properties gave it away: some SELECTs return fully specified geometry column types, some don’t:

wirel=# d+ nuts1_2014;
Materialisierte Sicht „public.nuts1_2014“
Spalte | Typ | Attribute | Speicherung | Statistikziel | Beschreibung -----------+------------------------------+-----------+-------------+---------------+-------------- nuts1 | text | | extended | | nuts1name | character varying(16) | | extended | | geom | geometry(MultiPolygon,31287) | | main | | gkz14 | integer[] | | extended | | wirel=# d+ nuts1_2015; Materialisierte Sicht „public.nuts1_2015“ Spalte | Typ | Attribute | Speicherung | Statistikziel | Beschreibung -----------+------------------------------+-----------+-------------+---------------+-------------- nuts1 | text | | extended | | nuts1name | character varying(16) | | extended | | geom | geometry | | main | | gkz14 | integer[] | | extended | |

The fix was quick and easy: the column of tables can be changed using ALTER TABLE tablename ALTER COLUMN geometrycolumn SET DATA TYPE geometry(GeometryType,SRS-Code). For views, first a consistent geometry type for all rows has to be guaranteed (I chose to add an ST_Multi, which might cost some processing time, but I frankly didn't care about that – materialized views are generated only on forced update), then the geometry column has to be type-cast:

  gn.nuts1 AS nuts1,
  nn.nuts1name AS nuts1name,
  ST_Multi(ST_Buffer(ST_Collect(g.geom), 0::double precision))::geometry(MultiPolygon,31287) AS geom,
  array_agg(gn.gkz15) AS gkz15 
 FROM "gem_at_2015" g
  LEFT JOIN "gkz15-nuts1" gn ON g.gem_nr=gn.gkz15
  LEFT JOIN "nuts1-name" nn ON gn.nuts1=nn.nuts1
 GROUP BY gn.nuts1, nn.nuts1name;
CREATE INDEX ON nuts1_2015 USING GIST(geom);
CLUSTER nuts1_2015 USING nuts1_2015_geom_idx;
COMMENT ON MATERIALIZED VIEW nuts1_2015 is 'Österreich: NUTS1-Einheiten (2015)';


So schauts aus