¿Trabajando con datos PostGIS en R?

27

Trabajo con R casi todo el tiempo, y ahora lo estoy usando para hacer minería de datos espaciales.

Tengo una base de datos PostGIS con (obviamente) datos SIG.

Si quiero hacer un análisis espacial estadístico y trazar mapas, es la mejor manera de:

  • exportar las tablas como shapefiles o;
  • trabajar directamente a la base de datos?
nanounanue
fuente

Respuestas:

34

Si tiene la capacidad del controlador PostGIS en el paquete rgdal, entonces solo se trata de crear una cadena de conexión y usarla. Aquí me estoy conectando a mi base de datos local gisusando credenciales predeterminadas, por lo que mi DSN es bastante simple. Es posible que deba agregar un host, nombre de usuario o contraseña. Consulte los documentos de gdal para obtener información.

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

¿Qué tablas hay en esa base de datos?

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

Conseguir 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

Que tengo

> 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  

De lo contrario, puede utilizar la funcionalidad de la base de datos de R y consultar las tablas directamente.

> 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)

Eso devuelve la geometría de la característica df$geom, que necesitará convertir a spobjetos de clase (SpatialPolygons, SpatialPoints, SpatialLines) para hacer cualquier cosa. La función readWKT en rgeos puede ayudar con eso.

Las cosas a tener en cuenta son, por lo general, cosas como columnas de bases de datos que no pueden asignarse a tipos de datos R. Puede incluir SQL en la consulta para realizar conversiones, filtrar o limitar. Sin embargo, esto debería ayudarte a comenzar.

Hombre espacial
fuente
Gran respuesta, pero ¿cómo habilito la capacidad (el controlador Postgis) en rgadl? Estoy en Ubuntu 13.04 ...
nanounanue
¿Lo tienes? La función ogrDrivers () debería decirte en alguna parte. Si no, entonces esa es otra pregunta (probablemente mejor buscada en Google primero y luego preguntada en R-sig-geo)
Spacedman
En Ubuntu, el controlador se instala por defecto. Ese no es el caso en MacOS X. ¡Gracias!
nanounanue
En su código anterior, ¿es posible en el readOGRmétodo usar un sql en lugar de una tabla completa?
nanounanue
Actualmente creo que no. Hubo algunas conversaciones en r-sig-geo hace aproximadamente 2.5 años sobre esto, pero parece que no se ha hecho nada. Parece simple agregar una wherecláusula y pasarla a OGR a través de, setAttributeFilterpero todo eso debe hacerse en código C y C ++ ...
Spacedman
8

Si tiene datos en Postgis, no los exporte a shapefile. Desde mi punto de vista, es un paso atrás.

Puede consultar su base de datos postgis desde R utilizando sentencias SQL, importándolas como marcos de datos y, dado que está familiarizado con R, realice todas las estadísticas geográficas que necesite desde allí. Creo que también puede exportar su resultado geoestadístico a postgis.

Usando SQL con Postgis Functions también puede hacer todo tipo de análisis espacial, como operaciones de superposición, distancias, etc.

Para el trazado de mapas, usaría QGIS , un software OpenSource GIS, que puede leer postgis directamente (hasta donde sé que ese era el objetivo inicial del proyecto), y la próxima versión 2.0 viene con muchas características para producir mapas de gran apariencia .

Alexandre Neto
fuente
Ok, un gran consejo, pero dado que quiero automatizar todo en R (incluidas las parcelas) yendo a QGis, rompe el flujo, ¿no?
nanounanue
En ese caso, si se siente cómodo con él, simplemente use R para trazar sus mapas. Aun así, tener los diseños de los proyectos qgis preparados en base a los datos postgis (actualizados), también se actualizarían. Supongo que al final será una elección personal si usar R o QGIS.
Alexandre Neto
Gracias por su rápida respuesta, pero ¿cómo puedo hacer un diagrama usando R, desde una tabla en Postgis?
nanounanue 01 de
No tengo mucha experiencia con R y no sé cómo trazarías los datos vectoriales al usarlos (como dije que uso QGIS para eso), ¿cómo trazas los shapfiles en R? Para conectarse a PostgresSQL desde RI he usado RPostgreSQL antes. Creo que rgdal ]. ¡Buena suerte!
Alexandre Neto
5

El paquete sf recientemente introducido (sucesor de sp) proporciona las funciones st_read()y st_read_db(). Después de este tutorial y desde mi experiencia, es más rápido que las formas ya mencionadas. Como sf probablemente reemplazará a sp algún día, también es una buena decisión tener que mirar ahora;)

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

También puede acceder a la base de datos utilizando 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()usted puede cargar datos.

andschar
fuente
1
Esta es la solución más simple, hay una viñeta cran.r-project.org/web/packages/sf/vignettes/sf2.html que explica cómo usar sf
Cedric
2

Puede utilizar todas las herramientas al mismo tiempo en función de cada paso de su solución.

  • Si desea hacer un análisis geostático, utilice los paquetes de R.R. son más robustos y le permiten obtener un resultado más analítico. Puede importar datos basados ​​en consultas SQL.
  • Si desea agregar sus datos de forma lógica, puede usar PostGIS. ¿Puede responder consultas complejas como qué puntos están dentro de mis límites prescritos? Pero a gran escala.
  • Para el mapeo, puede usar R o QGIS. QGIS es más directo, con R podrías tener dificultades para lograr el resultado deseado.

Podríamos proporcionarle una respuesta más específica si nos da más detalles de su problema

nickves
fuente
¿Podría proporcionar un ejemplo del último punto, es decir, cómo puedo hacer si quiero trazar un mapa con R desde una tabla en Postgis?
nanounanue
@nanounanue sure: library ("rgdal") mydata = readOGR (dsn = "PG: dbname = <mydb>", layer = "schema.table") plot (mydata, axes = TRUE) title ("My Plot").
nickves
también eche un vistazo a esta página: wiki.intamap.org/index.php/PostGIS
nickves
2

También usaría una combinación de rgdal y RPostgreSQL. Entonces, el mismo código que @Guillaume, excepto con un tryCatch que maneja más líneas, un nombre de tabla pseudoaleatorio y el uso de una tabla no registrada para un mejor rendimiento. (Nota para mí: no podemos usar la tabla TEMP, porque no es visible desde 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")

Pero, esto todavía es dolorosamente lento:

Para un pequeño conjunto de polígonos (6 características, 22 campos):

parte postgis:

user  system elapsed
0.001   0.000   0.008

parte de readOGR:

user  system elapsed
0.313   0.021   1.436
Fred Moser
fuente
2

Ahora hay un paquete RPostGIS que puede importar geoms PostGIS en R con consultas SQL.

Guillaume
fuente
1

También puede combinar rgdal y RPostreSQL. Esta función de ejemplo crea una tabla temporal con RPostgreSQL y la envía a readOGR para la salida de un objeto espacial. Esto es realmente ineficiente y feo, pero funciona bastante bien. Tenga en cuenta que la consulta debe ser una consulta SELECT y el usuario debe tener acceso de escritura a la base de datos.

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)
}

Puedes llamarlo con algo como:

> 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")
Guillaume
fuente
0

Si devuelve una consulta con 'ST_AsText (geom) como geomwkt' y obtiene el resultado en datos, puede usar:

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))
}

Todavía dolorosamente lento ... 1 segundo por 100 geoms en una prueba.

ideamotor
fuente