Chapter 4. PostGIS anwenden: Datenverwaltung und Abfragen

Table of Contents
4.1. GIS Objekte
4.1.1. OpenGIS WKB und WKT
4.1.2. PostGIS EWKB, EWKT und Normalformen/kanonische Formen
4.1.3. SQL-MM Part 3
4.2. Der geographische Datentyp von PostGIS
4.2.1. Grundsätzliches zum geographischen Datentyp
4.2.2. Wann sollte man den geographischen Datentyp dem geometrischen Datentyp vorziehen
4.2.3. Fortgeschrittene FAQ's zum geographischen Datentyp
4.3. Verwendung von OGC-Standards
4.3.1. Die SPATIAL_REF_SYS Tabelle und Koordinatenreferenzsysteme
4.3.2. Der View GEOMETRY_COLUMNS
4.3.3. Erstellung einer räumlichen Tabelle
4.3.4. Geometrische Spalten in "geometry_columns" händisch registrieren
4.3.5. Wahrung der OGC-Konformität von Geometrien
4.3.6. DE-9IM-Matrix (DE-9IM)
4.4. GIS (Vektor) Daten laden
4.4.1. Daten via SQL laden
4.4.2. shp2pgsql: Verwendung des ESRI-Shapefile Laders
4.5. Geodaten abrufen
4.5.1. Daten mit SQL abrufen
4.5.2. Verwendung des Dumper
4.6. Erstellung von Indizes
4.6.1. GiST-Indizes
4.6.2. BRIN Indizes
4.6.3. SP-GiST Indizes
4.6.4. Verwendung von Indizes
4.7. Komplexe Abfragen
4.7.1. Vorteile von Indizes nutzen
4.7.2. Beispiele für Spatial SQL

4.1. GIS Objekte

Die Geoobjekte, die von PostGIS unterstützt werden, sind eine Obermenge der durch das OpenGIS Consortium (OGC) festgelegten "Simple Features". PostGIS unterstützt sämtliche Objekte und Funktionen, die in der OGC "Simple Features for SQL" Spezifikation normiert sind.

PostGIS erweitert den Standard mit der Unterstützung von 3DZ-, 3DM -und 4D-Koordinaten.

4.1.1. OpenGIS WKB und WKT

Die OpenGIS Spezifikation standardisiert zwei Möglichkeiten um Geoobjekte darzustellen: die Well-known-Text (WKT) und die Well-Known-Binary (WKB) Darstellung. Sowohl WKT als auch WKB enthalten Information über den Objekttyp und die Koordinaten, die das Objekt bilden.

Beispiele für die Textdarstellung (WKT) von Geoobjekten:

  • POINT(0 0)

  • LINESTRING(0 0,1 1,1 2)

  • POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))

  • MULTIPOINT((0 0),(1 2))

  • MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))

  • MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))

  • GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))

Die OpenGIS Spezifikation verlangt auch, dass das der Identifikator des Koordinatenreferenzsystem (SRID) im internen Format der Geoobjekte mit abgespeichert ist.

Für die Ein- und Ausgabe dieser Formate stehen die folgenden Schnittstellen zur Verfügung

bytea WKB = ST_AsBinary(geometry);
text WKT = ST_AsText(geometry);
geometry = ST_GeomFromWKB(bytea WKB, SRID);
geometry = ST_GeometryFromText(text WKT, SRID);

Eine gültige Einfügeanweisung, um ein räumliches OGC-Objekt zu erzeugen und einzufügen, wäre:

INSERT INTO geotable ( the_geom, the_name )
  VALUES ( ST_GeomFromText('POINT(-126.4 45.32)', 312), 'A Place');

4.1.2. PostGIS EWKB, EWKT und Normalformen/kanonische Formen

Das OGC Format unterstützt nur 2D-Geometrien, und die dazugehörige SRID ist *niemals* in den Eingabe/Ausgabe-Darstellungen eingebettet.

Zur Zeit sind die erweiterten Formate von PostGIS eine Obermenge von den OGC-Formaten (jeder gültige WKT/WKB ist auch ein gültiger EWKT/EWKB). Dies kann sich in der Zukunft allerdings ändern, insbesondere dann, wenn OGC ein neues Format einführt, welches mit diesen Erweiterungen im Widerspruch steht. Daher sollten SIE sich BITTE NICHT auf diese Eigenschaft verlassen!

PostGIS EWKB/EWKT add 3DM, 3DZ, 4D coordinates support and embedded SRID information.

Beispiele für die Textdarstellung (EWKT) von erweiterten Geoobjekten:

  • POINT(0 0 0) -- XYZ

  • SRID=32632;POINT(0 0) -- XY mit SRID

  • POINTM(0 0 0) -- XYM

  • POINT(0 0 0 0) -- XYZM

  • SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- XYM mit SRID

  • MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1))

  • POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0))

  • MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)),((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0)))

  • GEOMETRYCOLLECTIONM( POINTM(2 3 9), LINESTRINGM(2 3 4, 3 4 5) )

  • MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4) )

  • POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)), ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)), ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)), ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)), ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )

  • TRIANGLE ((0 0, 0 9, 9 0, 0 0))

  • TIN( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )

Für die Konvertierung zwischen diesen Formaten stehen die folgenden Schnittstellen zur Verfügung:

bytea EWKB = ST_AsEWKB(geometry);
text EWKT = ST_AsEWKT(geometry);
geometry = ST_GeomFromEWKB(bytea EWKB);
geometry = ST_GeomFromEWKT(text EWKT);

Zum Beispiel würde eine gültige Eingabeanweisung, um räumliche PostGIS Objekte zu erzeugen und einzufügen, wie folgt lauten:

INSERT INTO geotable ( the_geom, the_name )
  VALUES ( ST_GeomFromEWKT('SRID=312;POINTM(-126.4 45.32 15)'), 'A Place' )

Die "kanonische Form" eines PostgreSQL Datentyps ist jene Darstellung die man mit einer einfachen Abfrage (ohne Funktionsaufruf) erhält und bei der sichergestellt ist, dass sie von einem einfachen INSERT, UPDATE oder COPY angenommen wird. Beim geometrischen Datentyp von PostGIS sind dies:

- Ausgabe
  - binary: EWKB
        ascii: HEXEWKB (EWKB in hex form)
- Eingabe
  - binary: EWKB
        ascii: HEXEWKB|EWKT 

Zum Beispiel liest die folgende Anweisung EWKT ein und gibt HEXEWKB in Normalform aus:

=# SELECT 'SRID=4;POINT(0 0)'::geometry;

geometry
----------------------------------------------------
01010000200400000000000000000000000000000000000000
(1 row)

4.1.3. SQL-MM Part 3

Die "SQL Multimedia Applications Spatial" Spezifikation erweitert die SQL Spezifikation für Simple Features indem es eine Reihe von kreisförmig interpolierten Kurven definiert.

Die Definitionen in SQL-MM schließen 3DM-, 3DZ- und 4D-Koordinaten ein, erlauben allerdings nicht das Einbinden von Information über SRID.

Die Erweiterungen zur Well-known-Text-Darstellung werden zur Zeit noch nicht zur Gänze unterstützt. Im Folgenden werden Beispiele für einige einfache gekrümmte Geometrien gezeigt:

  • CIRCULARSTRING(0 0, 1 1, 1 0)

    CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0)

    CIRCULARSTRING ist der grundlegende Kurventyp, ähnlich wie LINESTRING in der linearen Welt. Ein einziges Segment benötigt drei Punkte, den Anfangs- und den Endpunkt (erster und dritter) und irgendeinen weiterer Punkt auf dem Kreisbogen. Eine Ausnahme ist der geschlossene Kreis, wo Anfangs- und Endpunkt ident sind. In diesem Fall muss der zweite Punkt dem Kreismittelpunkt entsprechen. Um Kreisbögen aneinanderzuketten, wird der Endpunkt des vorangehenden Bogens zum Anfangspunkt des nächstfolgenden Bogens, genauso wie beim LINESTRING. D.h., dass ein Kreisbogen eine ungerade Anzahl an Punkten grösser als 1 aufweisen muss.

  • COMPOUNDCURVE(CIRCULARSTRING(0 0, 1 1, 1 0),(1 0, 0 1))

    Eine zusammengesetzte Kurve ist eine einzelne, durchgängige Kurve, die sowohl gekrümmte (kreisförmige) als auch gerade Segmente aufweist. Dies bedeutet, daß die Komponenten nicht nur wohlgeformt sein müssen, sondern auch der Endpunkt einer jeden Komponente (außer der letzten) mit dem Anfangspunkt der nachfolgenden Komponente zusammenfallen muss.

  • CURVEPOLYGON(CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0),(1 1, 3 3, 3 1, 1 1))

    Beispiel einer zusammengesetzten Kurve in einem Kurvenpolygon: CURVEPOLYGON(COMPOUNDCURVE(CIRCULARSTRING(0 0,2 0, 2 1, 2 3, 4 3),(4 3, 4 5, 1 4, 0 0)), CIRCULARSTRING(1.7 1, 1.4 0.4, 1.6 0.4, 1.6 0.5, 1.7 1) )

    Ein CurvePolygon hat, genau wie ein Polygon, einen äußeren Ring und keinen oder mehrere innere Ringe. Der Unterschied liegt darin, dass ein Ring aus Kreisbögen, Geraden oder zusammengesetzten Strecken bestehen kann.

    Ab PostGIS 1.4 werden zusammengesetzte Kurven/CompoundCurve in einem Kurvenpolygon/CurvePolygon unterstützt.

  • MULTICURVE((0 0, 5 5),CIRCULARSTRING(4 0, 4 4, 8 4))

    Eine MultiCurve ist eine Sammelgeometrie von Kurven, welche aus Geraden, Kreisabschnitte oder zusammengesetzten Abschnitten bestehen kann.

  • MULTISURFACE(CURVEPOLYGON(CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0),(1 1, 3 3, 3 1, 1 1)),((10 10, 14 12, 11 10, 10 10),(11 11, 11.5 11, 11 11.5, 11 11)))

    Dies ist eine Sammelgeometrie von Oberflächen, welche (lineare) Polygone oder Kurvenpolygone sein können.

[Note]

Alle Gleitpunkt Vergleiche der SQL-MM Implementierung werden mit einer bestimmten Toleranz ausgeführt, zurzeit 1E-8.

4.2. Der geographische Datentyp von PostGIS

Der geographische Datentyp bietet native Unterstützung für Geoobjekte die durch "geographische" Koordinaten (manchmal auch als "geodätische" Koordinaten, "Länge/Breite" oder "Breite/Länge" bezeichnet) festgelegt sind. Geographische Koordinaten sind Kugelkoordinaten, die durch Winkel (in Grad) angegeben werden.

Der geometrische Datentyp von PostGIS beruht auf der Ebene. Die kürzeste Entfernung zwischen zwei Punkten einer Ebene entspricht einer Gerade. Das bedeutet, dass geometrische Berechnungen (wie Flächen, Distanzen, Längen, Schnittpunkte, etc.) im kartesischen Koordinatensystem mit geradlinigen Vektoren ausgeführt werden können.

Der geographische Datentyp von PostGIS beruht auf einer Kugel. Die kürzeste Verbindung zwischen zwei Punkten auf einer Kugeloberfläche ist ein Bogenteil eines Großkreises. D.h., dass Berechnungen auf geographische Datentypen (wie Flächen, Distanzen, Längen, Schnittpunkte, etc.) auf der Kugeloberfläche mit einer komplexeren Mathematik durchgeführt werden müssen. Für genauere Messungen müssen die Berechnungen das Rotationsellipsoid der Erde in Betracht ziehen.

