GeoJSON Feature Collection per JavaScript-Object erzeugen

Im Rahmen von Michaels Pacific Crest Wanderung habe ich basierend auf OpenLayers, jQuery und den Daten seines SPOT Satellite Messenger eine Webanwendung gebaut, auf der man rund um die Uhr verfolgen kann, wo er sich gerade befindet. Bei der Nutzung von OpenLayers stellt man sich zwangsläufig irgendwann die Frage, in welchem Format man die Location-Daten denn überhaupt abrufen möchte.

In einem Projekt von 2010 für walking-away.de (Beispiel), habe ich beispielsweise GPX-Dateien verwendet. Die Methode bietet sich vor allem immer dann an, wenn man die Daten beispielsweise aus einem Navigationsgerät von Garmin und co. auslesen möchte um sie im Web darzustellen. Diese Geräte bieten meistens die aufgezeichneten Tracks im GPX-Format an.

Im Fall von live.everyday-madness.de habe ich mich dazu entschlossen, auf das GeoJSON-Format zu setzen. JSON als solches ist momentan ohnehin der absolute Shooting-Star was den Datenaustausch im Web angeht. GeoJSON setzt, wie der Name schon sagt, auf die JSON „Sprache“ auf und definiert, wie Daten übermittelt werden müssen. Über GeoJSON lassen sich Punkte (Points), Tracks (LineStrings) oder auch Formen (Polygon) definieren. OpenLayers zum Beispiel unterstützt in der aktuellen Version alle im GeoJSON Standard definierten Typen. Zudem lassen sich mehrer Typen zu so genannten „Feature Collections“ zusammen fassen. Eine Feature Collection besteht beispielsweise aus einem Track (als LineString) und einem Start- bzw. Endpunkt-Marker (als Point). Eine solche Feature Collection kann anschließend als JSON-String von beispielsweise OpenLayers eingelesen werden und stellt sämtliche in ihr enthaltenen Daten dar.

In meinem Fall habe ich die Roh-Daten (Koordinaten, Timestamp, Nachrichten-Inhalt, Typ) per AJAX und JSON (nicht GeoJSON) über den Umweg über PHP aus einer MySQL Datenbank geholt. Natürlich hätte ich die den GeoJSON-String bereits per PHP zusammen setzen können, allerdings benötigte ich ohnehin noch weitere Daten aus der Datenbank, weshalb ich mich dazu entschlossen habe, den GeoJSON-String erst in JavaScript zusammen zu setzen um ihn dann an OpenLayers zu übergeben.

Doch wie erstellt man nun möglichst einfach eine Feature Collection in JavaScript? Der einfachste Weg ist, eine Kombination aus Objekten und Arrays zu verwenden.

Beispiel:

Wir wollen eine Feature Collection erstellen, die eine Linie zwischen zwei Punkten sowie einen einzelnen Wegpunkt, nämlich den Start-Punkt unseres Tracks, enthält.

Die Roh-Daten könnten entsprechend wie folgt aussehen:

1
2
3
4
5
6
7
8
9
10
11
/* Start-Punkt unseres Tracks (Längen- und Breitengrad)*/
var starLat = 52.27257085;
var startLon = 10.5038435071781;
 
/* Track, bestehend aus zwei Punkten (A und B) */
/* A */
var aLat = 52.26423091720975;
var aLon = 10.551831722259521
/* B */
var bLat = 52.26734322071651
var bLon = 10.560779571533203

Um diese Daten in eine gültige Feature Collection zu übertragen, können wir uns einfachen JavaScript Boardmitteln bedienen. Wir erstellen als erstes ein Object, welches sämtliche Daten hält. Dieses Objekt nenne wir im Beispiel „geoJsonData“:

1
2
3
4
/* Vorbereitung des Objekts für die Feature Collection */
var geoJsonData = new Object();
geoJsonData.type = "FeatureCollection";
geoJsonData.features = new Array();

Nachdem das „Basis“-Objekt für die Feature Collection erstellt wurde, können wir nun das Point-Feature für unseren Start-Punkt vorbereiten:

1
2
3
4
5
6
/* Vorbereitung des Point Features */
var geoJsonPointFeature = new Object();
geoJsonPointFeature.geometry = new Object();
geoJsonPointFeature.properties = new Object();
geoJsonPointFeature.type = "Feature";
geoJsonPointFeature.geometry.type = "Point";

