//import processing.opengl.*; OSMMercator mercator; float pixelsPerMinute; PFont scaleFont; PFont headerFont; PFont labelFont; Hashtable ridership = new Hashtable(); Hashtable stations = new Hashtable(); Hashtable connections = new Hashtable(); Hashtable routes = new Hashtable(); boolean showTimeBands = false; Station selected = null; Station ellipseCentre = null; PImage ellipses = null; HScrollbar hs1; //import processing.video.*; //MovieMaker mm; // Declare MovieMaker object void setup() { size(900,500); //size(1300,800); // Setup Horizontal Scrollbar // mm = new MovieMaker(this, width , height, "nyc9.mov", 30, MovieMaker.MOTION_JPEG_A, MovieMaker.HIGH); int offset = 60; hs1 = new HScrollbar(width/2+ offset-50, height-10, width/2 - offset, 8, 1); //width/2 - offset //size(screen.width,screen.height,OPENGL); //size(screen.width,screen.height); // pixelsPerMinute = 5.0; // JAVA2D, 800, 500 //pixelsPerMinute = width/150.0; pixelsPerMinute = width/100.0; smooth(); // strange things happen with GL ellipses? // scaleFont = loadFont("CenturyGothic-10b.vlw"); // limited chars headerFont = loadFont("ArialMT-26.vlw"); //loadFont("CenturyGothic-24.vlw"); scaleFont = labelFont = loadFont("ArialMT-10.vlw"); println("Parsing Ridership"); parseRidership(); println("Parsing Stations"); parseStations(); println("Parsing Connections"); parseConnections(); println("Parsing Routes"); parseRoutes(); double maxLon=Double.MIN_VALUE,minLon=Double.MAX_VALUE,maxLat=Double.MIN_VALUE,minLat=Double.MAX_VALUE; Iterator statIter = stations.values().iterator(); while(statIter.hasNext()) { Station s = (Station)statIter.next(); // println(s); maxLon = Math.max(s.lon,maxLon); minLon = Math.min(s.lon,minLon); maxLat = Math.max(s.lat,maxLat); minLat = Math.min(s.lat,minLat); } // println(maxLon+","+minLon+","+maxLat+","+minLat); // println("minLat: " + minLat); // println("maxLat: " + maxLat); // println("minLon: " + minLon); // println("maxLon: " + maxLon); // hack...remove later // minLat = 51.4022; // maxLat = 51.7052; // minLon = -0.611; // maxLon = 0.251; //mercator = new OSMMercator((minLat+maxLat)/2.0, (minLon+maxLon)/2.0, 1.05*(maxLon-minLon)/width, width, height); mercator = new OSMMercator((minLat+maxLat)/2.0, (minLon+maxLon)/2.0, 1.00*(maxLon-minLon)/width, width+500, height); //pixelsPerMinute = 1.0/(float)mercator.kilometerinpixels(); statIter = stations.values().iterator(); while(statIter.hasNext()) { Station s = (Station)statIter.next(); s.mapx = (float)mercator.x(s.lon); s.mapy = (float)mercator.y(s.lat); s.screenx = s.mapx; s.screeny = s.mapy; s.targetx = s.mapx; s.targety = s.mapy; } /* Iterator conns = connections.values().iterator(); while (conns.hasNext()) { Iterator lineIter = ((Collection)conns.next()).iterator(); while(lineIter.hasNext()) { Connection c = (Connection)lineIter.next(); Route r = (Route)routes.get(c.line); print(c.line + "\t" + c.a.id + "\t" + c.b.id + "\t" + r.name + "\t" + c.a.name + "\t" + c.b.name + "\n"); } } */ // noLoop(); } void draw() { ellipse(mouseX, mouseY, 20, 20); // mm.addFrame(); // Add window's pixels to movie background(0); smooth(); Iterator statIter = stations.values().iterator(); while(statIter.hasNext()) { Station s = (Station)statIter.next(); s.animate(); } Iterator conns = connections.values().iterator(); while (conns.hasNext()) { Iterator lineIter = ((Collection)conns.next()).iterator(); while(lineIter.hasNext()) { Connection c = (Connection)lineIter.next(); Route r = (Route)routes.get(c.line); fill(r.colour); noStroke(); Station station_a = c.a; Station station_b = c.b; Hashtable a_ridership = (Hashtable)ridership.get(station_a.id); Hashtable b_ridership = (Hashtable)ridership.get(station_b.id); int radius_a = 5; int radius_b = 5; if (a_ridership != null) { Integer a_ridership_for_selected_year = (Integer)a_ridership.get(getSelectedYear()); radius_a = a_ridership_for_selected_year != null ? ridershipToRadiusSize(a_ridership_for_selected_year) : 5; } if (b_ridership != null) { Integer b_ridership_for_selected_year = (Integer)b_ridership.get(getSelectedYear()); radius_b = b_ridership_for_selected_year != null ? ridershipToRadiusSize(b_ridership_for_selected_year) : 5; } //int radius_a = 5; //int radius_b = 5; ellipse(station_a.screenx,station_a.screeny,radius_a,radius_a); ellipse(station_b.screenx,station_b.screeny,radius_b,radius_b); } } conns = connections.values().iterator(); while (conns.hasNext()) { Iterator lineIter = ((Collection)conns.next()).iterator(); while(lineIter.hasNext()) { Connection c = (Connection)lineIter.next(); Route r = (Route)routes.get(c.line); stroke(r.colour); strokeWeight(2.0); line(c.a.screenx,c.a.screeny,c.b.screenx,c.b.screeny); } } conns = connections.values().iterator(); while (conns.hasNext()) { Iterator lineIter = ((Collection)conns.next()).iterator(); while(lineIter.hasNext()) { Connection c = (Connection)lineIter.next(); Route r = (Route)routes.get(c.line); if (r.stripe != 0) { stroke(r.stripe); strokeWeight(3.0); line(c.a.screenx,c.a.screeny,c.b.screenx,c.b.screeny); } } } float minDist = Float.MAX_VALUE; ellipseMode(CENTER); strokeWeight(1.0); statIter = stations.values().iterator(); selected = null; while(statIter.hasNext()) { Station s = (Station)statIter.next(); float distance = dist(mouseX,mouseY,s.screenx,s.screeny); if (distance < minDist && distance < 20.0) { selected = s; minDist = distance; } if (s.totalLines-s.rail > 1) { stroke(0); fill(255); ellipse(s.screenx,s.screeny,5.0,5.0); } } if (selected != null) { stroke(255,255,0); fill(255,255,0); ellipse(selected.screenx,selected.screeny,5.0,5.0); String label = selected.name; if (selected.timeToCentre != 0 && showTimeBands) { label += " (" + selected.timeToCentre + " minutes travel)"; //line(ellipseCentre.targetx,ellipseCentre.targety,selected.screenx,selected.screeny); } textFont(labelFont,10); textAlign(CENTER); rectMode(CENTER); fill(200); noStroke(); triangle(selected.screenx,selected.screeny,selected.screenx+5,selected.screeny-6,selected.screenx+5,selected.screeny+6); fill(255); stroke(200); rect(selected.screenx+(textWidth(label)/2)+8,selected.screeny,textWidth(label)+4,16); fill(0); text(label,selected.screenx+(textWidth(label)/2)+8,selected.screeny+4); } stroke(255); strokeWeight(2.0); pushMatrix(); translate(width/20,height-(width/20)); textAlign(CENTER); textFont(scaleFont,10); fill(255); if (showTimeBands) { // fill (0); // ellipse(0,5, 20, 20); line(0,5,10*pixelsPerMinute,5); noStroke (); fill (0); ellipse(0,5, 20, 20); fill (255); ellipse(0,5, 5, 5); line(0,0,0,5); text("0",0,20); //line(5*pixelsPerMinute,0,5*pixelsPerMinute,5); //text("5",5*pixelsPerMinute,15); line(10*pixelsPerMinute,0,10*pixelsPerMinute,5); noStroke (); fill (0); ellipse(10*pixelsPerMinute,5, 20, 20); fill (255); ellipse(10*pixelsPerMinute,5, 5, 5); text("10",10*pixelsPerMinute,20); //ellipse(5,5,0,10*pixelsPerMinute); //line(0,5,10*pixelsPerMinute,5); text("minutes",5*pixelsPerMinute,25); } else { line(0.0,0.0,0.0,5.0); text("0",0,15); line(5.0/(float)mercator.kilometerinpixels(),0.0,5.0/(float)mercator.kilometerinpixels(),5.0); text("5",5/(float)mercator.kilometerinpixels(),15); line(10.0/(float)mercator.kilometerinpixels(),0.0,10.0/(float)mercator.kilometerinpixels(),5.0); text("10",10/(float)mercator.kilometerinpixels(),15); line(0.0,5.0,10.0/(float)mercator.kilometerinpixels(),5.0); text("kilometers",5.0/(float)mercator.kilometerinpixels(),25); } popMatrix(); textAlign(CENTER); textFont(headerFont,16); fill (255); if (showTimeBands) { text("Time to Travel from " + ellipseCentre.name,width/2,width/20); } else { text("Geographic NYC Subway Map (click a station for travel time map)",width/2,width/20); } if (keyPressed && (key == 'g' || key == 'G')) { layoutAbout(0); } // Scroll bar updates. hs1.update(); hs1.display(); textAlign(CORNER); fill(255); //text("Year: " + hs1.getPos(), 50, 15); text("Year: " + getSelectedYear(), 50, 15); handleTimeRuler(); } int getSelectedYear() { //TODO: fix hardcoding of years issue int sliderPosition = hs1.getPos(); return (sliderPosition / 8) + 1960; } void handleTimeRuler() { if (keyPressed && (key == 't' || key == 'T')) { showTimeBands = true; } if (keyPressed && (key == 'y' || key == 'Y')) { showTimeBands = false; } if (showTimeBands) { stroke(255); strokeWeight(2.0); line(ellipseCentre.targetx,ellipseCentre.targety,mouseX,mouseY); float x1 = ellipseCentre.targetx; int x2 = mouseX; float y1 = ellipseCentre.targety; int y2 = mouseY; float theta = atan2((y2-y1),(x2-x1)); float c = sqrt(sq(x2-x1) + sq(y2-y1)); int interval = (int)(c/(pixelsPerMinute*10)); float x_prime = ((pixelsPerMinute*10 * cos(theta))); float y_prime = ((pixelsPerMinute*10 * sin(theta))); stroke(0); strokeWeight(10.0); // print tick marks for (int index = 1; index <= interval; index++) { fill(255); ellipse(ellipseCentre.targetx+(index*x_prime),ellipseCentre.targety+(index*y_prime),5.0,5.0); } } } int ridershipToRadiusSize(int numRiders) { // if (numRiders < 1000000) { // return 5; // } else { // return numRiders / 500000; // } if (numRiders > 26000000) return 56; if (numRiders > 24000000) return 52; if (numRiders > 22000000) return 48; if (numRiders > 20000000) return 44; if (numRiders > 18000000) return 40; if (numRiders > 16000000) return 36; if (numRiders > 14000000) return 32; if (numRiders > 12000000) return 28; if (numRiders > 10000000) return 24; if (numRiders > 9000000) return 22; if (numRiders > 8000000) return 20; if (numRiders > 7000000) return 18; if (numRiders > 6000000) return 16; if (numRiders > 5000000) return 14; if (numRiders > 4000000) return 12; if (numRiders > 3000000) return 10; if (numRiders > 2000000) return 8; if (numRiders > 1000000) return 6; return 5; } void mouseReleased() { if (selected != null) { layoutAbout(selected); } } color stringToColor(String string) { if (!string.equals("NULL")) { return unhex("FF"+string.substring(1,7)); } else { return 0; } } void layoutAbout(int centreID) { if (centreID == 0) { Iterator statIter = stations.values().iterator(); while(statIter.hasNext()) { Station s = (Station)statIter.next(); s.targetx = s.mapx; s.targety = s.mapy; } showTimeBands = false; } else { Object s = stations.get(new Integer(centreID)); if (s != null) { layoutAbout((Station)s); } } } void layoutAbout(Station centre) { updateShortestPaths(centre); ellipseCentre = centre; ellipses = null; showTimeBands = true; // centre.targetx = width/2.0; // centre.targety = height/2.0; centre.targetx = centre.mapx; centre.targety = centre.mapy; Iterator statIter = stations.values().iterator(); while(statIter.hasNext()) { Station s = (Station)statIter.next(); layout(s,centre); } } void layout(Station a, Station centre) { float ang = atan2(a.mapy - centre.mapy, a.mapx - centre.mapx); float rad = pixelsPerMinute * (float)a.timeToCentre; // todo: limit to min(width/2,height/2)? a.targetx = centre.targetx + (rad * cos(ang)); a.targety = centre.targety + (rad * sin(ang)); } // cribbed from here in double quick time: http://www.cs.cmu.edu/~crpalmer/sp/ void updateShortestPaths(Station centre) { Iterator statIter = stations.values().iterator(); while (statIter.hasNext()) { Station s = (Station)statIter.next(); s.timeToCentre = (s == centre) ? 0 : Integer.MAX_VALUE; s.pathParent = null; } Stack queue = new Stack(); queue.push(centre); while (queue.size() > 0) { Collections.sort(queue); Station v = (Station)queue.pop(); for (int i = 0; i < v.conns.size(); i++) { Connection c = (Connection)v.conns.get(i); Station u = (c.a == v) ? c.b : c.a; if (c.time + v.timeToCentre < u.timeToCentre) { u.pathParent = v; u.timeToCentre = c.time + v.timeToCentre; queue.push(u); } } } } void keyPressed() { if (key == ' ') { // mm.finish(); // Finish the movie if space bar is pressed! } }