Dávkové nahrání dat

Dávkové zpracování a následné nahrávání dat z různých zdrojů do databáze je nejzákladnějším úkonem při budování databáze prostorových dat. Data obvykle nahráváme ze souborových formátů, případně z webových služeb. Řetězec úkonů mezi uchopením nějakého zdroje dat a jeho konečným umístěním do databáze bychom nejspíš označili termínem ETL. Pro standardní formáty s úspěchem můžeme využít utility knihovny GDAL, jmenovitě ogr2ogr. Pro import Esri Shapefile můžeme využít loader shp2pgsql instalovaný spolu s PostGISem.

shp2pgsql

shp2pgsql je utilitka distribuovaná spolu s PostGISem, která vrací data v dump formátu na standardní výstup. Je tedy možné, v případě potřeby, upravit výstup z této utility například unixovým nástrojem sed či případně uložit do souboru a ručně zeditovat.

shp2pgsql umí data přidat do již existující tabulky, tak potřebnou tabulku vytvořit. Lze také pouze vytvořit na základě dat prázdnou tabulku a samotná data do ní nenahrávat. Více v manuálu.

Poznámka

Hlavním limitem pro použití shp2pgsql jsou samotné limity formátu Esri Shapefile. Jde například o zkracování názvů sloupců, formát nedovoluje délku názvu sloupce větší než 10 znaků. Dále můžete mít problém s některými datovými typy, například numeric bude surově zakrácen na int, což může způsobit problémy mimo jiné u dat ve formátu VFK, které používají primární klíče numeric(30).

V našem příkladě zkusíme nahrát datovou vrstvu ulic.

shp2pgsql Ulice_cp1250.shp ukol_1.ulice | psql pokusnik 2> err

Tabulka se vůbec nevytvoří, problém je s kódováním. Atributová tabulka vstupních dat je v kódování cp1250 a naše databáze v UTF8. Použijeme proto přepínač -W pro nastavení kódování vstupního souboru.

shp2pgsql -W cp1250 Ulice_cp1250.shp ukol_1.ulice | psql pokusnik 2> err

Pohled do geometry_columns nám odhalí poměrně nepříjemný fakt a to, že naše nově přidaná vrstva nemá správný souřadnicový systém (SRID 5514), nýbrž neznámý souřadnicový systém s kódem 0.

SELECT * FROM geometry_columns WHERE f_table_schema = 'ukol_1';
f_table_catalog   | skoleni
f_table_schema    | ukol_1
f_table_name      | ulice
f_geometry_column | geom
coord_dimension   | 2
srid              | 0
type              | MULTILINESTRING

Musíme tedy rozšířit předešlý příkaz o zadání SRID, které má být nové vrstvě přiřazeno (přídáme přepínačý -d, který stavající tabulku nejprve odstraní).

shp2pgsql -d -W cp1250 -s 5514 Ulice_cp1250.shp ukol_1.ulice | psql pokusnik 2> err

Tip

SRID vrstvy, je samozřejmě možné změnit u hotové vrstvy a to příkazem UpdateGeometrySRID, nicméně v případě, že nad takovou tabulkou už máte kupříkladu postavené pohledy, bude to nutně znamenat je všechny přegenerovat, přičemž si můžete (a také nemusíte) vyrobit nepříjemný chaos v právech. Je tedy lepší na toto pamatovat a tabulky již vytvářet se správným SRID.

Tip

K utilitě shp2pgsql existuje také obrácený nástroj pgsql2shp, který slouží k exportu tabulek do formátu Esri Shapefile. Jeho použití je jednoduché a najdete ho na každém stroji s PostGISem. Nicméně, jak již bylo zmíněno, Esri Shapefile je zastaralý formát. Při jeho použití může dojít k degradaci dat, tudíž ho má smysl použít jen pokud příjemce dat vyžaduje výslovně tento formát.

Poznámka pro pokročilé

Příklad jednoduchého skriptu pro dávkový import souborů ve formátu Esri Shapefile z aktuálního adresáře

#!/bin/sh

for f in *.shp; do
    echo $f
    shp2pgsql -d -D $f ukol_1.${f%%.shp} 2>/dev/null | \
     psql pokusnik >/dev/null 2>err
done

exit 0

ogr2ogr

Nástroj ogr2ogr je součástí balíku utilit distribuovaných s knihovnou GDAL. Slouží k převodu dat mezi nejrůznějšími GIS formáty. Mimo jiné, od verze 1.11 podporuje také Výměnný formát RÚIAN, což je v našich podmínkách velice užitečné. Kromě převodů mezi různými formáty geodat můžeme ogr2ogr použít i pro transformaci mezi souřadnicovými systémy.

ogr2ogr se umí buď připojit rovnou do databáze, nebo umí generovat (případně posílat na STDOUT) data v dump formátu PostgreSQL.

Driver PostgreSQL

