At the moment, Unfolding does not fully support 3D transformations out of the box. But you can easily use some 3D library (such as the great PeasyCam) and create your own. Functionality such as 3D picking (screen to location coordinates) are missing, but you should be able to work on something like Jer's vis.
Here is a simple example displaying earthquake data as 3D bars on a MapBox map. Basically, it is the GeoRSSMarkerApp example extended by PeasyCam.
- import peasy.*;
import processing.opengl.*;
import codeanticode.glgraphics.*;
import de.fhpotsdam.unfolding.*;
import de.fhpotsdam.unfolding.geo.*;
import de.fhpotsdam.unfolding.utils.*;
import de.fhpotsdam.unfolding.providers.*;
de.fhpotsdam.unfolding.Map map;
PeasyCam cam;
List<Earthquake> earthquakes = new ArrayList<Earthquake>();
public void setup() {
size(800, 600, GLConstants.GLGRAPHICS);
noStroke();
map = new de.fhpotsdam.unfolding.Map(this, -700, -400, 1400, 800, new MapBox.ControlRoomProvider());
map.setTweening(true);
map.zoomToLevel(2);
cam = new PeasyCam(this, 500);
cam.rotateX(-0.8);
cam.rotateY(-0.4);
cam.rotateZ(0.4);
cam.setMinimumDistance(50);
cam.setMaximumDistance(500);
loadRSSGeoLocations();
}
public void draw() {
directionalLight(166, 166, 196, -60, -60, -60);
background(0);
map.draw();
for (Earthquake earthquake : earthquakes) {
float xy[] = map.getScreenPositionFromLocation(earthquake.location);
drawEarthquakeMarker(xy[0], xy[1], earthquake.richterValue);
}
}
public void drawEarthquakeMarker(float x, float y, float value) {
fill(#790C78, 100);
pushMatrix();
translate(x, y);
float height = map(value, 4, 8, 20, 200);
drawTruncatedCone(10, 10, 0, height);
popMatrix();
noStroke();
}
public void drawTruncatedCone(float r1, float r2, float d1, float d2) {
float ang = 0;
int pts = 120;
beginShape(QUAD_STRIP);
for (int i=0; i<=pts; i++) {
float px = cos(radians(ang))*r1;
float py = sin(radians(ang))*r1;
vertex(px, py, d1);
float px2 = cos(radians(ang))*r2;
float py2 = sin(radians(ang))*r2;
vertex(px2, py2, d2);
ang+=360/pts;
}
endShape();
}
public void loadRSSGeoLocations() {
// Load RSS feed
String url = "http://earthquake.usgs.gov/earthquakes/catalogs/eqs7day-M5.xml";
XMLElement rss = new XMLElement(this, url);
// Get all items
XMLElement[] itemXMLElements = rss.getChildren("channel/item");
for (int i = 0; i < itemXMLElements.length; i++) {
// Adds lat,lon as locations for each item
XMLElement latXML = itemXMLElements[i].getChild("geo:lat");
XMLElement lonXML = itemXMLElements[i].getChild("geo:long");
String title = itemXMLElements[i].getChild("title").getContent();
float richterValue = Float.valueOf(title.substring(2, 5));
float lat = Float.valueOf(latXML.getContent());
float lon = Float.valueOf(lonXML.getContent());
Earthquake earthquake = new Earthquake(new Location(lat, lon), richterValue);
earthquakes.add(earthquake);
}
}
public void keyPressed() {
if (key == '+') {
map.zoomLevelIn();
}
if (key == '-') {
map.zoomLevelOut();
}
}
class Earthquake {
Location location;
float richterValue;
Earthquake(Location location, float richterValue) {
this.location = location;
this.richterValue = richterValue;
}
}