//import processing.opengl.*;
/**
*
Hidden features
- s: Sommerverbindungen
- w: Winterverbindungen
- g: Grundkarte
- (p: Speichern als pdf)
*/
OSMMercator mercator;
float pixelsPerMinute;
PFont scaleFont;
PFont headerFont;
PFont labelFont;
Hashtable stations = new Hashtable();
Hashtable connections = new Hashtable();
Hashtable routes = new Hashtable();
boolean showTimeBands = false;
Station selected = null;
Station ellipseCentre = null;
PImage ellipses = null;
boolean record = false;
String jahreszeit;
void setup() {
size(1000,800);
//size(screen.width,screen.height,OPENGL);
//size(screen.width,screen.height);
// pixelsPerMinute = 5.0; // JAVA2D, 800, 500
pixelsPerMinute = width/500.0;
smooth(); // strange things happen with GL ellipses?
// scaleFont = loadFont("CenturyGothic-10b.vlw"); // limited chars
headerFont = loadFont("GillSansMT-16.vlw"); //loadFont("CenturyGothic-24.vlw");
scaleFont = labelFont = loadFont("GillSansMT-10.vlw");
readAll(1);
setupPart2();
}
void readAll(int som_win) {
parseStations("stationsTI_unicode.csv");
if (som_win == 2) {
parseConnections("linesTI_Wi.csv");
parseRoutes("routesTI_Wi.csv");
jahreszeit = "Winter";
}
else {
parseConnections("linesTI.csv");
parseRoutes("routesTI.csv");
jahreszeit = "Sommer";
}
}
void setupPart2() {
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);
float scale = min((float) (maxLon-minLon)/width, (float) (maxLat-minLat)/height);
mercator = new OSMMercator((minLat+maxLat)/2.0, (minLon+maxLon)/2.0, 1.5*scale, width, 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() {
if (record) {
hint(ENABLE_NATIVE_FONTS);
beginRecord(PDF, "output_airolo2.pdf");
}
background(255);
smooth();
if (showTimeBands) {
// if (ellipses != null) {
// image(ellipses,0,0);
// }
// else {
ellipseMode(CENTER);
for (float r = max(width,height), i=0; r > 0; r -= pixelsPerMinute*20, i++) {
fill(i%2==0?233:255);
noStroke();
ellipse(ellipseCentre.targetx,ellipseCentre.targety,2*r,2*r);
}
// ellipses = get(0,0,width,height);
// }
}
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();
//TODO brauchts das?
ellipse(c.a.screenx,c.a.screeny,10.0,10.0);
ellipse(c.b.screenx,c.b.screeny,10.0,10.0);
// von uns
stroke(255,255,0);
fill(255,255,0);
String label = c.a.name;
textFont(labelFont,9);
textAlign(CENTER);
rectMode(CENTER);
fill(200);
noStroke();
triangle(c.a.screenx,c.a.screeny,c.a.screenx+5,c.a.screeny-6,c.a.screenx+5,c.a.screeny+6);
fill(255);
stroke(200);
rect(c.a.screenx+(textWidth(label)/2)+8,c.a.screeny,textWidth(label)+4,12);
fill(0);
text(label,c.a.screenx+(textWidth(label)/2)+8,c.a.screeny+4);
//
}
}
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.colour != 0) {
stroke(r.colour);
strokeWeight(1.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,15.0,15.0);
String label = selected.name;
if (selected.timeToCentre != 0 && showTimeBands) {
label += " (Reisezeit: " + selected.timeToCentre + " min.)";
}
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,12);
fill(0);
text(label,selected.screenx+(textWidth(label)/2)+8,selected.screeny+4);
}
stroke(0);
strokeWeight(1.0);
pushMatrix();
translate(width/20,height-(width/20));
textAlign(CENTER);
textFont(scaleFont,10
);
fill(0);
if (showTimeBands) {
line(0,0,0,5);
text("0",0,15);
line(20*pixelsPerMinute,0,20*pixelsPerMinute,5);
text("20",20*pixelsPerMinute,15);
line(40*pixelsPerMinute,0,40*pixelsPerMinute,5);
text("40",40*pixelsPerMinute,15);
line(0,5,40*pixelsPerMinute,5);
text("min",2*pixelsPerMinute,25);
}
else {
line(0.0,0.0,0.0,5.0);
text("0",0,15);
line(10.0/(float)mercator.kilometerinpixels(),0.0,10.0/(float)mercator.kilometerinpixels(),5.0);
text("10",10/(float)mercator.kilometerinpixels(),15);
line(20.0/(float)mercator.kilometerinpixels(),0.0,20.0/(float)mercator.kilometerinpixels(),5.0);
text("20",20/(float)mercator.kilometerinpixels(),15);
line(0.0,5.0,20.0/(float)mercator.kilometerinpixels(),5.0);
text("km",2.0/(float)mercator.kilometerinpixels(),25);
}
popMatrix();
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();
ellipse(c.a.screenx,c.a.screeny,10.0,10.0);
ellipse(c.b.screenx,c.b.screeny,10.0,10.0);
// von uns
writeStation(c.a);
writeStation(c.b);
}
}
textAlign(CENTER);
textFont(headerFont,16);
if (showTimeBands) {
text("Reisezeiten von " + ellipseCentre.name+ " (" + jahreszeit + ")",width/2,width/20);
}
else {
text("San Gottardo: Klick auf einen Ort fŸr die Reisezeit!",width/5,width/20);
textFont(headerFont,16);
textAlign(LEFT);
text("w = Winter",width/20,width/12);
text("s = Sommer",width/20,width/10);
text("g = reale Distanzen",width/20,width/8.5);
}
if (record) {
endRecord();
record = false;
}
}
void mouseReleased() {
if (selected != null) {
layoutAbout(selected);
}
}
void keyPressed() {
int nrSel = 0;
if (keyPressed && (key == 'g' || key == 'G')) {
layoutAbout(0);
return;
}
if (keyPressed && (key == 'w' || key == 'W' || key == 's' || key == 'S')) {
if (selected != null) {
nrSel = selected.id;
}
connections.clear();
stations.clear();
routes.clear();
if (keyPressed && (key == 's' || key == 'S')) readAll(1);
else readAll(2);
setupPart2();
showTimeBands = false;
layoutAbout(nrSel);
}
if (keyPressed && (key == 'p' || key == 'P')) {
record = true;
}
}
color stringToColor(String string) {
if (!string.equals("NULL")) {
return unhex("FF"+string.substring(0,6));
}
else {
return 0;
}
}
color stringToColor0(String string) {
println(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 writeStation(Station x) {
stroke(255,255,0);
fill(255,255,0);
String label = x.name;
textFont(labelFont,10);
textAlign(CENTER);
rectMode(CENTER);
fill(200);
noStroke();
triangle(x.screenx,x.screeny,x.screenx+5,x.screeny-6,x.screenx+5,x.screeny+6);
fill(255);
stroke(200);
rect(x.screenx+(textWidth(label)/2)+8,x.screeny,textWidth(label)+4,12);
fill(0);
text(label,x.screenx+(textWidth(label)/2)+8,x.screeny+4);
if (x.rail == 5) {
fill(255);
stroke(255,0,0);
ellipse(x.screenx,x.screeny,10.0,10.0);
}
}