Nejdříve převedeme data z předešlého příkladu. Použijeme driver PostgreSQL, který se připojuje přímo k databázi.

Nahrání Esri Shapefile pomocí ogr2ogr z příkazové řádky

export SHAPE_ENCODING="cp1250"
ogr2ogr -f PostgreSQL PG:dbname=pokusnik -a_srs 'EPSG:5514' Ulice_cp1250.shp \
   -nlt MULTILINESTRING \
   -lco 'GEOMETRY_NAME=geom' \
   -nln ukol_1.ulice

V prvním řádku uvedeme kódování atributových dat vstupního souboru.

Tip

V případě, že bychom chtěli použít pro import jiné kódování, než je UTF8, nastavíme kódování pro PostgreSQL do proměnné prostředí PGCLIENTENCODING.

Varování

Příkaz export funguje pod Linuxem, ve Windows se proměnné prostředí nastavují jinak.

Tip

Proměnnou prostředí SHAPE_ENCODING použijeme v případě, že chceme použít při výstupu do Esri Shapefile jiné kódování, než je UTF8.

Parametr -f nastaví výstupní formát na PostgreSQL, PG: nastaví parametry připojení k databázi. Lze také nastavit např. aktivní schéma - tj. schéma, do kterého budou vstupní data importována.

Tip

U dávkového nahrávání je výhodné nastavit si heslo do souboru s autentizací.

Parametr -a_srs slouží k nastavení výstupního souřadnicovýho systému, v tomto případě nastavíme souřadnicový systém na EPSG:5514, tedy S-JTSK.

Poznámka pro pokročilé

Pokud bychom chtěli data v rámci importu transformovat, tak použijeme volby t_srs a s_srs. Souřadnicový systém můžeme zadávat i v zápisu knihovny Proj.4.

Následuje název vstupního souboru. Po něm je použit parametr -nlt, který slouží k zadání typu geometrie, v našem případě je to MULTILINESTRING, ogr2ogr totiž z nějakého důvodu v tomto případě chybně identifikuje geometrii vstupního souboru jako LINESTRING.

Nakonec pomoci -nln nastavíme nový název vrstvy (včetně názvu schématu, které ovšem musí být předem vytvořeno).

Driver PGDump

Driver PGDump místo přímého spojení s databází zapisuje do souboru (nebo na STDOUT). To může být užitečné například v případě, že nemáme přímé připojení k databázi, nebo když chceme převedený soubor nějakým způsobem dále upravit. Můžeme ho například poslat rourou přes sed a potom rovnou na psql. Tento postup však bude fungovat na OS, kde je tento nástroj dosupný, např. GNU/Linux.

Nahrání Esri Shapefile pomocí ogr2ogr (PGDump) z příkazové řádky

Zde přejmenujeme ulici Kaštanová na Jírovcová.

export PG_USE_COPY=YES
ogr2ogr -f PGDump /dev/stdout -a_srs 'EPSG:5514' Ulice_cp1250.shp \
-lco 'GEOMETRY_NAME=geom' \
-nlt MULTILINESTRING -nln ukol_1.ulice_3 \
| sed 's/Kaštanová/Jírovcová/g' \
| psql pokusnik 2> err

V prvním řádku nastavíme proměnnou prostředí PG_USE_COPY. Tím řekneme, že data mají být přenesena jako COPY tabname FROM STDIN, namísto řady INSERT statementů. Stejným způsobem by fungoval i PostgreSQL driver.

Parametry na dalším řádku již známe. Jedná se o formát, následuje název výstupního souboru (v našem případě odesíláme na stdout), výstupní souřadnicový systém a vstupní soubor. Další řádek též nepřináší nic nového. Nakonec nahradíme pomocí sedu „Kaštanová“ za „Jírovcová“ a odešleme rourou na psql.

Důležité

PostgreSQL driver může mít problém vytvořit tabulku s více geometrickými sloupci, potom nezbývá než použít PGDump driver nebo vytvořit tabulku ručně.

Poznámky k dalším formátům

Esri Shapefile není samozřejmě jediný formát, se kterým ogr2ogr pracuje. Předvedeme si, jak snadno nahrát soubor ve formátu GML.

GML

Nahrání GML pomocí ogr2ogr z příkazové řádky

Data ke stažení zde.

ogr2ogr -f PGDump /dev/stdout -a_srs 'EPSG:5514' \
-lco 'GEOMETRY_NAME=adresnibod' \
adres_mista.gml.gz \
-nln ukol_1.adresy | \
psql pokusnik 2> err

VFR

Nahrání Výměnného formátu RÚIAN (VFR) pomocí ogr2ogr z příkazové řádky

Nejprve vytvoříme nové schéma

psql gismentors -h training.gismentors.eu -U skoleni -W -c "create schema ltm"

A poté naimportujeme data vybrané obce (Litoměřice - 564567)

