Category: Emerging Multimedia

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:

3d-map1

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:

  1. import and parse the map data
  2. draw and simplify the data
  3. 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:

e00

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();
}

map-points

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;
	}
}

map-pixelated1

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

EMM Project 1 Redo

Made some changes to timing and camera movement. Also, I’m experimenting with hosting my videos on Vimeo, since my massive number of viewers is eating up all my bandwidth. Actually I just think it’s annoying to have lots of quicktime movies trying to load whenever you go to a site.

EMM Project 1 Final

View Large > 

EMM Project 1 Animation Progress

View Large >

EMM Project 1 Comps

Comps for my project 1 animation:

proj1comp.jpg

Comp Viewer >

EMM Song Updates

Updated Version of the Song:

emm-1-sketches.jpg

And preliminary sketches for the animation to go with it.

EMM Song preliminary

Preliminary version of my song:

EMM Song Research

The first project for Emerging Multimedia Design and Imaging is to create a song in Garage Band to evoke a certain emotion. Here are some songs I am using as reference for the specific emotion (links are to youtube):

And some other reference songs:

Feeling: Surreal

Characteristics: Slow, starts gradually,

Common Instruments in the emotion reference songs: undistorted guitar, piano, violin