Tutorial Visualisierung

In diesem Tutorial werde ich Schritt für Schritt erklären, wie wir basierend aus den Daten zur Politisch motivierten Kriminalität, die im jährlichen Bericht der Berliner Polizei als PDF veröffentlicht werden, eine einfache Visualisierung, basierend auf HTML5 und Javascript erstellt haben.

Kriminalitätsstatistiken für die Jahre 2002 bis 2012

W3Schools – HTML

W3Schools – Javascript

 

1. Schritt: Übertragung der Daten in ein Google Spreadsheet.

Als ersten Schritt müssen die Daten aus dem PDF ausgelesen werden und in ein maschinenlesbares Format übertragen werden. PDF ist ein proprietäres binär gespeichertes Format. Das heißt, dass sich seine Inhalte nicht im Textformat auslesen lassen. Insofern können wir keine Javascript-Funktion schreiben, welche direkt die Werte aus den PDF-Berichten ausliest.

Die Daten müssen zur Verarbeitung in Javascript am besten im JSON-Format vorliegen. JSON steht für Javascript Object Notation und ist im Grunde genommen ein Datenformat, welches Objekte durch eine Liste von Key-Value-Paaren beschreibt (wobei ein Wert auch ein neues Objekt sein kann, Verschachtelung ist also möglich). Hier ist ein Beispiel wie ein JSON-Objekt, welches sich für unsere Visualisierung gut eignen würde, aufgebaut sein könnte:

{
“Marzahn” : {
“2004” : {
“links” : 23,
“rechts” : 32,
“pmak” : 13
},
“2005” : {
“links”: 32,
[...]
},
[...]
},
“Reinickendorf” : {
[...]
},
[...]
}

 

Die einfachste Herangehensweise wäre gewesen, die Daten von den PDFs direkt in dieser Form in eine Javascript-Datei zu speichern. Allerdings ist die Pflege der Daten im JSON-Format nicht so komfortabel. Daher haben wir uns für ein Google Spreadsheet entschieden, um die Daten dynamisch laden zu können, da Google eine JSON-API zum Abruf der Spreadsheet-Inhalte bereitstellt (dazu später mehr). Das bietet später den Vorteil, dass man die Daten bei eventuellen Änderungen leicht in der Tabellenansicht anpassen kann, und sie auch leichter erweitern und konfigurieren kann.

Da die Menge der Daten überschaubar ist, haben wir sie manuell übertragen.

 

https://docs.google.com/spreadsheet/ccc?key=0AkZ-n2ZLLLWydE9USWJTWkU1UXc2TEM4S0p4LTB1ZVE#gid=0

 

Im Dokument haben wir drei Sheets für die jeweiligen Kategorien angelegt (“Rechts”, “Links” und “PMAK”, politisch motivierte Ausländer-Kriminalität). In den Tabellen haben wir in den Zeilen die Bezirke und spaltenweise die Jahreswerte eingetragen:

2. Schritt: Abruf der Daten aus dem Spreadsheet via Javascript

 