Da die zugrunde liegende Mathematik wesentlich schwieriger ist, gibt es weniger Funktionen für den geographischen Datentyp, als für den geometrischen Datentyp. Mit der Zeit werden neue Algorithmen hinugefügt und die Möglichkeiten des geographischen Datentyps erweitert werden.

Es verwendet den Datentyp geography, der allerdings von den Funktionen der Bibliothek GEOS in keiner Weise unterstützt wird. Als provisorische Lösung kann man zwischen dem geometrischen und dem geographischen Datentypen hin- und herkonvertieren.

Vor PostGIS 2.2 hat der geographische Datentyp nur WGS 84 Länge und Breite (SRID:4326) unterstützt. Ab PostGIS 2.2 kann dieser jedes Koordinatenreferenzsystem verwenden, das auf Länge und Breite basiert und in der Tabelle spatial_ref_sysaufgeführt ist. Sie können sogar Ihr eigenes Polarkoordinatenreferenzsystem hinzufügen, wie unter geography type is not limited to earth beschrieben.

Unabhängig vom verwendeten Koordinatenreferenzsystem sind die Einheiten der ausgegebenen Messungen (ST_Distance, ST_Length, ST_Perimeter, ST_Area) und der Eingabe für ST_DWithin in Meter.

Der geographische Datentyp verwendet die Typmod-Formatangabe von PostgreSQL, sodass eine Tabelle mit einem geographischen Attribut in einem einzigen Schritt erstellt werden kann. Es werden alle Formate des OGC-Standards unterstützt, mit Ausnahme von Kurven.

4.2.1. Grundsätzliches zum geographischen Datentyp

Der geographische Datentyp unterstützt Geometrien, ausgenommen sind jedoch Kuven, TINS und POLYHEDRALSURFACEs. Daten vom geometrischen Datentyp, welche eine SRID von 4326 aufweisen, werden implizit in den geographischen Datentyp umgewandelt. Sie können Daten auch entsprechend der EWKT- und EWKB-Konvention einfügen.

  • POINT: Erstellung einer 2D-Punkttabelle mit dem geographischen Datentyp. Wenn die SRID nicht festgelegt ist, wird 4326 WGS 84 Länge und Breite angenommen:

    CREATE TABLE ptgeogwgs(gid serial PRIMARY KEY, geog geography(POINT) );

    POINT: Erstellung einer Tabelle mit 2D-Punkten als geographischen Datentyp in NAD83 Länge und Breite:

    CREATE TABLE ptgeognad83(gid serial PRIMARY KEY, geog geography(POINT,4269) );

    Erstellung einer Punkttabelle mit Z-Koordinaten und explizit angegebener SRID

    CREATE TABLE ptzgeogwgs84(gid serial PRIMARY KEY, geog geography(POINTZ,4326) );
  • LINESTRING

    CREATE TABLE lgeog(gid serial PRIMARY KEY, geog geography(LINESTRING) );
  • POLYGON

    -- Polygon in NAD 1927 Länge und Breite
    CREATE TABLE lgeognad27(gid serial PRIMARY KEY, geog geography(POLYGON,4267) );
  • MULTIPOINT

  • MULTILINESTRING

  • MULTIPOLYGON

  • GEOMETRYCOLLECTION

Die Attribute des geographischen Datentyps werden in dem System View geography_columns registriert.

Nun überprüfen Sie bitte ob Ihre Tabelle in der gespeicherten Abfrage "geography_columns" aufscheint.

Sie können eine neue Tabelle mit einer geographischen Datentypspalte mit der Syntax von CREATE TABLE erstellen.

CREATE TABLE global_points (
    id SERIAL PRIMARY KEY,
    name VARCHAR(64),
    location GEOGRAPHY(POINT,4326)
  );

Beachten Sie bitte, dass die Spalte "location" den geographischen Datentyp verwendet und dieser zwei optionale Modifikatoren unterstützt: Einen Typmodifikator, der die geometrische Form und die Dimension der Geometriespalte festlegt; ein Modifikator für SRID, der den Identifikator für das Koordinatenreferenzsystem auf eine bestimmte Zahl einschränkt.

Für den Typmodifikator sind folgende Werte erlaubt: POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON. Der Modifikator unterstützt auch Einschränkungen der Dimensionalität durch Nachsilben: Z, M und ZM. So erlaubt zum Beispiel ein Modifikator mit dem Wert 'LINESTRINGM' nur die Eingabe von Linienzügen mit drei Dimensionen, wobei die dritte Dimension als Kilometrierung/measure behandelt wird. Ebenso verlangt 'POINTZM' die Eingabe von vierdimensionalen Daten.

Wenn Sie keine SRID angeben, dann wird standardmäßig die SRID 4326 WGS84 Länge/Breite verwendet und alle Berechnungen in WGS84 ausgeführt.

Sobald Sie Ihre Tabelle erstellt haben, können Sie diese in der Tabelle "geography_columns" sehen:

-- Metadaten abfragen
SELECT * FROM geography_columns;

Sie können Daten auf dieselbe Art und Weise in die Tabelle einfügen wie Sie dies bei einer Geometriespalte tun würden:

-- Ein paar Daten in die Testtabelle einfügen
INSERT INTO global_points (name, location) VALUES ('Town', 'SRID=4326;POINT(-110 30)');
INSERT INTO global_points (name, location) VALUES ('Forest', 'SRID=4326;POINT(-109 29)');
INSERT INTO global_points (name, location) VALUES ('London', 'SRID=4326;POINT(0 49)');

Die Erstellung eines Index funktioniert gleich wie beim Datentyp GEOMETRY. PostGIS erkennt, dass es sich um den Datentyp GEOGRAPHY handelt und erzeugt einen entsprechenden, auf einer Kugeloberfläche basierenden Index anstelle des üblichen planaren Index, der für den Datentyp GEOMETRY verwendet wird.

-- Einen sphärischen Index auf die Testtabelle legen
  CREATE INDEX global_points_gix ON global_points USING GIST ( location );

Anfrage und Messfunktionen verwenden die Einheit Meter. Daher sollten Entfernungsparameter in Metern ausgedrückt werden und die Rückgabewerte sollten ebenfalls in Meter (oder Quadratmeter für Flächen) erwartet werden.

-- Eine Distanzabfrage; Beachten Sie bitte, dass London ausserhalb der 1000km Toleranz liegt
  SELECT name FROM global_points WHERE ST_DWithin(location, 'SRID=4326;POINT(-110 29)'::geography, 1000000);

Sie können die Mächtigkeit von GEOGRAPHY erfahren, indem Sie berechnen, wie nahe ein Flugzeug, das von Seattle nach London (LINESTRING(-122.33 47.606, 0.0 51.5)) fliegt, an Reykjavik (POINT(-21.96 64.15)) vorbeikommt.

-- Die Entfernung mittels GEOGRAPHY ausrechnen (122.2km)
  SELECT ST_Distance('LINESTRING(-122.33 47.606, 0.0 51.5)'::geography, 'POINT(-21.96 64.15)':: geography);

-- Die Entfernung mittels GEOMETRIE ausrechnen (13.3 "degrees")
  SELECT ST_Distance('LINESTRING(-122.33 47.606, 0.0 51.5)'::geometry, 'POINT(-21.96 64.15)':: geometry);

Ausprobieren von verschiedenen Projektionen in Länge/Breie. Es ist jedes Koordinatenreferenzsystem in Länge und Breite zulässig, das in der Tabelle spatial_ref_sys aufgeführt ist.

-- NAD 83 in Länge und Breite
SELECT 'SRID=4269;POINT(-123 34)'::geography;
                    geography
----------------------------------------------------
 0101000020AD1000000000000000C05EC00000000000004140
(1 row)

-- NAD27  in Länge und Breite
SELECT 'SRID=4267;POINT(-123 34)'::geography;

                    geography
----------------------------------------------------
 0101000020AB1000000000000000C05EC00000000000004140
(1 row)

-- NAD83 UTM Zone in Meter, resultiert in einem Fehler, da metrische Projektion
SELECT 'SRID=26910;POINT(-123 34)'::geography;

ERROR:  Nur Koordinatensysteme in Länge und Breite werden vom geographischen Datentyp unterstützt.
LINE 1: SELECT 'SRID=26910;POINT(-123 34)'::geography;

Mit dem Datentyp GEOGRAPHY wird die wahre, kürzeste Entfernung auf der Kugeloberfläche zwischen Reykjavik und der Flugstrecke entlang des Großkreises von Seattle nach London errechnet.

Great Circle mapper Beim geometrischen Datentyp wird die Entfernung sinnloserweise in einem kartesischen Koordinatensystem zwischen Reykjavik und einer Geraden von Seattle nach London errechnet und auf einer ebenen Weltkarte angezeigt. Dem Namen nach mag das Ergebnis in der Einheit "Grad" angegeben sein, da es aber in keiner Weise irgendeinem wahren Winkel zwischen den Punkten entspricht, ist sogar die Verwendung der Bezeichnung "Grad" falsch.

4.2.2. Wann sollte man den geographischen Datentyp dem geometrischen Datentyp vorziehen

Der geographische Datentyp speichert die Koordinaten in Form von Länge und Breite. Er hat allerdings den Nachteil, dass für den Datentyp GEOGRAPHY weniger Funktionen zur Verfügung stehen, als für den Datentyp GEOMETRY und diese auch mehr CPU-Zeit beanspruchen.

Welchen Datentyp Sie wählen, sollte aufgrund der zu erwartenden Flächenausdehnung ihrer Anwendung festgelegt werden. Erstrecken sich Ihre Daten über den gesamten Globus oder über eine große kontinentale Fläche, oder sind sie auf einen Staat, ein Land oder eine Gemeinde beschränkt.

  • Wenn sich Ihre Daten in einem kleinen Bereich befinden, werden Sie vermutlich eine passende Projektion wählen und den geometrischen Datentyp verwenden, da dies in Bezug auf die Rechenleistung und die verfügbare Funktionalität die bessere Lösung ist.

  • Wenn Ihre Daten global sind oder einen ganzen Kontinent bedecken, ermöglicht der geographische Datentyp ein System aufzubauen, bei dem Sie sich nicht um Projektionsdetails kümmern müssen. Sie speichern die Daten als Länge und Breite und verwenden dann jene Funktionen, die für den geographischen Datentyp definiert sind.

  • Wenn Sie keine Ahnung von Projektionen haben, sich nicht näher damit beschäftigen wollen und die Einschränkungen der verfügbaren Funktionalität für den geographischen Datentyp in Kauf nehmen können, ist es vermutlich einfacher für Sie, den geographischen anstatt des geometrischen Datentyps zu verwenden.

Für einen Vergleich, welche Funktionalität von Geography vs. Geometry unterstützt wird, siehe Section 14.11, “PostGIS Function Support Matrix”. Für eine kurze Liste mit der Beschreibung der geographischen Funktionen, siehe Section 14.4, “PostGIS Geography Support Functions”

4.2.3. Fortgeschrittene FAQ's zum geographischen Datentyp

4.2.3.1. Werden die Berechnungen auf einer Kugel oder auf einem Rotationsellipsoid durchgeführt?
4.2.3.2. Wie schaut das mit der Datumsgrenze und den Polen aus?
4.2.3.3. Wie lang kann ein Bogen sein, damit er noch verarbeitet werden kann?
4.2.3.4. Warum dauert es so lange, die Fläche von Europa / Russland / irgendeiner anderen großen geographischen Region zu berechnen?

4.2.3.1.

Werden die Berechnungen auf einer Kugel oder auf einem Rotationsellipsoid durchgeführt?

