Syftet med spatiala databaser är att kunna utföra sökningar i databasen som normalt kräver GIS-funktioner på skrivbordet. För att PostGIS ska kunna användas effektivt måste man veta vilka spatiala funktioner som finns tillgängliga, hur de ska användas i frågor och se till att lämpliga index finns på plats för att ge bra prestanda.
Spatiala relationer anger hur två geometrier interagerar med varandra. De är en grundläggande egenskap för att ställa frågor om geometri.
Enligt OpenGIS Simple Features Implementation Specification for SQL är "det grundläggande tillvägagångssättet för att jämföra två geometrier att göra parvisa tester av skärningarna mellan de två geometriernas inre, yttre och yttre delar och att klassificera förhållandet mellan de två geometrierna baserat på posterna i den resulterande 'skärningsmatrisen'"
I teorin om punktuppsättningstopologi kategoriseras punkterna i en geometri som är inbäddad i ett 2-dimensionellt rum i tre uppsättningar:
Gränsen för en geometri är uppsättningen av geometrier med nästa lägre dimension. För POINTs, som har dimensionen 0, är gränsen den tomma mängden. Gränsen för en LINESTRING är de två ändpunkterna. För POLYGONERär gränsen linjeverket för de yttre och inre ringarna.
Det inre av en geometri är de punkter i en geometri som inte ligger i begränsningen. För POINTsär det inre själva punkten. Det inre av en LINESTRING är den uppsättning punkter som finns mellan ändpunkterna. För POLYGONERär det inre den areella ytan inuti polygonen.
En geometris utsida är resten av det rum i vilket geometrin är inbäddad; med andra ord alla punkter som inte ligger i geometrins inre eller på dess gräns. Det är en 2-dimensionell icke sluten yta.
DE-9IM ( Dimensionally Extended 9-Intersection Model ) beskriver det spatiala förhållandet mellan två geometrier genom att ange dimensionerna för de 9 skärningspunkterna mellan ovanstående uppsättningar för varje geometri. Dimensionerna på skärningspunkterna kan formellt representeras i en 3x3 skärningsmatris.
För en geometri g betecknas det inre, det yttre och det yttre med beteckningarna I(g), B(g) och E(g). Dim(s) betecknar också dimensionen hos en mängd s med domänen {0,1,2,F}:
0 => punkt
1 => linje
2 => område
F => tom uppsättning
Med hjälp av denna notation är intersektionsmatrisen för två geometrier a och b: är:
| Inredning | Begränsning | Exteriör | |
|---|---|---|---|
| Inredning | dim( I(a) ∩ I(b) ) | dim( I(a) ∩ B(b) ) | dim( I(a) ∩ E(b) ) |
| Begränsning | dim( B(a) ∩ I(b) ) | dim( B(a) ∩ B(b) ) | dim( B(a) ∩ E(b) ) |
| Exteriör | dim( E(a) ∩ I(b) ) | dim( E(a) ∩ B(b) ) | dim( E(a) ∩ E(b) ) |
The following overlapping polygons provide a concrete example. ST_Relate computes the matrix 212101212; it matches the overlap pattern T*T***T** used by ST_Overlaps.
WITH example AS (
SELECT
'POLYGON ((140 140,140 122,135 105,126 100,118 99,110 94,100 86,97 73,102 59,98 49,87 38,70 30,55 29,40 30,28 38,20 50,14 66,10 84,6 100,4 119,4 143,6 166,10 180,18 191,28 195,40 190,55 189,67 186,86 179,112 165,124 158,133 148,140 140),(48 177,40 177,30 174,24 166,22 159,25 155,30 153,40 154,45 157,52 162,53 169,51 174,48 177))'::geometry AS a,
'POLYGON ((75 63,79 50,87 38,95 31,108 25,124 22,140 18,154 11,166 6,176 10,184 21,188 35,190 58,190 82,193 104,190 121,185 139,178 154,166 163,154 171,139 172,124 171,112 165,96 152,92 142,92 126,86 116,79 110,75 104,72 94,73 86,75 76,75 63))'::geometry AS b
), relation AS (
SELECT a, b, ST_Relate(a, b) AS matrix
FROM example
)
SELECT matrix,
ST_RelateMatch(matrix, 'T*T***T**') AS matches_overlap_pattern,
ST_Overlaps(a, b) AS overlaps
FROM relation;
matrix | matches_overlap_pattern | overlaps -----------+-------------------------+---------- 212101212 | t | t (1 row)
The executable query and generated overlay above are the visual source for the relation. The query below renders the nine cells of the same kind of matrix as a 3-by-3 set of examples. The final exterior/exterior cell is clipped to a finite viewport, since the true exterior/exterior intersection is unbounded.
WITH example AS (
SELECT
ST_MakeEnvelope(0, 0, 6, 4) AS a,
ST_MakeEnvelope(3, -1, 8, 3) AS b,
ST_MakeEnvelope(-1, -2, 9, 5) AS viewport
), parts AS (
SELECT
a,
b,
ST_Boundary(a) AS ba,
ST_Boundary(b) AS bb,
viewport
FROM example
)
SELECT
ST_Relate(a, b) AS matrix,
ST_AsText(ST_Normalize(a)) AS input_a,
ST_AsText(ST_Normalize(b)) AS input_b,
ST_AsText(ST_Normalize(ST_Intersection(a, b))) AS "2 I(a) ∩ I(b)",
ST_AsText(ST_Normalize(ST_Intersection(a, bb))) AS "1 I(a) ∩ B(b)",
ST_AsText(ST_Normalize(ST_Difference(a, b))) AS "2 I(a) ∩ E(b)",
ST_AsText(ST_Normalize(ST_Intersection(ba, b))) AS "1 B(a) ∩ I(b)",
ST_AsText(ST_Normalize(ST_Intersection(ba, bb))) AS "0 B(a) ∩ B(b)",
ST_AsText(ST_Normalize(ST_Collect(
'LINESTRING(0 0,0 4,6 4,6 3)'::geometry,
'LINESTRING(0 0,3 0)'::geometry
))) AS "1 B(a) ∩ E(b)",
ST_AsText(ST_Normalize(ST_Difference(b, a))) AS "2 E(a) ∩ I(b)",
ST_AsText(ST_Normalize(ST_Collect(
'LINESTRING(3 -1,8 -1,8 3,6 3)'::geometry,
'LINESTRING(3 -1,3 0)'::geometry
))) AS "1 E(a) ∩ B(b)",
ST_AsText(ST_Normalize(ST_Difference(viewport, ST_Union(a, b))))
AS "2 E(a) ∩ E(b) clipped"
FROM parts;
När man läser från vänster till höger och uppifrån och ner representeras skärningsmatrisen av textsträngen '"212101212".'.
För mer information, se:
För att göra det enkelt att fastställa vanliga spatiala relationer definierar OGC SFS en uppsättning namngivna predikat för spatiala relationer. PostGIS tillhandahåller dessa som funktionerna ST_Contains, ST_Crosses, ST_Disjoint, ST_Equals, ST_Intersects, ST_Overlaps, ST_Touches, ST_Within. Det definierar också de icke-standardiserade relationspredikaten ST_Covers, ST_CoveredBy, och ST_ContainsProperly.
Spatiala predikat används vanligtvis som villkor i SQL WHERE- eller JOIN-klausuler. De namngivna spatiala predikaten använder automatiskt ett spatialt index om ett sådant finns tillgängligt, så det finns inget behov av att använda bounding box-operatorn && också. Till exempel:
SELECT city.name, state.name, city.geom FROM city JOIN state ON ST_Intersects(city.geom, state.geom);
För mer information och illustrationer, se PostGIS Workshop.
In some cases the named spatial relationships are insufficient to provide a desired spatial filter condition. These requirements can be expressed by computing the full DE-9IM intersection matrix with ST_Relate.
För att testa ett visst spatialt förhållande används ett intersektionsmatrismönster. Detta är matrisrepresentationen utökad med tilläggssymbolerna {T,*}:
T => intersection dimension is non-empty; i.e. is in {0,1,2}
* => bryr sig inte
Using intersection matrix patterns, specific spatial relationships can be evaluated succinctly. The ST_Relate and ST_RelateMatch functions can both test these patterns.
These road segments share a line segment. ST_Crosses does not find this relationship for linear features, because it only returns true when their interiors intersect at a point. The pattern 1*1***1** tests for a one-dimensional intersection instead.
WITH roads AS (
SELECT
'LINESTRING(10 10,40 90,70 110,140 110,170 130,190 190)'::geometry AS "road A",
'LINESTRING(10 190,50 130,90 110,130 110,160 70,180 10)'::geometry AS "road B"
), relation AS (
SELECT "road A", "road B", ST_Relate("road A", "road B") AS matrix
FROM roads
)
SELECT
ST_Intersection("road A", "road B") AS overlap,
matrix,
ST_Crosses("road A", "road B") AS "ST_Crosses",
ST_RelateMatch(matrix, '1*1***1**') AS "matches 1*1***1**"
FROM relation;
overlap | matrix | ST_Crosses | matches 1*1***1** ----------------------------+-----------+------------+------------------- LINESTRING(90 110,130 110) | 1F1FF0102 | f | t (1 row)
The next example finds a wharf which runs from the lake interior onto its shoreline, with one endpoint on the shoreline. The other wharves provide nearby counterexamples in the figure. The pattern 102101FF2 expresses the complete required relationship in one test.
WITH data AS (
SELECT
'POLYGON((-20 10,30 30,80 70,110 80,145 85,190 110,300 220,230 220,-20 220,-20 10))'::geometry AS lake,
'MULTILINESTRING((120 180,145 85,110 80),(10 130,30 70),(90 140,110 80,94.81118881118876 75.83496503496498),(150 160,180 80))'::geometry AS wharves
), wharf AS (
SELECT lake, (item).path[1] AS id, (item).geom
FROM data CROSS JOIN LATERAL ST_Dump(wharves) AS item
), relation AS (
SELECT id, geom, ST_Relate(lake, geom) AS matrix
FROM wharf
)
SELECT
id AS wharf,
geom AS "matching wharf",
matrix
FROM relation
WHERE ST_RelateMatch(matrix, '102101FF2')
ORDER BY id;
wharf | matching wharf | matrix
-------+-----------------------------------+-----------
1 | LINESTRING(120 180,145 85,110 80) | 102101FF2
(1 row)
När du konstruerar frågor med spatiala villkor är det viktigt att se till att ett spatialt index används, om ett sådant finns (se Avsnitt 4.9, ”Spatiala index”), för bästa prestanda. För att göra detta måste en spatial operator eller en indexmedveten funktion användas i en WHERE- eller ON-sats i frågan.
Spatiala operatorer inkluderar bounding box-operatorer (av vilka den vanligaste är &&; se Avsnitt 7.10.1, ”Operatorer för avgränsande box” för en fullständig lista) och avståndsoperatorer som används i frågor om närmaste granne (den vanligaste är <->; se Avsnitt 7.10.2, ”Avståndsoperatorer” för en fullständig lista)
Indexmedvetna funktioner lägger automatiskt till en bounding box-operator till det spatiala villkoret. Indexmedvetna funktioner inkluderar de namngivna spatiala relationspredikaten ST_Contains, ST_ContainsProperly, ST_CoveredBy, ST_Covers, ST_Crosses, ST_Intersects, ST_Overlaps, ST_Touches, ST_Within, ST_Within, och ST_3DIntersects, och avståndspredikaten ST_DWithin, ST_DFullyWithin, ST_3DDFullyWithin, och ST_3DDWithin.)
Funktioner som ST_Distance använder inte index för att optimera sin drift. Till exempel skulle följande fråga vara ganska långsam på en stor tabell:
SELECT geom FROM geom_table WHERE ST_Distance(geom, 'SRID=312;POINT(100000 200000)') < 100
Denna fråga väljer ut alla geometrier i geom_table som ligger inom 100 enheter från punkten (100000, 200000). Den kommer att vara långsam eftersom den beräknar avståndet mellan varje punkt i tabellen och den angivna punkten, dvs. en ST_Distance() -beräkning görs för varje rad i tabellen.
Antalet rader som bearbetas kan minskas avsevärt genom att använda den indexmedvetna funktionen ST_DWithin:
SELECT geom FROM geom_table WHERE ST_DWithin(geom, 'SRID=312;POINT(100000 200000)', 100)
Denna fråga väljer samma geometrier, men den gör det på ett effektivare sätt. Detta möjliggörs genom att ST_DWithin() använder &&-operatorn internt på en utökad begränsningsbox för frågegegeometrin. Om det finns ett spatialt index på geom kommer frågeplaneraren att inse att den kan använda indexet för att minska antalet rader som skannas innan avståndet beräknas. Det spatiala indexet gör det möjligt att endast hämta poster med geometrier vars avgränsande rutor överlappar den expanderade omfattningen och som därför kan ligga inom det avstånd som krävs. Det faktiska avståndet beräknas sedan för att bekräfta om posten ska inkluderas i resultatuppsättningen.
För mer information och exempel, se PostGIS Workshop.
I exemplen i detta avsnitt används en tabell med linjära vägar och en tabell med polygonala kommungränser. Definitionen av tabellen bc_roads är:
Column | Type | Description ----------+-------------------+------------------- gid | integer | Unique ID name | character varying | Road Name geom | geometry | Location Geometry (Linestring)
Definitionen av tabellen bc_municipality är:
Column | Type | Description ---------+-------------------+------------------- gid | integer | Unique ID code | integer | Unique ID name | character varying | City / Town Name geom | geometry | Location Geometry (Polygon)
|
5.3.1. |
Vad är den totala längden på alla vägar, uttryckt i kilometer? |
|
Du kan besvara den här frågan med ett mycket enkelt SQL-test: Code
SELECT sum(ST_Length(geom))/1000 AS km_roads FROM bc_roads; Rasterutdata
km_roads ------------------ 70842.1243039643 |
|
|
5.3.2. |
Hur stor är staden Prince George, räknat i hektar? |
|
Denna fråga kombinerar ett attributvillkor (på kommunens namn) med en spatial beräkning (av polygonområdet): Code
SELECT ST_Area(geom)/10000 AS hectares FROM bc_municipality WHERE name = 'PRINCE GEORGE'; Rasterutdata
hectares ------------------ 32657.9103824927 |
|
|
5.3.3. |
Vilken är den största kommunen i provinsen, sett till ytan? |
|
Denna fråga använder en spatial mätning som ett ordningsvärde. Det finns flera sätt att närma sig detta problem, men det mest effektiva är nedan: Code
SELECT name, ST_Area(geom)/10000 AS hectares FROM bc_municipality ORDER BY hectares DESC LIMIT 1; Rasterutdata
name | hectares ---------------+----------------- TUMBLER RIDGE | 155020.02556131 Observera att vi måste beräkna arean för varje polygon för att kunna besvara den här frågan. Om vi gjorde det här mycket skulle det vara vettigt att lägga till en områdeskolumn i tabellen som kan indexeras för prestanda. Genom att beställa resultaten i fallande riktning och använda PostgreSQL-kommandot "LIMIT" kan vi enkelt välja bara det största värdet utan att använda en aggregerad funktion som MAX (). |
|
|
5.3.4. |
Hur långa är de vägar som helt och hållet ingår i varje kommun? |
|
Detta är ett exempel på en "spatial join", som sammanför data från två tabeller (med en join) med hjälp av en spatial interaktion ("contained") som join-villkor (i stället för den vanliga relationella metoden med join på en gemensam nyckel): Code
SELECT m.name, sum(ST_Length(r.geom))/1000 as roads_km FROM bc_roads AS r JOIN bc_municipality AS m ON ST_Contains(m.geom, r.geom) GROUP BY m.name ORDER BY roads_km; Rasterutdata
name | roads_km ----------------------------+---------- SURREY | 1539.476 VANCOUVER | 1450.331 LANGLEY DISTRICT | 833.793 BURNABY | 773.769 PRINCE GEORGE | 694.376 ... Den här frågan tar ett tag, eftersom varje väg i tabellen sammanfattas i slutresultatet (ca 250 000 vägar för exempeltabellen). För mindre dataset (flera tusen poster på flera hundra) kan svaret vara mycket snabbt. |
|
|
5.3.5. |
Skapa en ny tabell med alla vägar inom staden Prince George. |
|
Detta är ett exempel på en "overlay", som tar in två tabeller och matar ut en ny tabell som består av spatialt klippta eller skurna resultat. Till skillnad från den "spatial join" som demonstreras ovan skapar den här frågan nya geometrier. En overlay är som en turboladdad spatial join och är användbar för mer exakt analysarbete: Code
CREATE TABLE pg_roads as SELECT ST_Intersection(r.geom, m.geom) AS intersection_geom, ST_Length(r.geom) AS rd_orig_length, r.* FROM bc_roads AS r JOIN bc_municipality AS m ON ST_Intersects(r.geom, m.geom) WHERE m.name = 'PRINCE GEORGE'; |
|
|
5.3.6. |
Hur lång är "Douglas St" i Victoria i kilometer? |
|
Code
SELECT sum(ST_Length(r.geom))/1000 AS kilometers FROM bc_roads r JOIN bc_municipality m ON ST_Intersects(m.geom, r.geom WHERE r.name = 'Douglas St' AND m.name = 'VICTORIA'; Rasterutdata
kilometers ------------------ 4.89151904172838 |
|
|
5.3.7. |
Vilken är den största kommunpolygonen som har ett hål? |
|
Code
SELECT gid, name, ST_Area(geom) AS area FROM bc_municipality WHERE ST_NRings(geom) > 1 ORDER BY area DESC LIMIT 1; Rasterutdata
gid | name | area -----+--------------+------------------ 12 | SPALLUMCHEEN | 257374619.430216 |