# Visualisierung interaktive Risikoleiter

## Entwicklungsumgebung

Um dieses Projekt zu ändern oder weiterzuentwickeln, werden [`nodejs`][nodejs] und `npm` benötigt. Die Abhängigkeiten werden wie üblich per `npm install` installiert.

Für die Entwicklung wurden Teile von ES6 verwendet und mit [`rollup`][rollup], [`babel`][babel] und [`postcss`][postcss] transpiliert. Die einzelnen Schritte des Build Prozesses sind in einfachen `npm` Scripts definiert.

`npm run watch` beispielsweis startet den Entwicklungsserver, transformiert Javascript und CSS Dateien bei Änderungen für das *Development* Target und lädt die Visualisierung im Browser.

Die folgenden Scripts sind in `package.json` definiert und können mit `npm run …` ausgeführt werden:

```
- prepare         Wird ausgeführt, nachdem `npm install` aufgerufen wurde. In diesem Schritt werden die statischen Abhängigkeiten erstellt.
- build           Generiert Javascript und CSS Dateien (einmalig) und kopiert alle generierten Dateien inklusive html-Dateien in das Verzeichnis `dist`.
- build:data      Transformiert die Rohdaten in eine JSON-Datei für die Visualisierung.
- build:html      Generiert HTML Dateien für Visualisierung und umgebenden Text.
- build:static    Generiert die von der Anwendung benötigten JSON- und HTML-Dateien.
- build:dev       Generiert Javascript und CSS Dateien für einen *Development Build*.
- build:prod      Generiert Javascript und CSS Dateien für einen *Production Build*.
- watch           Startet den Entwicklungsserver führt bei Änderungen einen Development Build durch.
```

In der Datei `rollup.config.js` werden die Konfigurationen für `rollup` und `postcss` dynamisch generiert. Für den *Development Build* werden SourceMaps erstellt, dagegen werden Javascript und CSS Dateien für den *Production Build* mit [`terser`][terser] bzw. [`cssnano`][cssnano] optimiert.

Im *prepare* Schritt werden die HTML-Dateien von einem nodejs Script für das entsprechende *Target* generiert. Die eigentliche Visualisierung wird hier exemplarisch per `iframe` in einer umgebenden Webseite eingebettet.

Der Rahmentext für diese Seite wird aus einem Markdown Dokument in HTML konvertiert. Der darin enthaltene `iframe` *Tag* wird dynamisch konfiguriert und das Ergebnis in einer HTML-Datei gespeichert.

Die Datei `config.json` enthält die Konfigurationen sowohl zum Generieren der HTML-Dateien als auch für die Erstellung der Javascript- und CSS-Dateien.

Nach dem Ausführen eines *Builds* sind alle Dateien, die für die Funktionalität der Website / Web App erforderlich sind im Verzeichnis `dist` zu finden:

```
├── index.html                            Diese HTML-Seite ist der Einstiegspunkt und wird vom Web-Server ausgeliefert
├── risk-ladder.html                      Die Visualisierung wird in einem iframe in der index.html eingebunden
├── css
│   ├── index.css                         Stylesheet für die umgebende Webseite
│   └── main[.min].css                    Stylesheet für die Visualisierung
├── data
│   ├── data_de.json                      Daten der Visualisierung (deutsch)
│   └── data_en.json                      Daten der Visualisierung (englisch)
├── fonts
│   ├── ASAP                              Fonts für die Visualisierung
│   ├── montserrat                        Font für Überschriften der umgebenden Seite (wird entfallen)
│   └── lora                              Font für den Fließtext der umgebenden Seite (wird entfallen)
├── img
│   ├── BMJV_Web_Master_de_WBZ.png        BMJV Logo
│   ├── HardingCenter_Logo_de_RGB.png     Harding Zentrum Logo
│   └── mpib.png                          Max-Planck Institut fuur Bildungsforschung Logo
└── js
    ├── main[.min].js                     Javascript der Visualisierung
    ├── [main.js.map]                     Javascript Sourcemap (wenn ein Development Build durchgeführt wurde)
    ├── blob-stream.min.js                Wird von pdfkit benötigt
    └── pdfkit.min.js                     Fettleibige Bibliothek zum generieren von PDF Dateien im Browser  
```

Wird ein Webserver konfiguriert, dieses Verzeichnis auszuliefern, sollte die Visualisierung enstsprechend erreichbar und funktional sein.

## Verzeichnisstruktur