Standardmäßig werden alle Entfernungs- und Flächenberechnungen auf dem Referenzellipsoid ausgeführt. Das Ergebnis der Berechnung sollte in lokalen Gebieten gut mit dem planaren Ergebnis zusammenpassen - eine gut gewählte lokale Projektion vorausgesetzt. Bei größeren Gebieten ist die Berechnung über das Referenzellipsoid genauer als eine Berechnung die auf der projizierten Ebene ausgeführt wird.

Alle geographischen Funktionen verfügen über eine Option um die Berechnung auf einer Kugel durchzuführen. Dies erreicht man, indem der letzte boolesche Eingabewert auf 'FALSE' gesetzt wird. Dies beschleunigt die Berechnung einigermaßen, insbesondere wenn die Geometrie sehr einfach gestaltet ist.

4.2.3.2.

Wie schaut das mit der Datumsgrenze und den Polen aus?

Alle diese Berechnungen wissen weder über Datumsgrenzen noch über Pole Bescheid. Da es sich um sphärische Koordinaten handelt (Länge und Breite), unterscheidet sich eine Geometrie, die eine Datumsgrenze überschreitet vom Gesichtspunkt der Berechnung her nicht von irgendeiner anderen Geometrie.

4.2.3.3.

Wie lang kann ein Bogen sein, damit er noch verarbeitet werden kann?

Wir verwenden Großkreisbögen als "Interpolationslinie" zwischen zwei Punkten. Das bedeutet, dass es für den Join zwischen zwei Punkten zwei Möglichkeiten gibt, je nachdem, aus welcher Richtung man den Großkreis überquert. Unser gesamter Code setzt voraus, dass die Punkte von der "kürzeren" der beiden Strecken her durch den Großkreis verbunden werden. Als Konsequenz wird eine Geometrie, welche Bögen von mehr als 180 Grad aufweist nicht korrekt modelliert.

4.2.3.4.

Warum dauert es so lange, die Fläche von Europa / Russland / irgendeiner anderen großen geographischen Region zu berechnen?

Weil das Polygon so verdammt groß ist! Große Flächen sind aus zwei Gründen schlecht: ihre Begrenzung ist riesig, wodurch der Index dazu tendiert, das Geoobjekt herauszuholen, egal wie Sie die Anfrage ausführen; die Anzahl der Knoten ist riesig, und Tests (wie ST_Distance, ST_Contains) müssen alle Knoten zumindest einmal, manchmal sogar n-mal durchlaufen (wobei N die Anzahl der Knoten im beteiligten Geoobjekt bezeichnet).

Wenn es sich um sehr große Polygone handelt, die Abfragen aber nur in kleinen Gebieten stattfinden, empfehlen wir wie beim geometrischen Datentyp, dass Sie die Geometrie in kleinere Stücke "denormalisieren". Dadurch kann der Index effiziente Unterabfragen auf Teile des Geoobjekts ausführen, da eine Abfrage nicht jedesmal das gesamte Geoobjekt herausholen muss. Konsultieren Sie dazu bitte die Dokumentation der FunktionST_Subdivide. Nur weil Sie ganz Europa in einem Polygon speichern *können* heißt das nicht, dass Sie dies auch tun *sollten*.

4.3. Verwendung von OGC-Standards

Die OpenGIS "Simple Features Specification for SQL" standardisert die Datentypen von Geoobjekten, die Funktionen die benötigt werden um diese zu verarbeiten, sowie die Metadatentabellen. Um sicherzustellen, dass die Metadaten konsistent bleiben, werden Vorgänge wie das Erstellen oder das Löschen einer Geometriespalte, durch dafür eigens von OpenGIS festgelegten Prozeduren ausgeführt.

Es gibt zwei OpenGIS Metadatentabellen: SPATIAL_REF_SYS und GEOMETRY_COLUMNS. Die SPATIAL_REF_SYS Tabelle enthält die numerischen Identifikatoren und textlichen Beschreibungen der in der Datenbank verwendeten Koordinatensysteme.

4.3.1. Die SPATIAL_REF_SYS Tabelle und Koordinatenreferenzsysteme

Die Tabelle "spatial_ref_sys" ist eine mit PostGIS kommende und OGC-konforme Datenbanktabelle, die über 3000 bekannte Koordinatenreferenzsysteme enthält, sowie Details zur Koordinatentransformation zwischen diesen.

Obwohl in der PostGIS Tabelle "spatial_ref_sys" über 3000 der gebräuchlichsten Koordinatenreferenzsysteme definiert sind, die mit der Bibliothek "Proj4" gehandhabt werden können, enthält sie nicht alle bekannten Projektionen. Sie können auch ihre eigenen Projektionen in der Tabelle definieren, falls Sie mit den Konstrukten von "Proj4" vertraut sind. Sie sollten nicht außer Acht lassen, dass die meisten Koordinatenreferenzsysteme regional sind und außerhalb des vorgesehenen Bereichs keinen Sinn haben.

Eine hervorragende Quelle zum Auffinden von Koordinatenreferenzsystemen, welche nicht in der Grundmenge enthalten sind, ist http://spatialreference.org/

Einige der häufiger eingesetzten Koordinatenreferenzsysteme sind: 4326 - WGS 84 Long Lat, 4269 - NAD 83 Long Lat, 3395 - WGS 84 World Mercator, 2163 - US National Atlas Equal Area, Koordinatenreferenzsysteme für jede NAD 83, WGS 84 und UTM Zone - UTM Zonen sind ideal für Messungen, decken aber nur 6 Grad breite, vertikale Zonen ab.

Verschiedenste Koordinatenreferenzsysteme "US State Plane" (auf Meter und Fuß basierend) - üblicherweise 2 pro US Staat. Die meisten auf Meter basierten befinden sich in der Grundmenge, aber viele der auf Fuß basierten, oder von ESRI erzeugten müssen von spatialreference.org heruntergeladen werden.

Genauere Angaben zur Ermittlung der UTM Zone für ein bestimmtes Gebiet finden Sie unter utmzone PostGIS plpgsql helper function.

Die SPATIAL_REF_SYS Tabelle ist folgendermaßen definiert:

CREATE TABLE spatial_ref_sys (
  srid       INTEGER NOT NULL PRIMARY KEY,
  auth_name  VARCHAR(256),
  auth_srid  INTEGER,
  srtext     VARCHAR(2048),
  proj4text  VARCHAR(2048)
)

Die SPATIAL_REF_SYS Spalten folgendermaßen:

SRID

Ein ganzzahliger Wert, der das Koordinatenreferenzsystem (SRS) innerhalb der Datenbank eindeutig ausweist.

AUTH_NAME

Der Name des Standards oder der Normungsorganisation, unter dem dieses Koordinatenreferenzsystem zitiert wird. Zum Beispiel ist "EPSG" ein gültiger AUTH_NAME.

AUTH_SRID

Die von der in AUTH_NAME zitierten Quelle festgelegte ID des Koordinatenreferenzsystems. Im Falle von EPSG ist dies der EPSG Projektionscode.

SRTEXT

Die Well-Known-Text Darstellung des Koordinatenreferenzsystems. Ein Beispiel dazu:

PROJCS["NAD83 / UTM Zone 10N",
  GEOGCS["NAD83",
        DATUM["North_American_Datum_1983",
          SPHEROID["GRS 1980",6378137,298.257222101]
        ],
        PRIMEM["Greenwich",0],
        UNIT["degree",0.0174532925199433]
  ],
  PROJECTION["Transverse_Mercator"],
  PARAMETER["latitude_of_origin",0],
  PARAMETER["central_meridian",-123],
  PARAMETER["scale_factor",0.9996],
  PARAMETER["false_easting",500000],
  PARAMETER["false_northing",0],
  UNIT["metre",1]
]

Für eine Auflistung der EPSG Projektionscodes und deren entsprechende WKT Darstellung siehe http://www.opengeospatial.org/. Eine allgemeine Erläuterung zu WKT finden Sie in der OpenGIS "Coordinate Transformation Services Implementation Specification" unter http://www.opengeospatial.org/standards. Information zur European Petroleum Survey Group (EPSG) und deren Datenbank über Koordinatenreferenzsysteme finden Sie unter http://www.epsg.org.

PROJ4TEXT

PostGIS verwendet die Bibliothek "Proj4" zur Koordinatentransformation. Die Spalte PROJ4TEXT enthält eine Proj4 Zeichenfolge mit der Definition des Koordinatensystems für eine bestimmte SRID. Zum Beispiel:

+proj=utm +zone=10 +ellps=clrk66 +datum=NAD27 +units=m

Weiterführende Information finden Sie auf der Proj4 Webseite unter http://trac.osgeo.org/proj/. Die Datei spatial_ref_sys.sql enthält sowohl SRTEXT als auch PROJ4TEXT Definitionen aller EPSG Projektionen.

4.3.2. Der View GEOMETRY_COLUMNS

GEOMETRY_COLUMNS ist ein View der den Systemkatalog der Datenbank ausliest. Er hat folgende Struktur:

\d geometry_columns
View "public.geometry_columns"
      Column       |          Type          | Modifiers
-------------------+------------------------+-----------
 f_table_catalog   | character varying(256) |
 f_table_schema    | character varying(256) |
 f_table_name      | character varying(256) |
 f_geometry_column | character varying(256) |
 coord_dimension   | integer                |
 srid              | integer                |
 type              | character varying(30)  |

Die Spalten bedeuten:

F_TABLE_CATALOG, F_TABLE_SCHEMA, F_TABLE_NAME

Der vollständige Name der Tabelle, welche die Geometriespalte enthält. Die Bezeichnungen "catalog" und "schema" kommen von Oracle. Es gibt keine Entsprechung in PostgreSQL für "catalog", weshalb diese Spalte leer bleibt - für "schema" wird der Name des Schemas in PostgreSQL verwendet (standardmäßig public).

F_GEOMETRY_COLUMN

Der Name der Geometriespalte in der Feature-Tabelle.

COORD_DIMENSION

Die räumliche Dimension (2-, 3- oder 4-dimensional) der Geometriespalte.

SRID

Der Identifikator des Koordinatenreferenzsystems, welches für die Geometrie in dieser Tabelle verwendet wird. Dieser ist ein Fremdschlüssel, der sich auf die Tabelle SPATIAL_REF_SYS bezieht.

TYPE

Der Datentyp des Geoobjekts. Um die räumliche Spalte auf einen einzelnen Datentyp zu beschränken, benutzen Sie bitte: POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION oder die entsprechenden XYM Versionen POINTM, LINESTRINGM, POLYGONM, MULTIPOINTM, MULTILINESTRINGM, MULTIPOLYGONM und GEOMETRYCOLLECTIONM. Für uneinheitliche Kollektionen (gemischete Datentypen) können Sie den Datentyp "GEOMETRY" verwenden.

[Note]

Dieses Attribut gehört (wahrscheinlich) nicht zur OpenGIS Spezifikation, wird aber benötigt um homogene Datentypen zu gewährleisten.

4.3.3. Erstellung einer räumlichen Tabelle

Die Erzeugung einer Tabelle mit räumlichen Daten kann in einem Schritt ausgeführt werden. Dies wird im folgenden Beispiel demonstriert, welches eine Straßentabelle mit einer geometrischen Spalte für 2D Linienzüge in WGS84 Länge/Breite erzeugt

CREATE TABLE ROADS (ID serial, ROAD_NAME text, geom geometry(LINESTRING,4326) );

Wir können zusätzliche Spalten hinzufügen, indem wir den normalen ALTER TABLE Befehl verwenden. Wir zeigen dies im nächsten Beispiel, wo wir einen 3D-Linienzug hinzufügen.

ALTER TABLE roads ADD COLUMN geom2 geometry(LINESTRINGZ,4326);

4.3.4. Geometrische Spalten in "geometry_columns" händisch registrieren

