VE Tutorial: Mapping e00 files in Processing
Here’s a brief tutorial on how to create a 3D visualization of a map in Processing, using .e00 files as source data. The end result will look something like this:
The height of the various bars is based on the number of urban areas in that region. Data is from the US Census Bureau.
There are 3 main things you have to do to create this:
- import and parse the map data
- draw and simplify the data
- create and export 3D objects based on the data
1. Importing the Map Data
.eoo files are raw text files, storing groups of longitude and latitude coordinates:
Text files can be read into processing through the loadStrings function:
String lines[] = loadStrings("ua99_d00.e00");
This creates an array of every line in the file. Since .eoo put each coordinate on its own line, this is perfect for what we want. Now we can split up each line into the numbers it contains:
for (int i=0; i < lines.length; i++) { //go through each line
String[] c = split(trim(lines[i]), ' ');
ArrayList vals = new ArrayList();
for (int j = 0; j < c.length; j++) {
float val = parseFloat(c[j]);
if (!Float.isNaN(val)) vals.add(val);
}
//now we have an ArrayList of all the numbers for this line
}
We can see that lines with coordinates have 2 numbers separated by spaces, and other lines, between groups, have either less or more numbers. The coordinates are all we care about, so we’ll pretty much be ignoring all other numbers. To make things easier. I created a Point class, which holds a coordinate, and a PointGroup class, which will hold a group of Points:
class Point {
float x,y;
Point(float aX, float aY) {
x = aX;
y = aY;
}
}
class PointGroup {
ArrayList pointsList;
PointGroup() {
pointsList = new ArrayList();
}
void addPoint(Point aPoint) {
pointsList.add(aPoint);
}
}
Now, we can add to our number parsing code, so if a line is a coordinate, it adds it to the current PointGroup, and if it’s not, it ends that PointGroup and creates a new one.
ArrayList groups = new ArrayList();</strong>
for (int i=0; i < lines.length; i++) { //go through each line
String[] c = split(trim(lines[i]), ' ');
ArrayList vals = new ArrayList();
for (int j = 0; j < c.length; j++) {
float val = parseFloat(c[j]);
if (!Float.isNaN(val)) vals.add(val);
}
//now we have an ArrayList of all the numbers for this line
if (vals.size() != 2) {
groups.add(new PointGroup());
} else {
PointGroup currentGroup = (PointGroup) groups.get(groups.size() - 1);
}
}
Now, we have all of the map data parsed into memory, and all we have to do to draw it out is connect the dots within each PointGroup.
2. Draw and Simplify the Data
Now, we can draw all of the data onto a BufferImage in memory. If all you want to do is draw the map to the screen, there is no need for a buffer image. However, since we want this file to eventually display in 3D, we can’t draw our points to the screen, so we create an Image in memory:
PGraphics buf = createGraphics(800, 600);
This essentially creates another canvas for you to apply any drawing commands to – a little bit like a MovieClip in Flash that isn’t being displayed on the stage. Now, we can cycle through our PointGroups, and connect all of the points inside. In this case, we are using beginShape(POINTS) because we don’t want the lines to connect, but normally you would just use beginShape(). I hardcoded in the boundary values I was using for the map of the US, but it would be fairly easy to make the map automatically scale to fit the image.
minX = -140;
maxX = -60;
minY = 5;
maxY = 60;
buf.smooth();
buf.stroke(255);
buf.noFill();
for (int i = 0; i < groups.size(); i++) {
PointGroup aGroup = (PointGroup) groups.get(i);
ArrayList points = aGroup.pointsList;
beginShape(POINTS);
for (int j = 0; j < points.size(); j++) {
Point aPoint = (Point) points.get(j);
vertex(aPoint.x, aPoint.y);
}
endShape();
}
Now, we can create a function that will basically pixelate this image, so that the color value of each square reflect the average color of that area (which in this case, reflects the number of populated places). We could create a new buffer image if we wanted to display the image, but since don’t we can just create a new pixel array:
int squareWidth = 5;
int squareHeight = 5;
int[] averages = new int[buf.width * buf.height];
for(int gx = 0; gx < buf.width / squareWidth; gx++) {
for (int gy = 0; gy < buf.height / squareHeight; gy++) {
int count = 0;
for (int x = 1; x < squareWidth ; x++) {
for (int y = 1; y < squareHeight; y++) {
if (pixels[ x + gx * squareWidth + (y + gy*squareHeight) * buf.width] != color(0,0,0)) count++;
}
}
// Saves as a color value, although we don't ever display it
val = round(255 * count / (squareWidth * squareHeight));
averages[gx + ceil(buf.width / squareWidth)] = val;
}
}
3. Create and Export 3D Objects
Next, we can create the 3D objects that we want and export a DXF file. We will use the Cube class from the Brick Tower example by Ira Greenberg that ships with processing. The code simple goes through the array we just set up, and creates a stack of cubes of corresponding height:
for (int x = 1; x < buf.width / squareWidth; x++) {
for (int y = 1; y < buf.height / squareHeight; y++) {
int num = img.pixels[x + (y * squareHeight + 1) * round(buf.width / squareWidth)];
if (num > 0) {
for (int i = 0; i < num; i++) {
Cube brick = new Cube(squareWidth-1, squareHeight-1, 2);
pushMatrix();
translate(x * squareWidth, y * squareHeight, i * 3);
brick.create();
popMatrix();
}
}
}
}
Then, we can output it as a DXF:
beginRaw(DXF, "output.dxf"); //code from above endRaw();
Run this code, then open up Cinema 4D, and choose File > Merge, then select the output.dxf file that Processing created. Select the object and move it to 0,0,0 since it comes into Cinema somewhere way off the screen. You’ll notice the polygon faces look really weird – that’s because some of the polygon normals are reversed.
Throw everything inside a Connect Object to fix this(It may take a little while to process), then convert the connect object back to a polygon. Remove the phong tag, so that Cinema doesn’t try to smooth the edges of the cubes. That’s it! Throw some materials on it and render it out.
I’ll post up the source code once I get a chance to clean it up a bit.
Temporary Files



