Ormai uso Postgis 2.0 da 3/4 di anno e, sebbene mi diverta molto, l'eccessivo tempo di elaborazione delle query lo ha reso sostanzialmente inutilizzabile per il mio caso d'uso.
Tendo a fare un forte geoprocessing su set di dati comunali che spesso hanno centinaia di migliaia di multipoligoni. Questi multipoligoni sono talvolta modellati in modo molto irregolare e possono variare da 4 punti a 78.000 punti per multipoligono.
Ad esempio, quando interseco un set di dati del pacco con 329.152 multipoligoni con un set di dati della giurisdizione contenente 525 multipoligoni, ottengo le seguenti statistiche per il tempo totale impiegato:
ArcGIS 10.0 (on same host with windows 7 OS): 3 minutes
Postgis:56 minutes (not including geometry pre-processing queries)
In altre parole, ci vuole il 1500% di tempo in più per fare questo incrocio in Postgis che in ArcGIS - e questa è una delle mie domande più semplici!
Uno dei motivi per cui ArcGIS è presumibilmente più veloce è dovuto a indici migliori. Alcuni programmatori hanno recentemente scoperto come funzionano questi indici e mi chiedo se qualcuno sappia come costruire questi indici in Postgis (o costruire tabelle che imitino gli indici). Forse questo risolverebbe la maggior parte dei problemi di velocità in Postgis. Posso solo sperare che ci debba essere un modo, soprattutto perché ArcGIS può usare solo 4 GB di RAM mentre potrei usare fino a 4 volte quello per il mio server Postgis!
Naturalmente ci sono molte ragioni per cui i postgis possono funzionare lentamente, quindi fornirò una versione dettagliata delle mie specifiche di sistema:
Machine: Dell XPS 8300
Processor: i7-2600 CPU @ 3.40 GHz 3.40 GHz
Memory: Total Memory 16.0 GB (10.0 GB on virtual machine)
Platform: Ubuntu Server 12.04 Virtual Box VM
Potgres Version: 9.1.4
Postgis Version: POSTGIS="2.0.1 r9979" GEOS="3.3.5-CAPI-1.7.5" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.9.1, released 2012/05/15" LIBXML="2.7.8" LIBJSON="UNKNOWN" TOPOLOGY RASTER
Descrivo anche in dettaglio l' intero processo di installazione che ho usato per impostare Postgis, compresa la creazione della VM stessa .
Ho anche aumentato la memoria condivisa dai 24 MB predefiniti a 6 GB nel file conf ed eseguito i seguenti comandi per consentire l'esecuzione di postgres:
sudo sysctl -w kernel.shmmax=7516192768 (I know this setting is deleted every time you restart the OS)
sudo /etc/init.d/postgresql restart
Per quanto posso dire, questo non fa assolutamente nulla di evidente in termini di prestazioni.
Ecco i collegamenti ai dati che ho usato per questo test:
- Pacchi: tcad_parcels_06142012.shp.zip da City of Austin, TX
- Giurisdizioni: Confini giurisdizionali da City of Austin, TX
Ecco i passaggi che ho preso per elaborare i dati:
ArcGIS
- Aggiungi set di dati ad ArcMap
- Imposta il sistema di coordinate sui piedi centrali del Texas (srid 2277)
- Utilizza lo strumento Intersezione dal menu a discesa
PostGIS
Importa i pacchi utilizzando:
shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "tcad_parcels_06142012.shp" "public"."tcad_parcels_06142012" |psql -d postgis_testing -U postgres -h local_ip -p 5432
Giurisdizione di importazione utilizzando:
shp2pgsql -c -s 2277 -D -i -I -W UTF-8 "jurisdictions.shp" "public"."jurisdictions" |psql -d postgis_testing -U postgres -h local_ip -p 5432
Pulisci geometria non valida nei pacchi:
DROP TABLE IF EXISTS valid_parcels;
CREATE TABLE valid_parcels(
gid serial PRIMARY KEY,
orig_gid integer,
geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_parcels USING gist (geom);
INSERT INTO valid_parcels(orig_gid,geom)
SELECT
gid
orig_gid,
st_multi(st_makevalid(geom))
FROM
tcad_parcels_06142012;
CLUSTER valid_parcels USING valid_parcels_geom_idx;
Pulisci geometria non valida nelle giurisdizioni:
DROP TABLE IF EXISTS valid_jurisdictions;
CREATE TABLE valid_jurisdictions(
gid serial PRIMARY KEY,
orig_gid integer,
geom geometry(multipolygon,2277)
);
CREATE INDEX ON valid_jurisdictions USING gist (geom);
INSERT INTO valid_jurisdictions(orig_gid,geom)
SELECT
gid
orig_gid,
st_multi(st_makevalid(geom))
FROM
jurisdictions;
CLUSTER valid_jurisdictions USING valid_jurisdictions_geom_idx;
Esegui cluster:
cluster;
Esegui analisi vuoto:
vacuum analyze;
Esegui intersezione su tabelle pulite:
CREATE TABLE parcel_jurisdictions(
gid serial primary key,
parcel_gid integer,
jurisdiction_gid integer,
isect_geom geometry(multipolygon,2277)
);
CREATE INDEX ON parcel_jurisdictions using gist (isect_geom);
INSERT INTO parcel_jurisdictions(parcel_gid,jurisdiction_gid,isect_geom)
SELECT
a.orig_gid parcel_gid,
b.orig_gid jurisdiction_gid,
st_multi(st_intersection(a.geom,b.geom))
FROM
valid_parcels a, valid_jurisdictions b
WHERE
st_intersects(a.geom,b.geom);
Spiegare Analizza query intersezione:
Total runtime: 3446860.731 ms
Index Cond: (geom && b.geom)
-> Index Scan using valid_parcels_geom_idx on valid_parcels a (cost=0.00..11.66 rows=2 width=1592) (actual time=0.030..4.596 rows=1366 loops=525)
-> Seq Scan on valid_jurisdictions b (cost=0.00..113.25 rows=525 width=22621) (actual time=0.009..0.755 rows=525 loops=1)
Nested Loop (cost=0.00..61428.74 rows=217501 width=24213) (actual time=2.625..3445946.889 rows=329152 loops=1)
Join Filter: _st_intersects(a.geom, b.geom)
Da tutto ciò che ho letto, la mia query di intersezione è efficiente e non ho assolutamente idea di cosa sto facendo di sbagliato perché la query impieghi 56 minuti su una geometria pulita!