Zwei Fälle bei denen Sie dies benötigen könnten sind SQL-Views und Masseninserts. Beim Fall von Masseninserts können Sie die Registrierung in der Tabelle "geometry_columns" korrigieren, indem Sie auf die Spalte einen CONSTRAINT setzen oder ein "ALTER TABLE" durchführen. Falls Ihre Spalte Typmod basiert ist, geschieht die Registrierung beim Erstellungsprozess auf korrekte Weise, so dass Sie hier nichts tun müssen. Auch Views, bei denen keine räumliche Funktion auf die Geometrie angewendet wird, werden auf gleiche Weise wie die Geometrie der zugrunde liegenden Tabelle registriert.

-- Angenommen Sie erstellen folgenden View
CREATE VIEW  public.vwmytablemercator AS
        SELECT gid, ST_Transform(geom,3395) As geom, f_name
        FROM public.mytable;

-- Für eine korrekte Registrierung
-- wird eine Typumwandlung der Geometrie benötigt
--
DROP VIEW public.vwmytablemercator;
CREATE VIEW  public.vwmytablemercator AS
        SELECT gid, ST_Transform(geom,3395)::geometry(Geometry, 3395) As geom, f_name
        FROM public.mytable;

-- Wenn Sie sicher sind, das es sich bei der Geometrie um ein 2D-Polygon handelt, können Sie folgendes tun
DROP VIEW public.vwmytablemercator;
CREATE VIEW  public.vwmytablemercator AS
        SELECT gid, ST_Transform(geom,3395)::geometry(Polygon, 3395) As geom, f_name
        FROM public.mytable;
-- Angenommen Sie haben eine abgeleitete Tabelle über ein Masseninsert erzeugt
SELECT poi.gid, poi.geom, citybounds.city_name
INTO myschema.my_special_pois
FROM poi INNER JOIN citybounds ON ST_Intersects(citybounds.geom, poi.geom);

-- Einen 2D Index auf die neue Tabelle legen
CREATE INDEX idx_myschema_myspecialpois_geom_gist
  ON myschema.my_special_pois USING gist(geom);

-- Falls Ihre Punkte 3D-Punkte oder 3M-Punkte sind,
-- können Sie einen ND-Index anstatt eines 2D-Indexes erstellen
CREATE INDEX my_special_pois_geom_gist_nd
        ON my_special_pois USING gist(geom gist_geometry_ops_nd);

-- Um die Geometriespalte der neuen Tabelle in geometry_columns händisch zu registrieren.
-- Beachten Sie bitte, dass dies auch die zugrundeliegende Struktur der Tabelle ändert,
-- um die Spalte Typmod basiert zu machen.
SELECT populate_geometry_columns('myschema.my_special_pois'::regclass);

-- Wenn Sie PostGIS 2.0 verwenden und aus welchem Grund auch immer
-- das alte Verhalten mit auf CONSTRAINTs basierender Definition benötigen
-- (wie im Fall von vererbten Tabellen bei denen nicht alle Kindtabellen denselben Datentyp und dieselbe SRID aufweisen),
-- setzen Sie das optionale Argument "use_typmod" auf FALSE
SELECT populate_geometry_columns('myschema.my_special_pois'::regclass, false); 

Obwohl die alte auf CONSTRAINTs basierte Methode immer noch unterstützt wird, wird eine auf Constraints basierende Geometriespalte, die direkt in einem View verwendet wird, nicht korrekt in geometry_columns registriert. Eine Typmod basierte wird korrekt registriert. Im folgenden Beispiel definieren wir eine Spalte mit Typmod und eine andere mit Constraints.

CREATE TABLE pois_ny(gid SERIAL PRIMARY KEY, poi_name text, cat text, geom geometry(POINT,4326));
SELECT AddGeometryColumn('pois_ny', 'geom_2160', 2160, 'POINT', 2, false);

In psql:

\d pois_ny;

Wir sehen, das diese Spalten unterschiedlich definiert sind -- eine mittels Typmodifizierer, eine nutzt einen Constraint

Table "public.pois_ny"
  Column   |         Type          |                       Modifiers

-----------+-----------------------+------------------------------------------------------
 gid       | integer               | not null default nextval('pois_ny_gid_seq'::regclass)
 poi_name  | text                  |
 cat       | character varying(20) |
 geom      | geometry(Point,4326)  |
 geom_2160 | geometry              |
Indexes:
    "pois_ny_pkey" PRIMARY KEY, btree (gid)
Check constraints:
    "enforce_dims_geom_2160" CHECK (st_ndims(geom_2160) = 2)
    "enforce_geotype_geom_2160" CHECK (geometrytype(geom_2160) = 'POINT'::text
        OR geom_2160 IS NULL)
    "enforce_srid_geom_2160" CHECK (st_srid(geom_2160) = 2160)

Beide registrieren sich korrekt in "geometry_columns"

SELECT f_table_name, f_geometry_column, srid, type
        FROM geometry_columns
        WHERE f_table_name = 'pois_ny';
f_table_name | f_geometry_column | srid | type
-------------+-------------------+------+-------
pois_ny      | geom              | 4326 | POINT
pois_ny      | geom_2160         | 2160 | POINT

Jedoch -- wenn wir einen View auf die folgende Weise erstellen

CREATE VIEW vw_pois_ny_parks AS
SELECT *
  FROM pois_ny
  WHERE cat='park';

SELECT f_table_name, f_geometry_column, srid, type
        FROM geometry_columns
        WHERE f_table_name = 'vw_pois_ny_parks';

Die Typmod basierte geometrische Spalte eines View registriert sich korrekt, die auf Constraint basierende nicht.

f_table_name   | f_geometry_column | srid |   type
------------------+-------------------+------+----------
 vw_pois_ny_parks | geom              | 4326 | POINT
 vw_pois_ny_parks | geom_2160         |    0 | GEOMETRY

Dies kann sich bei zukünftigen Versionen von PostGIS ändern, vorerst müssen Sie aber folgendes ausführen, um die auf Constraint basierende Spalte eines View korrekt zu registrieren:

DROP VIEW vw_pois_ny_parks;
CREATE VIEW vw_pois_ny_parks AS
SELECT gid, poi_name, cat,
  geom,
  geom_2160::geometry(POINT,2160) As geom_2160
  FROM pois_ny
  WHERE cat = 'park';
SELECT f_table_name, f_geometry_column, srid, type
        FROM geometry_columns
        WHERE f_table_name = 'vw_pois_ny_parks';
f_table_name   | f_geometry_column | srid | type
------------------+-------------------+------+-------
 vw_pois_ny_parks | geom              | 4326 | POINT
 vw_pois_ny_parks | geom_2160         | 2160 | POINT

4.3.5. Wahrung der OGC-Konformität von Geometrien

PostGIS ist mit den Open Geospatial Consortium (OGC) OpenGIS Spezifikationen konform. Daher setzen viele PostGIS Methoden voraus, dass die Geometrien mit denen sie rechnen sowohl "Simple" als auch "Valid" sind . Zum Beispiel hat es keinen Sinn, die Fläche eines Polygons zu berechnen, das eine Insel aufweist, die ausserhalb des Polygons festgelegt ist, oder ein Polygon aus einer Begrenzungslinie zu konstruieren, welche nicht "simple" ist.

Entsprechend der OGC Spezifikationen ist eine simple Geometrie eine solche, die sich nicht selbst überschneidet oder berührt und bezieht sich in erster Linie auf 0- und 1-dimensionale Geometrien (insbesondere [MULTI]POINT, [MULTI]LINESTRING). Andererseits bezieht sich die Validität einer Geometrie hauptsächlich auf 2-dimensionale Geometrien (insbesondere [MULTI]POLYGON) und definiert die Menge an Aussagen, welche ein valides/gültiges Polygon auszeichnen. Die Beschreibung einer jeden geometrischen Klasse schließt bestimmte Bedingungen mit ein, welche die Simplizität und Validität von Geometrien näher beschreiben.

Da ein POINT ein 0-dimensionales geometrisches Objekt ist, ist er von vornherein simple.

MULTIPOINTs sind simple, wenn sich keine zwei Koordinaten (POINTs) decken (keine identischen Koordinatenpaare aufweisen).

Ein LINESTRING ist simple, wenn er nicht zweimal durch denselben POINT geht (ausgenommen bei Endpunkten, wo dieser als linearer Ring benannt wird und zusätzlich als geschlossen angesehen wird).

(a)

(b)

(c)

(d)

(a) und (c) sind simple LINESTRINGs, (b) und (d) nicht.

Ein MULTILINESTRING ist nur dann simple, wenn alle seine Elemente "simple" sind und die einzigen Überschneidungen zwischen zwei Elementen nur an jenen POINTs auftreten, die an den Begrenzungen der beiden Elemente liegen.

(e)

(f)

(g)

(e) und (f) sind simple MULTILINESTRINGs, (g) nicht.

Definitionsgemäß ist ein POLYGON immer simple. Es ist valid, wenn sich keine zwei Ringe an der Begrenzung (bestehend aus einem äußeren Ring und inneren Ringen) kreuzen. Die Begrenzung eines POLYGONs darf an einem POINT schneiden, allerdings nur als Tangente (insbesondere nicht an einer Linie). Ein POLYGON darf keine Schnittlinien oder "Spikes" aufweisen und die inneren Ringe müssen zur Gänze im äußeren Ring enthalten sein.

(h)

(i)

(j)

(k)

(l)

(m)

(h) und (i) sind valide POLYGONe, (j-m) können nicht als einzelne POLYGONe dargestellt werden, aber (j) und (m) können als ein valides MULTIPOLYGON dargestellt werden.

Ein MULTIPOLYGON ist dann und nur dann valide, wenn alle seine Elemente valide sind und sich das Innere zweier Elemente nicht überschneidet. Die Begrenzungen zweier Elemente können sich berühren, allerdings nur an einer endlichen Anzahl von POINTs.

(n)

(o)

(p)

(n) und (o) sind keine validen MULTIPOLYGONs. Hingegen ist (p) valid.

Die meisten von der GEOS Bibliothek implementierten Funktionen beruhen auf der Annahme, dass die verwendete Geometrie - entsprechend der OpenGIS Simple Feature Spezifikation - valide ist. Um die Simplizität und Validität einer Geometrie festzustellen, können Sie ST_IsSimple() und ST_IsValid() verwenden.

-- Üblicherweise hat es keinen Sinn lineare Geometrien
-- auf Validität zu überprüfen, da immer TRUE zurückgegeben wird.
-- Aber in diesem Beispiel erweitert PostGIS die OGC Definition von IsValid
-- indem es FALSE zurückgibt, wenn ein LineString weniger als 2 *eindeutige* Stützpunkte aufweist.
gisdb=# SELECT
   ST_IsValid('LINESTRING(0 0, 1 1)'),
   ST_IsValid('LINESTRING(0 0, 0 0, 0 0)');

 st_isvalid | st_isvalid
------------+-----------
      t     |     f

Standardmäßig überprüft PostGIS eine Geometrieeingabe nicht auf Validität, da Validitätstests von komplexen Geometrien, insbesondere Polygonen, viel CPU Zeit beanspruchen. Fall Sie Ihren Datenquellen nicht trauen, können Sie eine Überprüfung Ihrer Tabellen durch eine "Check Constraint"/Prüfbeschränkung erzwingen:

ALTER TABLE mytable
  ADD CONSTRAINT geometry_valid_check
        CHECK (ST_IsValid(the_geom));

Falls Sie irgendwelche seltsamen Fehlermeldungen, wie "GEOS Intersection() threw an error!" erhalten, obwohl sie eine PostGIS Funktion mit validen Eingabegeometrien aufgerufen haben, ist es wahrscheinlich dass Sie einen Fehler in PostGIS oder einer von PostGIS verwendeten Bibliothek gefunden haben. In diesem Fall sollten Sie das PostGIS Entwicklerteam kontaktieren. Dasselbe gilt, wenn eine PostGIS Funktion auf eine valide Eingabegeometrie eine invalide Geometrie zurückgibt.

