Ok - I had to re-learn things today and I only did it last week so I'm documenting now so I don't forget and never do this later.

What is Tilegrams
Topojson > Hex map, via algorithmic black magic.

Carto can do that
But Carto can't do multiple hexes per region. And although that isn't always relevant, it very much was for the Turkish election, where tiny geographic regions (like Istanbul) were worth 98 Parlimentary seats.

Cool. Let's go
So afaik no documentation for adding new maps, so I had to use a combination of looking at the examples provided. Netherlands was the most useful, as it seemed to be calculating that one on the fly, unlike the US/UK maps that have pre-compiled instances stored in the repo. There is also an open pull request (https://github.com/PitchInteractiveInc/tilegrams/pull/85) to add Ireland. Both useful.

What do I do

  1. Get a topojson
  2. Make a dictionary of region ID codes to region Names (most of the examples stored this in a json file in the format: { "ID": { "name": "NAME" } }
  3. Add the topojson to /resources/GeographyResource.js
    • import the json
    • import the dictionary/hash
    • add a projection of the map (Tilegrams takes the topojson, renders it to a canvas using D3 then does it's manipulation and turns out a hex map). This is a bit fiddly, but basically the same process as finding the projection center/scale/translate points for any D3 project. I missed the scale when I first did this and battled negative co-ordinates for too long.
    • add these to a new entry in the GeographyResource constructor
  4. Add the id to the top level features object of the topojson using resources/MapResource.js
    • I found that tilegrams seems to be using a non-standard version of topojson (or every map I've seen isn't implementing standards). So I had to create an ID field in every feature, and take it from the .properties section of the feature.
  if (!this._topoJson.objects[this._objectId].geometries[0].id) {

  mockIdField() {
    const obj = this._topoJson.objects[this._objectId];
    for (let i = 0; i < obj.geometries.length; i++) {
        obj.geometries[i].id = i.toString();
        // obj.geometries[i].properties.id

ID's looked like 'ASITUR002001003' in the Turkish topo so I... used the loop counter instead. Probably don't do that.

  1. Add your dataset to resources/DatasetResource.js

    • These were in CSV format in the examples so I kept to that. Your ID needs to match the MapResource ID as above, and then the number is the number of hexes that will be mapped to that region. So in my case, this was seats to region.
    • import csv file and add a new entry in the constructor
    • defaultResolution could be interesting if you were using population data, as some of the examples are.
  2. Profit?

(Before iteration)


That should be enough to get it up and running, but here are some things I got caught out by:

  1. Geo projection being wrong. It won't tell you.
  2. tileEdge is NaN. The whole project is a react component that reloads. A lot. It seems to assume you are importing GeoJSON when you are not and that can reset all the arc entries to a series of arrays filled with [NaN, NaN]. To get around this, comment out the GeoJSON lines. It doesn't appear to do any harm.

graphics/MapGraphic.js:L79 - iterateCartogram()

    const topoJson = exporter.fromGeoJSON(this._stateFeatures, mapResource.getObjectId())
    this._stateFeatures = topogram(topoJson, topoJson.objects[mapResource.getObjectId()].geometries)
Share this