Table data; Scale s1; Scale s2; // Daten in Processing Form: String[] laender = {}; float[] education = {}; float[] jobs = {}; float[] income = {}; float[] safety = {}; float[] health = {}; float[] environment = {}; float[] civicEngagement = {}; float[] accessibility = {}; float[] housing = {}; // setze den jeweiligen Eintrag in diesem Array // auf TRUE um einen Punkt selektiert zu zeichnen: boolean[] active; // Abstand von Graph-Rändern zum eigentlich verfügbaren Platz // (z.B. für Labels, Tickmarks) float abstand = 20; // Selektionsbox float boxX1, boxY1, boxX2, boxY2; void setup(){ size(900,420); // Daten laden data = loadTable("data.csv", "header"); // Übertrage die Daten in Arrays: // (mit dieser Schreibweise der for-Schleife durchläuft // Processing alle vorhanden Werte für data.rows()) for(TableRow row: data.rows()){ laender = append(laender, row.getString(0)); education = append(education, row.getFloat(1)); jobs = append(jobs, row.getFloat(2)); income = append(income, row.getFloat(3)); safety = append(safety, row.getFloat(4)); health = append(health, row.getFloat(5)); environment = append(environment, row.getFloat(6)); civicEngagement = append(civicEngagement, row.getFloat(7)); accessibility = append(accessibility, row.getFloat(8)); housing = append(housing, row.getFloat(9)); } active = new boolean[data.getRowCount()]; // Skalen initialisieren s1 = new Scale(); s1.calculateDataLimits(education, jobs); s1.calculateScreenLimits(30,10,360,360); s2 = new Scale(); s2.calculateDataLimits(income, safety); s2.calculateScreenLimits(440,10,360,360); // Highlighten: Datenpunkte lassen sich highlighten, indem // man den jeweiligen Wert von "active" auf true setzt. Um // z.B. das erste Land zu highlighten würde man folgende Zeile nutzen: //active[0] = true; // initialisiere Selektionsbox boxX1 = boxY1 = boxX2 = boxY2 = -1; } /* Klassen ("class") definieren zusammengehörige Variablen und Funktionen. Sie können wie normale Datentypen (float, int, etc.) verwendet werden. Zugriff auf Funktionen und Variablen erfolgt mit Punkt (also um z.B. auf den Wert von "minX" in einem "Scale"-Objekt s zuzugreifen, tippt man "s.minX". Funktionen aufrufen geht genauso: "s.myScale(x,y);") */ class Scale { float minX, minY, maxX, maxY; float screenMinX, screenMinY, screenMaxX, screenMaxY; // Findet die Minima und Maxima in den Daten und speichert // sie in den zugehörigen Variablen. void calculateDataLimits(float[] xData, float[] yData){ minX = 9999999; maxX = 0; minY = 9999999; maxY = 0; for(int i = 0; i < xData.length; i++){ if(xData[i] < minX) minX = xData[i]; if(xData[i] > maxX) maxX = xData[i]; if(yData[i] < minY) minY = yData[i]; if(yData[i] > maxY) maxY = yData[i]; } } // Berechnet den verfügbaren Bildschirmplatz für den Scatterplot. // Nimmt dabei als Parameter die x-Position (visX), die y-Position (visY), // die Breite (visWidth) und Höhe (visHeight) des Scatterplots. void calculateScreenLimits(float visX, float visY, float visWidth, float visHeight){ screenMinX = visX + abstand; screenMaxX = visX + visWidth - abstand; screenMinY = visY + abstand; screenMaxY = visY + visHeight - abstand; } // Berechnet die Bildschirmposition für die Werte x und y // Gibt sie als zweiteiliges Float-Array zurück (Position 0: x-Koordinate, // Position 1: y-Koordinate) float[] myScale(float x, float y){ float[] result = { ((x - minX) / (maxX-minX)) * (screenMaxX-screenMinX) + screenMinX, ((y - minY) / (maxY-minY)) * (screenMinY-screenMaxY) + screenMaxY }; return result; } } /* Zeichnet einen Scatterplot. Die meisten Information dafür zieht die Funktion aus dem übergebenen Parameter "s" vom Typ Scale. dataX sind die Daten in x-Richtung, dataY die Daten in y-Richtung. nameX ist der Name der für die x-Achse angezeigt werden soll, nameY ist der Name der für die y-Achse angezeigt werden soll. */ void drawScatterplot(float[] dataX, float[] dataY, Scale s, String nameX, String nameY){ // zeichne Punkte: strokeWeight(3); for(int i = 0; i < dataX.length; i++){ if(active[i]){ stroke(255,0,0); } else { stroke(0); } float[] coords = s.myScale(dataX[i], dataY[i]); point(coords[0], coords[1]); } // zeichne Skalen: stroke(128); strokeWeight(1); // horizontal line(s.screenMinX, s.screenMaxY, s.screenMaxX, s.screenMaxY); // vertikal line(s.screenMinX, s.screenMinY, s.screenMinX, s.screenMaxY); fill(0); // horizontale tick marks textAlign(CENTER, TOP); for(float i = s.minX; i <= s.maxX; i+= 1.0){ float[] coords = s.myScale(i,0); line(coords[0], s.screenMaxY - 5, coords[0], s.screenMaxY + 5); text(nf(i,1,1), coords[0], s.screenMaxY + 5); } // vertikale tick marks textAlign(RIGHT, CENTER); for(float i = s.minY; i <= s.maxY; i+= 1.0){ float[] coords = s.myScale(0,i); line(s.screenMinX - 5, coords[1], s.screenMinX + 5, coords[1] ); text(nf(i,1,1), s.screenMinX - 5, coords[1]); } // Dimensionsnamen // horizontal textAlign(CENTER, TOP); text(nameX, s.screenMinX + (s.screenMaxX - s.screenMinX)/2.0, s.screenMaxY + 20); // vertikal textAlign(CENTER, BOTTOM); // sollen vertikal gezeichnet werden, daher drehe das ganze Koordinatensystem: rotate(-PI/2); text(nameY,-s.screenMaxY + (s.screenMaxY - s.screenMinY)/2.0,s.screenMinX - 25); // und stelle das ursprüngliche Koordinatensystem wieder her: rotate(PI/2); } // Zeichne Selektionsbox: void drawSelectionBox(){ if(boxX1 > -1){ stroke(255); fill(#93CCEA, 128); rect(min(boxX1,boxX2), min(boxY1,boxY2), abs(boxX1-boxX2), abs(boxY1-boxY2)); } } // Selektionsbox: void mousePressed(){ boxX1 = boxX2 = mouseX; boxY1 = boxY2 = mouseY; } void mouseDragged(){ boxX2 = mouseX; boxY2 = mouseY; } void mouseReleased(){ boxX1 = boxY1 = boxX2 = boxY2 = -1; } void setPointsActive(float[] dataX, float[] dataY, Scale s){ for(int i = 0; i < dataX.length; i++){ float[] coords = s.myScale(dataX[i], dataY[i]); if(coords[0] >= min(boxX1, boxX2) && coords[0] <= min(boxX1, boxX2) + abs(boxX1 - boxX2) && coords[1] >= min(boxY1, boxY2) && coords[1] <= min(boxY1, boxY2) + abs(boxY1 - boxY2)){ active[i] = true; } } } void draw(){ background(255); // setze "active" Werte zurück (werden in setPointsActive neu gesetzt): for(int i = 0; i < active.length; i++){ active[i] = false; } setPointsActive(education, jobs, s1); setPointsActive(income, safety, s2); // zeichne Selektionsbox drawSelectionBox(); // zeichne ersten Scatterplot (education, jobs) drawScatterplot(education, jobs, s1, "Bildung", "Arbeitsmarkt"); // zeichne zweiten Scatterplot (income, safety) drawScatterplot(income, safety, s2, "Einkommen", "Sicherheit"); }