Block Map Visualisation

I really like the blocky style of maps above, and I think they may be a useful to a visualisation that is dependant upon area (like population per square km). These particular images have a few problems:

  • They are propriety (and I don't want to pay for them)
  • They do not come in a format with the ability to access the individual blocks in a programatic or useful way (i.e. by latitude and longitude).

I decided to have a go at creating such a graph with the knowledge I gathered from writing the post D3.js Geo is fun and my Mihimihi project.

Land Ahoy?

The first problem I came across is finding if a point (lat,lon) is over land. Although it sounds simple I found it quite complicated.

I first tried the Raphaël's Element.isPointInside function. On testing this function only finds if a point is inside or outside the bounding box of a path, which would be useful only if all countries where rectangular.

I then tried to use getIntersectionList, which again seemed to not be fully implemented in chrome for SVG paths and just returned if a point was in a bounding boxes.

I have not tried document.elementFromPoint as I assumed it was implemented in a similar manner to getIntersectionList.

Enter Canvas

D3.js Geo has the ability to render to canvas instead of a SVG document (as described here). Drawing a map to canvas has various pros and cons, using canvas:

  • removes SVG's ability to handle events
  • removes ability to style with CSS markup
  • gives simple access to read map point by point

Given that a core function of the project is to test whether a point is in land or not, and render other elements given the information, this tradeoff is not that bad.

Implementation

The general implementation is relativly simple:

  1. Draw the map to an invisible canvas
  2. Divide the map into blocks and for each block find if it has a significant amount of land.
  3. Draw Block to HTML with a class representing its land value.

This tutorial should be followable if you have read my previous post D3.js Geo is fun.

Draw the Map onto Canvas

This just reads a TopoJSON file and draws the result to canvas.

    width = $(window).width()
    height = $(window).height()

    projection = d3.geo.equirectangular()
      .scale(150)
      .translate([width/2,height/2])
      .rotate([-180,0]);

    multi_polygon = topojson.object(worldtopo, worldtopo.objects.land)

    canvas = d3.select("body").append("canvas")
      .attr("width", width-10)
      .attr("height", height-10)
      .css('display','none')

    context = canvas.node().getContext("2d");

    path = d3.geo.path()
      .projection(projection)
      .context(context);

    path(multi_polygon);
    context.fill();
    context.stroke();

Block out the Map

This algorithm creates a grid of squares across the entire page and calculates the average_color (algorithm provided here) of each block and if it is above a certain value (here 120) it tags the block with the class 'land'.

    size = 20
    xdivs = ~~(width/size)-1
    ydivs = ~~(height/size)-1
    for y in [0..ydivs]
      row = $("<div id='#{y}_row'></div>").appendTo($('body'))
      for x in [0..xdivs]
        box = $("<div id='#{x}_col' class='block' ></div>").appendTo(row)
        l = x*size 
        t = y*size
        h = size
        w = size
        box.css('left', l).css('top',t).css('width',w).css('height',h)
        v = average_color(context.getImageData(l,t,h,w))
        if v > 120
          box.addClass('land')
        else
          box.addClass('water')
          box.css('top',t)

Example

I have implemented these ideas at http://block-map.maori.geek.nz.

Some minor additions to this example are:

  • The ability to change the scale, rotation, and block size using the URL parameters.
  • A random drop animation
  • A scale of land sizes (small, medium, large)
  • Auto scaling to window size on load.

This example is implemented in Node.js, and is available on github.

Future Work

I have not yet to build the reverse lookup function f(lat,lon) -> block. This will be necessary to make this visualisation represent an interesting dataset.

comments powered by Disqus. comments powered by Disqus