```
├── README.md
├── assets
│   ├── css
│   │   └── index.css                               // Stildefinitionen der umgebenden HTML Seite
│   ├── fonts
│   │   ├── Asap-Bold.ttf
│   │   ├── Asap-Italic.ttf
│   │   ├── Asap-Regular.ttf
│   │   ├── AsapCondensed-Regular.ttf
│   │   ├── lora
│   │   │   ├── Lora-Regular.ttf
│   │   │   └── SIL\ Open\ Font\ License.txt
│   │   └── montserrat
│   │       ├── Montserrat-SemiBold.otf
│   │       └── SIL\ Open\ Font\ License.txt
│   ├── img                                         // Logos für die umgebende HTML Seite
│   │   ├── BMJV_Web_Master_de_WBZ.png
│   │   ├── HardingCenter_Logo_de_RGB.png
│   │   ├── jobloss.png
│   │   └── mpib.png
│   └── js                                          // Externe Javascript Bibliotheken
│       ├── blob-stream.js
│       ├── blob-stream.min.js
│       ├── pdfkit.js
│       └── pdfkit.min.js
│   ├── prepareData.js                              // Generiert json Datei aus Rohdaten
│   └── preprocess.js                               // Generiert HTML Dateien für Visualisierung und umgebendes HTML
├── build.json                                      // Build Konfiguration
├── data                                            // Rohdaten
│   ├── data_de.json
│   ├── data_en.json
│   ├── data_old.json
│   ├── original
│   │   ├── RA_web_demorisiken_3d_Leiter.xls
│   │   └── ra_da_risikostrasse.xlsx
│   └── risk-ladder.tsv
├── package.json
├── rollup.config.js                                // Build Prozess
└── src
    ├── css
    │   └── main.css                                // Stildefinitionen für Visualisierung
    │   └── open-iconic -> ../../_resources/open-iconic
    ├── html
    │   ├── markdown
    │   │   └── index.md                            // Markdown für Kontextinformationen des umgebenden HTML
    │   └── skeletons
    │       ├── main.html                           // Template für umgebendes HTML
    │       └── visualisation.html                  // Template für Visualisierung
    └── js
        ├── configuration.js                        // Konfiguration der Visualisierung
        ├── controller.js                           // Controller für die Koordination der Komponenten der Visualisierung
        ├── d3-custom.js                            // Definition der verwendeten Module von d3
        ├── environment.js                          // Implementierung der Hintergrundgrafiken
        ├── infoView.js                             // Darstellung der Informationen zu einzelnen Einträgen
        ├── logos                                   // Definitionen der Logos als Javascript Objekte für Einbindung als Vektorgrafiken im PDF
        │   ├── BMJV_logo.js
        │   ├── RA_logo_1280.js
        │   └── hc_logo.js
        ├── main.js                                 // Einstiegspunkt für Implementierung der Visualisierung
        ├── pdfExport.js                            // Generieren des PDF
        ├── progressBar.js                          // Darstellung des Fortschrittsbalkens
        ├── referenceClass.js                       // Label für Referenzklasse
        ├── scrollView.js                           // Darstellung der Elemente in der Pseudo-3D Ansicht
        ├── selectionList.js                        // Implementierung der Auswahlliste
        ├── utilities.js                            // Zeuch halt
        └── zoomButtons.js                          // Implementierung der Zoom Buttons
```

## Wie ändere ich Inhalte und Daten?

### Texte und Labels

Texte und Labels können in der Datei `config.json` geändert werden. Dort ist sowohl der Text des Eingabefeldes der Suche, als auch die Texte zu den Detailinformationen und des PDFs definiert.

### Daten

Für diese Visualisierung sind die hierarchischen Rohdaten in der Datei `data/jobloss_[de|en].tsv` definiert. Bevor sie in der Visualisierung verwendet werden können, müssen sie in eine JSON Datei transformiert werden. Dies geschieht im Schrit `build:data` des Build Prozesses. Bei Beibehaltung der Struktur der Daten können diese so beliebig ausgetauscht werden. Alle Bezeichner der interaktiven Visualisierung werden aus diesen Daten gelesen.

## Generelle Architektur

[d3-inspirierte modulare Architektur][d3reusable] mit Abweichungen

Konstanten sind ausgelagert in eine Konfigurationsklasse (muss das überhaupt eine Klasse sein?).

Das grafische Interface wurde in folgende Module aufgeteilt:

**infoView.js**  
Beim Klick auf einen Eintrag in der Pseudo-3D-Ansicht werden Detailinformationen zum entsprechenden Ereignis in einem Fenster am Horizont dargestellt. Diese Darstellung ist hier definiert.

**selectionList.js**  
Die Lesezeichenliste enthält initial fünf zufällig ausgewählte Ereignisse. Durch Klick auf ein Ereignis wird zu diesem gefahren. Ereignisse können aus der Liste durch Klick auf den Entfernen-Button gelöscht werden. Wird ein Kategorie-Filter ausgewählt, werden dadurch Einträge für ausgeblendete Ereignisse deaktiviert.

**progressBar.js**  
Fortschrittsbalken, der anzeigt, wieviel des Wegs noch vor einem liegt.

**referenceClass.js**  
Dynamisches Label für die jeweilige Referenzklasse