[Note]

Eine streng konforme OGC-Geometrie hat keine Z- oder M-Werte. Die Funktion ST_IsValid() betrachtet höhere geometrische Dimensionen nicht als invalide! Aufrufe von AddGeometryColumn() fügen einen Check-Constraint für die geometrische Dimension hinzu, weshalb es hier ausreicht 2 anzugeben.

4.3.6. DE-9IM-Matrix (DE-9IM)

Manchmal kommt es vor, dass die typischen räumlichen Aussagen (ST_Intersects, ST_Contains, ST_Crosses, ST_Touches, ...) an sich nicht ausreichen, um den verlangten räumlichen Filter auf geeignete Weise zu liefern.

Betrachten Sie zum Beispiel einen linearen Datensatz, der ein Straßennetz darstellt. Es kann sein, dass ein GIS-Analyst die Aufgabe hat, alle Straßenabschnitte herauszufinden, die sich gegenseitig nicht an einem Punkt sondern entlang einer Linie kreuzen, da dies möglicherweise einer Unternehmensvorschrift widerspricht. In diesem Fall liefert ST_Crosses nicht den passenden räumlichen Filter, da bei linearen Geoobjekten nur dann TRUE zurückgeben wird, wenn sie sich an einem Punkt kreuzen.

Eine zweistufige Lösung kann sein, dass man zuerst die eigentliche Verschneidung (ST_Intersection) von Straßenabschnittspaaren, die sich räumlich überschneiden (ST_Intersects) ausführt und anschließend den ST_GeometryType der Verschneidung mit 'LINESTRING' vergleicht (vermutlich muss man sich mit Fällen auseinandersetzen die GEOMETRYCOLLECTIONs von [MULTI]POINTs, [MULTI]LINESTRINGs, etc. zurückgeben).

Eine elegantere/schnellere Lösung wäre sicherlich wünschenswert.

Ein zweites (theoretisches) Beispiel wäre, dass ein GIS-Analyst versucht, alle Anlegestellen oder Kais, welche die Begrenzung eines Sees entlang einer Linie überschneiden und bei denen nur ein Ende der Anlegestelle an der Küste liegt. Anders ausgedrückt, wo ein Kai nicht zur Gänze im See liegt, da er den See entlang einer Linie schneidet und seine Endpunkte sowohl zur Gänze in und auf der Begrenzung des Sees liegen. Dazu kann es nötig sein, dass der Analyst eine Kombination von Aussagen ausführen muss, um die gesuchten Geoobjekte herauszufiltern:

Somit stürzen wir uns auf die DE-9IM-Matrix, oder kurz DE-9IM

4.3.6.1. Theorie

Gemäß der OpenGIS Simple Features Implementation Specification for SQL, ist der grundlegende Ansatz für einen Lagevergleich von zwei geometrischen Objekten, die paarweise Überprüfung der Verschneidung des Inneren, der Begrenzung und des Äusseren der beiden geometrischen Objekte und der Einstufung der Beziehung zwischen den beiden geometrischen Objekten an Hand der Einträge in die resultierende 'Verschneidungs'-Matrix.

Boundary

Die Begrenzung einer Geometrie ist die geometrische Grundmenge der nächst kleineren Dimension. Bei POINTs, die die Dimension 0 haben ist die Begrenzung die leere Menge. Die Begrenzung eines LINESTRINGs sind die zwei Endpunkte. Bei POLYGONen entspricht die Begrenzung jenen Linien, die die äußeren und inneren Ringe zusammensetzen.

Interior

Die Innenseite/interior einer Geometrie besteht aus jenen Punkten einer Geometrie, die zurückbleiben, wenn die Außenbegrenzung/boundary entfernt wird. Bei POINTs ist die Innenseite der POINT selbst. Die Innenseite eines LINESTRINGs ist die Menge der echten Punkte zwischen den Endpunkten. Bei POLYGONen entspricht die Innenseite der Fläche innerhalb des Polygons.

Exterior

Die Außenseite/exterior einer Geometrie ist durch die Grundgesamtheit gegeben. Das ist jene Fläche, die nicht auf der Innenseite/interior oder auf der Begrenzung der Geometrie liegt.

Gegeben sei die Geometrie a, wobei I(a), B(a), und E(a) das Innere/Interior, die Begrenzung/Boundary und dasÄussere/Exterior von a sind; die mathematische Formulierung der Matrix lautet:

 InteriorBoundaryExterior
Interiordim( I(a) ∩ I(b) )dim( I(a) ∩ B(b) )dim( I(a) ∩ E(b) )
Boundarydim( B(a) ∩ I(b) )dim( B(a) ∩ B(b) )dim( B(a) ∩ E(b) )
Exteriordim( E(a) ∩ I(b) )dim( E(a) ∩ B(b) )dim( E(a) ∩ E(b) )

Wobei dim(a), so wie von ST_Dimension festgelegt, die Dimension von a ist, aber zu der Domäne von {0,1,2,T,F,*} gehört.

  • 0 => point

  • 1 => line

  • 2 => area

  • T => {0,1,2}

  • F => Leere Menge

  • * => braucht nicht zu kümmern

Bildlich schaut dies für zwei überlappende Polygongeometrien folgendermaßen aus:

 

 InteriorBoundaryExterior
Interior

dim(...) = 2

dim(...) = 1

dim(...) = 2

Boundary

dim(...) = 1

dim(...) = 0

dim(...) = 1

Exterior

dim(...) = 2

dim(...) = 1

dim(...) = 2

Von links nach rechts und von oben nach unten gelesen wird die Dimensionsmatrix durch '212101212' dargestellt.

Eine Beziehungsmatrix, welche das erste Beispiel von zwei Linien, die sich auf einer Linie schneiden, abbildet, würde '102101FF2' entsprechen.

-- Identifizierung der Strassenabschnitte, die eine Linie kreuzen
SELECT a.id
FROM roads a, roads b
WHERE a.id != b.id
AND a.geom && b.geom
AND ST_Relate(a.geom, b.geom, '1*1***1**');

Eine Beziehungsmatrix, welche das zweite Beispiel mit den Kais, die teilweise an der Uferlinie des Sees liegen, abbildet, würde '102101FF2' entsprechen.

-- Ermittlung von Dämmen, die teilweise an der Uferlinie eines Sees liegen
SELECT a.lake_id, b.wharf_id
FROM lakes a, wharfs b
WHERE a.geom && b.geom
AND ST_Relate(a.geom, b.geom, '102101FF2');

Für weiterführende Information siehe:

4.4. GIS (Vektor) Daten laden

Sobald Sie eine räumliche Tabelle erstellt haben, können Sie GIS Daten in die Datenbank laden. Zurzeit gibt es zwei Möglichkeiten, Daten in die PostGIS/PostgreSQL Datenbank zu importieren: die Verwendung von formatierten SQL-Anweisungen oder der Shapefile Loader/Dumper.

4.4.1. Daten via SQL laden

Wenn Sie Ihre Daten in eine Textdarstellung konvertieren können, dann ist möglicherweise die Verwendung von formatiertem SQL der leichteste Weg um die Daten in PostGIS zu importieren. Wie bei Oracle und anderen Datenbanken, können die Daten über Masseninserts geladen werden, indem eine große Textdatei, in der sich zahlreiche SQL "INSERT" Anweisungen befinden, an die SQL-Konsole weitergeleitet wird.

Eine Importdatei (z.B. roads.sql) könnte folgendermaßen aussehen:

BEGIN;
INSERT INTO roads (road_id, roads_geom, road_name)
  VALUES (1,'LINESTRING(191232 243118,191108 243242)','Jeff Rd');
INSERT INTO roads (road_id, roads_geom, road_name)
  VALUES (2,'LINESTRING(189141 244158,189265 244817)','Geordie Rd');
INSERT INTO roads (road_id, roads_geom, road_name)
  VALUES (3,'LINESTRING(192783 228138,192612 229814)','Paul St');
INSERT INTO roads (road_id, roads_geom, road_name)
  VALUES (4,'LINESTRING(189412 252431,189631 259122)','Graeme Ave');
INSERT INTO roads (road_id, roads_geom, road_name)
  VALUES (5,'LINESTRING(190131 224148,190871 228134)','Phil Tce');
INSERT INTO roads (road_id, roads_geom, road_name)
  VALUES (6,'LINESTRING(198231 263418,198213 268322)','Dave Cres');
COMMIT;

Diese Datei kann dann über die "psql" SQL-Konsole sehr leicht nach PostgreSQL weitergeleitet werden:

psql -d [database] -f roads.sql

4.4.2. shp2pgsql: Verwendung des ESRI-Shapefile Laders

Der shp2pgsql Datenlader wandelt ESRI Shapefiles in eine SQL-Datei um, die für das Einfügen in eine PostGIS/PostgreSQL Datenbank mit der "psql"-Konsole, sowohl im Geometrie- als auch im Geographie-Format, geeignet ist. Der Loader besitzt eine Reihe von Betriebsmodi, die durch Flags auf der Befehlszeile ausgewählt werden:

Zusätzlich zu dem befehlszeilenorientierten Lader "shp2pgsql" gibt es auch die graphische Schnittstelle shp2pgssql-gui, welche fast ebensoviele Optionen wie der befehlszeilenorientierte Lader zur Verfügung stellt. Für viele Anwender, die mit der Befehlszeile nicht versiert sind, oder mit PostGIS erst beginnen, ist die GUI möglicherweise einfacher zu bedienen. Sie kann auch in PgAdminIII als Plugin konfiguriert werden.

(c|a|d|p) Dies sind sich gegenseitig ausschließende Optionen:

-c

Erstellt eine neue Tabelle und füllt sie von einem Shapefile her. Dies ist der Standardmodus.

-a

Fügt Daten aus dem Shapefile zu der Datenbanktabelle hinzu. Beachten Sie bitte, falls Sie diese Option verwenden um mehrere Dateien zu laden, dass die Attribute und Datentypen in den Dateien übereinstimmen müssen.

-d

Löscht die Datenbanktabelle, bevor eine neue Tabelle mit den Daten vom Shapefile befüllt wird.

-p

Erzeugt nur den SQL-Code zur Erstellung der Tabelle, ohne irgendwelche Daten hinzuzufügen. Kann verwendet werden, um die Erstellung und das Laden einer Tabelle vollständig zu trennen.

-?

Zeigt die Hilfe an.

-D

Verwendung des PostgreSQL "dump" Formats für die Datenausgabe. Kann mit -a, -c und -d kombiniert werden. Ist wesentlich schneller als das standardmäßige SQL "insert" Format. Verwenden Sie diese Option wenn Sie sehr große Datensätze haben.

-s [<FROM_SRID>:]<SRID>

Erstellt und befüllt die Geometrietabelle mit der angegebenen SRID. Optional kann für das Shapefile eine FROM_SRID angegeben werden, worauf dann die Geometrie in die Ziel-SRID projiziert wird.

-k

Erhält die Groß- und Kleinschreibung (Spalte, Schema und Attribute). Beachten Sie bitte, dass die Attributnamen in Shapedateien immer Großbuchstaben haben.

-i

Wandeln Sie alle Ganzzahlen in standard 32-bit Integer um, erzeugen Sie keine 64-bit BigInteger, auch nicht dann wenn der DBF-Header dies unterstellt.

-I

Einen GIST Index auf die Geometriespalte anlegen.

-m

