La libreria ancora non fa parte della distribuzione ufficiale Debian, prelevato il pacchetto postgis_0.9.1-4_i386.deb
dal sito 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:
$ 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.
$ 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
.
$ 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
.
$ 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?
Vedere un po' come fare il restore, perché alcuni oggetti vanno restorati come superutente, altri possono essere restorati come utente non privilegiato.
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 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).
Sono gli oggetti specificati dalle specifiche Simple Features for SQL del consorzio 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.