**scrollView.js**  
Die Implementierung der interaktiven Pseudo-3D Ansicht des Balkendiagramms der Ereigniswahrscheinlichkeiten ist hier definiert. Beim Klick auf den Balken werden Detailinformationen zum Ereignis angezeigt, der Klick auf das Lesezeichen-Symbol fügt eine Referenz auf das Ereignis der Auswahlliste hinzu bzw. entfernt sie aus dieser.

**zoomButtons.js**  
Zoom-Buttons auf dem Fortschrittsbalken zum Springen zu vorherigen bzw. folgenden Ereignissen.

**environment.js**  
Hintergrund mit Himmel und Straße mit mitlaufenden Straßenmarkierungen.

Einstiegspunkt der Visualisierung ist die Datei `main.js`, in der die Events der HTML-Formularelemente behandelt werden. Hier wird auch der Controller erzeugt, der die einzelnen (interaktiven) SVG Interface-Elemente erstellt und den Informationsfluss kontolliert.

## PDF Export

Zum PDF export werden für eine zur Visualisierung passende Darstellung binäre Fonts geladen. Zum dynamischen Landen von Resourcen wird der `fetch` *Polyfill* [`whatwg-fetch`][whatwg-fetch] verwendet.

Das PDF wird mit [`pdfkit.js`][pdfkit] erstellt, das außerdem [`blob-stream`][blob-stream] erfordert.

Um eine Ähnlichkeit zur Visualisierung zu gewähren, aber gleichzeitig den üppiger vorhandenen Platz eines A5 Landscape zu nutzen, wird anstatt der ASAP Condensed die ASAP eingesetzt.

Nachdem `pdfkit` sehr gewichtig ist (1.8 MB) und sich die npm Version nicht problemlos kompilieren lässt (Informationen dazu fehlen komplett), werden `pdfkit` und `blob-stream` in der `index.html` per `<script>` *Tag* geladen.
Das initiale Laden der Visualisierung wird dadurch natürlich verlangsamt. `pdfkit` befindet sich im Umbau und wird auf ES6 portiert, es sieht aber nicht so aus, als ob die Bibliothek dadurch modularer oder kleiner werden würde. Stattdessen sind im Build für Browser 1 MB hinzugekommen. Abhilfe würde die Auslagerung des Generierens des PDFs auf den Server schaffen, oder zumindest das asynchrone Laden der Bibliothek.

## Implementierung der Pseudo 3D-Ansicht

Bei der initialen herausgezoomten Ansicht wird die minimale Skalierung angewendet, so dass der größte Wert am unteren Ende der Straße in maximaler Größe dargestellt wird. Alle anderen Ereignisse befinden sich nach hinten in die Bildebene gestaffelt. Die maximale Skalierung (maximaler *Zoom*) wird so berechnet, dass das unwahrscheinlichste Ereignis am unteren Bildrand maximiert angezeigt wird. Alle anderen Einträge befinden sich dann für den Betrachter hinter sich, jenseits der *Near-Clipping Plane*.

Die pseudo-Perspektive basiert rein grafisch auf dem Dreieck, das die Straße darstellt. Interagiert wird per *natürlichem Scrolling*, das durch einen überdimensionierten HTML `<div>` im Hintergrund erzwungen wird. Die Scrollposition dient als Interpolationsfaktor zwischen den jeweils maximalen bzw. minimalen Skalierungsfaktoren. Ist der Scrollbar am oberen Fensterende, so ist die minimale Skalierung effektiv, wenn er am unteren Ende steht, wird die maximale Skalierung angewendet.

Um diese Werte auf die Straße zu übertragen, werden sie von der Höhe des Scrollbereiches auf die Höhe des Straßendreiecks projiziert.

## Icons

Die verwendeten Icons ("bookmark", "circle-x", "data-transfer-upload", "data-transfer-download") stammen von [OpenIconic][openiconic]. Sie sind im HTML Template der Visualisierung jeweils als SVG `<symbol>` definiert und werden per `#use` Direktive in den entsprechenden d3 Modulen `scrollView.js` und `zoomButtons.js` referenziert.

## Notizen und Bemerkungen

Die Skalierungen wurden größtenteils unter Nichtbeachtung der dafür vorgesehenen *scale*-Funktionen von d3 implementiert. In zukünftige Versionen sollte dies bereinigt werden.

- [computed width and height for arbitrary element][stack:dimensions]

[nodejs]: https://nodejs.org/
[rollup]: https://rollupjs.org/
[babel]: https://babeljs.io/
[postcss]: https://postcss.org
[terser]: https://terser.org
[cssnano]: https://cssnano.co

[babel-polyfill]: https://babeljs.io/docs/usage/polyfill/
[blob-stream]: https://github.com/devongovett/blob-stream
[d3reusable]: https://bost.ocks.org/mike/chart/
[openiconic]: https://useiconic.com/open
[pdfkit]: https://pdfkit.org
[stack:dimensions]: https://stackoverflow.com/questions/21990857/d3-js-how-to-get-the-computed-width-and-height-for-an-arbitrary-element#21991405
[whatwg-fetch]: https://github.com/github/fetch
