Jupyter at Bryn Mawr College |
|||
Public notebooks: /services/public/dblank / CS110 Intro to Computing / 2015-Spring / Lectures |
In this notebook, we continue to look at methods of displaying information visually. See Part 1
To explore these ideas, we need to introduce a new construct in Processing: the array.
Arrays are contiguous blocks of memory used to store similar things. Effectively this allows you to have variables that you can reference by a number.
You can define a variable to hold an unspecified number of integers with:
int [] values = new int[] { 1, 2, 3 };
You can also assign the variable with an array of values at the same time. We use the new
keyword to create the array that holds the elements 1, 2, and 3:
int [] values = new int[] { 1, 2, 3 };
To refer to the first and second items, we use:
values[0]
values[i]
Let's see the array in use.
In the last lecture, we used a SVG image of the united states.
Here, we use a meta-command to download the file again:
%download http://upload.wikimedia.org/wikipedia/commons/archive/3/32/20091105194402%21Blank_US_Map.svg
And a meta-command to rename the file:
! mv 20091105194402%21Blank_US_Map.svg usa-wikipedia.svg
In this example, we want to use the states as before, but also be able to plot longitude and latitude on the map.
Since the map was just a picture (and not drawn) I wasn't sure what coordinate system it was in.
After a bit of trial and error, I found that the Albers conic projection was a good fit.
In this example, I plot some longitude and latitude points, and kept adjusting the xoffset, xscale, yoffset, and yscale until the points lined up pretty well. Normally, we would know these values, but the wikipedia map did not provide those.
If you click, it will plot those points, and some cites.
Notice that the albers function returns an array containing the x and the y values:
PShape usa;
PShape michigan;
PShape ohio;
void setup() {
size(959, 593);
background(255);
usa = loadShape("usa-wikipedia.svg");
michigan = usa.getChild("MI");
ohio = usa.getChild("OH");
}
void draw() {
// Draw the full map
shape(usa, 0, 0);
noLoop();
}
float[] albers(lat, lng) {
lat0 = 23.0 * (PI/180); // Latitude_Of_Origin
lng0 = -96.0 * (PI/180); // Central_Meridian
phi1 = 30.0 * (PI/180); // Standard_Parallel_1
phi2 = 50.0 * (PI/180); // Standard_Parallel_2
n = 0.5 * (sin(phi1) + sin(phi2));
c = cos(phi1);
C = c * c + 2 * n * sin(phi1);
p0 = sqrt(C - 2 * n * sin(lat0)) / n;
theta = n * (lng * PI/180 - lng0);
p = sqrt(C - 2 * n * sin(lat * PI/180)) / n;
x = p * sin(theta);
y = p0 - p * cos(theta);
return new float[2] { x, y };
}
void plot(lat, lon, c) {
// Values to scale the lat, lon to fit on the USA map:
xoffset = 485;
xscale = 1245;
yoffset = 630;
yscale = 1250;
float[2] xy = albers(lat, lon);
fill(c);
ellipse(xoffset + xy[0] * xscale, yoffset - xy[1] * yscale, 10, 10);
}
void mousePressed() {
for (lat = 30; lat <= 50; lat += 5) {
for (lon = 70; lon <= 130; lon += 5) {
plot(lat, -lon, color(255));
}
}
// Some Cites:
plot(39.790942, -86.147685, color(255, 0, 128));
plot(47.042418, -122.893077, color(255, 128, 0));
plot(30.4518, -84.27277, color(255, 0, 0));
plot(44.323535, -69.765261, color(255, 0, 255));
plot(33.448457, -112.073844, color(255, 255, 0));
}
You can use this to overlay data, such as population density, locations of events, etc. You might want to use the 4th element of color(), the alpha value, to make the colors transparent.
Here is a diagram style that is used a lot in genomic and biological data:
http://circos.ca/intro/genomic_data/
How could we make such a chart?
First, let's make some tests using different curves.
I found that the bezier, with control points at the center looks pretty good:
noFill();
stroke(255, 0, 0);
bezier(10, 10, 50, 50, 50, 50, 90, 10);
For this example, we'll reuse the rotateAround functions from the last assignment. This is used to find the starting and stopping points around a center.
Then, we draw a bezier curve between them:
int rotateAroundX(int x1, int y1, int length, int angle) {
return x1 + length * cos(angle);
}
int rotateAroundY(int x1, int y1, int length, int angle) {
return y1 - length * sin(angle);
}
void setup() {
size(300, 300);
background();
}
void draw() {
// Center:
cx = width/2;
cy = height/2;
// Random degree, converted to radians:
p1 = random(360) * PI/180;
// Random degree, converted to radians:
p2 = random(360) * PI/180;
// Find where p1 would be:
x1 = rotateAroundX(cx, cy, width/2, p1);
y1 = rotateAroundY(cx, cy, width/2, p1);
// Find where p2 would be:
x2 = rotateAroundX(cx, cy, width/2, p2);
y2 = rotateAroundY(cx, cy, width/2, p2);
// Connect p1 to p2:
noFill();
stroke(random(255), random(255), random(255));
bezier(x1, y1, cx, cy, cx, cy, x2, y2);
}
What could you use this to represent?
In this example, we construct three arrays:
The idea is we are representing information about a relationship. For example, it might represent a tweet (from one person, sent to (or mentioning) another).
int rotateAroundX(int x1, int y1, int length, int angle) {
return x1 + length * cos(angle);
}
int rotateAroundY(int x1, int y1, int length, int angle) {
return y1 - length * sin(angle);
}
void setup() {
size(300, 300);
background();
}
void draw() {
// Center:
cx = width/2;
cy = height/2;
int[] from = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int[] to = new int[] {1, 3, 3, 6, 3, 1, 2, 1, 2, 3 };
string[] name = new string[] {"Helen",
"Mary",
"Teyvonia",
"Glenda",
"Bertrude",
"Elvie",
"Sarah",
"Mary",
"Trisha",
"Karen" };
// First, let's draw the names around the circle:
for (int i = 0; i < 10; i++) {
p1 = from[i] * 36 * PI/180;
p2 = to[i] * 36 * PI/180;
x1 = rotateAroundX(cx, cy, width/2, p1);
y1 = rotateAroundY(cx, cy, width/2, p1);
fill(0);
text(name[i], x1, y1);
}
// Next, we connect up the people:
for (int i = 0; i < 10; i++) {
p1 = from[i] * 36 * PI/180;
p2 = to[i] * 36 * PI/180;
// Find where p1 would be:
x1 = rotateAroundX(cx, cy, width/2, p1);
y1 = rotateAroundY(cx, cy, width/2, p1);
// Find where p2 would be:
x2 = rotateAroundX(cx, cy, width/2, p2);
y2 = rotateAroundY(cx, cy, width/2, p2);
// Connect p1 to p2:
noFill();
strokeWeight(random(5));
stroke(random(255), random(255), random(255));
bezier(x1, y1, cx, cy, cx, cy, x2, y2);
}
noLoop();
}
In this example, I randomly drew the connecting line in a random color and weight. That information could also be stored in an array.
The idea of a heatmap is to show intensity as a visual representation, often color. Here we see the number of discussions between two people represented as a heatmap. Why is the diagonal white?