Lavorare con i dati PostGIS in R?


27

Lavoro con R quasi sempre, e ora lo sto usando per fare il mining di dati spaziali.

Ho un database PostGIS con (ovviamente) dati GIS.

Se voglio fare analisi spaziali statistiche e tracciare mappe è il modo migliore per:

  • esportare le tabelle come shapefile o;
  • lavorare direttamente nel database?

Risposte:


34

Se hai la capacità del driver PostGIS nel pacchetto rgdal, allora è solo una questione di creare una stringa di connessione e usarla. Qui mi collego al mio database locale gisusando le credenziali predefinite, quindi il mio DSN è piuttosto semplice. Potrebbe essere necessario aggiungere un host, un nome utente o una password. Vedi i documenti di gdal per informazioni.

> require(rgdal)
> dsn="PG:dbname='gis'"

Quali tabelle ci sono in quel database?

> ogrListLayers(dsn)
 [1] "ccsm_polygons"         "nongp"                 "WrldTZA"              
 [4] "nongpritalin"          "ritalinmerge"          "metforminmergev"      

Prendine uno:

> polys = readOGR(dsn="PG:dbname='gis'","ccsm_polygons")
OGR data source with driver: PostgreSQL 
Source: "PG:dbname='gis'", layer: "ccsm_polygons"
with 32768 features and 4 fields
Feature type: wkbMultiPolygon with 2 dimensions

Che cosa ho?

> summary(polys)
Object of class SpatialPolygonsDataFrame
Coordinates:
        min      max
x -179.2969 180.7031
y  -90.0000  90.0000
Is projected: NA 
proj4string : [NA]
Data attributes:
      area         perimeter       ccsm_polys      ccsm_pol_1   
 Min.   :1.000   Min.   :5.000   Min.   :    2   Min.   :    1  
 1st Qu.:1.000   1st Qu.:5.000   1st Qu.: 8194   1st Qu.: 8193  
 Median :1.000   Median :5.000   Median :16386   Median :16384  
 Mean   :1.016   Mean   :5.016   Mean   :16386   Mean   :16384  
 3rd Qu.:1.000   3rd Qu.:5.000   3rd Qu.:24577   3rd Qu.:24576  
 Max.   :2.000   Max.   :6.000   Max.   :32769   Max.   :32768  

Altrimenti è possibile utilizzare la funzionalità del database di R ed eseguire query direttamente sulle tabelle.

> require(RPostgreSQL)
Loading required package: RPostgreSQL
Loading required package: DBI
> m <- dbDriver("PostgreSQL")
> con <- dbConnect(m, dbname="gis")
> q="SELECT ST_AsText(the_geom) AS geom from ccsm_polygons LIMIT 10;"
> rs = dbSendQuery(con,q)
> df = fetch(rs,n=-1)

Ciò restituisce la geometria della funzione df$geom, che dovrai convertire in spoggetti di classe (SpatialPolygons, SpatialPoints, SpatialLines) per fare qualsiasi cosa. La funzione readWKT in rgeos può essere d'aiuto.

Le cose di cui fare attenzione sono di solito cose come le colonne del database che non possono essere associate a tipi di dati R. È possibile includere SQL nella query per eseguire conversioni, filtraggio o limitazione. Questo dovrebbe iniziare però.


Ottima risposta, ma come abilito la funzionalità (il driver Postgis) in rgadl? Sono in Ubuntu 13.04 ...
nanounanue,

