–––
Maps and Movement

The next area we are ging to look at is making a world map in processing. This will serve as a review of ther topics we've covered so far and introduce you to some other useful processing functions. Creating a equirecangular world map is pretty straight forward processing but if we needed to do a deeper dive into mapping there are better tools. One that's particularly powerful is d3. I have a d3 world map template that I will try to demo later.

As our starting point for this map let's begin with code that let's us save a pdf with non-outlined type

// Courtesy of Ben Fry

import processing.pdf.*;

PFont f;

void setup() {
 size(800, 800); 
 
 /// for fonts to remain LIVE they must be placed here
 ///  /Library/Fonts
 /// Also you must use the font's PostScript name, which use can use font book to check
 f = createFont("Georgia", 30);
 noLoop();
}

void draw() {
 beginRecord(PDF, "LiveText.pdf"); 
 background(50);
 textMode(MODEL); // This is the important part
 textFont(f);
 textSize(40);
 fill(200);
 textAlign(CENTER);
 text("This text is not outlined", width/2, height/2);
 endRecord();
 println("PDF Saved!");
}

Please note that for this to work your font needs to be either .ttf or .otf and needs to be in the root of the /System/Library/Fonts folder. You also need to use the font's PostScript name. If you don't need to keep your text live in export you can change the text mode to textMode(SHAPE) and place your font in the data folder of your sketch.

Next let's look at importing a vector file to use as a background image. We first declare a global variable called PShape baseMap. Then we draw that basemap with a shape function.

import processing.pdf.*;

PShape baseMap;
PFont f;

void setup() {
  size(960, 540);
  f = createFont("Georgia", 12);
  //noLoop();
  baseMap = loadShape("WorldMap.svg");
}

void draw() {
  //beginRecord(PDF, "test.pdf");
  background(0, 0, 0);
  textMode(MODEL);
  textFont(f);
  shape(baseMap, 0, 0, width, height);
  //endRecord();
  //println("PDF Saved!");
}

The round globe has been flattend out using an eguirectangular projection. The type of projection is very handy for us because it has evenly distributes latitude and longitude along the width and height of the canvas. The downside is that the type of projection introduces disortion into the shapes of the land masses particularly at the poles.

image

Next let's bring in our data. We are going to start with three fields: latitude, longitude, population. We'll also turn noLoop() back on.

import processing.pdf.*;

Table myTable;
PShape baseMap;
PFont f;

void setup() {
  size(960, 540);
  f = createFont("Georgia", 12);
  //noLoop();
  baseMap = loadShape("WorldMap.svg");
  myTable = loadTable("WorldCities - Sheet1.csv", "header");
  noLoop();
}

void draw() {
  //beginRecord(PDF, "test.pdf");
  background(0, 0, 0);
  textMode(MODEL);
  textFont(f);
  shape(baseMap, 0, 0, width, height);

  for (int i=0; i < myTable.getRowCount(); i++) {
    TableRow row = myTable.getRow(i);
    float lng = row.getFloat("lng");
    println(lng);
    float lat = row.getFloat("lat");
    println(lat);
    float pop = row.getFloat("pop");
    println(pop);
  }
  //endRecord();
  //println("PDF Saved!");
}

Latitude measures from -180 to 180. Longitude measures from -90 to 90. Let's use those values to map that lat and long to our canvas coordinates. We'll use a centered rectangle to test our placement.

image
import processing.pdf.*;

Table myTable;
PShape baseMap;
PFont f;

void setup() {
  size(960, 540);
  f = createFont("Georgia", 12);
  //noLoop();
  baseMap = loadShape("WorldMap.svg");
  myTable = loadTable("WorldCities - Sheet1.csv", "header");
  noLoop();
}

void draw() {
  //beginRecord(PDF, "test.pdf");
  background(0, 0, 0);
  textMode(MODEL);
  textFont(f);
  shape(baseMap, 0, 0, width, height);

  for (int i=0; i < myTable.getRowCount(); i++) {
    TableRow row = myTable.getRow(i);
    float lng = row.getFloat("lng");
    float graphLong = map(lng, -180, 180, 0, width);
    float lat = row.getFloat("lat");
    float graphLat = map(lat, 90, -90, 0, height);
    float pop = row.getFloat("pop");
    //println(pop);
    rectMode(CENTER);
    rect(graphLong,graphLat,2,2);
  }
  //endRecord();
  //println("PDF Saved!");
}

Next let's map() our popluation data into a usable range.

import processing.pdf.*;

Table myTable;
PShape baseMap;
PFont f;
float popMax;

void setup() {
  size(960, 540);
  f = createFont("Georgia", 12);
  //noLoop();
  baseMap = loadShape("WorldMap.svg");
  myTable = loadTable("WorldCities - Sheet1.csv", "header");

  noLoop();
}

void draw() {
  //beginRecord(PDF, "test.pdf");
  background(0, 0, 0);
  textMode(MODEL);
  textFont(f);
  shape(baseMap, 0, 0, width, height);

  String[] columnArrayMax = (myTable.getStringColumn("pop"));
  popMax = max(float(columnArrayMax));


  for (int i=0; i < myTable.getRowCount(); i++) {
    TableRow row = myTable.getRow(i);
    float lng = row.getFloat("lng");
    float graphLong = map(lng, -180, 180, 0, width);
    float lat = row.getFloat("lat");
    float graphLat = map(lat, 90, -90, 0, height);
    float pop = row.getFloat("pop");    
    float scalePop = map(pop, 0, popMax, 0, 100);
    println(scalePop);
    rectMode(CENTER);
    rect(graphLong, graphLat, 2, 2);
  }
  //endRecord();
  //println("PDF Saved!");
}