Last active
April 23, 2016 20:09
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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