PostGIS pro vývojáře Pavel Stěhule Department of Mapping and Cartography, Faculty of Civil Engineering Czech Technical University in Prague stehule kix.fsv.cvut.cz Kĺıčová slova: Database systems, GIS, development Abstrakt Systémy GIS prvńı generace ukládaly svá data do soubor̊u v proprietárńıch formátech. Daľśı generace těchto systém̊u dokázaly spolupracovat s databázemi (zejména čerpat data s databáźı). Soudobé GIS systémy se jǐz zcela spoléhaj́ı na databáze. Tyto požadavky GIS systém̊u musely reflektovat databázové systémy. Nejstarš́ı SQL systémy v̊ubec s prostorovými daty nepoč́ıtaly. Úvod Až standard ANSI SQL2000 v části věnované podpoře multimediálńıch dat obsahuje popis prostorových (spatial) dat. Z komerčńıch databáźı je na prvńım mı́stě, co se týče podpory prostorových dat, databázový systém fy. Oracle. Poč́ınaje verźı Oracle 7 existuje rozš́ı̌reńı Oracle, samostatně distribuované, řeš́ıćı podporu prostorových dat. Toto rozš́ı̌reńı se nazývá Oracle spatial. Odpověd́ı o.s. světa byla implementace podpory prostorových dat pro RDBMS PostgreSQL a to tak, jak předpokládá standard OpenGIS. Jednou z charakteristik PostgreSQL je právě jeho rozšǐritelnost. V PostgreSQL lze relativně jednoduše navrhnout vlastńı datové typy, vlastńı operace a operandy nad těmito typy. Touto vlastnost́ı byl systém PostgreSQL mezi o.s. databázovými systémy výjimečný. Proto cel- kem logicky byl PostgreSQL použit pro o.s. implementaci standardu OpenGIS. Část stan- dardu OpenGIS (chronologicky předcháźı SQL2000) je zaměřena na SQL databáze, které by měly sloužit jako uložǐstě prostorových dat. Vycháźı z SQL92 rozš́ı̌reného o geometrické typy (SQL92 with Geometry Types), definuje metadata popisuj́ıćı funkcionalitu systému co se týká geometrických dat, a definuje datové schéma. Databáze, jejichž datový model respek- tuje normu OpenGIS, může sloužit jako datový server libovolné GIS aplikaci, která vycháźı z tohoto standardu. Dı́ky tomu může, v celé řadě př́ıpad̊u, PostgreSQL zastoupit komerčńı db systémy. Implementace standardu OpenGIS pro PostgreSQL se nazývá PostGIS. Postgre- SQL základńı geometrické typy má, úkolem PostGISu je hlavně jejich obaleńı do specifického (určeného normou) datového modelu. SQL/MM-Spatial sice vycháźı z OpenGIS nicméně neńı kompatibilńı. PostGIS je certifikován pro OpenGIS, částečně také implementuje SQL/MM. Geinformatics FCE CTU 2007 71 PostGIS pro vývojáře PostGIS obsahuje: � nové datové typy (geometry), � nové operátory (&& pr̊unik, @kompletně obsažen), � nové funkce (distance, transform), � nové tabulky (Geometry columns, Spatial ref sys) slouž́ı jako systémové tabulky, po- skytuj́ı prostor pro metadata. Standard OpenGIS je množina dokument̊u detailně popisuj́ıćı aplikačńı rozhrańı a datové formáty v oblasti GIS systémů. Tato dokumentace je určena vývojář̊um a jej́ım ćılem je dosažeńı interoperability aplikaćı vyv́ıjených členy konsorcia Open Geospatial Consortium (www stránky OGC). Oblast, kterou pokrývá PostGIS je popsána v dokumentu ”OpenGIS® Simple Features Specification For SQL” Názvy datových typ̊u v SQL/MM odvozených z OpenGISu vznikly spojeńım prefixu ST (spatial type) a názvu datového typu v OpenGISu. OpenGIS je obecněǰśı, poč́ıtá s mi- nimálně dvěma variantami implementace geometrických typ̊u, a k tomu potřebnému zázemı́. SQL/MM-Spatial se dá s jistou mı́rou tolerance chápat jako podmnožina OpenGISu. Implementace vlastńıch funkćı v PostgreSQL Vzhledem k faktu, že vlastńı datové typy lze implementovat pouze v prg. jazyce C a že PostGIS je implementován v C, bude popsán návrh modulu pouze s využit́ım jazyka C. Při návrhu funkćı v C se prakticky všude použ́ıvaj́ı makra z PostgreSQL knihovny. Důvod̊u je několik: � použ́ıváńı univerzálńıho typu Varlena pro typy s variabilńı délkou, který je v př́ıpadě, že je deľśı než 2K transparentně (jak pro uživatele, tak pro vývojáře) komprimován. � odlǐsný zp̊usob předáváńı parametr̊um (v PostgreSQL nepř́ımo prostřednictv́ım určené hodnoty typu struct obsahuj́ıćı ukazatel na pole parametr̊u, pole s informaćı, který parametr je NULL, a počtem parametr̊u). � tento zp̊usob voláńı neńı závislý na programovaćım jazyku C Ukázka implementace funkce concat text spojuj́ıćı dva řetězce dohromady: 01 PG_FUNCTION_INFO_V1(concat_text); 02 03 Datum 04 concat_text(PG_FUNCTION_ARGS) 05 { 06 text *arg1 = PG_GETARG_TEXT_P(0); 07 text *arg2 = PG_GETARG_TEXT_P(1); 08 int32 new_text_size = VARSIZE(arg1) + VARSIZE(arg2) - VARHDRSZ; 09 text *new_text = (text *) palloc(new_text_size); 10 11 VARATT_SIZEP(new_text) = new_text_size; 12 memcpy(VARDATA(new_text), VARDATA(arg1), VARSIZE(arg1) - VARHDRSZ); 13 memcpy(VARDATA(new_text) + (VARSIZE(arg1) - VARHDRSZ), 14 VARDATA(arg2), VARSIZE(arg2) - VARHDRSZ); 15 PG_RETURN_TEXT_P(new_text); 16 } Geinformatics FCE CTU 2007 72 PostGIS pro vývojáře Komentáře: 01 Registrace funkce s volaćı konvenćı 1. 03 Každá funkce dosažitelná z SQL muśı vracet univerzálńı datový typ Datum (společný typ pro datové typy s fixńı velikost́ı (menš́ı než 64bite) a datový typ varlena). 06 Źıskáńı prvńıho parametru (resp. ukazatele na něj a korektńı přetypováńı, př́ıpadně de- komprimace). 07 Źıskáńı prvńıho parametru (resp. ukazatele na něj a korektńı přetypováńı, př́ıpadně de- komprimace). 08 Datový typ varlena je podobný stringu v Pascalu, prvńı čtyři byte obsahuj́ı údaj o délce s rozd́ılem, že údaj obsahuje velikost celé hodnoty včetně záhlav́ı. Velikost hlavičky je uložena v konst. VARHDRSZ. Výpočet velikosti vrácené hodnoty typu datum (součet velikost́ı obou řetězc̊u + velikost hlavičky). 09 PostgreSQL má vlastńı správu paměti, tud́ıž se pamět’ nealokuje voláńım funkce malloc, ale palloc. PostgreSQL přiděluje pamět’ z tzv. pamět’ových kontext̊u (persistentńı, trans- akce, voláńı funkce). Při dokončeńı operace se uvolňuje odpov́ıdaj́ıćı pamět’ový kontext. Explicitńı voláńı pfree má smysl jen u funkćı, které by bez explicitńıho uvolněńı paměti si nárokovali př́ılǐs paměti. Použit́ı pamět’ových kontext̊u snižuje riziko tzv. memory le- aku, tj. že vývojář zapomene vrátit alokovanou pamět’. Také snižuj́ı náročnost vráceńı paměti. Mı́sto několikanásobného uvolněńı malých blok̊u paměti se volá jednorázová operace. Daľśım př́ıjemným efektem je nižš́ı fragmentace paměti. 15 Přetypováńı z typu text na datum (př́ıpadná komprimace). Každá interńı funkce se muśı před vlastńım použit́ım zaregistrovat pro jej́ı použit́ı v SQL. 01 CREATE FUNCTION concat_text(text, text) RETURNS text 02 AS ’DIRECTORY/funcs’, ’concat_text’ 03 LANGUAGE C STRICT; Komentáře: 01 Funkce concat text má dva parametry typu text a vraćı text. 02 Tuto funkci PostgreSQL nalezne v knihovně (souboru) ’DIRECTORY/funcs’ pod názvem ’concat text’. 03 Jedná se o binárńı knihovnu. V př́ıpadě, že jeden parametr je NULL, výsledkem voláńı funkce je hodnota NULL (atribut STRICT). Pro voláńı existuj́ıćıch PostgreSQL funkćı (pod volaj́ıćı konvenćı 1) muśıme použ́ıt specifický zp̊usob jejich voláńı, resp. předáńı parametr̊u. Často použ́ıvanou funkćı je textin, což je funkce, která slouž́ı pro převod z-̌retězce (klasického řetězce v jazyce C ukončeného nulou) na řetězec typu varlena. Následuj́ıćı funkce vrát́ı konstantńı řetězec ”Hello World!”. 01 PG_FUNCTION_INFO_V1(hello_world); 02 03 Datum 04 hello_word(PG_FUNCTION_ARGS) 05 { 06 PG_RETURN_DATUM(DirectFunctionCall1(textin, CStringGetDatum("Hello World!"))); 07 } Geinformatics FCE CTU 2007 73 PostGIS pro vývojáře Komentáře: 06 CStringGetDatum provád́ı pouze přetypováńı Bez použit́ı funkce DirectFunctionCall1(1 na konci názvu funkce má význam jeden argument. Tato funkce existuje ve variantách pro jeden až devět argument̊u.) by výše zmı́něná funkce vypadala následovně (z ukázky je vidět předáváńı parametr̊u v V1 volaj́ıćı konvenci): 01 PG_FUNCTION_INFO_V1(hello_world); 02 03 Datum 04 hello_word(PG_FUNCTION_ARGS) 05 { 06 FunctionCallInfoData locfcinfo; 07 08 InitFunctionCallInfoData(locfcinfo, fcinfo->flinfo, 1, NULL, NULL); 09 10 locfcinfo.arg[0] = CStringGetDatum("Hello World!") 11 locfcinfo.argnull[0] = false; 12 13 PG_RETURN_DATUM(textin(&locfinfo)); 14 } Komentáře: 08 Datová struktura locfcinfo je inicializována pro jeden argument. 13 Př́ımé voláńı funkce textin. Jelikož tato funkce vraćı typ Datum, který je výsledný typ funkce hello world, nedocháźı k přetypováńı. Implementace vlastńıch datových typ̊u v PostgreSQL V PostgreSQL je každý datový typ určen svoj́ı vstupńı a výstupńı, a sadou operátor̊u a funkćı, které tento typ podporuj́ı. Vstupńı funkce je funkce, která převád́ı řetězec na binárńı hodnotu. Výstupńı funkce inverzně převád́ı binárńı hodnotu na řetězec. Podporované operátory a funkce pak pracuj́ı s binárńı hodnotou. V př́ıpadě vkládáńı nových záznamů se vstupńı funkce volaj́ı automaticky, v př́ıpadě výraz̊u je nutné v některých př́ıpadech volat explicitńı konverzi. Explicitńı konverze se v PostgreSQL provede třemi r̊uznými zp̊usoby: � zápisem typu následovaný řetězcem � použit́ım ANSI SQL operátoru CAST � použit́ım binárńıho operátoru pro přetypováńı ’::’ (neńı standardem) Ukázka: SELECT rodne_cislo ’7307150xxx’; SELECT CAST(’730715xxx’ AS rodne_cislo); SELECT ’730715xxx’::rodne_cislo; OpenGIS, coby nezávislý standard, přidává vlastńı zp̊usob zápisu. V OpenGISu je poč́ıtáno i s variantou, že data jsou uložená textově, v př́ıpadě že databázový systém nelze rozš́ı̌rit o geometrické typy (což neńı př́ıpad PostgreSQL). Nicméně PostGIS nepouž́ıvá geometrické typy PostgreSQL. Typ se zapisuje př́ımo do řetězce následovaný vlastńımi daty, které jsou uzavřené v závorkách. Tento zápis se označuje jako ’Well-Known Text (WKT)’. Pro přečteńı hodnoty tohoto typu se použ́ıvá funkce GeometryFromText. Geinformatics FCE CTU 2007 74 PostGIS pro vývojáře Ukázka: INSERT INTO SPATIALTABLE ( THE_GEOM, THE_NAME ) VALUES ( GeomFromText(’POINT(-126.4 45.32)’, 312), ’A Place’); Kromě textového formátu je v OpenGISu ještě definován binárńı formát ’Well-Know Binary (WKB)’. Konverzi z binárńıho formátu do textového (čitelná podoba) formátu provád́ı funkce asewkt (astext). Interně PostGIS ukládá data v binárńım formátu WKB. Pokud máme vstupńı a výstupńı funkci k dispozici, můžeme zaregistrovat nový datový typ př́ıkazem CREATE TYPE. 01 CREATE OR REPLACE FUNCTION rodne_cislo_in (cstring) 02 RETURNS rodne_cislo AS ’rc.so’,’rodne_cislo_in’ LANGUAGE ’C’; 03 04 CREATE OR REPLACE FUNCTION rc_out (rodne_cislo) 05 RETURNS cstring AS ’rc.so’, ’rodne_cislo_out’ LANGUAGE ’C’; 06 07 CREATE TYPE rodne_cislo ( 08 internallength = 8, 09 input = rc_in, 10 output = rc_out 11 ); Komentáře: 08 Jedná se o datový typ s pevnou délkou osmi bajt̊u. Pokud chceme datovým typ použ́ıt v databázi, muśıme implementaci datového typu rozš́ı̌rit o implementaci základńıch binárńıch operátor̊u. Poté bude možné použ́ıt vlastńı datový typ v klauzuli WHERE a ORDER BY. 01 CREATE OR REPLACE FUNCTION rodne_cislo_eq (rodne_cislo, rodne_cislo) 02 RETURNS bool AS ’rc.so’,’rodne_cislo_eq’ LANGUAGE ’C’ STRICT; 03 04 CREATE OR REPLACE FUNCTION rodne_cislo_ne (rodne_cislo, rodne_cislo) 05 RETURNS bool AS ’rc.so’, ’rodne_cislo_ne’ LANGUAGE ’C’ STRICT; 06 07 CREATE OR REPLACE FUNCTION rodne_cislo_lt (rodne_cislo, rodne_cislo) 08 RETURNS bool AS ’rc.so’,’rodne_cislo_lt’ LANGUAGE ’C’ STRICT; 09 10 CREATE OR REPLACE FUNCTION rodne_cislo_le (rodne_cislo, rodne_cislo) 11 RETURNS bool AS ’rc.so’, ’rodne_cislo_le’ LANGUAGE ’C’ STRICT; 12 13 CREATE OPERATOR = ( 14 leftarg = rodne_cislo, 15 rightarg = rodne_cislo, 16 procedure = rodne_cislo_eq 17 commutator = =, 18 negator = <>, 19 restrict = eqsel, 20 join = eqjoinsel 21 ); 22 23 CREATE OPERATOR <> ( 24 leftarg = rodne_cislo, 25 rightarg = rodne_cislo, 26 procedure = rodne_cislo_ne 27 ); 28 29 CREATE OPERATOR <( 30 leftarg = rodne_cislo, 31 rightarg = rodne_cislo, 32 procedure = rodne_cislo_le 33 ); 34 Geinformatics FCE CTU 2007 75 PostGIS pro vývojáře 35 CREATE OPERATOR <= ( 36 leftarg = rodne_cislo, 37 rightarg = rodne_cislo, 38 procedure = rodne_cislo_le 39 ); Komentáře: 13 Registrace binárńıho operátoru rovno. 14 Levý argument je typu rodné č́ıslo. 15 Pravý argument je typu rodné č́ıslo. 16 Název funkce, která zajǐst’uje operaci porovnáńı pro datový typ rodne cislo. 17 Rovná se je komutátorem sama sebe, nebot’ plat́ı že x = y <=> y = x. 18 Plat́ı, že x = y <=> N OT (x <> y). 19 Operátor je silně restriktivńı v př́ıpadě, že jedńım argumentem je konstanta, tj. výsledkem je malá podmnožina tabulky. 20 Operátoru se přǐrazuje funkce odhadu selektivity. Výše uvedené operátory stále nestač́ı k tomu, aby se nad sloupcem s vlastńım typem mohl vytvořit index. Každý datový typ muśı mı́t definovanou alespoň jednu tř́ıdu operátor̊u, což je v podstatě seznam operátor̊u doplněný o jejich sémantický význam. Kromě operátor̊u je potřeba určit tzv. podp̊urnou funkci F (a, b), jej́ıž parametry jsou kĺıče a výsledkem celé č́ıslo (a > b => F (a, b) = 1; a = b => F (a, b) = 0; a < b => F (a, b) = −1). 01 CREATE OR REPLACE FUNCTION rodne_cislo_cmp (rodne_cislo, rodne_cislo) 02 RETURNS int AS ’rc.so’, ’rodne_cislo__cmp’ LANGUAGE ’C’ STRICT; 03 04 CREATE OPERATOR CLASS rodne_cislo_ops 05 DEFAULT FOR TYPE rodne_cislo USING btree AS 06 OPERATOR 1 <, 07 OPERATOR 2 <=, 08 OPERATOR 3 = , 09 OPERATOR 4 >=, 10 OPERATOR 5 >, 11 FUNCTION 1 rodne_cislo_cmp (rodne_cislo, rodne_cislo); Komentáře: 06 Strategie 1 má význam menš́ı než. 07 Strategie 2 má význam menš́ı rovno než. 08 Strategie 3 má význam rovno. 09 Strategie 4 má význam vetš́ı rovno než. 10 Strategie 5 má význam větš́ı než. 11 Určeńı podp̊urné funkce. Geinformatics FCE CTU 2007 76 PostGIS pro vývojáře Ukázka použit́ı PostGISu OpenGIS předpokládá uložeńı dat do klasických databázových tabulek. Nicméně k tomu, aby tyto tabulky dokázali přeč́ıst GIS aplikace je nutné do datového schématu přidat dvě systémové (z pohledu OpenGIS) tabulky. geometry columns obsahuje informace o sloupćıch geometrii geoprvk̊u, spatial ref sys obsahuje informace o souřadnicových systémech použ́ıvaných systémem. 01 create table user_locations (gid int4, user_name varchar); 02 select AddGeometryColumn (’db_mapbender’,’user_locations’,’the_geom’,’4326’,’POINT’,2); 03 insert into user_locations values (’1’,’Morissette’,GeometryFromText(’POINT(-71.060316 48.432044)’, 4326)); Komentáře: 01 vytvořeńı tabulky fakt̊u (feature table) 02 do tabulky user location přidá sloupec s názvem the geom (následně přidá do tabulky geometry columns nový řádek s metadaty o sloupci the geom) 03 Plněńı tabulky daty Kromě plněńı datových tabulek pomoćı SQL př́ıkaz̊u PostGIS obsahuje nástroj pro import datových (shape) soubor̊u, tzv. shape loader. Dı́ky němu je možné importovat data v několika formátech. Namátkou podporované formáty jsou Shape, MapInfo, DGN, GML. K urychleńı operaćı prováděných nad prostorovými daty lze použ́ıt prostorový index. Post- greSQL podporuje několik typ̊u index̊u, pro GIS lze použ́ıt (ve verzi 8.2) formáty GIST a GIN. 01 CREATE INDEX 02 ON 03 USING GIST ( [ GIST_GEOMETRY_OPS ] ); Komentáře: 03 GIST GEOMETRY OPS určuje výše zmı́něnou tř́ıdu operátor̊u. Analýza obsahu distribuce PostGIS 1.2.1 Struktura adresáře: � ./ – Sestavovaćı a instalačńı skripty � ./lwgeom – Zdrojový kód knihoven � ./java/ejb – Podpora EJB Java � ./java/jdbc – JDBC ovladač pro PostgreSQL rozš́ı̌rený o podporu GIS objekt̊u � ./java/pljava – PostgreSQL PL/Java rozš́ı̌rená o prostorové objekty � ./doc – Dokumentace � ./loader – Programy zajǐst’uj́ıćı konverzi ESRI Shape soubor̊u do SQL (resp. PostGIS) a inverzńı transformaci PostGIS dat do Shape soubor̊u (pgsql2shp a shp2pgsql) Geinformatics FCE CTU 2007 77 PostGIS pro vývojáře � ./topology – Počátečńı implementace modelu topology � ./utils – Pomocné skripty (aktualizace, profilace) � ./extras – Kód, který se nedostal do hlavńıho stromu (WFS locks, ukázka wkb parseru) � ./regress – Regresńı testy Závislosti: � proj4 - knihovna realizuj́ıćı transformace mezi projekcemi � geos – knihovna implementuj́ıćı topologické testy (PostgreSQL je třeba překládat s podporou C++) Typ LWGEOM nahrazuje p̊uvodńı typ GEOMETRY PostGISu. Oproti němu je menš́ı (data jsou uložená binárně – pro POINT(0,0) je to úspora z 140 bajt̊u na 21 bajt̊u), podporuje 1D, 2D, 3D a 4D souřadnice, interně vycháźı z OGC WKB typu a také jeho textová prezentace je OGC WKB. Typ LWGEOM nahradil předchoźı typ GEOMETRY ve verzi 1.0. LW znamená Light Weight (vylehčený). Interně v PostgreSQL se použ́ıvá identifikátor PG LWGEOM. Hodnoty se serializuj́ı rekurzivně, za hlavičkou specifikuj́ıćı typ a atributy se serializuj́ı vlastńı data. Jádro PostGISu je schované v implementaci typu LWGEOM. Jako parser je, v prostřed́ı UNIX obvyklý, použitý generátor překladač̊u Lex a yacc (konkrétně jejich implementace Bi- son). Syntaxe je určena v souborech wktparse.lex (lexikálńı elementy, kĺıčová slova, č́ısla) a wktparse.y (syntaktické elementy) 01 /* MULTIPOINT */ 02 03 geom_multipoint : 04 MULTIPOINT { alloc_multipoint(); } multipoint { pop(); } 05 | 06 MULTIPOINTM { set_zm(0, 1); alloc_multipoint(); } multipoint {pop(); } 07 08 multipoint : 09 empty 10 | 11 { alloc_counter(); } LPAREN multipoint_int RPAREN { pop(); } 12 13 multipoint_int : 14 mpoint_element 15 | 16 multipoint_int COMMA mpoint_element 17 18 mpoint_element : 19 nonempty_point 20 | 21 /* this is to allow MULTIPOINT(0 0, 1 1) */ 22 { alloc_point(); } a_point { pop(); } Komentáře: 03 Povoleným zápisem je MULTIPOINT seznam nebo MULTIPOINTM seznam. 08 Seznam může být prázdný nebo je posloupnost́ı č́ısel uzavřený v závorkách. 13 Seznam je bud’to o jednom prvku nebo seznam a čárkou oddělený element. 16 Rekurzivńı definice seznamu. Geinformatics FCE CTU 2007 78 PostGIS pro vývojáře 22 a point je 2D 3D 4D hodnota zapsaná jako posloupnost n č́ısel oddělených mezerou. Serializace a deserializace načteného syntaktického stromu je řešena v souboru lwgeom inout.c. 01 CREATE FUNCTION geometry_in(cstring) 02 RETURNS geometry 03 AS ’@MODULE_FILENAME@’,’LWGEOM_in’ 04 LANGUAGE ’C’ _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable); 05 06 CREATE FUNCTION geometry_out(geometry) 07 RETURNS cstring 08 AS ’@MODULE_FILENAME@’,’LWGEOM_out’ 09 LANGUAGE ’C’ _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable); 10 11 CREATE TYPE geometry ( 12 internallength = variable, 13 input = geometry_in, 14 output = geometry_out, 15 ); Definice typu geometry je v souboru lwpostgis.sql.in spolu s definicemi daľśıch deśıtek da- tabázových objekt̊u. Zcela zásadńı jsou tabulky spatial ref sys a geometry columns. 01 ------------------------------------------------------------------- 02 -- SPATIAL_REF_SYS 03 ------------------------------------------------------------------- 04 CREATE TABLE spatial_ref_sys ( 05 srid integer not null primary key, 06 auth_name varchar(256), 07 auth_srid integer, 08 srtext varchar(2048), 09 proj4text varchar(2048) 10 ); 11 12 ------------------------------------------------------------------- 13 -- GEOMETRY_COLUMNS 14 ------------------------------------------------------------------- 15 CREATE TABLE geometry_columns ( 16 f_table_catalog varchar(256) not null, 17 f_table_schema varchar(256) not null, 18 f_table_name varchar(256) not null, 19 f_geometry_column varchar(256) not null, 20 coord_dimension integer not null, 21 srid integer not null, 22 type varchar(30) not null, 23 CONSTRAINT geometry_columns_pk primary key ( 24 f_table_catalog, 25 f_table_schema, 26 f_table_name, 27 f_geometry_column ) 28 ) WITH OIDS; Řada funkćı PostGISu jsou realizována v jazyce PL/pgSQL. Což je jazyk SQL procedur v prostřed́ı PostgreSQL vycházej́ıćı z PL/SQL fy. Oracle (který vycháźı z prg. jazyka ADA). Je to celkem logické, d́ıky integraci SQL jsou SQL př́ıkazy zapsány úsporně a čitelně. 001 ----------------------------------------------------------------------- 002 -- ADDGEOMETRYCOLUMN 003 -- , , , , , , 004 ----------------------------------------------------------------------- 005 -- 006 -- Type can be one of geometry, GEOMETRYCOLLECTION, POINT, MULTIPOINT, POLYGON, 007 -- MULTIPOLYGON, LINESTRING, or MULTILINESTRING. 008 -- 009 -- Types (except geometry) are checked for consistency using a CHECK constraint 010 -- uses SQL ALTER TABLE command to add the geometry column to the table. 011 -- Addes a row to geometry_columns. Geinformatics FCE CTU 2007 79 PostGIS pro vývojáře 012 -- Addes a constraint on the table that all the geometries MUST have the same 013 -- SRID. Checks the coord_dimension to make sure its between 0 and 3. 014 -- Should also check the precision grid (future expansion). 015 -- Calls fix_geometry_columns() at the end. 016 -- 017 ----------------------------------------------------------------------- 018 CREATEFUNCTION AddGeometryColumn(varchar,varchar,varchar,varchar,integer,varchar,integer) 019 RETURNS text 020 AS 021 ’ Komentáře: 018 Tento zdrojový kód se v PostGISu zpracovává ještě preprocesorem, takže na prvńı pohled neplatné kĺıčové slovo CREATEFUNCTION je správné. Důvodem je potřeba jedné verze zdrojových kód̊u použitelných pro r̊uzné verze PostgreSQL, kdy mezi nejstarš́ı verźı 7.4 a nejnověǰśı 8.2 je zřetelný rozd́ıl v možnostech a i v zápisu uložených procedur. Jinak tyto verze od sebe děĺı tři roky. Přestože oficiálně nejstarš́ı podporovaná verze je 7.4, v kodu je řada odkaz̊u na verzi 7.2. 022 DECLARE 023 catalog_name alias for $1; 024 schema_name alias for $2; 025 table_name alias for $3; 026 column_name alias for $4; 027 new_srid alias for $5; 028 new_type alias for $6; 029 new_dim alias for $7; 030 rec RECORD; 031 schema_ok bool; 032 real_schema name; 033 BEGIN 034 035 IF ( not ( (new_type =’’GEOMETRY’’) or 036 (new_type =’’GEOMETRYCOLLECTION’’) or 037 (new_type =’’POINT’’) or 038 (new_type =’’MULTIPOINT’’) or 039 (new_type =’’POLYGON’’) or 040 (new_type =’’MULTIPOLYGON’’) or 041 (new_type =’’LINESTRING’’) or 042 (new_type =’’MULTILINESTRING’’) or 043 (new_type =’’GEOMETRYCOLLECTIONM’’) or 044 (new_type =’’POINTM’’) or 045 (new_type =’’MULTIPOINTM’’) or 046 (new_type =’’POLYGONM’’) or 047 (new_type =’’MULTIPOLYGONM’’) or 048 (new_type =’’LINESTRINGM’’) or 049 (new_type =’’MULTILINESTRINGM’’) or 050 (new_type = ’’CIRCULARSTRING’’) or 051 (new_type = ’’CIRCULARSTRINGM’’) or 052 (new_type = ’’COMPOUNDCURVE’’) or 053 (new_type = ’’COMPOUNDCURVEM’’) or 054 (new_type = ’’CURVEPOLYGON’’) or 055 (new_type = ’’CURVEPOLYGONM’’) or 056 (new_type = ’’MULTICURVE’’) or 057 (new_type = ’’MULTICURVEM’’) or 058 (new_type = ’’MULTISURFACE’’) or 059 (new_type = ’’MULTISURFACEM’’)) ) 060 THEN 061 RAISE EXCEPTION ’’Invalid type name - valid ones are: 062 GEOMETRY, GEOMETRYCOLLECTION, POINT, 063 MULTIPOINT, POLYGON, MULTIPOLYGON, 064 LINESTRING, MULTILINESTRING, 065 CIRCULARSTRING, COMPOUNDCURVE, Geinformatics FCE CTU 2007 80 PostGIS pro vývojáře 066 CURVEPOLYGON, MULTICURVE, MULTISURFACE, 067 GEOMETRYCOLLECTIONM, POINTM, 068 MULTIPOINTM, POLYGONM, MULTIPOLYGONM, 069 LINESTRINGM, MULTILINESTRINGM 070 CIRCULARSTRINGM, COMPOUNDCURVEM, 071 CURVEPOLYGONM, MULTICURVEM or MULTISURFACEM’’; 072 return ’’fail’’; 073 END IF; 074 075 IF ( (new_dim >4) or (new_dim <0) ) THEN 076 RAISE EXCEPTION ’’invalid dimension’’; 077 return ’’fail’’; 078 END IF; 079 080 IF ( (new_type LIKE ’’%M’’) and (new_dim!=3) ) THEN 081 082 RAISE EXCEPTION ’’TypeM needs 3 dimensions’’; 083 return ’’fail’’; 084 END IF; 085 086 IF ( schema_name != ’’’’ ) THEN 087 schema_ok = ’’f’’; 088 FOR rec IN SELECT nspname FROM pg_namespace WHERE text(nspname) = schema_name LOOP 089 schema_ok := ’’t’’; 090 END LOOP; 091 092 if ( schema_ok <> ’’t’’ ) THEN 093 RAISE NOTICE ’’Invalid schema name - using current_schema()’’; 094 SELECT current_schema() into real_schema; 095 ELSE 096 real_schema = schema_name; 097 END IF; 098 099 ELSE 100 SELECT current_schema() into real_schema; 101 END IF; 102 103 -- Add geometry column 104 105 EXECUTE ’’ALTER TABLE ’’ || 106 quote_ident(real_schema) || ’’.’’ || quote_ident(table_name) 107 || ’’ ADD COLUMN ’’ || quote_ident(column_name) || 108 ’’ geometry ’’; 109 Komentáře: 105 Prostřednictv́ım dynamického SQL přidává sloupec do ćılové tabulky fakt̊u. 110 -- Delete stale record in geometry_column (if any) 111 112 EXECUTE ’’DELETE FROM geometry_columns WHERE 113 f_table_catalog = ’’ || quote_literal(’’’’) || 114 ’’ AND f_table_schema = ’’ || 115 quote_literal(real_schema) || 116 ’’ AND f_table_name = ’’ || quote_literal(table_name) || 117 ’’ AND f_geometry_column = ’’ || quote_literal(column_name); 118 119 -- Add record in geometry_column 120 121 EXECUTE ’’INSERT INTO geometry_columns VALUES (’’ || 122 quote_literal(’’’’) || ’’,’’ || 123 quote_literal(real_schema) || ’’,’’ || 124 quote_literal(table_name) || ’’,’’ || 125 quote_literal(column_name) || ’’,’’ || 126 new_dim || ’’,’’ || new_srid || ’’,’’ || Geinformatics FCE CTU 2007 81 PostGIS pro vývojáře 127 quote_literal(new_type) || ’’)’’; 128 Komentáře: 112 Prostřednictv́ım dynamického SQL ruš́ı sloupec, pokud byl takový, v tabulce metadat geometry columns. 121 Prostřednictv́ım dynamického SQL vkládá metadata o sloupci do tabulky geometry columns. 129 -- Add table checks 130 131 EXECUTE ’’ALTER TABLE ’’ || 132 quote_ident(real_schema) || ’’.’’ || quote_ident(table_name) 133 || ’’ ADD CONSTRAINT ’’ 134 || quote_ident(’’enforce_srid_’’ || column_name) 135 || ’’ CHECK (SRID(’’ || quote_ident(column_name) || 136 ’’) = ’’ || new_srid || ’’)’’ ; 137 138 EXECUTE ’’ALTER TABLE ’’ || 139 quote_ident(real_schema) || ’’.’’ || quote_ident(table_name) 140 || ’’ ADD CONSTRAINT ’’ 141 || quote_ident(’’enforce_dims_’’ || column_name) 142 || ’’ CHECK (ndims(’’ || quote_ident(column_name) || 143 ’’) = ’’ || new_dim || ’’)’’ ; 144 145 IF (not(new_type = ’’GEOMETRY’’)) THEN 146 EXECUTE ’’ALTER TABLE ’’ || 147 quote_ident(real_schema) || ’’.’’ || quote_ident(table_name) 148 || ’’ ADD CONSTRAINT ’’ 149 || quote_ident(’’enforce_geotype_’’ || column_name) 150 || ’’ CHECK (geometrytype(’’ || 151 quote_ident(column_name) || ’’)=’’ || 152 quote_literal(new_type) || ’’ OR (’’ || 153 quote_ident(column_name) || ’’) is null)’’; 154 END IF; 155 156 return 157 real_schema || ’’.’’ || 158 table_name || ’’.’’ || column_name || 159 ’’ SRID:’’ || new_srid || 160 ’’ TYPE:’’ || new_type || 161 ’’ DIMS:’’ || new_dim || chr(10) || ’’ ’’; 162 END; 163 ’ 164 LANGUAGE ’plpgsql’ _VOLATILE_STRICT; -- WITH (isstrict); 165 166 ---------------------------------------------------------------------------- 167 -- ADDGEOMETRYCOLUMN ( ,
, , , , ) 168 ---------------------------------------------------------------------------- 169 -- 170 -- This is a wrapper to the real AddGeometryColumn, for use 171 -- when catalogue is undefined 172 -- 173 ---------------------------------------------------------------------------- 174 CREATEFUNCTION AddGeometryColumn(varchar,varchar,varchar,integer,varchar,integer) RETURNS text AS ’ 175 DECLARE 176 ret text; 177 BEGIN 178 SELECT AddGeometryColumn(’’’’,$1,$2,$3,$4,$5,$6) into ret; 179 RETURN ret; 180 END; 181 ’ 182 LANGUAGE ’plpgsql’ _STABLE_STRICT; -- WITH (isstrict); Geinformatics FCE CTU 2007 82 PostGIS pro vývojáře 183 184 ---------------------------------------------------------------------------- 185 -- ADDGEOMETRYCOLUMN (
, , , , ) 186 ---------------------------------------------------------------------------- 187 -- 188 -- This is a wrapper to the real AddGeometryColumn, for use 189 -- when catalogue and schema are undefined 190 -- 191 ---------------------------------------------------------------------------- 192 CREATEFUNCTION AddGeometryColumn(varchar,varchar,integer,varchar,integer) RETURNS text AS ’ 193 DECLARE 194 ret text; 195 BEGIN 196 SELECT AddGeometryColumn(’’’’,’’’’,$1,$2,$3,$4,$5) into ret; 197 RETURN ret; 198 END; 199 ’ 200 LANGUAGE ’plpgsql’ _VOLATILE_STRICT; -- WITH (isstrict); Komentáře: 174 Přet́ıžeńı funkćı (tj. existuje v́ıce funkćı stejného jména s r̊uznými parametry) se v PostgreSQL (dle standardu ANSI) použ́ıvá také k náhradě nepodporovaných volitelných parametr̊u. Funkce definované na řádćıch 174 a 192 se použ́ıvaj́ı v př́ıpadě, že chyb́ı hodnoty parametr̊u katalog a schéma. V ANSI SQL se nepouž́ıvá termı́n databáze, ale katalog, který obsahuje jemněǰśı děleńı na jednotlivá schémata. Zdrojový kód ESRI ArcSDE podporovaná podmnožiny SQL/MM funkćı je v souboru sqlmm.sql. 01 -- PostGIS equivalent function: ndims(geometry) 02 CREATEFUNCTION ST_CoordDim(geometry) 03 RETURNS smallint 04 AS ’@MODULE_FILENAME@’, ’LWGEOM_ndims’ 05 LANGUAGE ’C’ _IMMUTABLE_STRICT; -- WITH (iscachable,isstrict); 06 07 -- PostGIS equivalent function: GeometryType(geometry) 08 CREATEFUNCTION ST_GeometryType(geometry) 09 RETURNS text 10 AS ’ 11 DECLARE 12 gtype text := geometrytype($1); 13 BEGIN 14 IF (gtype IN (’’POINT’’, ’’POINTM’’)) THEN 15 gtype := ’’Point’’; 16 ELSIF (gtype IN (’’LINESTRING’’, ’’LINESTRINGM’’)) THEN 17 gtype := ’’LineString’’; 18 ELSIF (gtype IN (’’POLYGON’’, ’’POLYGONM’’)) THEN 19 gtype := ’’Polygon’’; 20 ELSIF (gtype IN (’’MULTIPOINT’’, ’’MULTIPOINTM’’)) THEN 21 gtype := ’’MultiPoint’’; 22 ELSIF (gtype IN (’’MULTILINESTRING’’, ’’MULTILINESTRINGM’’)) THEN 23 gtype := ’’MultiLineString’’; 24 ELSIF (gtype IN (’’MULTIPOLYGON’’, ’’MULTIPOLYGONM’’)) THEN 25 gtype := ’’MultiPolygon’’; 26 ELSE 27 gtype := ’’Geometry’’; 28 END IF; 29 RETURN ’’ST_’’ || gtype; 30 END 31 ’ 32 LANGUAGE ’plpgsql’ _IMMUTABLE_STRICT; -- WITH (isstrict,iscachable); Komentáře: Geinformatics FCE CTU 2007 83 PostGIS pro vývojáře 02 Vytvořeńı synonyma pro PostGIS funkci. 08 Zapouzdřeńı PostGIS funkce kódem v plpgsql. V tomto př́ıpadě se st́ırá rozd́ıl mezi typy MULTIPOINT a MULTIPOINTM (analogicky u daľśıch typ̊u). Řešeńı výkonných funkćı v PostGISu Kromě vlastńı implementace datových typ̊u PostGIS obsahuje implementaci pomocných funkćı nad prostorovými daty. Následuj́ıćı př́ıklady jsou funkce z lwgeom functions basic.c, které mo- hou sloužit jako vzor pro vytvářeńı vlastńıch funkćı. Funkce LWGEOM makepoint se použ́ıvá pro vytvořeńı 2D bodu na základě zadaných souřadnic. 01 PG_FUNCTION_INFO_V1(LWGEOM_makepoint); 02 Datum LWGEOM_makepoint(PG_FUNCTION_ARGS) 03 { 04 double x,y,z,m; 05 LWPOINT *point; 06 PG_LWGEOM *result; 07 08 x = PG_GETARG_FLOAT8(0); 09 y = PG_GETARG_FLOAT8(1); 10 11 if ( PG_NARGS() == 2 ) point = make_lwpoint2d(-1, x, y); 12 else if ( PG_NARGS() == 3 ) { 13 z = PG_GETARG_FLOAT8(2); 14 point = make_lwpoint3dz(-1, x, y, z); 15 } 16 else if ( PG_NARGS() == 4 ) { 17 z = PG_GETARG_FLOAT8(2); 18 m = PG_GETARG_FLOAT8(3); 19 point = make_lwpoint4d(-1, x, y, z, m); 20 } 21 else { 22 elog(ERROR, "LWGEOM_makepoint: unsupported number of args: %d", 23 PG_NARGS()); 24 PG_RETURN_NULL(); 25 } 26 27 result = pglwgeom_serialize((LWGEOM *)point); 28 29 PG_RETURN_POINTER(result); 30 } Komentáře: 01, 02 Standardńı záhlav́ı funkce pro v1 volaj́ıćı konvenci 08, 09 Źıskáńı prvńıch dvou argument̊u typu float8 11 Pokud počet argument̊u funkce je roven dvěma, volá se exterńı funkce make lwpoint2d, jinak se zjǐst’uje počet argument̊u a podle něj se volá odpov́ıdaj́ıćı verze exterńı funkce. 22 Funkce elog se použ́ıvá pro vyvoláńı výjimky, pokud je level ERROR. V př́ıpadě, že level je NOTICE, zobraźı lad́ıćı hlášeńı. 24 Kód za elog(ERROR,...) se již neprovád́ı. V tomto př́ıpadě PG RETURN NULL() slouž́ı k utǐseńı překladače ohledně zobrazeńı varováńı. 27 Serializace objektu do typu PG LWGEOM. Geinformatics FCE CTU 2007 84 PostGIS pro vývojáře 29 Výstupem z funkce je ukazatel na serializovanou hodnotu objektu, provede se konverze na typ Datum. SQL registrace této funkce vypadá následovně: 01 CREATEFUNCTION makePoint(float8, float8) 02 RETURNS geometry 03 AS ’@MODULE_FILENAME@’, ’LWGEOM_makepoint’ 04 LANGUAGE ’C’ _IMMUTABLE_STRICT; -- WITH (iscachable,isstrict); 05 06 CREATEFUNCTION makePoint(float8, float8, float8) 07 RETURNS geometry 08 AS ’@MODULE_FILENAME@’, ’LWGEOM_makepoint’ 09 LANGUAGE ’C’ _IMMUTABLE_STRICT; -- WITH (iscachable,isstrict); 10 11 CREATEFUNCTION makePoint(float8, float8, float8, float8) 12 RETURNS geometry 13 AS ’@MODULE_FILENAME@’, ’LWGEOM_makepoint’ 14 LANGUAGE ’C’ _IMMUTABLE_STRICT; -- WITH (iscachable,isstrict); Komentáře: 01, 06, 11 Tato implementace je ukázkou přet́ıžené funkce (s r̊uzným počtem parametr̊u), kdy všechny varianty této přet́ıžené funkce sd́ıĺı jednu funkci implementovanou v jazyce C. Funkce LWGEOM inside circle point slouž́ı k určeńı, zda-li je bod uvnitř nebo vně kruhu. 01 PG_FUNCTION_INFO_V1(LWGEOM_inside_circle_point); 02 Datum LWGEOM_inside_circle_point(PG_FUNCTION_ARGS) 03 { 04 PG_LWGEOM *geom; 05 double cx = PG_GETARG_FLOAT8(1); 06 double cy = PG_GETARG_FLOAT8(2); 07 double rr = PG_GETARG_FLOAT8(3); 08 LWPOINT *point; 09 POINT2D pt; 10 11 geom = (PG_LWGEOM *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); 12 point = lwpoint_deserialize(SERIALIZED_FORM(geom)); 13 if ( point == NULL ) { 14 PG_FREE_IF_COPY(geom, 0); 15 PG_RETURN_NULL(); /* not a point */ 16 } 17 18 getPoint2d_p(point->point, 0, &pt); 19 20 PG_FREE_IF_COPY(geom, 0); 21 22 PG_RETURN_BOOL(lwgeom_pt_inside_circle(&pt, cx, cy, rr)); 23 } Komentáře: 11 Z TOAST hodnoty muśıme źıskat serializovanou hodnotu typu PG LWGEOM. TOAST je pro uživatele databáze (nikoliv pro vývojáře) transparentńı mechanismus zajǐst’uj́ıćı kompresi a uložeńı serializovaných řetězc̊u deľśıch než 2KB. PostgreSQL interně použ́ıvá datové stránky o 8KB a žádná do databáze uložená hodnota (vyjma tzv. BLOBu) nemůže tuto velikost přesáhnout. Toto omezeńı se obcháźı právě metodou nazvanou TOAST, kdy se deľśı hodnoty děĺı a ukládaj́ı do speciálńı tabulky do v́ıce řádk̊u po maximálně 2KB). 12 Deserializace typu point. Geinformatics FCE CTU 2007 85 PostGIS pro vývojáře 14 Bezpečné uvolněńı paměti (celá řada typ̊u se přenáš́ı hodnotou, a tud́ıž je nelze chápat jako ukazatele a nelze dealokovat pamět’ na kterou by se odkazovaly). 18 Konverze typu LWPOINT na typ POINT2D. 22 Vráceńı návratové hodnoty jako výsledku voláńı funkce lwgeom pt inside circle. Definice funkce lwgeom pt inside circle (measures.c): 01 lwgeom_pt_inside_circle(POINT2D *p, double cx, double cy, double rad) 02 { 03 POINT2D center; 04 05 center.x = cx; 06 center.y = cy; 07 08 if ( distance2d_pt_pt(p, ¢er) < rad ) return 1; 09 else return 0; 10 } Funkce LWGEOM inside circle point je registrována SQL př́ıkazem: 01 CREATEFUNCTION point_inside_circle(geometry,float8,float8,float8) 02 RETURNS bool 03 AS ’@MODULE_FILENAME@’, ’LWGEOM_inside_circle_point’ 04 LANGUAGE ’C’ _IMMUTABLE_STRICT; -- WITH (isstrict); Podpora indexu typu GiST v PostGISu Indexy typu R-tree jsou specifické právě pro prostorová v́ıcedimenzionálńı data (a pro ně byly navrženy). Aktuálńı verze PostgreSQL nab́ıźı již daľśı generaci této tř́ıdy databázových index̊u a to tzv. GiST (Generalized Search Tree) indexy. Jejich princip je stejný, širš́ı je ale jejich uplatněńı. GiST indexy se v PostgreSQL použ́ıvaj́ı pro fulltext, indexováńı obsahu poĺı, vlastńı podporu hierarchických dat a také samozřejmě pro geometrické typy. Jak již bylo zmı́něno, R-tree index předpokládá, že indexovaná data budou mı́t minimálně dvě dimenze. Index má stromovou strukturu, a každý nekoncový uzel obsahuje jednak odkaz na své potomky a hlavně geometrii nejmenš́ıho pravoúhlého n-rozměrného tělese obsahuj́ıćıho všechny potomky. GiST je aplikačńı rozhrańı, které umožňuje implementaci libovolného typu indexu: B-tree, R- tree. Výhodou GiST index̊u je možnost vytvořeńı doménově specifických index̊u vázaných na vlastńı typy vývojář̊um znalým doménové oblasti bez toho, aby se nutně staly databázovými specialisty (Rozhodně ale implementace GiST indexu nepatř́ı mezi triviálńı programováńı). Ukázkovým př́ıkladem je použit́ı GiST indexu v PostGISu. Kritériem, které se použije pro rozhodováńı, zda-li použ́ıt B-tree index nebo GiST index jsou operace, které chceme urychlit indexem. Pokud nám postačuje množina binárńıch operátor̊u <, =, >, pak je na mı́stě uvažovat o B-tree indexu. V opačném př́ıpadě nezbývá než použ́ıt GiST index, který je obecněǰśı než B-tree index. Prostorové indexy se daj́ı použ́ıt i pro klasická data. Jednodimenzionálńı data se ale muśı předt́ım převést na v́ıcedimenzionálńı. Ukázkovým př́ıpadem selháńı jednodimenzionálńıho př́ıpadu je následuj́ıćı př́ıklad. Mějme databázi událost́ı popsanou časem zahájeńı (start time) a časem ukončeńı (end time). Pokud budeme cht́ıt vypsat události, které prob́ıhaly v určitém čase naṕı̌seme dotaz s podmı́nkou 01 WHERE star_time < t AND end_time > (t + n) Geinformatics FCE CTU 2007 86 PostGIS pro vývojáře Indexace sloupc̊u start time a end time zcela jistě pomůže, nicméně v tomto př́ıpadě maj́ı in- dexy malou selektivitu (v pr̊uměru oba vraćı polovinu řádk̊u z tabulky). start time a end time jsou dvě jednodimenzionálńı řady, takže se celkem přirozeně nab́ıźı chápat je jako jednu dvou dimenzionálńı řadu a předchoźı podmı́nku transformovat do tvaru postaveného nad geomet- rickými operátory. 01 WHERE box(point(start_time - t, start_time - t), 02 point(end_time - (t + n), end_time - (t + n)) @ 03 box(point(start_time, start_time), 04 point(end_time, end_time)) Komentáře: 01 Zápis neńı validńı. Z d̊uvodu čitelnosti neobsahuje nezbytnou konverzi z typu Date na celá č́ısla. 02 Operátor @ má význam kompletně obsažen. Popis a použit́ı GiST indexu GiST index je vyvážený strom obsahuj́ıćı vždy dvojice kĺıč (predikát), ukazatel na data na hraničńıch uzlech stromu (listech) a dvojice tvrzeńı, ukazatel na potomky ve vnitřńıch uzlech stromu. Dvojice tvrzeńı, ukazatel se označuje jako záznam indexu. Každý uzel může obsaho- vat v́ıce záznamů indexu. Tvrzeńı (predicates) je vždy platné pro všechny kĺıče dostupné z daného uzlu. To je koncept, který se objevuje ve všech na stromech založených indexech. U již zmı́něného R-tree indexu je tvrzeńım ohraničuj́ıćı obdélńık obsahuj́ıćı všechny body (R-tree je index navržený pro prostorová data), které jsou dostupné z vnitřńıho uzlu. Každý GiST index je definován následuj́ıćımi operacemi: Operace nad kĺıči – tyto metody jsou specifické pro danou tř́ıdu objekt̊u a defacto určuj́ı konfiguraci GiST indexu (Key Methods): Consistent, Union, Compress, Decompress, Penalty, PickSplit Operace nad stromem – obecné operace, které volaj́ı operace nad kĺıči (Tree Methods): Search (Consistent), Insert (Penalty, PickSplit), Delete (Consistent). Operace nad kĺıči (v závorce smysl operace v př́ıpadě prostorových dat): Základńı datovou strukturou použ́ıvanou ve funkćıch implementuj́ıćıch GiST index je GIS- TENTRY: 01 /* 02 * An entry on a GiST node. Contains the key, as well as its own 03 * location (rel,page,offset) which can supply the matching pointer. 04 * leafkey is a flag to tell us if the entry is in a leaf node. 05 */ 06 typedef struct GISTENTRY 07 { 08 Datum key; 09 Relation rel; 10 Page page; 11 OffsetNumber offset; 12 bool leafkey; 13 } GISTENTRY; Komentáře: Geinformatics FCE CTU 2007 87 PostGIS pro vývojáře consistent Pokud v záznamu indexu je zaručeno, že tvrzeńı nevyhovuje dotazu s da- nou hodnotou, pak vraćı logickou hodnotu nepravda. Jinak vraćı logickou hodnotu pravda. Pokud operace nesprávně vrát́ı log. hodnotu pravda, pak tato chyba nemá vliv na výsledek, pouze ovlivńı efektivitu algoritmu (true, pokud docháźı k překryvu, jinak false). union Pro danou množinu záznamů indexu vraćı takové tvrzeńı, které je platné pro všechny záznamy v množině. compress Převád́ı data do vhodného formátu pro fyzické uložeńı na stránce indexu (V př́ıpadě prostorových dat se urč́ı hraničńı obdélńık). decompress Opak metody compress. Převád́ı binárńı reprezentaci indexu tak, aby s ńı mohlo API manipulovat (načte se hraničńı obdélńık). penalty Vraćı hodnotu ve významu ceny za vložeńı nové položky do konkrétńı části stromu. Položka je vložena do té části stromu, kde je tato hodnota (penalty) nejnižš́ı (Zjǐst’uje se, o kolik by se zvětšila plocha hraničńıho obdélńıku). picksplit Ve chv́ıli, kdy je nutné rozdělit stránku indexu, tato funkce určuje, které položky z̊ustanou na p̊uvodńı stránce, a které se přesunou na novou stranu indexu. same Vraćı logickou hodnotu pravda pokud jsou dvě položky identické. 09 Identifikátor tabulky 10 Identifikace datové stránky 11 Pozice na datové stránce a GistEntryVector 01 /* 02 * Vector of GISTENTRY structs; user-defined methods union and pick 03 * split takes it as one of their arguments 04 */ 05 typedef struct 06 { 07 int32 n; /* number of elements */ 08 GISTENTRY vector[1]; 09 } GistEntryVector; 10 11 #define GEVHDRSZ (offsetof(GistEntryVector, vector)) Následuj́ıćı př́ıklad je ukázkou metody union, která vraćı nejmenš́ı možný obdélńık pro všechny zadané body: 01 PG_FUNCTION_INFO_V1(LWGEOM_gist_union); 02 Datum LWGEOM_gist_union(PG_FUNCTION_ARGS) 03 { 04 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); 05 int *sizep = (int *) PG_GETARG_POINTER(1); 06 int numranges, 07 i; 08 BOX2DFLOAT4 *cur, 09 *pageunion; 10 11 numranges = entryvec->n; 12 cur = (BOX2DFLOAT4 *) DatumGetPointer(entryvec->vector[0].key); 13 14 pageunion = (BOX2DFLOAT4 *) palloc(sizeof(BOX2DFLOAT4)); Geinformatics FCE CTU 2007 88 PostGIS pro vývojáře 15 memcpy((void *) pageunion, (void *) cur, sizeof(BOX2DFLOAT4)); 16 17 for (i = 1; i < numranges; i++) 18 { 19 cur = (BOX2DFLOAT4*) DatumGetPointer(entryvec->vector[i].key); 20 21 if (pageunion->xmax < cur->xmax) 22 pageunion->xmax = cur->xmax; 23 if (pageunion->xmin > cur->xmin) 24 pageunion->xmin = cur->xmin; 25 if (pageunion->ymax < cur->ymax) 26 pageunion->ymax = cur->ymax; 27 if (pageunion->ymin > cur->ymin) 28 pageunion->ymin = cur->ymin; 29 } 30 31 *sizep = sizeof(BOX2DFLOAT4); 32 33 PG_RETURN_POINTER(pageunion); 34 } Komentáře: 04 Prvńı argument obsahuje ukazatel na GistEntryVector 05 Druhý argument obsahuje ukazatel na velikost vrácené datové struktury 14, 15 Vytvořeńı prostoru pro výstupńı strukturu pageunion a jej́ı naplněńı prvńım prvkem vektoru. 12, 19 Naplněńı struktury cur (iterace po prvćıch GiST vektoru, který obsahuje prvky typu Datum (v tomto př́ıpadě ukazatele na typ BOX2DFLOAT4) 21-28 Hledáńı nejmenš́ıho možného obdélńıka obsahuj́ıćıho všechny zadané body 33 Předáńı ukazatele na výstupńı strukturu Každá funkce podporuj́ıćı GiST index se muśı nejdř́ıve zaregistrovat jako PostgreSQL funkce a potom všechny relevantńı funkce ještě jednou objev́ı v registraci GiST indexu: CREATEFUNCTION LWGEOM_gist_union(bytea, OPAQUE_TYPE) RETURNS OPAQUE_TYPE AS ’@MODULE_FILENAME@’ ,’LWGEOM_gist_union’ LANGUAGE ’C’; 01 CREATE OPERATOR CLASS gist_geometry_ops 02 DEFAULT FOR TYPE geometry USING gist AS 03 OPERATOR 1 << RECHECK, 04 OPERATOR 2 &< RECHECK, 05 OPERATOR 3 && RECHECK, 06 OPERATOR 4 &> RECHECK, 07 OPERATOR 5 >> RECHECK, 08 OPERATOR 6 ~= RECHECK, 09 OPERATOR 7 ~ RECHECK, 10 OPERATOR 8 @ RECHECK, 11 OPERATOR 9 &<| RECHECK, 12 OPERATOR 10 <<| RECHECK, 13 OPERATOR 11 |>> RECHECK, 14 OPERATOR 12 |&> RECHECK, 15 FUNCTION 1 LWGEOM_gist_consistent (internal, geometry, int4), 16 FUNCTION 2 LWGEOM_gist_union (bytea, internal), 17 FUNCTION 3 LWGEOM_gist_compress (internal), 18 FUNCTION 4 LWGEOM_gist_decompress (internal), 19 FUNCTION 5 LWGEOM_gist_penalty (internal, internal, internal), 20 FUNCTION 6 LWGEOM_gist_picksplit (internal, internal), Geinformatics FCE CTU 2007 89 PostGIS pro vývojáře 21 FUNCTION 7 LWGEOM_gist_same (box2d, box2d, internal); \ 01 CREATE OPERATOR CLASS gist_geometry_ops Závěr Ćılem této práce bylo připravit podklady umožňuj́ıćı snažš́ı orientaci v implementaci stan- dardu OpenGIS v prostřed́ı o.s. databázového systémy PostgreSQL – PostGIS. Nejd̊uležitěǰśı komponenty systému PostGIS byly popsány, zbytek nikoliv. Což ani nebylo ćılem práce. Ačkoliv neńı pravděpodobné, že by někdo mohl navrhnout vlastńı rozš́ı̌reńı PostGISu bez předchoźıch znalost́ı PostgreSQL, C a vlastńıch GIS aplikaćı, doufám, že d́ıky této práci mo- hou vznikat daľśı rozš́ı̌reńı postavené nad t́ımto, poměrně velice úspěšným produktem. Literatura � Správa časoprostorových dat v prostřed́ı PostgreSQL/PostGIS, Antońın ORLÍK � http://postgis.refractions.net/docs/ � Návrh a realizace UDF v C pro PostgreSQL1 � Access Methods for Next-Generation Database Systems2 � Spatial Data Management3 1 http://www.pgsql.cz/index.php/N%C3%A1vrh a realizace UDF v c pro PostgreSQL#N \ .C3.A1vrh vlastn.C3.ADch datov.C3.BDch typ.C5.AF 2 http://citeseer.ist.psu.edu/rd/0%2C448594%2C1%2C0.25%2CDownload/http://citeseer.ist \ .psu.edu/cache/papers/cs/22615/http:zSzzSzs2k-ftp.cs.berkeley.edu:8000zSz%7Emar \ celzSzdisszSzdiss.pdf/access-methods-for-next.pdf 3 http://www.mapbender.org/presentations/Spatial Data Management Arnulf Christl/Spati \ al Data Management Arnulf Christl.pdf Geinformatics FCE CTU 2007 90 http://postgis.refractions.net/docs/ http://www.pgsql.cz/index.php/N%C3%A1vrh_a_realizace_UDF_v_c_pro_PostgreSQL#N.C3.A1vrh_vlastn.C3.ADch_datov.C3.BDch_typ.C5.AF http://citeseer.ist.psu.edu/rd/0%2C448594%2C1%2C0.25%2CDownload/http://citeseer.ist.psu.edu/cache/papers/cs/22615/http:zSzzSzs2k-ftp.cs.berkeley.edu:8000zSz%7EmarcelzSzdisszSzdiss.pdf/access-methods-for-next.pdf http://www.mapbender.org/presentations/Spatial_Data_Management_Arnulf_Christl/Spatial_Data_Management_Arnulf_Christl.pdf