Ce l'hai? La funzione ogrDrivers () dovrebbe dirti da qualche parte. In caso contrario, questa è un'altra domanda (probabilmente è meglio cercare prima su
google

In Ubuntu, il driver è installato per impostazione predefinita. Questo non è il caso di MacOS X. Grazie!
nanounanue,

Nel tuo codice sopra, è possibile nel readOGRmetodo utilizzare un sql invece di una tabella completa?
nanounanue,

Attualmente penso di no. Ci sono state delle chiacchiere su r-sig-geo circa 2.5 anni fa, ma sembra che non sia stato fatto nulla. Sembra semplice aggiungere una whereclausola e passarla a OGR tramite setAttributeFilterma che tutto deve essere fatto in codice C e C ++ ...
Spacedman

8

Se disponi di dati in Postgis, non esportarli in shapefile. Dal mio punto di vista, è un po 'un passo indietro.

Puoi interrogare il tuo database postgis da R usando le istruzioni SQL, importandole come frame di dati e, poiché hai familiarità con R, esegui tutta la geostatistica di cui hai bisogno da lì. Credo che tu possa anche esportare il tuo risultato geostatistico su Postgis.

Usando SQL con le funzioni Postgis puoi anche eseguire qualsiasi tipo di analisi spaziale, come operazioni di sovrapposizione, distanze e così via.

Per la stampa di mappe utilizzerei QGIS , un software OpenSource GIS, in grado di leggere direttamente i postgis (per quanto ne so che era l'obiettivo iniziale del progetto), e la prossima versione 2.0 include molte funzionalità per produrre mappe di grande impatto .


Ok ottimo consiglio, ma dal momento che voglio automatizzare tutto in R (compresi i grafici) andando a QGis, interrompe il flusso, non è vero?
nanounanue,

In tal caso, se ti senti a tuo agio, usa R per tracciare le tue mappe. Anche così, avendo i layout dei progetti qgis preparati sulla base dei dati postgis (aggiornati), si aggiornerebbero anche. Immagino che alla fine sarà una scelta personale se usare R o QGIS.
Alexandre Neto,

Grazie per la tua rapida risposta, ma come posso fare una trama usando R, da una tabella di Postgis?
nanounanue,

Non ho molta esperienza con R e non so come tratteresti i dati vettoriali utilizzandoli (come ho detto che uso QGIS per quello), come tracci i file shap in R? Per la connessione a PostgresSQL dal RI hanno usato prima RPostgreSQL . Penso a rgdal ]. In bocca al lupo!
Alexandre Neto,

5

Il pacchetto sf appena introdotto (successore di sp) fornisce le funzioni st_read()e st_read_db(). Dopo questo tutorial e dalla mia esperienza è più veloce dei modi già menzionati. Dato che un giorno probabilmente sf sostituirà sp è anche una buona chiamata dare un'occhiata ora;)

require(sf)
dsn = "PG:dbname='dbname' host='host' port='port' user='user' password='pw'"
st_read(dsn, "schema.table")

puoi anche accedere al DB usando RPostgreSQL:

require(sf)
require(RPostgreSQL)
drv <- dbDriver("PostgreSQL")
con <- dbConnect(drv, dbname = dbname, user = user, host = host, port = port, password = pw)

st_read_db(con, table = c("schema", "table"))
# or:
st_read_db(con, query = "SELECT * FROM schema.table")

dbDisconnect(con)
dbUnloadDriver(drv)

Con st_write()te puoi caricare i dati.


1
Questa è la soluzione più semplice, c'è una vignetta cran.r-project.org/web/packages/sf/vignettes/sf2.html che spiega come usare sf
Cedric

2

È possibile utilizzare tutti gli strumenti contemporaneamente in base a ciascun passaggio della soluzione.

  • Se si desidera eseguire analisi geostatiche, utilizzare i pacchetti R. R sono più robusti e consente un risultato più analitico. È possibile importare dati basati su query SQL.
  • Se desideri aggregare i tuoi dati in base a una logica, puoi utilizzare PostGIS. Puoi rispondere a domande complesse come quali molti punti rientrano nei miei limiti prescritti? Ma su grande scala.
  • Per il mapping, è possibile utilizzare R o QGIS. QGIS è più semplice, con R potresti lottare per ottenere il risultato desiderato.

Potremmo fornirti una risposta più specifica se ci fornissi maggiori dettagli dal tuo problema


Potresti fornire un esempio dell'ultimo punto, intendo, come posso fare se voglio tracciare una mappa con R da una tabella in Postgis?
nanounanue,

@nanounanue sure: library ("rgdal") mydata = readOGR (dsn = "PG: dbname = <mydb>", layer = "schema.table") trama (mydata, axes = TRUE) titolo ("My Plot").
Nicks

dai un'occhiata anche a questa pagina: wiki.intamap.org/index.php/PostGIS
nickves

2

Vorrei anche andare su una combinazione di rgdal e RPostgreSQL. Quindi, lo stesso codice di @Guillaume, ad eccezione di un tryCatch che gestisce più righe, un nome di tabella pseudo-casuale e l'uso di una tabella non blog per prestazioni migliori. (Nota per me: non possiamo usare la tabella TEMP, perché non è visibile da readOGR)

dbGetSp <- function(dbInfo,query) {
 if(!require('rgdal')|!require(RPostgreSQL))stop('missing rgdal or RPostgreSQL')
  d <- dbInfo
  tmpTbl <- sprintf('tmp_table_%s',round(runif(1)*1e5))
  dsn <- sprintf("PG:dbname='%s' host='%s' port='%s' user='%s' password='%s'",
    d$dbname,d$host,d$port,d$user,d$password
    )
  drv <- dbDriver("PostgreSQL")
  con <- dbConnect(drv, dbname=d$dbname, host=d$host, port=d$port,user=d$user, password=d$password)
  tryCatch({
    sql <- sprintf("CREATE UNLOGGED TABLE %s AS %s",tmpTbl,query)
    res <- dbSendQuery(con,sql)
    nr <- dbGetInfo(res)$rowsAffected
    if(nr<1){
      warning('There is no feature returned.');
      return()
    }
    sql <- sprintf("SELECT f_geometry_column from geometry_columns WHERE f_table_name='%s'",tmpTbl)
    geo <- dbGetQuery(con,sql)
    if(length(geo)>1){
      tname <- sprintf("%s(%s)",tmpTbl,geo$f_geometry_column[1])
    }else{
      tname <- tmpTbl;
    }
    out <- readOGR(dsn,tname)
    return(out)
  },finally={
    sql <- sprintf("DROP TABLE %s",tmpTbl)
    dbSendQuery(con,sql)
    dbClearResult(dbListResults(con)[[1]])
    dbDisconnect(con)
  })
}

Uso:

d=list(host='localhost', dbname='spatial_db', port='5432', user='myusername', password='mypassword')
spatialObj<-dbGetSp(dbInfo=d,"SELECT * FROM spatial_table")

Ma questo è ancora dolorosamente lento:

Per un piccolo set di poligoni (6 funzioni, 22 campi):

parte postgis:

user  system elapsed
0.001   0.000   0.008

parte readOGR:

user  system elapsed
0.313   0.021   1.436


1

Puoi anche combinare rgdal e RPostreSQL. Questa funzione di esempio crea una tabella temporanea con RPostgreSQL e la invia a readOGR per l'output di un oggetto spaziale. Questo è davvero inefficiente e brutto, ma funziona abbastanza bene. Si noti che la query deve essere una query SELECT e l'utente deve avere accesso in scrittura al database.

RPostGIS <- function(coninfo,query) {
  dsn=paste("PG:dbname='",coninfo$dbname,"' host='",coninfo$host,"' port='",coninfo$port,"' user='",coninfo$user,"' password='",coninfo$password,"'", sep='')
  drv <- dbDriver("PostgreSQL")
  con <- dbConnect(drv, user=coninfo$user, password=coninfo$password, dbname=coninfo$dbname)
  res <- dbSendQuery(con,paste('CREATE TABLE tmp1209341251dva1 AS ',query,sep=''))
  geo <- dbGetQuery(con,"SELECT f_geometry_column from geometry_columns WHERE f_table_name='tmp1209341251dva1'")
  if(length(geo)>1){
    tname=paste("tmp1209341251dva1(",geo$f_geometry_column[1],")")
  }else{
    tname="tmp1209341251dva1";
  }
  out <- tryCatch(readOGR(dsn,tname), finally=dbSendQuery(con,'DROP TABLE tmp1209341251dva1'))
  dbDisconnect(con)
  return(out)
}

Puoi chiamarlo con qualcosa del tipo:

> require('rgdal')
> require('RPostgreSQL')
> coninfo=list(host='localhost',dbname='spatial_db',port='5432',user='myusername',password='mypassword')
> spatial_obj<-RPostGIS(coninfo,"SELECT * FROM spatial_table")

0

Se si restituisce una query con "ST_AsText (geom) come geomwkt" e si recupera il risultato in dati, è possibile utilizzare:

library(rgeos);library(sp)
wkt_to_sp <- function(data) {
  #data is data.frame from postgis with geomwkt as only geom
  SpP <- SpatialPolygons(lapply(1:length(data$geomwkt), 
           function(x) Polygons(list(Polygon(readWKT(data$geomwkt[x]))),x)))
  data <- data[,!(names(data) == "geomwkt")]
  return(SpatialPolygonsDataFrame(SpP, data))
}

Ancora dolorosamente lento .... 1 secondo per 100 geomi su un test.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.