Das Point-Feature Objekt wurde damit erstellt. Wichtig sind hier vor allem die Attribute „type“ und „geometry.type“. Das Objekt füllen wir nun mit den Daten unseres Punktes (startLon, startLat) sowie einer beliebigen Namen. Und wo wir schon einmal dabei sind, setzen wir auch gleich ein passendes Datum als Eigenschaft (Property) mit dazu:

1
2
3
4
/* Die Daten für das Point Feature */
geoJsonPointFeature.geometry.coordinates = new Array(startLon, startLat);
geoJsonPointFeature.properties.name = "Der Startpunkt";
geoJsonPointFeature.properties.date = "29.04.2013";

Ähnlich verhält es sich mit dem LineString-Feature Objekt. Das LineString-Feature Objekt bereiten wir wieder vor und erstellen es wie folgt:

1
2
3
4
5
6
7
/* Vorbereitung des Track Features */
var geoJsonTrackFeature = new Object();
geoJsonTrackFeature.geometry = new Object();
geoJsonTrackFeature.geometry.coordinates = new Array();
geoJsonTrackFeature.properties = new Object();
geoJsonTrackFeature.type = "Feature";
geoJsonTrackFeature.geometry.type = "LineString";

Und nun füllen wir unsere Daten ein. Achtet darauf, dass der Grundaufbau zwar gleich zu dem des Point-Feature Objektes ist, jedoch innerhalb des Arrays „geoJsonTrackFeature.geometry.coordinates“ weitere Arrays benötigt werden, da wir mehrere Koordinaten einfügen wollen:

1
2
3
4
5
/* Die Daten für das Point Feature */
geoJsonTrackFeature.geometry.coordinates.push(new Array(aLon,aLat));
geoJsonTrackFeature.geometry.coordinates.push(new Array(bLon,bLat));
geoJsonTrackFeature.properties.name = "Eine Wanderung durch den Prinz-Albrecht-Park";
geoJsonTrackFeature.properties.date = "29.04.2013";

Unser Point-Feature Objekt für den Start-Punkt und auch das LineString-Feature Objekt für die unseren Track, bzw. unsere Wegstrecke haben wir erstellt und mit den Daten gefüllt. Um beide Objekte in unser eigentliche Feature Collection Objekt einzufügen, „pushen“ wir beide Objekte einfach in das „geoJsonData.features“-Array:

1
2
3
/* Die Features in das Objekt der Feature Collection "pushen" */
geoJsonData.features.push(geoJsonPointFeature);
geoJsonData.features.push(geoJsonTrackFeature);

Das Feature Collection Objekt enthält nun sämtliche Daten, um daraus einen gültigen GeoJSON-String erstellen zu können. JavaScript bietet hier eine sehr einfache Variante, einen JSON-String aus einem Objekt zu erzeugen. Dies gilt nicht nur für GeoJSON, sondern kann logischer Weise auch für andere Objekte genutzt werden. Erledigt wird das Ganze über die „JSON.stringify“-Funktion:

1
2
/* GeoJSON-String aus dem Feature Collection Objekt erzeugen */
var geoJsonString = JSON.stringify(geoJsonData);

Heraus kommt ein vollständig GeoJSON-Kompatibler String:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
    "type": "FeatureCollection",
    "features": [
        {
            "type": "Feature",
            "geometry": {
                "type": "Point",
                "coordinates": [
                    10.5038435071781,
                    52.27257085
                ]
            },
            "properties": {
                "name": "Der Startpunkt",
                "date": "29.04.2013"
            }
        },
        {
            "type": "Feature",
            "geometry": {
                "type": "LineString",
                "coordinates": [
                    [
                        10.551831722259521,
                        52.26423091720975
                    ],
                    [
                        10.560779571533203,
                        52.26734322071651
                    ]
                ]
            },
            "properties": {
                "name": "Eine Wanderung durch den Prinz-Albrecht-Park",
                "date": "29.04.2013"
            }
        }
    ]
}

Den kompletten GeoJSON-String könnt ihr übrigens auf dieser Website auf Gültigkeit überprüfen lassen. Die Website zeigt euch sogar auf einer Karte alle Daten (Features) an, die ihr definiert habt.

Sicherlich werden einige von euch die Generierung des GeoJSON-Strings eher auf Serverseitig (PHP, Perl, Python etc.) durchführen, da z.B. beim Abruf der Track- oder Point-Daten aus MySQL eine andere Sprache genutzt wird, jedoch kann es nicht schaden, auch den JavaScript-Weg zu kennen.

Falls Fragen offen sind: Die Kommentar-Funktion steht offen.