Geografische Illustrationen für Ebooks mit Vega-Lite

Werkzeuge zur Datenvisualisierung können viel, hier möchte ich aber nur einen bestimmten Anwendungsfall betrachten: eine vorhandene Weltkarte mit Routen nachzubauen, ergo die Visualisierung geografischer Daten.

Ein bekannter Kandidat für Datenvisualisierung im Web ist d3.js. d3.js gilt als low-level Javascript-Framework für Datenvisualisierung. Es ist primär dafür gemacht, in Webseiten eingebettet zu werden, und umfasst alle möglichen Arten von Diagramme, darunter auch geografische, Karten.

Schaut man etwas weiter, um nicht gleich direkt in die Javascript-Programmierung einsteigen zu müssen, findet man die Frameworks Vega und Vega-Lite. Die Vega-Familie baut auf d3.js auf und bietet die Möglichkeit, Diagramme mittels einer Visualisierungs-Grammatik zu definieren, und dadurch gleichzeitig zu erstellen. Wie der Namen schon andeutet: Vega-Lite konzentriert sich darauf, die Diagrammerstellung möglichst einfach zu machen, mit Vega selbst hat man etwas mehr Möglichkeiten, aber dafür auch mehr Arbeit.

Die mit beiden Lösungen erstellten Visualisierungen können für die interaktive Nutzung in Webseiten eingebettet werden, aber auch mit einfach als SVG- bzw. PNG-Dateien abgespeichert werden. Somit hätte man im Kontext der digitalen Bücher beide Bereiche des möglichen Konsums abgedeckt: eine interaktive Variante für die HTML-Fassung des Buchs, und Grafikdateien für E-Reader, die mit Javascript überfordert wären.

Auf welcher Ebene man mit der Visualisierung ansetzt, hängt von den Bedürfnissen ab. d3.js bietet einem natürlich alle Möglichkeiten, und für geografische Visualisierungen gibt es viele Varianten und Möglichkeiten. Vega/Vega-Lite nutzen zwar viel davon, aber nicht alles, wodurch der Spielraum eingeengt wird. Dafür muss man nicht gleich in den Code einsteigen. Zunächst habe ich Vega -Lite ausprobiert.

Diagramm-Definitionen für Vega-Lite sind JSON-Dokumente. Man könnte diese natürlich mit einem beliebigen Editor erstellen und die Ergebnisse dann mittels der mitgelieferten Generatoren erstellen lassen. Wesentlich einfacher ist es aber, die Diagramme mit dem interaktiven Editor des Projekts zu erstellen.

Ausprobieren kann man das mit der öffentlichen Instanz des Editors, die bei https://vega.github.io/editor/#/custom/vega-lite zu erreichen ist. Hier gibt man einfach die Definitionen ein und kann sofort das Ergebnis sehen. Hat man daran Gefallen gefunden, kann man sich den Editor auch auf der eigenen Maschine installieren. Die Vorgehensweise ist hier beschrieben.

Vorsicht! Will man eigene Daten über Dateien laden, sollte man die im Ordner public/data ablegen, nur dann werden sie vom Editor gefunden. Es gibt auch andere data Ordner, die nicht zum beabsichtigten Ergebnis führen.

 

Der Vega-Lite Editor

Der Vega-Lite Editor

Um eine geografische Visualisierung zu erstellen, benötigt man als erstens eine Karte. Mitgeliefert wird dafür die Datei data/world-110m.json. Mit ihr lässt sich eine Weltkarte als Basis erstellen.

Und schon hier, beim ersten Schritt, gibt es etwas zu beachten. Die Datei world-110.jsonenthält Geodaten für eine aktuelle Weltkarte, dass heißt, dass die Länder ihre aktuellen Grenzen haben. Möchte man nun eine Visualisierung eines historischen Stands machen, kann man mit diesen Grenzen nichts anfangen.

Benötigt man unbedingt historische Karten, muss man auf selbst die Suche gehen. Aber darauf möchte ich hier nicht weiter eingehen. Das ist Stoff eines weiteren Artikels.

Für meine historische Karte benötige ich nicht unbedingt Ländergrenzen, daher kann ich den einfachen Weg wählen und die Visualisierung der Ländergrenzen abschalten. Das geht über die Konfiguration des Felds features. Statt des Wertes countries sollte ich einfach den Wert land wählen können, um nur die Umrisse der Erdteile und Inseln angezeigt zu bekommen.

Leider aber funktioniert das mit dieser Kartendatei und dem Vega-Lite Editor (Stand Februar 2018) nicht. Statt der Weltkarte bekomme ich einfach nichts angezeigt. Das liegt nicht an der Datei, sondern an irgendeinem Problem im Editor.

Lösungen dafür gibt es zwei:

  1. man belässt die Einstellung auf countries und löscht die Umrisslinien indem man ihnen die gleiche Farbe gibt wie der Füllung, in diesem Falle also lightgray. Man verzichtet dadurch auf prägnante Umrisslinien.
  2. man nimmt eine andere Kartendatei, zum Beispiel TopoJSON World Atlas. Mit dieser Datei funktioniert die Einstellung land im Editor, und man kann auch die Umrisslinien nach Herzenslust gestalten

