Having seen a visualization by Robert Kosara of EagerEyes a loooong time ago, I wanted to try to reproduce it for Switzerland using Processing. This is the second installment of a two-parts post covering this project, in which I will describe how to arrive at the final result, the ZIPScribble Map. I’ll do that in some detail, maybe this is helpful to somebody.
In the first installment of this series I explained my process up to an intermediate result: a map depicting all the postal code locations in Switzerland, like this:
This involved creating a basemap with TileMill, using Till Nagel’s MercatorMap class in Processing, finding and downloading postal code data from Geonames and writing a Processing sketch which makes use of all these. In this second installment I will explain how to arrive at a ZIPScribble Map for Switzerland from the above intermediate result. So from the first part we have many things in place already. What is missing are basically three things:
- Functionality to sort the postal codes and their associated location
- Functionality to draw lines between individual postal code locations
- Functionality to notice changes in the postal codes and to react accordingly (e.g. changing the line colour when changing from postal codes 1### to 2###)
So in the remainder I will cover what it takes to include this abilities in the Processing sketch and to make it all work together.
Sorting postal codes and associated location
Sorting the postal codes and their location is necessary for the ZIPScribble Map, for in the map, all postal codes are visited in sequential order (from smallest to biggest or vice versa) and connected by a line. However, the Geonames dataset doesn’t come in this format. Of course, one could download the postal code data from Geonames, import the data in some spreadsheet programme, do the sorting and save the data to a comfortably accessible format.
However, for easy re-production of the ZIPScribble Map I aimed at automating the whole process and to start from the Geonames postal code dataset without any manual touching up.
So here is what I came up with after some research. Some sources suggested that in Processing/Java it would be easiest to implement the Comparable interface. Any class that implements the Comparable interface is sortable, i.e. objects instantiated from that class have an ordering termed natural ordering. The natural ordering is defined by the class’s compareTo method. The objects can then be sorted using Collections.sort().
Less abstractly speaking, I created a class Zippoint in my Processing sketch by including the following code after the draw() method (another example of a Comparable implementation (unrelated to Processing and this project) can be seen here):
[sourcecode language=”java”]
public class Zippoint implements Comparable {
float lat, lon;
String zipcode;
// Constructor
public Zippoint(String zipcode, float lat, float lon) {
this.zipcode = zipcode;
this.lat = lat;
this.lon = lon;
}
public int compareTo(Object other) {
return((this.zipcode).compareTo(((Zippoint)other).zipcode));
}
}
[/sourcecode]
Line 1 in above code snippet declares that Zippoint implements Comparable. Zippoint has three instance variables: lat, lon and zipcode for storing the coordinates and the postal code as read from the Geonames dataset (lines 2 and 3). The constructor is straight-forward. Finally, the compareTo method (lines 10 to 12) does the trick: It takes an object other and compares the zipcode of this zippoint to the zipcode of the other zippoint.
One could write one’s own functionality into this compareTo method. However, for my purpose it was good enough to re-use a compareTo method from the Java String class. The compareTo() method of the String class – used as follows: stringA.compareTo(stringB) – compares two strings lexicographically:
The character sequence represented by this
String
object is compared lexicographically to the character sequence represented by the argument string. The result is a negative integer if thisString
object lexicographically precedes the argument string. The result is a positive integer if thisString
object lexicographically follows the argument string.
That is all we need in order to have the Zippoint class up and running and in order to implement Comparable.
Side-note: String comparison is not problematic in this case. Since postal codes in Switzerland are always constituted by four digits, there are no problems with the String comparison. I.e., there is no situation where String comparison would sort a sequence of numbers like this: 101, 1011, 200, 2001.
Indeed, String comparison is useful in this context for several reasons: When Processing reads the data from the Geonames text file, it first reads everything as String. So keeping postal codes stored as strings rather than integers, saves a conversion. Secondly, and more importantly, some countries have postal codes which contain sequences of characters and are thus best represented as strings.
The following code snippet covers the part that reads the Geonames data from file, instantiates an object of the Zippoint class for every record, collects them in an ArrayListand does the sorting:
[sourcecode language=”java”]
String[] lines;
Iterator itr;
void setup() {
// …
// Read data from text file, build list, sort, set up iterator
int index = 0;
lines = loadStrings(“zipcodes_SWI.txt”);
List ziplist = new ArrayList();
while (index < lines.length) {
String[] row = split(lines[index], ‘t’);
ziplist.add(new Zippoint(row[1], float(row[9]), float(row[10])));
index++;
}
Collections.sort(ziplist);
itr = ziplist.iterator();
}
[/sourcecode]
From the first part of this project you are already familiar with line 7 which reads the postal code data from the text file. Line 8 defines an ArrayList object to store the Zippoint objects. In line 9 through 13 Processing iterates through the String array lines, gets the relevant substrings, creates a Zippoint object and adds it to the ArrayList object. Then line 14 does the sorting in ascending order (this, at last, is where we profit from having implemented the Comparable interface!). Line 15 finally sets up an iterator on the ArrayList object. This allows us to iterate over the Zippoint objects later in the draw() method.
Drawing lines between postal code locations
In this part I used the above mentioned iterator in order to (“ta-dah!” ;) iterate through the collection of zippoints. But before that, I define fill (none) and stroke colour and invoke a crucial method, beginShape():
[sourcecode language=”java” firstline=”17″]
noFill();
stroke(60,180,210);
beginShape();
while (itr.hasNext()) {
Zippoint zippoint = (Zippoint)itr.next();
vertex(mercatorMap.getScreenLocation(new PVector(zippoint.lat, zippoint.lon)).x, mercatorMap.getScreenLocation(new PVector(zippoint.lat, zippoint.lon)).y);
}
endShape();
[/sourcecode]
The pair of beginShape() and endShape() allows you to define forms using vertices, so it gives you more flexibility over standard forms like, for example, ellipse(). The Processing documentation states:
Using the beginShape() and endShape() functions allow creating more complex forms. beginShape() begins recording vertices for a shape and endShape() stops recording. […] After calling the beginShape() function, a series of vertex()commands must follow.
As you can see in the code snippet, the beginShape() method is followed by a call to vertex(), which is repeated for every zippoint the iterator finds in the sorted collection. vertex() is handed the latitude and longitude of the respective zippointas its two arguments, the translation from geographic coordinates to screen coordinates is done by Till Nagel’s mercatorMap.getScreenLocation() method – you can read more on this in part I of this tutorial.
The code so far results in the following map:
Not bad, but not yet done. For this to become a true ZIPScribble Map, the colour of the strokes connecting the postal codes has to change whenever the first part of the postal code changes (or, when we enter a new ‘postal area’). How is this done? Like so:
Switching visual style in response to change in postal code
This is not so hard anymore, given the code in the above code snippet which iterates through the zippoints. What I do is – abstractly speaking – saving the first n digits of the last zippoint and comparing them to the first n digits of the current zippoint in the iterator. If there is a change I finish drawing the shape, change the visual style using a new stroke() assignment and start a new shape. The easiest way of implementing this will also automatically ensure that differently coloured ZIPScribbles are disconnected from each other. It’s pretty straightforward and I will leave the implementation to the interested reader/programmer.
I have the colour switching itself (or more precisely: the choosing of a new colour) in a really naive way, I must admit. I had been trying various approaches (also some more intelligent ones) but wasn’t quite convinced by any of them.
What I do now is pretty much also the first approach I have ever come up with: I randomly select a new colour when a postal code discontinuity occurs.
As a small quantum of sophistication and in order to have enough contrast with the background map, I repeat the random colour shuffling, until the sum of the r,g,b values is in an acceptable range to make a good contrast with the basemap at least highly likely. Behold this “ingenuity”:
[sourcecode language=”java”]
int r = 255;
int g = 255;
int b = 255;
while (r+g+b > 500 || r+g+b < 450){
r = int(random(0,255));
g = int(random(0,255));
b = int(random(0,255));
}
[/sourcecode]
Readers: I’d be thankful for any pointers to more clever ways of choosing colour in this context.
Results
So, these are all the parts we need. Plugging them together correctly enables me to finally draw a ZIPScribble Map! (I’m excited here :)
For my ZIPScribble Maps I varied the number of postal code digits which are compared for detecting breaks in the postal codes. I call these levels:
- Level 1 ZIPScribble Map: One digit is compared. Thus, a discontinuity is detected, for example, between postal codes 8679 and 9000, but no discontinuity is detected between 8399 and 8400.
- Level 2 ZIPScribble Map: Two digits are compared. Thus, a discontinuity is detected between postal codes 8679 and 9000 as well as between 8399 and 8400.
- The Level 3 ZIPScribble Map works accordingly…
I have included some Level 1 to Level 3 examples with the two different backgrounds from Part I of this post series below:
This was the second part of the two parts tutorial on how to draw a ZIPScribble Map using freely available data and free software. As in Part I my thanks extend to Robert Kosara, TileMill, Till Nagel and Geonames!
5 thoughts on “ZIPScribble Map: Switzerland – Part II”