Skip to content

Instantly share code, notes, and snippets.

@abhshkdz
Last active April 23, 2016 20:09
Show Gist options
  • Save abhshkdz/ac0abf43fe9e90e84c0bb165e09638fe to your computer and use it in GitHub Desktop.
Save abhshkdz/ac0abf43fe9e90e84c0bb165e09638fe to your computer and use it in GitHub Desktop.
Processing code to draw a scatter plot of zip codes https://twitter.com/abhshkdz/status/528725432011882496
// From chapter 6, Visualizing Data: Exploring and Explaining Data with the Processing Environment by Ben Fry
// https://books.google.com/books?id=6jsVAiULQBgC
class Place
{
int code;
String name;
float x;
float y;
int partial[];
int matchDepth;
public Place(int code, String name, float x, float y)
{
this.code = code;
this.name = name;
this.x = x;
this.y = y;
partial = new int[7];
partial[6] = code;
partial[5] = partial[6] / 10;
partial[4] = partial[5] / 10;
partial[3] = partial[4] / 10;
partial[2] = partial[3] / 10;
partial[1] = partial[2] / 10;
}
void check()
{
// Default to zero levels of depth that match
matchDepth = 0;
if (typedCount != 0)
{
// Start from the greatest depth, and work backwards to see how many
// items match. Want to figure out the maximum match, so better to
// begin from the end.
for (int j = typedCount; j > 0; --j)
{
if (typedPartials[j] == partial[j])
{
matchDepth = j;
break; // Since starting at end, can stop now.
}
}
}
if (matchDepth == typedCount)
{
foundCount++;
}
}
void draw()
{
int xx = (int) TX(x);
int yy = (int) TY(y);
color c = dormantColor;
if (typedCount != 0)
{
if (matchDepth == typedCount)
{
c = highlightedColor;
}
else
{
c = unhighlightedColor;
}
}
set(xx, yy, c);
}
}
// From chapter 6, Visualizing Data: Exploring and Explaining Data with the Processing Environment by Ben Fry
// https://books.google.com/books?id=6jsVAiULQBgC
// data: https://dl.dropboxusercontent.com/u/19398876/zip-scatterplot/data.tsv
// column numbers in the data file
static final int CODE = 0;
static final int X = 1;
static final int Y = 2;
static final int NAME = 3;
int totalCount = 154809; // total number of places
int placeCount = 0; // number of places loaded
// min/max boundary of all points
// Actual coordinates (data.tsv)
float minX = 68.5341;
float maxX = 96.5431;
float minY = 8.083;
float maxY = 35.339;
// Alber's projected values (zips.tsv)
// float minX = -0.22102962;
// float maxX = 0.21157536;
// float minY = 0.13703418;
// float maxY = 0.61151624;
Place[] places;
// Border of where the map should be drawn on screen
float mapX1, mapY1;
float mapX2, mapY2;
color backgroundColor = #000000; // dark background color
// color dormantColor = #999966; // initial color of the map
color dormantColor = #AAAAAA;
// color highlightedColor = #CBCBCB; // color for selected points
color highlightedColor = #e74c3c;
// color unhighlightedColor = #66664C; // color for points that are not selected
color unhighlightedColor = #222222;
color badColor = #FFFF66; // text color when nothing found
PFont font;
String typedString = "";
char typedChars[] = new char[6];
int typedCount = 0;
int typedPartials[] = new int[7];
float messageX, messageY;
int foundCount = 1;
public void setup()
{
font = loadFont("../data/ScalaSans-Regular-14.vlw");
textFont(font);
textMode(MODEL);
messageX = 40;
messageY = height - 40;
// processData();
float width = 920;
float ratio = ((maxX - minX)/(maxY - minY));
float height = width / ratio;
size((int)width, (int)height, P3D);
mapX1 = 30;
mapX2 = width - mapX1;
mapY1 = 20;
mapY2 = height - mapY1;
readData();
}
public void draw()
{
background(backgroundColor);
for (int i=0; i<placeCount; i++)
{
places[i].draw();
}
text(typedString, messageX, messageY);
}
void processData()
{
String[] lines = loadStrings("../data/data.tsv");
String[] projected = new String[lines.length];
for (int i=0; i < lines.length; i++)
{
String pieces[] = split(lines[i], TAB);
int zip = int(pieces[CODE]);
float lon = float(pieces[X]);
float lat = float(pieces[Y]);
String name = pieces[NAME];
float phi0 = 0;
float lambda0 = radians(82.5);
float phi1 = radians(15f);
float phi2 = radians(30f);
float phi = radians(lat);
float lambda = radians(lon);
float n = 0.5f * (sin(phi1) + sin(phi2));
float theta = n * (lambda - lambda0); //radians(lon - lambda0);
float c = sq(cos(phi1)) + 2*n*sin(phi1);
float rho = sqrt(c - 2*n*sin(phi)) / n;
float rho0 = sqrt(c - 2*n*sin(phi0)) / n;
float x = rho * sin(theta);
float y = rho0 - rho*cos(theta);
projected[placeCount++] = zip + "\t" +
x + "\t" +
y + "\t" +
name;
}
PrintWriter tsv = createWriter("../data/zips.tsv");
for (int i = 0; i < placeCount; i++)
{
tsv.println(projected[i]);
}
tsv.flush();
tsv.close();
}
void readData()
{
String[] lines = loadStrings("../data/data.tsv");
places = new Place[totalCount];
for (int i = 0; i < lines.length; i++)
{
places[placeCount] = parsePlace(lines[i]);
placeCount++;
}
}
Place parsePlace(String line)
{
String pieces[] = split(line, TAB);
int zip = int(pieces[CODE]);
float x = float(pieces[X]);
float y = float(pieces[Y]);
String name = pieces[NAME];
return new Place(zip, name, x, y);
}
float TX(float x)
{
return map(x, minX, maxX, mapX1, mapX2);
}
float TY(float y)
{
return map(y, minY, maxY, mapY2, mapY1);
}
void keyPressed( ) {
if ((key == BACKSPACE) || (key == DELETE))
{
if (typedCount > 0)
{
typedCount--;
}
updateTyped();
}
else if ((key >= '0') && (key <= '9'))
{
if (typedCount != 6)
{ // Stop at 6 digits.
if (foundCount > 0)
{ // If nothing found, ignore further typing.
typedChars[typedCount++] = key;
}
}
}
updateTyped();
}
void updateTyped( ) {
typedString = new String(typedChars, 0, typedCount);
typedPartials[typedCount] = int(typedString);
for (int j = typedCount-1; j > 0; --j)
{
typedPartials[j] = typedPartials[j + 1] / 10;
}
foundCount = 0;
for (int i = 0; i < placeCount; i++)
{
// Update boundaries of selection
// and identify whether a particular place is chosen.
places[i].check();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment