====== PostGIS versione 0.9 ====== La libreria ancora non fa parte della distribuzione ufficiale Debian, prelevato il pacchetto ''postgis_0.9.1-4_i386.deb'' dal sito [[http://pkg-grass.alioth.debian.org/debian-gis/pool/p/postgis/|pkg-grass]] e installato. Alcune operazioni su PostgreSQL (creare un database, attivare il linguaggio PL/pgSQL, creare le estensioni PostGIS) bisogna eseguirle come utente privilegiato //postgres// del database. Con l'installazione predefinita di Debian è sufficiente operare come utente di sistema //postgres// (da ''root'' effetturare ''**su - postgres**''), in quanto il database effettua l'autenticazione ''ident sameuser''. Per gli esempi che seguono si presuppone che PostgreSQL accetti anche l'autenticazione con password (md5) per chi si connette via TCP/IP (host), aggiungere al file ''/etc/postgresql/pg_hba.conf'' una riga del tipo: host all all 127.0.0.1 255.255.255.255 md5 Ora si può creare un database e creare in esso le funzioni PostGIS. Queste hanno bisogno del linguaggio PL/pgSQL, quindi nel database deve essere attivato anche questo. La procedura completa può essere riassunta in questo modo: ===== Creazione di un database ===== $ su - # su - postgres $ psql template1 template1=# CREATE DATABASE strade; template1=# CREATE USER "strade" PASSWORD 'supersecret'; template1=# SELECT * FROM pg_user; ...annotare usesysid dell'utente strade, es. 101 template1=# UPDATE pg_database SET datdba=101 WHERE datname='strade'; template1=# \q Viene creato un database e un utente con password, il database viene dato in proprietà all'utente. ===== Abilitazione del linguaggio PL/pgSQL nel nuovo database ===== $ su - # su - postgres $ createlang plpgsql strade Creare un linguaggio significa che sarà possibile aggiungere a quel database funzioni o trigger scritti in quel linguaggio. Questa operazione è necessaria perché PostGIS comprende funzioni scritte in PL/pgSQL. In generale aggiungere un linguaggio ad un database richiede privilegi di superutente (//postgres//) e va fatto per ogni database che lo necessita. In alternativa si può creare un template (o modificare il default ''template1''), attivare il linguaggio in esso e creare il database a partire dal template. Per default PostgreSQL aggiunge il linguaggio con la clausola ''TRUSTED'', ciò significa che il linguaggio è considerato sicuro (non consente di aggirare i meccanismi di protezione) e quindi potrà essere utilizzato anche dagli utenti non privilegiati. Controllare lo stato di trust del linguaggio con il il comando ''createlang -l strade''. Vedere il manuale SQL a proposito di ''CREATE LANGUAGE''. ===== Abilitazione di PostGIS nel nuovo database ===== $ su - # su - postgres $ psql -f /usr/share/postgresql/postgis.sql strade Anche questa operazione deve essere fatta con i privilegi di superutente (//postgres//). Il motivo è che vengono definite diverse funzioni di tipo ''LANGUAGE 'C''' fornite dalla shared library ''/usr/lib/postgresql/lib/libpostgis.so''; agganciare un eseguibile esterno ad una funzione PostgreSQL è consentito al solo superutente. Oltre alle funzioni vengono create anche due tabelle: ''**geometry_columns**'' e ''**spatial_ref_sys**'' (e due relativi indici ''geometry_columns_pk'' e ''spatial_ref_sys_pkey''). Questi oggetti appena creati appartengono all'utente //postgres// e quindi è un problema far usare il database o farne fare un dump all'utente non privilegiato. Si cambia proprietario alle tabelle (e contestualmente agli indici) in questo modo: $ su - # su - postgres $ psql strade strade=# ALTER TABLE geometry_columns OWNER TO strade; strade=# ALTER TABLE spatial_ref_sys OWNER TO strade; Per consentire all'utente non privilegiato di lavorare, sarebbe sufficiente garantire la ''SELECT'' sulla tabella ''spatial_ref_sys'' e garantire tutti i privilegi (''ALL'') sulla ''geometry_columns''. Vedere il manuale SQL a proposito di ''GRANT''. Cosa ci va a finire in queste tabelle? Nella tabella ''spatial_ref_sys'' vengono memorizzate le caratteristiche dei vari sistemi di riferimento spaziale (per fare le conversioni?) e in generale verrà usata in sola lettura. I dati vengono caricati dallo script ''spatial_ref_sys.sql'' fornito con PostGIS: $ psql -h 127.0.0.1 -U strade -W -f /usr/share/postgresql/spatial_ref_sys.sql -d strade Le funzioni PostGIS manipolano record solo nella tabella ''geometry_columns''? Le funzioni disponibili si elencano dalla shell ''psql'' con il comando ''\df'', ma sono mescolate a quelle predefinite. L'unica differenza è che appartengono allo schema ''public'' invece che allo schema ''pg_catalog''. ===== Fare un backup del database ===== $ pg_dump -h 127.0.0.1 --create -U strade -W strade > strade_plpgsql_postgis.dump Dentro il dump ci saranno ovviamente le tabelle con i dati dell'utente, ma anche oggetti che devono essere ricreati con i privilegi di superutente (il linguaggio PL/pgSQL e le funzioni di PostGIS). Come si potrebbe fare un restore? ===== Fare un restore del database ===== Vedere un po' come fare il restore, perché alcuni oggetti vanno restorati come superutente, altri possono essere restorati come utente non privilegiato. ===== Creare tabelle con entità geometriche ===== Primo esempio: creazione di una tabella per memorizzare waypoint e inserimento di un record: psql -h 127.0.0.1 -U strade -W strade strade=> CREATE TABLE waypoints ( id SERIAL, elevation float, name varchar(6), comment varchar(20), description varchar(50), idsymbol integer ); strade=> SELECT AddGeometryColumn('waypoints','wpt',4326,'POINT',2); strade=> INSERT INTO waypoints (elevation, name, idsymbol, wpt) VALUES (46.749512, 'NICCO', 2, GeometryFromText('POINT(11.097390857 43.858187562)',4326)); Nella funzione ''AddGeometryColumn()'' il parametro 4326 è lo SRID (spatial referencing system identifier), in questo caso corrisponde al datum [[gis:glossario#wgs84|WGS84]] utilizzato dal GPS. Deve corrispondere allo ''srid'' di un record in ''spatial_ref_sys''. L'ultimo parametro è la dimensione, in questo caso si lavora sul piano in due dimensioni. Una colonna di tipo geometrico deve essere aggiunta con la funzione ''AddGeometryColumn()'', perché essa provvede a memorizzare informazioni supplementari in ''geometry_columns''. Per rimuovere da una tabella una colonna di tipo geometry: strade=> SELECT DropGeometryColumn('', 'waypoint','wpt'); Altro esempio: creare una tabella con il vettoriale delle strade. CREATE TABLE tracksegments (id SERIAL); SELECT AddGeometryColumn('tracksegments','trkseg',4326,'LINESTRING',3); INSERT INTO tracksegments (trkseg) VALUES ( GeometryFromText('LINESTRING( 11.132305 43.820643, 11.131639 43.820643, 11.132927 43.819184, 11.134493 43.819592, 11.136060 43.819249 )', 4326) ); Problema: come fare l'INSERT di una MULTILINESTRING con magari qualche migliaio di punti? Sicuramente con una sola INSERT (specialmente da PHP) si potrebbero avere dei problemi sulla eccessiva lunghezza della istruzione SQL. Idea: creare una tabella con i trackpoint e poi usare la funzione di aggregazione ''MakeLine()'' con qualcosa del tipo: SELECT MakeLine(the_geom) FROM ( SELECT the_geom FROM points ORDER BY time ) foo; Proviamo quindi in questo modo: CREATE TABLE trackpoints (id SERIAL); SELECT AddGeometryColumn('trackpoints','trkpt2d',4326,'POINT',2); INSERT INTO trackpoints (trkpt2d) VALUES (GeometryFromText('POINT(11.132305 43.820643)', 4326)); INSERT INTO trackpoints (trkpt2d) VALUES (GeometryFromText('POINT(11.131639 43.820643)', 4326)); INSERT INTO trackpoints (trkpt2d) VALUES (GeometryFromText('POINT(11.132927 43.819184)', 4326)); INSERT INTO trackpoints (trkpt2d) VALUES (GeometryFromText('POINT(11.134493 43.819592)', 4326)); INSERT INTO trackpoints (trkpt2d) VALUES (GeometryFromText('POINT(11.136060 43.819249)', 4326)); SELECT AddGeometryColumn('trackpoints','trkpt3d',4326,'POINT',3); INSERT INTO trackpoints (trkpt3d) VALUES (GeomFromEWKT('POINT(11.132305 43.820643 51.567871)', 4326)); INSERT INTO trackpoints (trkpt3d) VALUES (GeomFromEWKT('POINT(11.131639 43.820643 39.070679)', 4326)); INSERT INTO trackpoints (trkpt3d) VALUES (GeomFromEWKT('POINT(11.132927 43.819184 48.683960)', 4326)); INSERT INTO trackpoints (trkpt3d) VALUES (GeomFromEWKT('POINT(11.134493 43.819592 43.396606)', 4326)); INSERT INTO trackpoints (trkpt3d) VALUES (GeomFromEWKT('POINT(11.136060 43.819249 39.070679)', 4326)); Pare che la quarta dimensione (tempo) non sia supportata. Si vorrebbe memorizzare in un solo campo latitudine, longitudine, altezza e timestamp, ma l'operazione fallisce (PostGIS versione 0.9.1). ===== Entità geometriche supportate da PostGIS ===== Sono gli oggetti specificati dalle specifiche //{{gis:99-049.pdf|Simple Features for SQL}}// del consorzio [[http://www.opengeospatial.org/|OpenGIS Consortium (OGC)]]. Le specifiche OGC prevedono solo due dimensioni, mentre PostGIS supporta anche la terza (altezza). La quarta dimensione (tempo) pare ancora non supportata (versione PostGIS versione 0.9.1). Attenzione comunque anche ad usare la terza dimensione, pare che QGIS 0.6 non riesca ad usare layer PostGIS con tre dimensioni. **%%POINT(X Y)%%**\\ A Point is a 0-dimensional geometry and represents a single location in coordinate space. A Point has a x-coordinate value and a y-coordinate value. **%%MULTIPOINT(X Y, X Y, ...)%%**\\ A MultiPoint is a 0 dimensional geometric collection. The elements of a MultiPoint are restricted to Points. The points are not connected or ordered. **%%LINESTRING(X Y, X Y, ...)%%**\\ A LineString is a Curve with linear interpolation between points. Each consecutive pair of points defines a line segment. **%%MULTILINESTRING((X Y, X Y, ...), ...)%%**\\ A MultiLineString is a collection whose elements are LineStrings. **%%POLYGON((X Y, X Y, X Y, ...), (X Y, X Y, X Y, ...), ...))%%**\\ A Polygon is a planar Surface, defined by 1 exterior boundary and 0 or more interior boundaries. Each interior boundary defines a hole in the Polygon. Polygons are topologically closed. The boundary of a Polygon consists of a set of LinearRings (simple and closed Linestrings) that make up its exterior and interior boundaries. No two rings in the boundary cross, the rings in the boundary of a Polygon may intersect at a Point but only as a tangent... **%%MULTIPOLYGON(...)%%**\\ A MultiPoligon is a collection whose elements are Polygon. **%%GEOMETRYCOLLECTION(POINT(X Y),LINESTRING(X Y, X Y, ...), ...)%%**\\ A GeometryCollection is a collection whose elements others Geometry objects.