ogr2ogr -f PostgreSQL \
"PG:dbname=gismentors host=training.gismentors.eu user=skoleni password=XXX active_schema=ltm" \
/vsicurl/http://vdp.cuzk.cz/vymenny_format/soucasna/20170331_OB_564567_UKSH.xml.gz

Bližší informace: http://freegis.fsv.cvut.cz/gwiki/RUIAN

WFS

V ogr2ogr je možné pracovat i s webovými službami, například můžeme načíst katastrální území z WFS ČÚZK.

Nahrání WFS z příkazové řádky

ogr2ogr -f "PostgreSQL" PG:"dbname=pokusnik" \
"http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?\
service=WFS\
&request=GetFeature&version=2.0.0\
&srsName=urn:ogc:def:crs:EPSG::5514\
&typeNames=CP:CadastralZoning\
&featureid=CZ.605999" \
-nln ukol_1.katatest

Poznámka pro pokročilé

Ve WFS bývá nastaven limit na maximální počet prvků. V praxi není možné obvykle stáhnout větší objem dat. Můžeme však stahovat prvky po jednom. Z číselníku katastrálních území vybereme katastrální území Prahy.

Dávkového nahrání dat z WFS z příkazové řádky

wget http://www.cuzk.cz/CUZK/media/CiselnikyISKN/SC_SEZNAMKUKRA_DOTAZ/SC_SEZNAMKUKRA_DOTAZ.zip?ext=.zip
unzip SC_SEZNAMKUKRA_DOTAZ.zip?ext=.zip
psql -c "truncate table ukol_1.katatest" pokusnik;

cut -d ';' -f 7,8 SC_SEZNAMKUKRA_DOTAZ.csv | \
   tail -n +2 | \
   grep Praha |
   cut -d ';' -f 2 |
   while read kodku; do
      echo $kodku;
      ogr2ogr -append \
      -f "PostgreSQL" PG:"dbname=pokusnik" \
      "http://services.cuzk.cz/wfs/inspire-cp-wfs.asp?\
service=WFS\
&request=GetFeature&version=2.0.0\
&srsName=urn:ogc:def:crs:EPSG::5514\
&typeNames=CP:CadastralZoning&\
featureid=CZ.$kodku" \
      -nln ukol_1.katatest
    done;

Varování

Bagrování WFS ovšem není ideální způsob jak plnit databázi daty (limit na bbox a počet prvků tam není jen tak pro nic za nic). Tato data je možné získat i pohodlněji a šetrněji k infrastruktuře ČÚZK.

Zobrazení dat

Data si můžeme zobrazit a dále s nimi pracovat například v QGISu, viz školení pro začátečníky. Pokročilejší uživatelé mohou výužít funkce PostGISu, viz příklad níže.

Poznámka pro pokročilé

Na závěr si naše data zobrazíme ve formátu SVG.

SET SEARCH_PATH = public, ukol_1;
SELECT
XMLELEMENT(
   NAME svg,
   XMLATTRIBUTES(
      height, width, "viewBox"
      , 'http://www.w3.org/2000/svg' AS xmlns, '1.1' AS version)
   , linie, body, popisky
)
FROM
(
   SELECT
   600 AS height
   , 800 AS width
   , array_to_string(ARRAY[MIN(ST_XMIN(geom)) - 50, -1 * (MAX(ST_YMAX(geom))) - 50
      , (@(MAX(ST_XMAX(geom)) - MIN(ST_XMIN(geom)))) + 100
      , (@(MAX(ST_YMAX(geom)) - MIN(ST_YMIN(geom)))) + 100], ' ') AS "viewBox"
   , XMLAGG(
      XMLELEMENT(NAME path,
         XMLATTRIBUTES( ST_AsSVG(geom, 1, 0) AS d
            , 'rgb(55,230,100)' AS stroke
            , 25 AS "stroke-width"
            , 'none' AS fill
         )
      )
   ) linie
   FROM
   (
      SELECT geom AS geom FROM ulice --LIMIT 1
   ) ok
) podklad,
(
   SELECT
   XMLAGG(
      XMLELEMENT(NAME circle,
         XMLATTRIBUTES( ST_X(geom) AS cx, -1 * ST_Y(geom) AS cy, 150 AS r
            , 'black' AS stroke
            , 100 AS "stroke-width"
            , 'rgb(255,0,0)' AS fill
         )
      )
   ) body

   , XMLAGG(
      XMLELEMENT(NAME text,
         XMLATTRIBUTES( ST_X(geom) + 250 AS x, -1 * ST_Y(geom) AS y
            , 'Verdana' AS "font-family"
            , 750 AS "font-size"
            , 'rgb(0,0,0)' AS fill
         ), id
      )
   ) popisky
   FROM
   (
      SELECT id, geom_p AS geom FROM vesmirne_zrudice --LIMIT 1
   ) body
) data;