-m a_file_name bestimmt eine Datei, in welcher die Abbildungen der (langen) Spaltennamen in die 10 Zeichen langen DBF Spaltennamen festgelegt sind. Der Inhalt der Datei besteht aus einer oder mehreren Zeilen die jeweils zwei, durch Leerzeichen getrennte Namen enthalten, aber weder vorne noch hinten mit Leerzeichen versehen werden dürfen. Zum Beispiel:

COLUMNNAME DBFFIELD1
AVERYLONGCOLUMNNAME DBFFIELD2

-S

Erzeugt eine Einzel- anstatt einer Mehrfachgeometrie. Ist nur erfolgversprechend, wenn die Geometrie auch tatsächlich eine Einzelgeometrie ist (insbesondere gilt das für ein Mehrfachpolygon/MULTIPOLYGON, dass nur aus einer einzelnen Begrenzung besteht, oder für einen Mehrfachpunkt/MULTIPOINT, der nur einen einzigen Knoten aufweist).

-t <dimensionality>

Zwingt die Ausgabegeometrie eine bestimmte Dimension anzunehmen. Sie können die folgenden Zeichenfolgen verwenden, um die Dimensionalität anzugeben: 2D, 3DZ, 3DM, 4D.

Wenn die Eingabe weniger Dimensionen aufweist als angegeben, dann werden diese Dimensionen bei der Ausgabe mit Nullen gefüllt. Wenn die Eingabe mehr Dimensionen als angegeben aufweist werden diese abgestreift.

-w

Ausgabe im Format WKT anstatt WKB. Beachten Sie bitte, dass es hierbei zu Koordinatenverschiebungen infolge von Genauigkeitsverlusten kommen kann.

-e

Jede Anweisung einzeln und nicht in einer Transaktion ausführen. Dies erlaubt den Großteil auch dann zu laden, also die guten Daten, wenn eine Geometrie dabei ist die Fehler verursacht. Beachten Sie bitte das dies nicht gemeinsam mit der -D Flag angegeben werden kann, da das "dump" Format immer eine Transaktion verwendet.

-W <encoding>

Gibt die Codierung der Eingabedaten (dbf-Datei) an. Wird die Option verwendet, so werden alle Attribute der dbf-Datei von der angegebenen Codierung nach UTF8 konvertiert. Die resultierende SQL-Ausgabe enthält dann den Befehl SET CLIENT_ENCODING to UTF8, damit das Back-end wiederum die Möglichkeit hat, von UTF8 in die, für die interne Nutzung konfigurierte Datenbankcodierung zu decodieren.

-N <policy>

Umgang mit NULL-Geometrien (insert*, skip, abort)

-n

-n Es wird nur die *.dbf-Datei importiert. Wenn das Shapefile nicht Ihren Daten entspricht, wird automatisch auf diesen Modus geschaltet und nur die *.dbf-Datei geladen. Daher müssen Sie diese Flag nur dann setzen, wenn sie einen vollständigen Shapefile-Satz haben und lediglich die Attributdaten, und nicht die Geometrie, laden wollen.

-G

Verwendung des geographischen Datentyps in WGS84 (SRID=4326), anstelle des geometrischen Datentyps (benötigt Längen- und Breitenangaben).

-T <tablespace>

Den Tablespace für die neue Tabelle festlegen. Solange der -X Parameter nicht angegeben wird, benutzen die Indizes weiterhin den standardmäßig festgelegten Tablespace. Die PostgreSQL Dokumentation beinhaltet eine gute Beschreibung, wann es sinnvoll ist, eigene Tablespaces zu verwenden.

-X <tablespace>

Den Tablespace bestimmen, in dem die neuen Tabellenindizes angelegt werden sollen. Gilt für den Primärschlüsselindex und wenn "-l" verwendet wird, auch für den räumlichen GIST-Index.

Eine beispielhafte Sitzung, in welcher der Loader verwendet wird, um eine Eingabedatei zu erzeugen und anschließend hochzuladen, könnte folgendermaßen aussehen:

# shp2pgsql -c -D -s 4269 -i -I shaperoads.shp myschema.roadstable > roads.sql
# psql -d roadsdb -f roads.sql

Konvertierung und Import können über UNIX-Pipes in einem Schritt erfolgen:

# shp2pgsql shaperoads.shp myschema.roadstable | psql -d roadsdb

4.5. Geodaten abrufen

Daten können entweder über SQL oder mit dem Shapefile Loader/Dumper aus der Datenbank entnommen werden. Im Abschnitt über SQL werden einige Operatoren besprochen, die für Vergleiche und Abfragen von Geotabellen zur Verfügung stehen.

4.5.1. Daten mit SQL abrufen

Die direkteste Methode, um Daten aus der Datenbank abzurufen, ist eine SQL Select-Anfrage. Dadurch kann die Anzahl der resultierenden Datensätze und Attribute reduziert und in eine lesbare Textdatei überspielt werden:

db=# SELECT road_id, ST_AsText(road_geom) AS geom, road_name FROM roads;

road_id | geom                                    | road_name
--------+-----------------------------------------+-----------
          1 | LINESTRING(191232 243118,191108 243242) | Jeff Rd
          2 | LINESTRING(189141 244158,189265 244817) | Geordie Rd
          3 | LINESTRING(192783 228138,192612 229814) | Paul St
          4 | LINESTRING(189412 252431,189631 259122) | Graeme Ave
          5 | LINESTRING(190131 224148,190871 228134) | Phil Tce
          6 | LINESTRING(198231 263418,198213 268322) | Dave Cres
          7 | LINESTRING(218421 284121,224123 241231) | Chris Way
(6 rows)

Wie auch immer, manchmal wird eine Einschränkung notwendig sein, um die Anzahl der zurückgegebenen Werte zu reduzieren. Falls es sich um eine Beschränkung auf ein Attribut handelt, können Sie dieselbe SQL-Syntax verwenden wie bei jeder anderen Nicht-Geometrietabelle. Für räumliche Beschränkungen sind folgende Operatoren verfügbar/nützlich:

ST_Intersects

Diese Funktion bestimmt ob sich zwei geometrische Objekte einen gemeinsamen Raum teilen

=

Überprüft, ob zwei Geoobjekte geometrisch ident sind. Zum Beispiel, ob 'POLYGON((0 0,1 1,1 0,0 0))' ident mit 'POLYGON((0 0,1 1,1 0,0 0))' ist (ist es).

Beachte: vor PostGIS 2.4 wuden nur die umschreibenden Rechtecke der Geometrie verglichen.

Außerdem können Sie diese Operatoren in Anfragen verwenden. Beachten Sie bitte, wenn Sie eine Geometrie oder eine Box auf der SQL-Befehlszeile eingeben, dass Sie die Zeichensatzdarstellung explizit in eine Geometrie umwandeln müssen. 312 ist ein fiktives Koordinatenreferenzsystem das zu unseren Daten passt. Also, zum Beispiel:

SELECT road_id, road_name
  FROM roads
  WHERE roads_geom='SRID=312;LINESTRING(191232 243118,191108 243242)'::geometry;

Die obere Abfrage würde einen einzelnen Datensatz aus der Tabelle "ROADS_GEOM" zurückgeben, in dem die Geometrie gleich dem angegebenen Wert ist.

Überprüfung ob einige der Strassen in die Polygonfläche hineinreichen:

SELECT road_id, road_name
FROM roads
WHERE ST_Intersects(roads_geom, 'SRID=312;POLYGON((...))');

Die häufigsten räumlichen Abfragen werden vermutlich in einem bestimmten Ausschnitt ausgeführt. Insbesondere von Client-Software, wie Datenbrowsern und Kartendiensten, die auf diese Weise die Daten für die Darstellung eines "Kartenausschnitts" erfassen.

Der Operator "&&" kann entweder mit einer BOX3D oder mit einer Geometrie verwendet werden. Allerdings wird auch bei einer Geometrie nur das Umgebungsrechteck für den Vergleich herangezogen.

Die Abfrage zur Verwendung des "BOX3D" Objekts für einen solchen Ausschnitt sieht folgendermaßen aus:

SELECT ST_AsText(roads_geom) AS geom
FROM roads
WHERE
  roads_geom && ST_MakeEnvelope(191232, 243117,191232, 243119,312);

Achten Sie auf die Verwendung von SRID=312, welche die Projektion Einhüllenden/Enveloppe bestimmt.

4.5.2. Verwendung des Dumper

Der Tabellendumper pgsql2shp verbindet sich direkt mit der Datenbank und konvertiert eine Tabelle (evtl. durch eine Abfrage festgelegt) in eine Shapedatei. Die grundlegende Syntax lautet:

pgsql2shp [<options>] <database> [<schema>.]<table>
pgsql2shp [<options>] <database> <query>

Optionen auf der Befehlszeile:

-f <filename>

Ausgabe in eine bestimmte Datei.

-h <host>

Der Datenbankserver, mit dem eine Verbindung aufgebaut werden soll.

-p <port>

Der Port über den der Verbindungsaufbau mit dem Datenbank Server hergestellt werden soll.

-P <password>

Das Passwort, das zum Verbindungsaufbau mit der Datenbank verwendet werden soll.

-u <user>

Das Benutzername, der zum Verbindungsaufbau mit der Datenbank verwendet werden soll.

-g <geometry column>

Bei Tabellen mit mehreren Geometriespalten jene Geometriespalte, die ins Shapefile geschrieben werden soll.

-b

Die Verwendung eines binären Cursors macht die Berechnung schneller; funktioniert aber nur, wenn alle nicht-geometrischen Attribute in den Datentyp "text" umgewandelt werden können.

-r

RAW-Modus. Das Attribut gid wird nicht verworfen und Spaltennamen werden nicht maskiert.

-m filename

Bildet die Identifikatoren in Namen mit 10 Zeichen ab. Der Inhalt der Datei besteht aus Zeilen von jeweils zwei durch Leerzeichen getrennten Symbolen, jedoch ohne vor- oder nachgestellte Leerzeichen: VERYLONGSYMBOL SHORTONE ANOTHERVERYLONGSYMBOL SHORTER etc.

4.6. Erstellung von Indizes

Indizes ermöglichen das Arbeiten mit großen Datensätzen in einer Geodatenbank. Ohne Indizierung würde jede Featureanfrage einen "Full Table Scan" in der Datenbank benötigen. Die Indizierung beschleunigt die Suche, indem die Daten in einem Suchbaum strukturiert werden, der dann schnell durchlaufen werden kann um einen bestimmten Datensatz zu finden. PostgreSQL unterstützt standardmäßig drei Arten von Indizes: B-Baum, SP-GIST und GIST.

  • Ein B-Baum wird verwendet, wenn die Daten entlang einer Achse sortiert werden können; wie Zahlen, Buchstaben oder Datumsangaben. Geodaten können entlang einer raumfüllenden Kurve, Z-Kurve oder Hilbert-Kurve sortiert werden. Diese Darstellung erlaubt allerdings keine Beschleunigung der üblichen Operationen.

  • GiST (Generalized Search Tree) Indizes unterteilen die Daten in "Dinge auf einer Seite", "Dinge die sich überlagern", "Dinge die innerhalb liegen". Sie können auf eine Vielzahl von Datentypen, inklusive Geodaten angewendet werden. Um Geodaten zu indizieren verwendet PostGIS einen R-Baum der auf dem GIST Index aufsetzt.

4.6.1. GiST-Indizes

GIST (Generalized Search Tree) ist eine generische Datenstruktur. Zusätzlich zur Indizierung von Geodaten wird GIST auch zur Beschleunigung von Abfragen auf unregelmäßige Datenstrukturen (Ganzzahl-Felder, Spektraldaten, etc.) verwendet, welche über gewöhnlicher B-Baum Indizierung nicht zugänglich sind.

Sobald eine Geodatentabelle einige tausend Zeilen überschreitet, werden Sie einen Index erzeugen wollen, um die räumlichen Abfragen auf die Daten zu beschleunigen (außer Ihre Suche basiert lediglich auf Attributen, in diesem Fall werden Sie einen gewöhnlichen Index auf die Attribute setzen).

