Ziel dieses Kapitels ist die Transformation eines regelmäßigen Punktgitters eines Höhenmodells in eine geschlossene Oberfläche aus Dreiecken. Aus dieser Oberfläche werden wir wiederum eine X3D-Datei erstellen, die wir in X3DOM einbinden können. Alle hierzu nötigen Schritte werden wir direkt in PostGIS vornehmen.
Bemerkung
X3D steht für Extensible 3D und ist eine auf XML basierende Auszeichnungssprache für 3D-Modelle speziell für den Browser. Seit 2001 ist X3D der Standard für 3D-Inhalte im Internet.
Als Beispieldatensatz werden wir einen Ausschnitt des Digitialen Gelädenmodells (DGM50) des Landes Berlin nutzen, der uns freundlicherweise von der Senatsverwaltung für Stadtentwicklung und Umwelt Berlin bereitgestellt wurde.
Folgende Arbeitsschritte sind hierfür nötig:
Im ersten Schritt wollen wir uns einen Überblick über die gegebenen Daten verschaffen. Öffnen Sie QGIS (Application - Education - Quantum GIS) und laden Sie die Datei
/home/user/Documents/workshop_webgis3d/x3dom_data/DGM50_clip.shp
über Layer - Add Vector Layer.
Wie zu erwarten, sehen wir in QGIS ein gleichmäßiges Gitter an Punkten mit einem Abstand von jeweils 50m. Öffnen wir zusätzlich die Attributtabelle, werden wir erkennen, dass zu jedem Punkt sowohl die 2D-Koordinaten (x- und y-Werte im Soldner-Koordinatensystem) als auch eine z-Koordinate vorhanden ist.
Bevor wir die Daten in PostGIS einladen können, benötigen wir eine Datenbank für diese Daten. Öffnen Sie zunächst pgAdmin III (falls noch nicht geschehen, ansonsten unter Application - Development - pgAdmin III) und erstellen Sie eine neue Datenbank für dieses Modul z.B. mit dem Namen db_x3d und erweitern Sie diese um PostGIS, nachdem Sie sich auf der DB angemeldet haben.
CREATE DATABASE db_x3d;
CREATE EXTENSION postgis;
Nun können wir direkt über QGIS einen Importvorgang starten. Folgen Sie dazu den folgenden Schritten:
Bestätigen Sie die Eingaben mit OK, um den Importvorgang zu starten.
Nach erfolgreichem Import werden wir in der Datenbank eine neue Tabelle tbl_2d_dgm50_clip finden, die neben den Attributwerten x, y, z, eine Spalte mit der Geometrie (geom) besitzt. Diese beinhaltet jedoch noch nicht den jeweiligen z-Wert und muss daher zunächst hinzugefügt werden. Wir benötigen hierzu eine neue Tabelle mit einem Geometrietyp, der dies unterstützt:
-- create new table for PointZ
CREATE TABLE IF NOT EXISTS
tbl_3d_dgm50_clip_points (
id SERIAL,
geom geometry(PointZ,3068)
);
Die neue Tabelle tbl_3d_dgm50_clip_points besitzt mit PointZ diese Möglichkeit, ein Koordinatenpaar inklusive einer z-Koordinate aufzunehmen. Nun müssen wir diese Tabelle mit Leben füllen.
-- insert points (with z-value)
INSERT INTO tbl_3d_dgm50_clip_points(geom)
SELECT
ST_SetSRID(
ST_MakePoint(
ST_X(foo.geom),
ST_Y(foo.geom),
foo.z
), 3068
)
FROM
tbl_2d_dgm50_clip AS foo;
Das obige Statement erstellt mit ST_MakePoint für jeden Punkt der ursprünglichen Punkttabelle eine neue 3D-Punktgeometrie im Koordinatensystem EPSG:3068 (Soldner) und fügt sie der Tabelle tbl_3d_dgm50_clip_points hinzu. Um die späteren Operationen zu beschleunigen, empfiehlt es sich, zusätzlich einen Index zu erzeugen:
-- create gist index
CREATE INDEX tbl_3d_dgm50_clip_points_geom_gist
ON tbl_3d_dgm50_clip_points
USING gist
(geom);
Aus den vorhandenen 3D-Punktmenge wollen wir nun eine geschlossene Fläche generieren. Hierzu werden wir das Verfahren der Delaunay-Triangulation benutzen, welches in PostGIS über der Funktion ST_DelaunayTriangles() verfügbar ist. Nähere Informationen zu Verfahren und Funktion finden Sie in der PostGIS Dokumentation unter http://postgis.net/docs/ST_DelaunayTriangles.html.
Für die folgende Operation werden wir zunächst eine neue Tabelle tbl_3d_dgm50_clip_coll anlegen, die die Ausgabe der Funktion (GeometryCollectionZ) beinhalten wird:
-- create PolygonZ table
CREATE TABLE IF NOT EXISTS
tbl_3d_dgm50_clip_coll (
id SERIAL,
geom geometry(GeometryCollectionZ,3068)
);
ST_DelaunayTriangles() besitzt drei Parameter: geometry, tolerance und flags. geometry bestimmt die Eingangsgeomtrie (POINT), tolerance einen Bereich (in Einheiten der Projektion) um den Eingangspunkte zusammengefasst werden sollen und flags die Ausgabegeometrie (0 = POLYGON, 1 = MULTILINESTRING, 2 = TIN). Das folgende Statement ruft die Funktion für die Punktgeometrien aus tbl_3d_dgm50_clip_points ohne Toleranz auf und schreibt eine GeometryCollection (Sammlung von Polygonen) in tbl_3d_dgm50_clip_coll. Da wir die Triangulation nicht für jede einzelne Zeile der Eingabetabelle berechnen wollen (eine Triangulation mit nur einem Punkt ergibt keine gültigen Eingabewerte), fassen wir alle verfügbaren Geometrien per ST_Collect() zu einer COLLECTION zusammen.
-- calculate polygons
INSERT INTO tbl_3d_dgm50_clip_coll(geom)
SELECT
ST_DelaunayTriangles(ST_COLLECT(geom),0,0) AS geom
FROM
tbl_3d_dgm50_clip_points;
Hinweis: Die Ausführung dieses Befehls kann einige Minuten in Anspruch nehmen!
Auch für diese Tabelle erstellen wir nach erfolgter Berechnung einen GIST-Index:
-- create gist index
CREATE INDEX tbl_3d_dgm50_clip_coll_geom_gist
ON tbl_3d_dgm50_clip_coll
USING gist
(geom);
Bevor wir aus der GeometryCollection eine X3D-Datei berechnen können, müssen wir aus der Collection alle Einzelpolygone (PolygonZ) auslesen und in wiederum in eine neue Tabelle schreiben. Wir erstellen also zunächst eine neue Tabelle (...):
CREATE TABLE IF NOT EXISTS
tbl_3d_dgm50_clip_poly (
id SERIAL,
geom geometry(PolygonZ,3068)
);
Und fügen alle Einzelpolygone in diese Tabelle ein, indem wir mit ST_Dump() die Collection zerlegen und die einzelne Geometrie auslesen und einen Index erzeugen:
INSERT INTO tbl_3d_dgm50_clip_poly(geom)
SELECT
(ST_Dump(geom)).geom
FROM
tbl_3d_dgm50_clip_coll;
-- create gist index
CREATE INDEX tbl_3d_dgm50_clip_poly_geom_gist
ON tbl_3d_dgm50_clip_poly
USING gist
(geom);
Im letzten Schritt werden wir nun aus den Polygonen eine X3D Node generieren und verwenden hierzu die PostGIS Funktion ST_AsX3D() (http://postgis.net/docs/ST_AsX3D.html). Die Funktion liefert zunächst nur eine Repräsentation der Geometrie (in unserem Fall ein IndexedFaceSet), aber noch keine gültige X3D-Datei, die für die spätere Einbindung in den Viewer nötig ist. Daher betten wir die Funktion in ein Minimalgerüst einer X3D-Datei ein, die einen 3D-Szene beschreibt. Beachten Sie hierbei, dass die Angaben im Tag <Appearance> im Viewer überschrieben werden (können) und daher nicht das finale Aussehen des Modells bestimmen.
SELECT
'<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "http://www.web3d.org/specifications/x3d-3.0.dtd">
<X3D>
<Scene>
<Shape>
<Appearance>
<Material ambientIntensity="0.2" diffuseColor="#FFA673" emissiveColor="0 0 0" shininess="0.2" specularColor="0 0 0" transparency="0"/>
</Appearance>
' ||
ST_AsX3D(ST_Collect(geom))
|| '
</Shape>
</Scene>
</X3D>' AS geom
FROM
tbl_3d_dgm50_clip_poly;
Hinweis: Die Ausführung dieses Befehls kann einige Minuten in Anspruch nehmen!
Speichern Sie das Ergebnis dieses Aufrufs ab, indem Sie die Ergebniszeile selektieren und über File - Export das Export-Fenster aufrufen. Stellen Sie in diesem sicher, dass keine Anführungszeichen (Quote chars) und keine Spaltennamen (Column names) ausgegeben werden. Speichern Sie die Datei nun z.B. mit dem Namen dgm50_clip.x3d (Wichtig ist die Endung .x3d) im Ordner /home/user/Documents/workshop_webgis3d/x3dom_client/data/ ab.
Herzlichen Glückwunsch! Sie haben mit nur wenigen Schritten und komplett auf Datenbankseite aus einer Punktmenge eine valide X3D-Datei erstellt, die Sie nun mit X3DOM betrachten können. Wir werden im nächsten Kapitel diese Datei in den bereitgestellten X3DOM-Client einbinden.