Hat man die Karte wie gewünscht konfiguriert, kann man sich über die 3D-Projektion Gedanken machen. Die hier verwendete Projektion equirectangular habe ich gewählt, um eine möglichst schmale, über die Seitenreite gehende Darstellung zu bekommen.

"$schema": "https://vega.github.io/schema/vega-lite/v2.json",
"width": 800,
"height": 400,
"config": {
  "text": {"dy": -10, "fontSize": 10, "fontWeight":"bold"},
  "line": {"interpolate":"linear"},
  "point": {
    "filled": true
  }
},
"layer": [
  {
    "data": {
      "url": "data/world-atlas-110.json",
      "format": {
        "type": "topojson",
        "feature":"land"
      }
    },
    "projection": {
      "type": "equirectangular"
    },
    "mark": {
      "type": "geoshape",
      "fill":"lightgray",
      "stroke": "none"
     }
   },

Vega-Lite bietet das Ebenen – layer – an, um die Konfiguration der verschiedenen Kartenelemente sauber zu trennen. In der ersten Ebene werden hier die topographischen Informationen der Weltkarte geladen und konfiguriert, dass nur die Kontinente angezeigt werden. Da die Weltkarte nur Hintergrund sein soll, wird auf zusätzliche Konturen verzichtet.

Nach Karte und Projektion kommen nun die Daten, die ich eigentlich anzeigen möchte. Die Beispiele für geografische Illustrationen mit Vega-Lite zeigen, wie einfach es ist räumliche Verteilungen damit zu illustrieren. Bei Routendarstellungen wird es etwas schwieriger, wie sich zeigen wird.

Über dieser Ebene liegen nun noch drei weitere für

  • die Route
  • Markierungen
  • Namen

Alle drei werden aus einer CSV-Datei gespeist, die etwa so aussieht

Name,Typ,Länge,Breite,Reihenfolge
,,-180,14.602137,01
Hawaii,,-155.925927,19.478244,02
Galapagos,,-90.521,-0.537,03
Brasilien,mitte,-59.000000,-14.350000,04
Namibia,,14.505278,-22.9575,05
▶︎ Philippinen,start,121.050904,14.602137,06
,,180,14.602137,07

Die Ebenendefinition dafür lautet folgendermaßen:

    {
      "data": {
        "url": "data/vegalite.csv"
      },
      "projection": {
        "type": "equirectangular"
      },
      "mark": "line",
      "encoding": {
        "x": {
          "field": "Länge",
          "type": "longitude"
        },
        "y": {
          "field": "Breite",
          "type": "latitude"
        },
        "order": {
          "field": "Reihenfolge",
          "type": "ordinal"
        }
      }
    }

Die anderen Ebenen funktionieren ähnlich. Die Ebene entnimmt ihre Daten der CSV-Datei. Mit mark definiert man, was angezigt werden soll, hier also Linien, die Route. Im Abschnitt encoding werden Felder der CSV-Datei auf Koordinaten abgebildet. Interessant ist das dritte Feld, order. Es definiert ein Sortierkriterium für die in der CSV-Datei gespeicherten Koordinaten. Ohne diese Angabe kam es manhcmal zu merkwürdigen Routen.

Weltkarte mit Route in Vega-Lite

Weltkarte mit Route in Vega-Lite

Das Ergebnis sieht dann ungefähr wie in der Abbildung aus. Standardmäßig nutzt Vega-Lite gerade Linien, um Punkte zu verbinden. Das Ergebnis sieht auf den ersten Blick richtig aus, leider entsprechen die Linien, die die Route darstellen, nicht immer den geografischen Gegebenheiten.

Das liegt an der Art, wie diese Linien in Vega-Lite derzeit generiert werden. Während die Koordinaten der einzelnen Stationen korrekt der gewählten Projektion unterworfen werden, ist dies bei den Verbindungslinien nicht der Fall. Diese werden zwischen den X- und Y-Koordinaten der Diagrammfläche gezogen, und daher nicht der geografischen Projektion unterworfen. Das führt dazu, dass die dargestellten Routen unterwegs nicht immer die Punkte berühren, die sie geografisch eigentlich berühren müssten.

Um das etwas abzumildern, kann man statt geraden Linien (linear) Splines verwenden, dann sehen die Linien etwas runder aus. Also etwa so:

Gekrümmte Route in Vega-Lite

Gekrümmte Route in Vega-Lite

Dazu könnte man dann noch einige Zwischenstationen einfügen, um die geografischen Fehler zu reduzieren. Aber das ist schon wieder Arbeit, die man sich eigentlich nicht machen wollte.

Während sich also geografische Illustrationen mit Vega-Lite, die räumliche Verteilungen oder Beziehungen zwischen Punkten darstellen, relativ leicht und ohne Programmierung erstellen lassen, kann man geografisch genaue Routen derzeit nicht so einfach abbilden.