Die Syntax, mit der ein GIST-Index auf eine Geometriespalte gelegt wird, lautet:

CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometryfield] ); 

Die obere Syntax erzeugt immer einen 2D-Index. Um einen n-dimensionalen Index für den geometrischen Datentyp zu erhalten, können Sie die folgende Syntax verwenden:

CREATE INDEX [indexname] ON [tablename] USING GIST ([geometryfield] gist_geometry_ops_nd);

Die Erstellung eines räumlichen Indizes ist eine rechenintensive Aufgabe. Während der Erstellung wird auch der Schreibzugriff auf die Tabelle blockiert. Bei produktiven Systemen empfiehlt sich daher die langsamere Option CONCURRENTLY:

CREATE INDEX CONCURRENTLY [indexname] ON [tablename] USING GIST ( [geometryfield] ); 

Nachdem ein Index aufgebaut wurde sollte PostgreSQL gezwungen werden die Tabellenstatistik zu sammeln, da diese zur Optmierung der Auswertungspläne verwendet wird:

VACUUM ANALYZE [table_name] [(column_name)];

4.6.2. BRIN Indizes

Die Bezeichnung BRIN steht für "Block Range Index", eine generische Form des Indizierens und wurde mit PostgreSQL 9.5 eingeführt. BRIN ist ein verlustbehafteter Index, dessen Hauptzweck ist, einen Kompromiss sowohl bei der Lese- als auch bei der Schreibgeschwindigkeit anzubieten. Der Hauptverwendungszweck liegt bei sehr großen Tabellen, in denen einige Spalten einen natürlichen Bezug zu dem physischen Speicherplatz innerhalb der Tabelle haben. Zusätzlich zur Indizierung von GIS-Daten, werden BRIN-Indizes zur Beschleunigung von Suchabfragen auf unterschiedliche regelmäßige und unregelmäßige Datenstrukturen (Ganzzahlen, Felder etc.) verwendet.

Sobald eine Geodatentabelle ein paar tausend Zeilen überschreitet, werden Sie einen Index erzeugen wollen, um die räumlichen Abfragen auf die Daten zu beschleunigen (außer Ihre Suche basiert lediglich auf Attributen, in diesem Fall werden Sie einen gewöhnlichen Index auf die Attribute setzen). GIST Indizes sind sehr performant, solange ihre Dateigröße den verfügbaren Arbeitsspeicher der Datenbank nicht überschreitet, genügend Festplattenspeicher vorhanden ist und die Systembelastung durch Schreibvorgänge akzeptiert werden kann. Andernfalls bietet der BRIN Index eine Alternative.

Die Idee hinter einem BRIN-Index ist, dass nur das Umgebungsrechteck abgespeichert wird, dass die gesamte Geometrie eines oder mehrerer Tabellenblöcke umschließt; dies wird als "Range" bezeichnet. Es ist klar, dass diese Indizierungsmethode nur dann effizient sein kann, wenn die Daten physikalisch so angeordnet sind, dass sich die resultierenden Umgebungsrechtecke der "Block Ranges" gegenseitig ausschließen. Der resultierende Index ist zwar sehr klein, in vielen Fällen allerdings weniger effizient als ein GIST Index.

Die Erstellung eines BRIN-Index benötigt wesentlich weniger Zeit, als die Erstellung eines GIST-Index. Es ist durchaus üblich, dass die Erstellung des BRIN Index mehr als zehnmal so schnell ist, als die eines GIST Index. Da ein BRIN Index nur ein Umgebungsrechteck für einen oder mehrere Tabellenblöcke speichert, benötigt dieser oft bis zu tausendmal weniger Festplattenspeicher.

Sie können die Anzahl der Blöcke festlegen, die zu einen "Range" aufsummiert werden sollen. Wenn Sie die Anzahl verringern, wird der Index zwar größer, höchstwahrscheinlich aber zu einer besseren Performanz verhelfen.

Der Syntax zur Erstellung eines BRIN-Indizes auf eine geometrische Spalte lautet wie folgt:

CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geometryfield] ); 

Die obere Syntax erzeugt einen 2D-Index. Um einen 3-dimensionalen Index zu erhalten, können Sie die folgende Syntax verwenden:

CREATE INDEX [indexname] ON [tablename] USING BRIN ([geometryfield] brin_geometry_inclusion_ops_3d);

Sie können auch einen 4-dimensionalen Index über die 4D-Operatorklasse erstellen

CREATE INDEX [indexname] ON [tablename] USING BRIN ([geometryfield] brin_geometry_inclusion_ops_4d);

Die oberen Syntaxen verwenden die Standardeinstellung für die Anzahl der Blöcke in einem "Range", nämlich 128. Wenn Sie die Anzahl der Blöcke, die in einem Range zusammengefasst werden sollen, selbst festlegen wollen, verwenden Sie bitte die folgende Syntax

CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geometryfield] ) WITH (pages_per_range = [number]); 

Beachten Sie bitte auch, dass ein BRIN Index nur einen Indexwert für eine große Anzahl von Zeilen speichert. Wenn Ihre Tabelle eine Geometrie mit unterschiedlichen Dimensionen speichert, dann ist es wahrscheinlich dass der Index eine schlechte Performanz aufweist. Sie können diesen Performanzrückgang vermeiden, indem Sie die Operatorklasse mit der niedrigsten Dimension der gespeicherten Geometrie wählen.

Der BRIN-Index wird auch vom geographischen Datentyp unterstützt. Die Syntax zur Erstellung eines BRIN-Index auf eine "geographische" Spalte lautet wie folgt:

CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geographyfield] ); 

Die obere Syntax erzeugt den 2D-Index für Geoobjekte auf dem Referenzellipsoid.

Aktuell wird hierbei nur die "Inklusionsunterstützung" betrachtet; d.h. dass nur die Operatoren &&, ~ und @ für 2D (sowohl für den "geometrischen", als auch für den "geographischen" Datentyp) und nur der Operator &&& für 3D-Geometrie verwendet werden kann. Die kNN-Suche wird zur Zeit nicht unterstützt.

4.6.3. SP-GiST Indizes

SP-GiST steht als Abkürzung für "Space-Partitioned Generalized Search Tree", ein generischer Indextyp der partitionierte Baumstrukturen, wie Quadtree, k-d Baum und Radix-Trie unterstützt. Diese Datenstrukturen haben die gemeinsame Eigenschaft, dass sie den Suchraum in mehrere Partitionen unterteilen, die unterschiedlich groß sein können. Zusätzlich zur Indizierung von Geodaten wird der SP-GIST Index zur Beschleunigung der Suche von vielen Datentypen verwendet, wie bei Telefon Routing, IP Routing, String-Matching-Algorithmen, etc.

So wie der GiST Index, ist auch der SP-GiST Index insofern nicht verlustfrei, da nur die umschreibenden Rechtecke der Geoobjekte gespeichert werden. Der SP-GiST Index kann als Alternative zum GiST Index gesehen werden. Die Performanztests zeigten, dass der SP-GiST Index insbesondere bei vielen überlappenden Objekten Vorteile haben, wie dies bei sogenannten "Spaghettidaten" der Fall ist.

Sobald eine Geodatentabelle einige tausend Zeilen überschreitet, kann es sinnvoll sein einen SP-GIST Index zu erzeugen, um die räumlichen Abfragen auf die Daten zu beschleunigen. Die Syntax zur Erstellung eines SP-GIST Index auf eine "Geometriespalte" lautet:

CREATE INDEX [indexname] ON [tablename] USING SPGIST ( [geometryfield] ); 

Die obere Syntax erzeugt einen 2D-Index. Ein 3-dimensionaler Index für den geometrischen Datentyp können Sie mit der 3D Operatorklasse erstellen:

CREATE INDEX [indexname] ON [tablename] USING SPGIST ([geometryfield] spgist_geometry_ops_3d);

Die Erstellung eines räumlichen Indizes ist eine rechenintensive Aufgabe. Während der Erstellung wird auch der Schreibzugriff auf die Tabelle blockiert. Bei produktiven Systemen empfiehlt sich daher die langsamere Option CONCURRENTLY:

CREATE INDEX CONCURRENTLY [indexname] ON [tablename] USING SPGIST ( [geometryfield] ); 

Nachdem ein Index aufgebaut wurde sollte PostgreSQL gezwungen werden die Tabellenstatistik zu sammeln, da diese zur Optmierung der Auswertungspläne verwendet wird:

VACUUM ANALYZE [table_name] [(column_name)];

Ein SP-GiST Index kann Abfragen mit folgenden Operatoren beschleunigen:

  • <<, &<, &>, >>, <<|, &<|, |&>, |>>, &&, @>, <@, and ~=, für 2-dimensionale Iindices,

  • &/&, ~==, @>>, and <<@, für 3-dimensionale Indices.

kNN Suche wird zurzeit nicht unterstützt.

4.6.4. Verwendung von Indizes

Üblicherweise beschleunigen Indizes den Datenzugriff: Sobald der Index aufgebaut ist, entscheidet der Anfrageoptimierer, ob der Index den Auswertungsplan bescheunigt. Unglücklicherweise optimiert der Anfrageoptimierer von PostgreSQL die Verwendung von GIST Indizes nicht sehr gut, so dass manchmal eine Suche, welche die Verwendung eines räumlichen Index bedingen sollte, über einen Full Table Scan ausgeführt wird.

Wenn Sie bemerken, dass Ihre räumlichen Indizes (oder Ihre Attributindizes) nicht verwendet werden, gibt es mehrere Möglichkeiten:

  • Zunächst sollten Sie sich den Auswertungsplan ansehen und überprüfen ob Ihre Abfrage tatsächlich das berechnet was Sie benötigen. Eine unkontrollierte Join-Bedingung, entweder vergessen oder auf eine falsche Tabelle gesetzt, kann alle Datensätze Ihrer Tabelle mehrmals hinzuziehen. Fügen Sie das Schlüsselwort EXPLAIN an den Anfang Ihrer bAbfrage, um den Auswertungsplan zu erhalten.

  • Als nächstes sollten Sie sicherstellen, dass eine Statistik über die Anzahl und die Verteilung der Tabellenwerte erfasst wurde, damit dem Anfrageoptimierer bessere Informationen zur Entscheidungsfindung bezüglich zu verwendender Indizes zur Verfügung steht. VACUUM ANALYZE errechnet beide.

    Sie sollten ohnehin regelmäßig ein Vacuum Ihrer Datenbanken durchführen - viele PostgreSQL DBAs führen ein regelmäßigesVACUUM außerhalb der Spitzenzeiten mittels Cronjob aus.

  • Wenn VACUUM nicht hilft, können Sie den Anfrageoptimierer vorübergehend dazu zwingen den Index zu verwenden, indem Sie den Befehl set enable_seqscan to off; ausführen. Auf diese Weise können Sie feststellen, ob es dem Anfrageoptimierer überhaupt möglich ist, einen indexbeschleunigten Auswertungsplan für Ihre Abfrage zu erstellen. Sie sollten diesen Befehl nur zu Testzwecken: d.h. der Anfrageoptimierer weiß am besten Bescheid wann welcher Index verwendet werden soll. Sobald Sie Ihre Abfrage ausgeführt haben, sollten Sie daher ENABLE_SEQSCAN wieder auf ON stellen, damit weitere Abfragen den Anfrageoptimierer wie üblich nutzen können.

  • Wenn set enable_seqscan to off; bei Ihrer Abfrage hilft, dann ist Ihr Postgres vermutlich nicht mit Ihrer Hardware abgestimmt. Wenn Sie herausfinden, daß sich der Anfrageoptimierer bezüglich der Kosten des Full Table Scan im Verhältnis zum Index Scan irrt, können Sie versuchen den Wert von randam_page_cost in "postgresql.conf" zu reduzieren, oder set random_page_cost to 1.1; ausführen. Der Standardwert des Parameters ist 4, versuchen Sie ihn auf 1 (auf einer SSD) oder auf 2 (auf einem schnellen magnetischen Festplattenlaufwerk) zu setzen. Eine Verringerung des Wertes führt dazu, dass der Anfrageoptimierer eher den Index Scan verwendet.

  • Wennset enable_seqscan to off; bei Ihrer Abfrage nicht hilft, kann es sein, dass Sie ein Konstrukt verwenden das Postgres noch nicht entwirren kann. Eine Unterabfrage mit einem Inlineselect wäre so ein Fall -Sie müssen dies in eine Form bringen, die der Anfrageoptimierer nützen kann, z.B. mit einem LATERAL JOIN.

