Cómo leer eficientemente un archivo kml en R

39

Estoy trabajando con enormes archivos .kml (hasta 10 Gb) y necesito una forma eficiente de leerlos en R. Hasta ahora los he estado convirtiendo en shapefiles a través de QGIS y luego volviendo a R con readShapePoly y readOGR (el último, por cierto, es ~ 1000 más rápido que el anterior). Lo ideal sería recortar la etapa intermedia de QGIS, ya que es engorroso y lento.

¿Cómo leer archivos .kml directamente?

I veo que esto también se puede hacer con readOGR . Desafortunadamente, no puedo ver cómo implementar el ejemplo trabajado (después de una larga preparación del archivo .kml: xx <- readOGR(paste(td, "cities.kml", sep="/"), "cities") ). Parece que "ciudades" aquí es el nombre de los objetos espaciales.

Roger Bivand admite que "la forma en que uno descubre este nombre no es obvia, ya que el controlador KML en OGR lo necesita para acceder al archivo. Una posibilidad es:

system(paste("ogrinfo", paste(td, "cities.kml", sep="/")), intern=TRUE)

"

Pero esto tampoco funciona para mí. Aquí hay un archivo de prueba .kml para probarlo. Con esto en mi directorio de trabajo, readOGR("x.kml", "id") genera este mensaje de error:

Error in ogrInfo(dsn = dsn, layer = layer, encoding = encoding, use_iconv = use_iconv) : 
  Cannot open layer . 

Y system(paste("ogrinfo", "x.kml"), intern=TRUE) genera:

[1] "Had to open data source read-only."   "INFO: Open of 'x.kml'"               
[3] "      using driver 'KML' successful." "1: x (3D Polygon)"  

, que simplemente no entiendo.

¿Sería getKMLcoordinates {maptools} una alternativa válida?

También he intentado esto:

tkml <- getKMLcoordinates(kmlfile="x.kml", ignoreAltitude=T)
head(tkml[[1]])
tkml <- SpatialPolygons(tkml, 
                        proj4string=CRS("+init=epsg:3857"))

Las coordenadas se generan correctamente, pero mi intento de convertirlas de nuevo en un objeto de polígono falló con el siguiente mensaje:

Error in SpatialPolygons(tkml, proj4string = CRS("+init=epsg:3857")) : 
  cannot get a slot ("area") from an object of type "double"
    
pregunta RobinLovelace 16.04.2013 - 11:09

4 respuestas

35

Para leer un archivo KML con el controlador OGR, le das el nombre del archivo y el nombre de la capa.

El comentario de Roger es que el nombre de la capa está oculto en el archivo KML y, a menos que sepa cómo se creó el KML, no puede inferir el nombre de la capa a partir del nombre del archivo KML.

Mirando tu ejemplo KML, puedo ver:

<?xml version="1.0" encoding="utf-8" ?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document><Folder><name>x</name>
<Schema name="x" id="x">

Lo que me dice que el nombre de la capa es x , no id , y por lo tanto:

> foo = readOGR("/tmp/x.kml", "x")
OGR data source with driver: KML 
Source: "/tmp/x.kml", layer: "x"
with 1 features and 2 fields
Feature type: wkbPolygon with 2 dimensions

funciona bien.

Ahora, puede intentar obtener el nombre analizando el archivo KML como XML mediante un analizador XML de R, o puede quizás intentar leerlo en R como un archivo de texto hasta que encuentre la etiqueta de nombre .

El otro enfoque es ejecutar el programa ogrinfo de la línea de comandos que escupe los nombres de capa de un archivo KML:

$ ogrinfo /tmp/x.kml 
Had to open data source read-only.
INFO: Open of '/tmp/x.kml'
      using driver 'KML' successful.
1: x (Polygon)

aquí se muestra que hay una capa de polígono llamada x .

    
respondido por el Spacedman 16.04.2013 - 15:16
5

Si quieres hacer la forma alternativa usando maptool, esto debería funcionar:

tkml <- getKMLcoordinates(kmlfile="yourkml.kml", ignoreAltitude=T)
#make polygon
p1 = Polygon(tkml)
#make Polygon class
p2 = Polygons(list(p1), ID = "drivetime")
#make spatial polygons class
p3= SpatialPolygons(list(p2),proj4string=CRS("+init=epsg:4326"))

