09. PostGIS - prostorové SQL
Přednáška
-- Geometrické konstruktory
SELECT ST_GeomFromText('POINT(-686651 -1058147)');
SELECT ST_Point(-686651, -1058147);
SELECT 'POINT(-686651 -1058147)'::geometry;
SELECT ST_GeomFromText('POINT(-686651 -1058147)', 5514);
SELECT ST_SetSRID(ST_Point(-686651, -1058147), 5514);
SELECT ST_GeomFromEWKT('SRID=5514;POINT(-686651 -1058147)');
SELECT 'SRID=5514;POINT(-686651 -1058147)'::geometry;
-- Prostorový index
SELECT count(*)
FROM ruian.obce AS obce
JOIN osm.zeleznice AS zelez
ON ST_Intersects(zelez.geom, obce.geom);
-- Query planner
EXPLAIN
SELECT count(*)
FROM ruian.obce AS obce
JOIN osm.zeleznice AS zelez
ON ST_Intersects(zelez.geom, obce.geom);
-- ověření
SELECT obce.kod, count(*) as pocet
FROM ruian.obce AS obce
JOIN osm.zeleznice AS zelez
ON ST_Intersects(zelez.geom, obce.geom)
GROUP BY obce.kod
ORDER BY pocet DESC
LIMIT 10;
-- možná řešení
SELECT count(DISTINCT obce.kod)
FROM ruian.obce AS obce
JOIN osm.zeleznice AS zelez
ON ST_Intersects(zelez.geom, obce.geom);
-- explain analyze
EXPLAIN ANALYZE
SELECT count(DISTINCT obce.kod)
FROM ruian.obce AS obce
JOIN osm.zeleznice AS zelez
ON ST_Intersects(zelez.geom, obce.geom);
EXPLAIN ANALYZE
SELECT count(DISTINCT obce.kod)
FROM ruian.obce AS obce
JOIN osm.zeleznice AS zelez
ON zelez.geom && obce.geom
AND _st_intersects(zelez.geom, obce.geom);
-- bez prostoroveho indexu
EXPLAIN ANALYZE
SELECT count(DISTINCT obce.kod)
FROM ruian.obce AS obce
JOIN osm.zeleznice AS zelez
ON _st_intersects(zelez.geom, obce.geom);
Cvičení
Data Manupulation Language
Prostorové operátory
Vyhledání prvků v okolí daného bodu.
SELECT osm_id, st_astext(geom)
FROM osm.pozarni_stanice
WHERE geom @ st_expand(
st_geomfromtext('POINT(-768581 -1063422)', 5514),
20000);
-- vs.
SELECT osm_id, st_astext(geom)
FROM osm.pozarni_stanice
WHERE geom @ st_buffer(
st_geomfromtext('POINT(-768581 -1063422)', 5514),
20000);
-- vs.
SELECT osm_id, st_astext(geom)
FROM osm.pozarni_stanice
WHERE st_within(geom, st_buffer(
st_geomfromtext('POINT(-768581 -1063422)', 5514),
20000));
-- vs.
SELECT osm_id, st_astext(geom)
FROM osm.pozarni_stanice
WHERE geom <#> 'SRID=5514;POINT(-768581 -1063422)'::geometry < 20000;
Vyhledání prvků ležících kompletně/částečně uvnitř MMO.
SELECT count(*)
FROM osm.silnice AS s
JOIN
(
SELECT geom
FROM ruian.vusc
WHERE nutslau = 'CZ042'
) AS u
ON s.geom @ u.geom;
-- vs.
SELECT count(*)
FROM osm.silnice AS s
JOIN ruian.vusc as u
ON u.nutslau = 'CZ042'
AND s.geom && u.geom;
Prostorová analýza
Vyhledání nejjižněji položeného zájmového bodu.
Plošný prvek s vnitřní hranicí.
SELECT nazev, st_area(geom)/1e6 AS area_km
FROM ruian.vusc
WHERE st_nrings(geom) > 1
ORDER BY area_km DESC LIMIT 1;
Centroid (referenční bod) plošného prvku.
Určení výměry plošného prvku.
Seřazení plošných prvků podle výměry.
Celková délka liniových prvků.
Plošná zakulacenost prvků (poměr kvadrátu obvodu vůči ploše).
Najít zájmové body, které se nacházejí do vzdálenosti od plošného prvku.
SELECT osm_id FROM osm.pozarni_stanice AS p
JOIN ruian.obce AS k
ON k.nazev = 'Kladno'
AND st_distance(p.geom, k.geom) < 10000;
Zájmové body, které se nacházejí ve vzdálenosti od nejdelšího úseku liniového prvku.
SELECT osm_id, st_astext(geom)
FROM osm.pozarni_stanice
WHERE st_distance(
(
SELECT geom
FROM osm.silnice
WHERE typ = 1
ORDER BY st_length(geom) DESC
LIMIT 1
), geom) < 10000;
-- vs.
WITH s AS (
SELECT geom
FROM osm.silnice
WHERE typ = 1
ORDER BY st_length(geom) DESC
LIMIT 1)
SELECT osm_id, st_astext(p.geom)
FROM osm.pozarni_stanice as p
JOIN s
ON s.geom <-> p.geom < 10000;
Určit vzdálenost mezi zájmovými bodovými prvky.
SELECT (st_distance(
(
SELECT geom
FROM osm.pozarni_stanice
ORDER BY st_x(geom) DESC
LIMIT 1
),
(
SELECT geom
FROM osm.pozarni_stanice
ORDER BY st_x(geom) ASC
LIMIT 1
))/1e3
)::int as vzdalenost_km;
Prostorové predikáty
Vypsání sumárních údajů.
SELECT k.kod, k.nazev,
SUM(st_length(z.geom))/1000 AS zel_km
FROM osm.zeleznice AS z
JOIN ruian.vusc AS k
ON st_contains(k.geom, z.geom)
GROUP BY k.kod, k.nazev
ORDER BY zel_km;
-- vs.
SELECT k.kod, k.nazev,
SUM(st_length(z.geom))/1000 AS zel_km
FROM osm.zeleznice AS z
JOIN ruian.vusc AS k
ON st_intersects(z.geom, k.geom)
GROUP BY k.kod, k.nazev
ORDER BY zel_km;
-- vs.
SELECT k.kod, k.nazev,
SUM(st_length(st_intersection(z.geom, k.geom)))/1000 AS zel_km
FROM osm.zeleznice AS z
JOIN ruian.vusc AS k
ON z.geom && k.geom
GROUP BY k.kod, k.nazev
ORDER BY zel_km;
Liniové prvky, které leží do vzdálenosti od zájmových bodových prvků.
SELECT count(DISTINCT s.osm_id)
FROM osm.silnice AS s
JOIN osm.pozarni_stanice AS p
ON st_dwithin(s.geom, p.geom, 300);
-- vs.
SELECT count(DISTINCT s.osm_id)
FROM osm.silnice AS s
JOIN osm.pozarni_stanice AS p
ON p.geom && st_expand(s.geom, 300)
AND p.geom <-> s.geom < 300;
-- vs.
WITH pb AS (
SELECT st_buffer(geom, 300, 120) as geom
FROM osm.pozarni_stanice
)
SELECT count(DISTINCT s.osm_id)
FROM osm.silnice AS s
JOIN pb
ON st_intersects(s.geom, pb.geom);
Vytvořit liniové prvky, které leží do vzdálenosti od zájmových bodových prvků.
WITH pb AS (
SELECT st_buffer(geom, 300) as geom
FROM osm.pozarni_stanice
)
SELECT DISTINCT s.osm_id,
st_length(st_intersection(s.geom, pb.geom)) as geom
FROM osm.silnice AS s
JOIN pb
ON st_intersects(s.geom, pb.geom) ORDER BY osm_id LIMIT 5;
-- vs.
WITH pb AS (
SELECT st_buffer(geom, 300, 42) as geom
FROM osm.pozarni_stanice
)
SELECT DISTINCT s.osm_id,
st_length(st_intersection(s.geom, pb.geom)) as geom
FROM osm.silnice AS s
JOIN pb
ON st_intersects(s.geom, pb.geom) ORDER BY osm_id LIMIT 5;
Prostorová agregace
Najdi průsečíky železnic, kde se protínají přesně čtyři linie.
SELECT ST_AsText(point) FROM
(SELECT ST_StartPoint(geom) AS point FROM osm.zeleznice
UNION ALL
SELECT ST_EndPoint(geom) AS point FROM osm.zeleznice)
AS a GROUP BY point HAVING COUNT(*) = 4;
Konverze prvků
CREATE TABLE obce_s AS
SELECT kod, nazev, ST_GeometryN(geom,
generate_series(1, ST_NumGeometries(geom))) AS geom
FROM ruian.obce;
-- vs.
CREATE TABLE obce_s AS
SELECT kod, nazev, (ST_Dump(geom)).geom as geom
FROM ruian.obce;
Konverze prvku (singleparts) na multiprvek (multiparts).
Rozdělení linie na daný počet segmentů.
WITH linie AS (
SELECT st_boundary(geom) AS geom
FROM ruian.staty
), a AS (
SELECT generate_series(0,999,1) AS a
)
SELECT ST_LineMerge(
ST_LineSubstring(
geom, a * 0.001, (a + 1) * 0.001)
) AS geom
FROM a, linie;
-- jako procedura...
CREATE OR REPLACE FUNCTION rozdel_pocet(
p_geometry GEOMETRY, p_pocet_segmentu INTEGER)
RETURNS TABLE(v_hranice GEOMETRY)
AS $$
BEGIN
RETURN QUERY
WITH series AS (
SELECT generate_series(
0, p_pocet_segmentu-1, 1) AS series
)
SELECT ST_LineMerge(ST_LineSubstring(
p_geometry, series * 0.001, (series + 1) * 0.001)
) AS v_hranice
FROM series;
END; $$
LANGUAGE 'plpgsql';
SELECT rozdel_pocet(
p_geometry => st_boundary(geom), p_pocet_segmentu => 1000
)
FROM ruian.staty;
Rozdělení linie na segmenty s danou délkou.
WITH linie AS (
SELECT ogc_fid, st_boundary(geom) AS geom
FROM ruian.staty
)
SELECT ST_LineSubstring(geom, 2000 * n / length,
CASE
WHEN 2000 * (n + 1) < length
THEN 2000 * (n + 1) / length
ELSE 1
END) AS geom
FROM
(SELECT ogc_fid,
st_linemerge(geom) AS geom,
st_length(geom) AS length
FROM linie
) AS t
CROSS JOIN generate_series(0, 2000) AS n
WHERE n * 2000 / length < 1;
Data Definition Language
Vytvoření databáze.
Vytvoření tabulky (id, popisná data, geometrie).
CREATE TABLE poi (fid serial primary key,
nazev varchar(100),
geom geometry(point, 5514));
INSERT INTO poi (nazev, geom) VALUES (
'FSv CVUT v Praze',
st_transform(
'SRID=4326;POINT (14.3880675 50.1040214)'::geometry,
5514
));
Vytvoření tabulky jako výsledek dotazu:
CREATE TABLE okresy AS
SELECT okreskod, st_union(geom) AS geom FROM ruian.obce
GROUP BY okreskod;
-- Přidání primárního klíče:
ALTER TABLE okresy ADD COLUMN fid serial;
ALTER TABLE okresy ADD PRIMARY KEY (fid);
-- Omezení geometrie:
ALTER TABLE okresy ALTER COLUMN geom
TYPE geometry(MULTIPOLYGON, 5514)
USING st_multi(st_setsrid(geom, 5514));
--Sestavení prostorového indexu:
CREATE INDEX ON okresy USING gist (geom);
-- vs.
CREATE TABLE okresy (
fid serial PRIMARY KEY, okreskod int,
geom geometry(multipolygon, 5514)
);
INSERT INTO okresy (okreskod, geom)
SELECT okreskod, st_multi(st_union(geom)) AS geom
FROM ruian.obce GROUP BY okreskod;
CREATE INDEX ON okresy USING gist (geom);
Odstranění atributu geometrie/tabulky.
ALTER TABLE okresy DROP COLUMN geom;
DROP TABLE okresy;
-- vs.
SELECT DropGeometryColumn('okresy', 'geom');
SELECT DropGeometryTable('okresy');