4.7. Komplexe Abfragen

Sinn und Zweck der Geodatenbankfunktionalität ist, Abfragen innerhalb der Datenbank auszuführen, welche üblicherweise die Funktionalität eines Desktop-GIS benötigen würden. Um PostGIS effizient zu nutzen, müssen Sie die verfügbaren räumlichen Funktionen kennen und sicherstellen, dass die geeigneten Indizes vorhanden sind um eine gute Performanz zu gewährleisten. Die SRID von 312, die in diesen Beispielen verwendet wird, ist für bloße Demonstrationszwecke gedacht. Sie sollten eine ECHTE SRID aus der Tabelle "spatial_ref_sys" verwenden, die auch mit der Projektion Ihrer Daten übereinstimmen muss. Falls Ihren Daten kein Koordinatenreferenzsystem zugewiesen ist, sollten Sie genau eruieren warum dies so ist.

Wenn der Grund darin liegt, dass Sie etwas modellieren, für dass kein Koordinatenreferenzsystem festgelegt ist, wie der innere Aufbau eines Moleküls oder der Grundriss eines noch nicht gebauten Vergnügungsparks, so ist dies in Ordnung. Wenn der Standort des Vergnügungsparks bereits geplant wurde, dann ist die Wahl eines geeigneten Koordinatenreferenzsystems sinnvoll, auch wenn es nur darum geht sicherzustellen, dass der Vergnügungspark keine bereits bestehenden Strukturen überdeckt.

Sogar wenn Sie eine Mars Expedition planen, um die menschliche Rasse nach einem nuklearen Holocaust zu transportieren und Sie den Planeten Mars für die Besiedelung kartieren wollen, können Sie ein Koordinatenreferenzsystem wie Mars 2000 erstellen und dieses in die Tabelle spatial_ref_sys einfügen. Obwohl dieses Koordinatensystem für den Mars nicht planar ist (es ist in Grad des Referenzellipsoids), können Sie den geographischen Datentyp nutzen, um Längen- und Abstandsmessungen in Meter anstatt in Grad anzuzeigen.

4.7.1. Vorteile von Indizes nutzen

Wenn Sie eine Abfrage erstellen, müssen Sie beachten, dass nur die auf den umschreibenden Rechtecken basierenden Operatoren wie && die Vorteile eines räumlichen GIST Index ausnutzen können. Funktionen wie ST_Distance() können den Index nicht zur Optimierung heranziehen. Zum Beispiel würde die folgende Abfrage auf eine große Tabelle ziemlich langsam ablaufen:

SELECT the_geom
FROM geom_table
WHERE ST_Distance(the_geom, 'SRID=312;POINT(100000 200000)') < 100

Diese Abfrage wählt die geometrischen Objekte der Tabelle "geom_table" aus, die weniger als 100 Einheiten von dem Punkt (100000, 200000) entfernt liegen. Sie ist sehr langsam, da die Entfernung zwischen jedem Punkt in der Tabelle und dem gegebenen Punkt berechnet werden muss, d.h. eine ST_Distance() Berechnung pro Tabellenzeile. Wir können dies vermeiden, indem wir die indexbeschleunigte Einstufenfunktion ST_DWithin verwenden und so die Anzahl der benötigten Entfernungsberechnungen verringern:

SELECT the_geom
FROM geom_table
WHERE ST_DWithin(the_geom, 'SRID=312;POINT(100000 200000)', 100)

Diese Anfrage wählt dieselben geometrischen Objekte aus, allerdings auf effizientere Weise. Angenommen es existiert ein GIST Index auf der Spalte "the_geom" und der Anfrageoptimierer erkennt, dass der Index angewendet werden kann, um die Zeilenanzahl zu verringern, bevor das Ergebnis durch die Funktion ST_Distance() errechnet wird. Anmerkung: die Geometrie ST_MakeEnvelope, die vom Operator "&&" verwendet wird, ist ein Quadrat mit einer Seitenlänge von 200 Einheiten, dessen Mittelpunkt auf dem ursprünglichen Punkt liegt - dies ist unsere "Abfrage Box". Der Operator "&&" verwendet diesen Index, um die Ergebnismenge rasch auf die Geometrie zu reduzieren, deren Umgebungsrechtecke die "Abfrage Box" überlagern. Falls Unsere "Abfrage Box" wesentlich kleiner als die Gesamtausdehnung der gesamten Geometrietabelle ist, wird dadurch die Anzahl der Entfernungsberechnungen drastisch verringert- dies ist genau das, was wir wollen.

4.7.2. Beispiele für Spatial SQL

Die Beispiele in diesem Abschnitt verwenden zwei Tabellen, eine Tabelle mit linearen Straßen, und eine Tabelle mit polygonalen Verwaltungsgrenzen. Die Tabellendefinition der Tabelle bc_roads lautet:

Column      | Type              | Description
------------+-------------------+-------------------
gid         | integer           | Unique ID
name        | character varying | Road Name
the_geom    | geometry          | Location Geometry (Linestring)

Die Tabellendefinition für die Tabelle bc_municipality lautet:

Column     | Type              | Description
-----------+-------------------+-------------------
gid        | integer           | Unique ID
code       | integer           | Unique ID
name       | character varying | City / Town Name
the_geom   | geometry          | Location Geometry (Polygon)
4.7.2.1. Gesamtlänge aller Straßen in Kilometer?
4.7.2.2. Wieviele Hektar hat die Stadt Prince George?
4.7.2.3. Welche ist die flächengrößte Gemeinde der Provinz?
4.7.2.4. Welche Länge haben die Straßen, die zur Gänze innerhalb einer Gemeinde liegen?
4.7.2.5. Eine neue Tabelle erzeugen, die alle Straßen der Stadt Prince George beinhaltet.
4.7.2.6. Wie lange ist die "Douglas St" in Victoria in Kilometern?
4.7.2.7. Welches ist das größte Gemeindepolygon mit einer Lücke?

4.7.2.1.

Gesamtlänge aller Straßen in Kilometer?

Sie können diese Frage mit einer sehr einfachen SQL Anweisung beantworten:

SELECT sum(ST_Length(the_geom))/1000 AS km_roads FROM bc_roads;

km_roads
------------------
70842.1243039643
(1 row)

4.7.2.2.

Wieviele Hektar hat die Stadt Prince George?

Diese Abfrage kombiniert eine Attributbedingung (auf den Gemeindenamen) mit einer räumlichen Berechnung (der Fläche):

SELECT
  ST_Area(the_geom)/10000 AS hectares
FROM bc_municipality
WHERE name = 'PRINCE GEORGE';

hectares
------------------
32657.9103824927
(1 row)

4.7.2.3.

Welche ist die flächengrößte Gemeinde der Provinz?

Diese Abfrage verwendet eine räumliche Messung als Abfragefilter. Es gibt verschiedene Wege dieses Problem anzugehen, aber die effizienteste Methode ist folgende:

SELECT
  name,
  ST_Area(the_geom)/10000 AS hectares
FROM
  bc_municipality
ORDER BY hectares DESC
LIMIT 1;

name           | hectares
---------------+-----------------
TUMBLER RIDGE  | 155020.02556131
(1 row)

Um diese Anfrage zu beantworten, müssen wir die Fläche eines jeden Polygons berechnen. Wenn wir dies oft machen müssen, kann es aufgrund der Rechenleistung sinnvoll sein, eine eigene Flächenspalte an die Tabelle anzuhängen und mit einem Index zu versehen. Indem wir das Ergebnis in absteigender Reihenfolge sortieren und den PostgreSQL Befehl "LIMIT" einsetzen, können wir die größten Werte herausfiltern, ohne eine Aggregatfunktion wie max() verwenden zu müssen.

4.7.2.4.

Welche Länge haben die Straßen, die zur Gänze innerhalb einer Gemeinde liegen?

Dies ist ein Beispiel für einen "Spatial Join", da wir die Daten aus zwei Tabellen zusammenführen (einen Join ausführen) und als Join-Bedingung eine räumliche Interaktion ("contained") verwenden - anstelle des üblichen relationalen Ansatzes bei dem die Tabellen über einen gemeinsamen Schlüssel verknüpft werden:

SELECT
  m.name,
  sum(ST_Length(r.the_geom))/1000 as roads_km
FROM
  bc_roads AS r,
  bc_municipality AS m
WHERE
  ST_Contains(m.the_geom, r.the_geom)
GROUP BY m.name
ORDER BY roads_km;

name                        | roads_km
----------------------------+------------------
SURREY                      | 1539.47553551242
VANCOUVER                   | 1450.33093486576
LANGLEY DISTRICT            | 833.793392535662
BURNABY                     | 773.769091404338
PRINCE GEORGE               | 694.37554369147
...

Diese Abfrage dauert ein Weilchen, da sämtliche Straßen in der Tabelle in das endgültige Ergebnis aufsummiert werden müssen (über 250k Straßen in Unserem speziellen Beispiel). Bei kleineren Überlagerungen (ein paar tausend Datensätze auf ein paar Hundert) kann die Antwort sehr schnell zurückkommen.

4.7.2.5.

Eine neue Tabelle erzeugen, die alle Straßen der Stadt Prince George beinhaltet.

Dies ist ein Beispiel für ein "Overlay", das zwei Tabellen entgegennimmt und eine neue Tabelle ausgibt, welche die aus- und abgeschnittene Ergebnisgeometrie enthält. Anders als bei dem oben gezeigten "Spatial Join" erzeugt diese Abfrage eine neue Geometrie. Ein "Overlay" ist wie ein "Spatial Join" mit Turbolader und wird für genauere Analysen verwendet:

CREATE TABLE pg_roads as
SELECT
  ST_Intersection(r.the_geom, m.the_geom) AS intersection_geom,
  ST_Length(r.the_geom) AS rd_orig_length,
  r.*
FROM
  bc_roads AS r,
  bc_municipality AS m
WHERE
  m.name = 'PRINCE GEORGE'
        AND ST_Intersects(r.the_geom, m.the_geom);

4.7.2.6.

Wie lange ist die "Douglas St" in Victoria in Kilometern?

SELECT
  sum(ST_Length(r.the_geom))/1000 AS kilometers
FROM
  bc_roads r,
  bc_municipality m
WHERE
        r.name = 'Douglas St'
        AND m.name = 'VICTORIA'
        AND ST_Intersects(m.the_geom, r.the_geom);

kilometers
------------------
4.89151904172838
(1 row)

4.7.2.7.

Welches ist das größte Gemeindepolygon mit einer Lücke?

SELECT gid, name, ST_Area(the_geom) AS area
FROM bc_municipality
WHERE ST_NRings(the_geom) > 1
ORDER BY area DESC LIMIT 1;

gid  | name         | area
-----+--------------+------------------
12   | SPALLUMCHEEN | 257374619.430216
(1 row)