La clave aquí es que necesitas seguir un par de pasos para hacer una clase de polígono espacial.

    
respondido por el Seen 01.06.2014 - 21:17
1

No sé si esto sigue siendo un problema para otra persona, pero estuve corriendo en círculos por un tiempo con esto. Lo que finalmente funcionó para mí está abajo. Utiliza el paquete XML para obtener el xmlValue del nodo correcto. Tuve que establecer el parámetro layer de readOGR con el nombre de una de las carpetas dentro del archivo kml. Cuando configuré el parámetro layer en el archivo kml, obtendría el mismo error que RobinLovelace describió anteriormente.

A continuación se muestran muchas líneas de código que solo muestran cómo ver los distintos niveles de nodo del documento kml. Creo que esto será ligeramente diferente dependiendo de la fuente del kml. Pero debería poder usar la misma lógica para determinar el valor correcto del parámetro.

También, creé una lista de archivos kml para poder convertirla fácilmente en una función que se podría colocar en un par lapply - do.call . Esto podría luego extraer datos de una larga lista de archivos kml. O, una gran cantidad de subcarpetas dentro de un solo archivo kml, ya que parece que readOGR no puede manejar múltiples subcarpetas en un archivo kml.

library(rgdal); library(XML)

# SET WORKING DIRECTORY FIRST!!
dir <- getwd()

kmlfilelist <- list.files(dir, pattern =".kml$", full.names=TRUE, recursive=FALSE)

doc0 <- xmlTreeParse(kmlfilelist[2], useInternal = TRUE)
rootNode0 <- xmlRoot(doc0)
rootName0 <- xmlName(rootNode0)
element1Name0 <- names(rootNode0)

nodeNames <- names(rootNode0[1][[1]])

# entire rootNode - kml Document level
rootNode0[[1]]

# 1st element of rootNode - kml file name
rootNode0[[1]][[1]] 

# 2nd element of rootNode - kml Style Map 
rootNode0[[1]][[2]] 

# 3rd element of rootNode - Style
rootNode0[[1]][[3]]

# 4th element of rootNode - Style
rootNode0[[1]][[4]] 

# 5th element of rootNode - kml Folder with data in it.
rootNode0[[1]][[5]] 

# 5th element 1st subelement of rootNode - kml Folder name with data in it. 
#  What to set readOGR() layer parameter to.
rootNode0[[1]][[5]][[1]] 

kmlfoldername <- xmlValue(rootNode0[[1]][[5]][[1]]) # Folder name to set = layer.

readOGR(dsn=kmlfilelist[2], layer =  kmlfoldername)
    
respondido por el Clay 09.02.2016 - 23:09
0

No sé si debería haber modificado mi respuesta anterior. Quizás, pero eso cubre algunas cosas que no están en esta respuesta, así que decidí dejarlo.

De todos modos, el siguiente código funciona bien para mí. Busca todos los xmlNodes en el archivo kml que se llaman "Carpeta" y luego establece el parámetro layer de readOGR a ese xmlValue . Probado en el directorio de trabajo con alrededor de 6 archivos kml separados. La salida es una lista de objetos SpatialDataFrames importados. Cada SpatialDataFrame se puede subcontratar fácilmente de la lista.

Todavía no trata los archivos kml con múltiples nodos de carpeta. Pero esa función podría agregarse fácilmente con otra función apply anidada.

library(rgdal); library(XML)

# SET WORKING DIRECTORY FIRST!!
dir <- getwd()

kmlfilelist <- list.files(dir, pattern =".kml$", full.names=TRUE, recursive=FALSE)

ImportKml <- function (kmlfile) {
  doc0 <- xmlTreeParse(kmlfile, useInternal = TRUE)
  rootNode0 <- xmlRoot(doc0)
  rootName0 <- xmlName(rootNode0)
  element1Name0 <- names(rootNode0)

  kmlNodeNames <- unname(names(rootNode0[1][[1]]))
  kmlFolderNodeNum <- which(kmlNodeNames == "Folder")
  kmlFolderNodeName <- xmlValue(rootNode0[[1]][[kmlFolderNodeNum]][[1]])

  kmlIn <- readOGR(dsn=kmlfile, layer = kmlFolderNodeName)
}
ImportedKmls <- lapply(kmlfilelist, ImportKml)
    
respondido por el Clay 10.02.2016 - 02:13

Lea otras preguntas en las etiquetas