Google bietet eine HTTP-basierte API zum Abruf der Inhalte eines Spreadsheets an (siehe https://developers.google.com/google-apps/spreadsheets/#spreadsheets_api_urls_visibilities_and_projections).

Das bedeutet, dass man durch den Aufruf folgender URL die Inhalte des Spreadsheets in maschinenlesbarer Form abrufen kann:

 

https://spreadsheets.google.com/feeds/list/key/sheetId/public/basic

 

Damit das funktioniert, muss das Spreadsheet öffentlich gemacht sein. Dies erreicht man über das “File”-Menü und den Unterpunkt “Publish to the Web”. Per Klick auf “Start publishing” unter Auswahl des gewünschten Sheets bzw. aller Sheets, wird das Dokument über die API öffentlich zugänglich gemacht.

 

Als “key” muss man einen generierten Schlüsselwert über den das jeweilige Spreadsheet erreichbar ist eingeben. In unserem Fall ist das 0AkZ-n2ZLLLWydE9USWJTWkU1UXc2TEM4S0p4LTB1ZVE. Den Key zu einem Spreadsheet kann man in Erfahrung bringen, indem man im geöffneten Spreadsheet im “File”-Menü den Punkt “Share” auswählt. Im Fenster das sich nun öffnet, ist eine URL zum Zugriff auf das Spreadsheet angegeben, welches den Key enthält. Wahlweise ist der Key auch im “Publish to the Web”-Dialog in der URL unter “Get a Link to the published data” zu finden.

Der Abschnitt “sheetId” der URL muss durch den Index des jeweiligen Sheets innerhalb des Dokuments ersetzt werden. Wir haben drei Sheets (“Rechts”, “Links”, “PMAK”), deshalb müssen wir die URL drei mal aufrufen, jeweils mit den Sheet-IDs 1, 2 und 3. Wichtig ist hierbei, dass die Indizes mit 1 anfangen (nicht mit 0, wie in der Programmierung üblich).

 

Die Google API unterstützt zwei Datenformate zur Formatierung der Daten: XML und JSON. Als Standard wird XML zurückgegeben. Um die Daten in JSON zurückgegeben zu bekommen, muss man noch den Parameter alt=json an die URL anhängen. Unsere URL sieht bis jetzt folgendermaßen aus:

https://spreadsheets.google.com/feeds/list/0AkZ-n2ZLLLWydE9USWJTWkU1UXc2TEM4S0p4LTB1ZVE/1/public/basic?alt=json

 

Wenn man diese URL im Browser öffnet, sieht man die Daten des Spreadsheets im JSON-Format. Für Chrome gibt es die Erweiterung JSON View, die das JSON-Format in etwas strukturierterer Form darstellt, da bei der Auslieferung Zeilenumbrüche und Leerzeichen gespart werden. In JSON View sieht man die einzelnen Zeilen der Tabelle als JSON-Objekt in folgender Form:

{
id: { [...] },
[...]
title: {
type: "text",
$t: "Charlottenburg-Wilmersdorf"
},
content: {
type: "text",
$t: "a2006: 104, a2007: 87, a2008: 10, a2009: 2, a2010: 87, a2011: 90, a2012: 71"
},
[...]
}

An dieser Stelle liegt auch der Grund dafür, dass den Jahreszahlen jeweils ein “a” vorangestellt ist. Tut man das nicht, generiert die API merkwürdige Zufallswerte als Schlüssel für die Spalten, da reine Zahlen nicht als Spaltenüberschrift verstanden werden. Somit wäre die Zuordnung zu den jeweiligen Jahreszahlen nicht mehr dynamisch möglich.

 

Die Javascript-Funktion, die die Spreadsheets abruft, heißt getSheet() und befindet sich in der Datei spreadsheet_script.js. Da die von Google bereitgestellte JSON-Struktur etwas zu komplex ist, parsen wir sie mit Hilfe der Funktion handleResponse. Deswegen wird in der Funktion getSheet() auch der Parameter callback=handleResponse an die URL angehangen. Dadurch überführen wir die Daten eines Sheets in ein etwas simpleres Format und fügen es als Objekt dem Array spreadsheetData zu.

Nachdem die Methode für jedes Sheet aufgerufen wurde, enthält spreadsheetData für jedes Sheet ein Objekt das folgendermaßen aufgebaut ist:

{
title : "links"
rows : [
{
title : "Marzahn",
data : {
2005 : 123,
[...]
},
[...]
]
}

Weiterhin sind in der Datei spreadsheet_script.js noch Funktionen, um einzelne Daten aus dieser Struktur auszulesen (z.B. getSheetByTitle, getRowByTitleFromSheet, getYearValuesForBezirk).

 

3. Schritt: Visualisierung der Daten in SVG mit d3js

Die HTML-Seite zur Visualisierung ist an sich recht einfach aufgebaut. Die Karte von Berlin ist als SVG-Datei eingebettet und fungiert sozusagen als Hintergrund für die eigentlichen Balkendiagramme, die als Overlay über der Karte angezeigt werden. Die Karte ist als SVG-Datei auf der Seite der Piratenpartei freigegeben (http://wiki.piratenpartei.de/Datei:Berliner_Bezirke.svg). Der Slider zur Auswahl des Jahres ist als fertige Komponente eingebunden, und liefert die Möglichkeit, die Auswahl eines neuen Jahres (also das Betätigen des Sliders) als Event abzufangen. Ändert sich der Wert des Sliders (durch Benutzereingaben) so wird die Javascript-Funktion yearSelected() mit dem neu ausgewählten Jahr als Parameter aufgerufen. Die Funktion kümmert sich dann um das Update der Darstellung.

 

d3.js

 

d3.js ist eine Javascript-Library, die die Daten-basierte Manipulation von HTML und SVG-Dokumenten vereinfacht. Wir benutzen sie um die Balken über der Karte als Overlay einzublenden. Das Konzept von d3.js ist dabei etwas gewöhnungsbedürftig.

Grundlegend verwaltet d3.js die Darstellung immer auf Basis einer Datenquelle, in unserem Fall die Variable yearData. In dieser Variablen stellen wir ein Array zusammen, das die kompletten Details zur Darstellung der Balken enthält: Koordinaten, den eigentlichen Wert an sich (der nachher die Höhe des Balken bestimmt), einen String, der zusätzliche Style-Angaben (die Farbe, damit die einzelnen Balken “rechts”, “links” und “PMAK” nachher unterscheidbar sind), und den Namen des Bezirks.

 

yearData.push(new Array(koordinaten.x - 30, koordinaten.y,
values.links, "fill:red;", bezirk));
yearData.push(new Array(koordinaten.x, koordinaten.y, values.rechts,
"fill:black;", bezirk));
yearData.push(new Array(koordinaten.x + 30, koordinaten.y,
values.pmak, "fill:green;", bezirk));

Diese Daten werden dann über d3.js als Balken in einem SVG-Element angezeigt.

 

var rect = svg.selectAll("rect").data(yearData);
[...]
var rectEnter = rect.enter().append("rect");
rectEnter.attr("y", function(d){
return d[1] - d[2]
}).attr("x", function(d) {
return d[0];
}).attr("height", function(d) {
return d[2];
}).attr("width", 30)
.attr("style", function(d){
var additionalStyle;
if(selectedBezirk == ""){
additionalStyle = "fill-opacity:0.5"
}
else if (d[4] == selectedBezirk){
additionalStyle = "fill-opacity:1.0"
} else{
additionalStyle = "fill-opacity:0.2"
}
return d[3] + additionalStyle;
});

Über die selectAll(“rect”)-Methode bekommt man die Menge aller “rect”-Elemente (die Balken unserer Visualisierung). Über die data()-Methode wird diese Menge an das yearData-Array als Datenquelle gebunden. Die enter()-Methode gibt die Menge aller Datenelemente zurück, zu denen noch kein Element in der gebundenen Menge (noch kein “rect”-Element) existiert. Das ist also in erster Linie zur Initialisierung der Balken notwendig, später werden diese nur noch geupdatet. Über den append(“rect”) Befehl und die darauf angewandten attr()-Methoden wird für jedes noch nicht gebundene Datenelement ein Balken, mit den definierten Attributen, hinzugefügt (mehr dazu: SVG-rect Element) . Weiterhin findet man im “style”-Attribut noch eine Fallunterscheidung: Wenn kein Bezirk ausgewählt ist, sollen alle Balken mit halber Deckkraft (transparent)  angezeigt werden. Ist ein Bezirk ausgewählt, so soll der ausgewählte mit voller, die anderen mit 20-prozentiger Deckkraft angezeigt werden.

Die Attribut-Werte werden aus den in yearData vorhandenen Arrays genommen, die jeweils einen Balken beschreiben.

 

Zum Update der Balken benötigt d3.js noch einen etwas redundaten Aufruf:

rect.transition()
.duration(500).attr("y", function(d){
return d[1] - d[2]
}
).attr("x", function(d) {
return d[0];
}).attr("height", function(d) {
return d[2];
}).attr("width", 30)
.attr("style", function(d){
var additionalStyle;
if(selectedBezirk == ""){
additionalStyle = "fill-opacity:0.5"
}
else if (d[4] == selectedBezirk){
additionalStyle = "fill-opacity:1.0"
} else{
additionalStyle = "fill-opacity:0.2"
}
return d[3] + additionalStyle;
});

 

Die Aufrufe transition() und duration(500) bewirken, dass die Veränderung als Animation dargestellt wird, die 500 ms dauern (Balken schrumpfen/wachsen). Die wiederholten attr()-Aufrufe müssen sein, um dem System zu signalisieren, dass sich die zugrundeliegenden Daten geändert haben und die Attribut-Werte dementsprechend geupdatet werden sollen.

Die Darstellung der Text-Elemente (Wert-Beschriftung der Balken) funktioniert äquivalent zu den oben beschriebenen Rect-Elementen.

 

Durch die Visualisierung werden Unterschiede zwischen den Jahren wesentlich deutlicher als beim bloßen Betrachten der Werte in den Tabellen. Durch die Animation der Balken, wodurch sie beim Wechseln zwischen den Jahren wachsen bzw. schrumpfen, werden die Veränderungen noch offensichtlicher. Durch die Anzeige der Gesamtwerte sollen eventuelle Irritationen durch einzelne Ausreißer vermieden werden: Zum Beispiel sticht im Jahr 2010 ein sehr hoher Wert der linken Kriminalität in Friedrichshain-Kreuzberg auf den ersten Blick stark hervor, allerdings ist der Gesamtwert der rechten Kriminalität in diesem Jahr immer noch bemerkenswert höher.

 

One thought on “Tutorial Visualisierung

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

CAPTCHA-Bild

*