//import processing.opengl.*; import tomc.gpx.*; import com.processinghacks.arcball.*; Vector points = new Vector(); Vector triangles = new Vector(); Vector contourEdges = new Vector(); // Vector of Vectors of Edges Vector contourPolygons = new Vector(); // Vector of Vectors of Polygons PFont font; GPX gpx; SetupRunner setupRunner; void setup() { size(600,400,P3D); //size(screen.width,screen.height,OPENGL); // currently breaks with noLoop :( //smooth(); ArcBall arcball = new ArcBall(width/2,height/2,0,height/2,this); font = loadFont("GillSansMT-Bold-18.vlw"); gpx = new GPX(this); setupRunner = new SetupRunner(); new Thread(setupRunner).start(); } class SetupRunner implements Runnable { String status = ""; boolean finished = false; public void run() { points.clear(); triangles.clear(); contourEdges.clear(); contourPolygons.clear(); finished = false; status = "parsing GPX..."; redraw(); gpx.parse("Greenwich.gpx"); status = "finding extents of data..."; redraw(); double minLat=90.0, maxLat=-90.0, minLon=180.0, maxLon=-180.0, minEle=255.0, maxEle=0.0; // opposites for (int i = 0; i < gpx.getTrackCount(); i++) { GPXTrack trk = gpx.getTrack(i); for (int j = 0; j < trk.size(); j++) { GPXTrackSeg trkseg = trk.getTrackSeg(j); for (int k = 0; k < trkseg.size(); k++) { GPXPoint pt = trkseg.getPoint(k); minLat = Math.min(minLat, pt.lat); maxLat = Math.max(maxLat, pt.lat); minLon = Math.min(minLon, pt.lon); maxLon = Math.max(maxLon, pt.lon); minEle = Math.min(minEle, pt.ele); maxEle = Math.max(maxEle, pt.ele); } } } status = "projecting points into screen space..."; redraw(); // projection for turning lat/lon into screen space // something isn't quite right with this class (scale is off I think), but it gives you an idea of what's going on OSMMercator projection = new OSMMercator((minLat+maxLat)/2.0, (minLon+maxLon)/2.0, 2.0*Math.max((maxLon-minLon)/width,(maxLat-minLat)/height), width, height); // project all the GPXPoints for (int i = 0; i < gpx.getTrackCount(); i++) { GPXTrack trk = gpx.getTrack(i); for (int j = 0; j < trk.size(); j++) { GPXTrackSeg trkseg = trk.getTrackSeg(j); for (int k = 0; k < trkseg.size(); k++) { GPXPoint pt = trkseg.getPoint(k); double x = projection.x(pt.lon); double y = projection.y(pt.lat); double z = pt.ele; Point3d p = new Point3d(x,y,z); points.add(p); } } } status = "adding planar/grid points..."; redraw(); // add grid points // (there's almost certainly a better way to do this bit) int gridw = width/32; int gridh = height/32; boolean offset = true; boolean jitter = true; for (int i = 0; i <= gridw; i++) { for (int j = 0; j <= gridh; j++) { float x = i*width/gridw; float y = j*height/gridh; if (offset && (j%2==0) && j!=0 && j!=gridh && i!=0 && i!=gridw) { x += width/(2*gridw); } if (jitter) { x += random(-1.0,1.0) * width/(4*gridw); y += random(-1.0,1.0) * height/(4*gridh); } points.add(new Point3d(x,y,0.0)); } } status = "stripping duplicate points..."; redraw(); // probably slow but should get the job done // remaining height is average of all coincident heights - maybe also try min/max? Vector dupes = new Vector(); for (int i = 0; i < points.size()-1; i++) { Point3d p1 = (Point3d)points.get(i); int count = 1; for (int j = i+1; j < points.size(); j++) { Point3d p2 = (Point3d)points.get(j); if (Point3d.coincident(p1,p2)) { dupes.add(p2); p1.z += p2.z; count++; } } if (count > 1) { p1.z /= (double)count; } } points.removeAll(dupes); // jitter duplicate points - alternative to removing them /* status = "jittering duplicate points"; for (int i = 0; i < points.size()-1; i++) { Point3d p1 = (Point3d)points.get(i); int count = 1; for (int j = i+1; j < points.size(); j++) { Point3d p2 = (Point3d)points.get(j); if (coincident(p1,p2)) { p2.x += random(-1.0,1.0); p2.y += random(-1.0,1.0); } } } println("done");*/ status = "triangulating " + points.size() + " points..."; redraw(); triangles = Triangulate.triangulate(points); status = "contouring " + triangles.size() + " triangles..."; redraw(); double contourStep = 25.0; double startLevel = 5.0; // Math.max(contourStep,Math.ceil(minEle/contourStep)*contourStep); for (double level = startLevel; level <= maxEle; level += contourStep) { // print(" " + level + ":"); Vector edges = Contours.contour(triangles, level); if (edges.size() > 0) { contourEdges.add(edges); } // print(edges.size() + " "); } //Vector contourPolygons = new Vector(); //for (int i = 0; i < contourEdges.size(); i++) { //contourPolygons.add(EdgeMerge.merge((Vector)contourEdges.get(i))); //} finished = true; //loop(); } } void draw() { if (setupRunner.finished) { background(255); lights(); //smooth(); rectMode(CORNER); ellipseMode(CENTER); // flatten out z - 255 is too high scale(1,1,0.2); // draw triangle mesh //stroke(0); noStroke(); fill(255); // 255,64 beginShape(TRIANGLES); for (int i = 0; i < triangles.size(); i++) { Triangle t = (Triangle)triangles.get(i); //fill((float)t.p1.z); vertex(t.p1.x,t.p1.y,t.p1.z); //fill((float)t.p2.z); vertex(t.p2.x,t.p2.y,t.p2.z); //fill((float)t.p3.z); vertex(t.p3.x,t.p3.y,t.p3.z); } endShape(); // draw points - redder for higher ele for (int i = 0; i < points.size(); i++) { Point3d p = (Point3d)points.get(i); if (p.z > 0.0) { pushMatrix(); translate(p.x,p.y,p.z); noStroke(); fill((float)p.z,0.0,0.0); ellipse(0.0,0.0,5.0,5.0); popMatrix(); } } // draw contour edges for each contour level strokeWeight(2.0); stroke(0,40); noFill(); //smooth(); for (int i = 0; i < contourEdges.size(); i++) { Vector edges = (Vector)contourEdges.get(i); for (int j = 0; j < edges.size(); j++) { Edge e = (Edge)edges.get(j); //line(e.p1.x,e.p1.y,e.p2.x,e.p2.y); line(e.p1.x,e.p1.y,e.p1.z+1,e.p2.x,e.p2.y,e.p2.z+1); } } //noSmooth(); } else { background(255); fill(100); textFont(font,18); textAlign(CENTER); text(setupRunner.status,width/2,height/2); } } /* void keyPressed() { if (key == 's' || key == 'S') { print("saving PDF contours"); double level = 5.0; for (int i = 0; i < contourEdges.size(); i++) { Vector edges = (Vector)contourEdges.get(i); beginRecord(PDF, "contours"+level+".pdf"); background(255); stroke(0); strokeWeight(0.5); for (int j = 0; j < edges.size(); j++) { Edge e = (Edge)edges.get(j); line(e.p1.x,e.p1.y,e.p2.x,e.p2.y); } endRecord(); // saves the file print("."); level += 25.0; } println("done"); } else if (key == ' ') { saveFrame(); } } */ // // convenience methods which cast doubles to floats // because the Triangulate and Contouring functions use doubles... // void vertex(double x, double y, double z) { vertex((float)x,(float)y,(float)z); } void translate(double x, double y, double z) { translate((float)x,(float)y,(float)z); } void line(double x1, double y1, double z1, double x2, double y2, double z2) { line((float)x1, (float)y1, (float)z1, (float)x2, (float)y2, (float)z2); } void line(double x1, double y1, double x2, double y2) { line((float)x1, (float)y1, (float)x2, (float)y2); } static public void main(String args[]) { PApplet.main(new String[] { "--present", "gpx_contours" }); }