import proxml.*; // stage dimensions int STAGE_WIDTH = 830; int STAGE_HEIGHT = 600; float HALF_STAGE_WIDTH = 415.0; float HALF_STAGE_HEIGHT = 300.0; // xmlInOut object to load the cab list XMLInOut cabsLoad; // xml element to store the cab list XMLElement cabs; // the picked cab String pickedCab; // xmlInOut object to load the individual cab xml XMLInOut pointsLoad; // xml element to store and load the drawn points XMLElement points; // a list of coordinates Vector coords = new Vector(); // a counter for stepping through the global coordinates int nowCoord = 1; // the total number of coordinates int numCoords = 0; // the last time (in ms) that a cab position was drawn float lastDrawnAt; // speed of the drawing (smaller number = slower ) float timeSpeed = 2; // camera x & z float cameraX = HALF_STAGE_WIDTH; float cameraZ = 0.0; // mouse pressed x & y float pressedX = 0.0; float pressedY = 0.0; // the sum of x & y values for rotating & zooming float sumX = 0.0; float sumY = HALF_STAGE_HEIGHT; boolean cabpicked = false; boolean dataLoaded = false; PFont fontA; void setup(){ // set size & use P3D for rendering size( STAGE_WIDTH, STAGE_HEIGHT, P3D ); // camera defined: camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ); // defaults: camera(width/2.0, height/2.0, ((height/2.0) / tan(PI*60.0 / 360.0), width/2.0, height/2.0, 0, 0, 1, 0); camera( HALF_STAGE_WIDTH, 150.0, HALF_STAGE_HEIGHT / tan( PI * 60 / 360 ), HALF_STAGE_WIDTH, HALF_STAGE_HEIGHT, 0.0, 0.0, 1.0, 0.0 ); lastDrawnAt = millis(); background( 0 ); // loading text fontA = loadFont("Helvetica-Bold-48.vlw"); // Set the font and its size (in units of pixels) textFont(fontA, 48); fill(100, 100, 100); text("Choosing a cab...", HALF_STAGE_WIDTH - 206, HALF_STAGE_HEIGHT); } void draw() { if ( dataLoaded ) { // get the current and last coordinates Coord lastCoord = ( Coord )coords.elementAt( nowCoord + 1 ); Coord thisCoord = ( Coord )coords.elementAt( nowCoord ); // get the time difference in ms int deltaTime = thisCoord.timed - lastCoord.timed; // and the current time float nowTime = millis(); float deltaNow = ( nowTime - lastDrawnAt ) * timeSpeed; // if the mouse is pressed... boolean mouseActivity = false; if ( mousePressed ) { // get deltas float deltaX = mouseX - pressedX; float deltaY = pressedY - mouseY; // remember the mouse activity if ( deltaX != 0.0 || deltaY != 0.0 ) { mouseActivity = true; } // calculate & limit sums sumX += deltaX; while ( sumX > HALF_STAGE_WIDTH ) { sumX -= HALF_STAGE_WIDTH; } while ( sumX < 0 ) { sumX += HALF_STAGE_WIDTH; } sumY = max( min( sumY + deltaY, HALF_STAGE_HEIGHT ), 10.0 ); // update pressed pressedX = mouseX; pressedY = mouseY; // zoom float fov = sumY / HALF_STAGE_HEIGHT * PI / 4; // perspective(fov, aspect, zNear, zFar) // The default values are: perspective( PI/3.0, width/height, cameraZ/10.0, cameraZ*10.0 ) // where cameraZ is ((height/2.0) / tan(PI*60.0/360.0)) float zoomZ = 150.0 / tan( fov / 2.0 ); perspective( fov, 1, zoomZ / 10.0, zoomZ * 10.0 ); // rotation float theta = ( PI / 3 ) + ( sumX / HALF_STAGE_WIDTH * TWO_PI ); cameraX = thisCoord.lon - ( HALF_STAGE_WIDTH * cos( theta ) ); cameraZ = thisCoord.lat - ( HALF_STAGE_WIDTH * sin( theta ) ); } if ( deltaNow > deltaTime || mouseActivity ) { // clear the background background( 0 ); // tell the camera to look at the new spot camera( cameraX, 150.0, cameraZ, thisCoord.lon, HALF_STAGE_HEIGHT, thisCoord.lat, 0.0, 1.0, 0.0 ); // redraw the entire trail up to the new segment for ( int i = numCoords; i >= nowCoord; i-- ) { Coord coordA = ( Coord )coords.elementAt( i + 1 ); Coord coordB = ( Coord )coords.elementAt( i ); // if the point is ten away from the most recent... if ( i < nowCoord + 10 ) { // color it yellow stroke( 255, 255, 0, 100 ); } else { // color it white stroke( 255, 50 ); } // draw from coordinate A to coordinate B line( coordA.lon, HALF_STAGE_HEIGHT + ( ( i - nowCoord ) / 20 ), coordA.lat, coordB.lon, HALF_STAGE_HEIGHT + ( ( i - nowCoord - 1) / 20 ), coordB.lat ); } // draw a yellow dot at the cab position translate( thisCoord.lon, HALF_STAGE_HEIGHT, thisCoord.lat ); rotateX( 1.57 ); fill( 255, 255, 0 ); noStroke(); ellipse( 0, 0, 1, 1 ); // count down nowCoord nowCoord--; // save the draw time lastDrawnAt = nowTime; // reset nowCoord if there are no more points to draw if ( nowCoord <= 0 ) { nowCoord = 0; } } } else { if ( cabpicked ) { try { // load points from file if it exists pointsLoad = new XMLInOut( this ); points = pointsLoad.loadElementFrom( "http://cabspotting.exploratorium.edu/cab.xml.cgi?cab=" + pickedCab + "&m=7200" ); //points = pointsLoad.loadElementFrom( sketchPath + "/cab.xml.cgi?cab=" + pickedCab + "&m=7200" ); // process the points initPoints(); // remember that the data's loaded dataLoaded = true; } catch ( InvalidDocumentException ide ) { background( 0 ); // Set the font and its size (in units of pixels) textFont(fontA, 48); fill(100, 100, 100); text("Data Not Loaded!", HALF_STAGE_WIDTH - 206, HALF_STAGE_HEIGHT); noLoop(); } } else { try { // load points from file if it exists cabsLoad = new XMLInOut( this ); cabs = cabsLoad.loadElementFrom( "http://cabspotting.exploratorium.edu/cabs.xml.cgi?m=1440" ); //cabs = cabsLoad.loadElementFrom( sketchPath + "/cabs.xml.cgi?m=1440" ); // pick the cab we're going to visualize pickCab(); // remember that the cab's been picked cabpicked = true; // change the text background( 0 ); // Set the font and its size (in units of pixels) textFont(fontA, 48); fill(100, 100, 100); text("Loading cab data...", HALF_STAGE_WIDTH - 206, HALF_STAGE_HEIGHT); } catch ( InvalidDocumentException ide ) { background( 0 ); // Set the font and its size (in units of pixels) textFont(fontA, 48); fill(100, 100, 100); text("Cabs Not Loaded!", HALF_STAGE_WIDTH - 206, HALF_STAGE_HEIGHT); noLoop(); } } } } // handle the mouse press void mousePressed(){ // save the location of the mouse pressedX = mouseX; pressedY = mouseY; } // pick a cab from the list void pickCab(){ XMLElement acab; int aupdates; int mostUpdates = -1; int numChildren = cabs.countChildren(); for( int itemC = 0; itemC < numChildren; itemC++ ){ // get the original data from the XML file acab = cabs.getChild(itemC); aupdates = acab.getIntAttribute( "updates" ); // if this cab has the most updates so far... if ( aupdates > mostUpdates ) { // save its id mostUpdates = aupdates; pickedCab = acab.getAttribute( "id" ); } } //println( "picked cab "+pickedCab ); } // process all points saved in the xml file void initPoints(){ XMLElement apoint; float lastLon = 0.0; float lastLat = 0.0; int numChildren = points.countChildren(); for( int itemP = 0; itemP < numChildren; itemP++ ){ // get the original data from the XML file apoint = points.getChild(itemP); float lon = apoint.getFloatAttribute( "lon" ); float lat = apoint.getFloatAttribute( "lat" ); int timed = apoint.getIntAttribute( "time" ); // transform the longitude/latitude to a coordinate on the screen lon = STAGE_HEIGHT + ( ( lon + 122 ) * 1000 ); lat = STAGE_HEIGHT - ( ( lat - 37.3 ) * 1000 ); // save the points and the time Coord newCoord = new Coord(); newCoord.lon = lon; newCoord.lat = lat; newCoord.timed = timed; coords.addElement( newCoord ); } // save the count in nowCoord nowCoord = coords.size() - 2; numCoords = nowCoord; } class Coord { float lon = 0; float lat = 0; int timed = 0; }