Loading...
Logo
Processing Forum
Hi,
 
I've just started experimenting with Unfolding with the aim of building some 3D geometry on top of a map. I'm struggling to figure out how to rotate the map in the third dimension, it looks like map.outerRotate is only in the x-y plane.
 
Although I'm working at a smaller scale, the required 3D effect is similar to Jer Thorp's amazing twitter visualisation:
 
http://blog.blprnt.com/blog/blprnt/just-landed-processing-twitter-metacarta-hidden-data
 
Any ideas would be great. Thanks!
 

Replies(3)

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.



Copy code
  1. 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;
      }
     
    }



Thanks for the reply.
 
I had imported peasy, but not managed to set it up correctly so your example was really helpful.
 
Thanks again.
Hi, 

I was testing the code above but I've noted that RSS coordinates and base map coordinates don't match. When I removed the camera everything matched. Do you have any idea of how to solve this problem? or did you find another way to draw 3D geometries over a flat map.

Thanks!