KML-Datei in R lesen?

42

Ich arbeite mit riesigen .kml-Dateien (bis zu 10 GB) und brauche eine effiziente Methode, um sie in R zu lesen. Bisher habe ich sie über QGIS in Shapefiles konvertiert und dann mit readShapePoly und readOGR (letzteres) wieder in R ist übrigens ~ 1000 schneller als die erstere). Ich möchte die QGIS-Zwischenstufe idealerweise streichen, da sie umständlich und langsam ist.

Wie lese ich .kml-Dateien direkt ein?

Ich sehe, dass dies auch mit readOGR möglich ist . Leider kann ich nicht sehen, wie das Beispiel implementiert wird (nach langwieriger Vorbereitung der .kml-Datei:) xx <- readOGR(paste(td, "cities.kml", sep="/"), "cities"). Es scheint, dass "Städte" hier der Name der räumlichen Objekte ist.

Roger Bivand gibt zu, dass "es nicht offensichtlich ist, wie man diesen Namen entdeckt, da der KML-Treiber in OGR ihn benötigt, um auf die Datei zuzugreifen. Eine Möglichkeit ist:

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

"

Aber das funktioniert auch bei mir nicht. Hier ist eine .kml-Testdatei zum Anprobieren . readOGR("x.kml", "id")Generiert in meinem Arbeitsverzeichnis die folgende Fehlermeldung:

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

Und system(paste("ogrinfo", "x.kml"), intern=TRUE)generiert:

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

, was ich einfach nicht verstehe.

Wäre getKMLcoordinates{maptools} eine gültige Alternative?

Ich habe es auch versucht:

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

Die Koordinaten werden korrekt generiert, aber mein Versuch, sie wieder in ein Polygonobjekt umzuwandeln, schlug mit der folgenden Meldung fehl:

Error in SpatialPolygons(tkml, proj4string = CRS("+init=epsg:3857")) : 
  cannot get a slot ("area") from an object of type "double"
RobinLovelace
quelle
1
Sie können die Layer in kml mit der Funktion ogrListLayers von rgdal abrufen.
Mario Becerra

Antworten:

37

Um eine KML mit dem OGR-Treiber zu lesen, geben Sie den Dateinamen und den Layernamen an.

Rogers Kommentar ist, dass der Ebenenname in der KML-Datei verborgen ist. Wenn Sie nicht wissen, wie die KML erstellt wurde, können Sie den Ebenennamen nicht aus dem KML-Dateinamen ableiten.

In Ihrem Beispiel-KML sehe ich:

<?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">

Was mir sagt, dass der Ebenenname xnicht idist und so:

> 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

funktioniert gut.

Jetzt können Sie versuchen, den Namen zu erhalten , indem die KML als XML - Parsing eines R XML - Parser verwenden, oder Sie können vielleicht versuchen , es in R als Textdatei zu lesen , bis Sie das Namensschild zu finden.

Der andere Ansatz besteht darin, das Befehlszeilenprogramm ogrinfo auszuführen, das die Ebenennamen einer KML-Datei ausgibt:

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

Hier wird angezeigt, dass es eine Polygonebene mit dem Namen gibt x.

Raumfahrer
quelle
Vielen Dank für Ihre Antwort Spaced - hat das Problem sofort gelöst. Es ist eine klare Erklärung, die mich zum Stapeltausch bringt! Eine Bonuspunktfrage: Kann ich mit demselben Befehl eine Teilmenge der Daten einlesen (z. B. die ersten 1 Million Polygone)? Ansonsten wird nach einer Aufteilung der riesigen kmls mit einem externen Programm gesucht.
Robin Lovelace
2
KML als XML ist nicht wirklich für den Direktzugriff gedacht. Die wirkliche Lösung besteht darin, Ihre räumlichen Daten in eine räumliche Datenbank zu stellen und einige räumliche Indizes für die Geschwindigkeit zu haben. Schauen Sie sich PostGIS an.
Spacedman
OK, guter Plan - Ich habe dem Kunden gesagt, dass PostGIS der richtige Weg für so große Datenmengen ist, und bin überzeugt, dass es die richtige Option für die Art von Dingen ist, die er tun möchte. Gute Ausrede für mich, es richtig zu lernen!
Robin Lovelace
Es gibt auch die räumliche Erweiterung für sqlite , eine dateibasierte Datenbank, die keine Installation eines Dienstes erfordert und weniger Konfiguration erfordert als PostGIS.
Frank
seltsam systemin R benötigt path.expandauf ~für ogrinfozu arbeiten, auch wenn es in Ordnung auf dem nicht ausgedehnten Weg auf der Kommandozeile gearbeitet (macOS; Sys.which('ogrinfo')und which ogrinfowieder die gleichen Pfade)
MichaelChirico
5

Wenn Sie MapTool alternativ verwenden möchten, sollte dies funktionieren:

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

Der Schlüssel hier ist, dass Sie einige Schritte durchlaufen müssen, um eine räumliche Polygonklasse zu erstellen.

Gesehen
quelle
hi @Seen, ich habe deinen Ansatz ausprobiert, aber es scheint nicht zu funktionieren? Ich habe einen Fehler: Fehler im Polygon (tkml): Koordinaten müssen eine zweispaltige Matrix sein> Kopf (tkml) [[1]] [1] -87.88141 30.49800 und ich habe es als Liste Koordinatenliste zur Matrix? tahnks!
Maycca
1

Ich weiß nicht, ob das für irgendjemanden noch ein Problem ist, aber ich bin eine Weile damit im Kreis gelaufen. Was schließlich für mich funktioniert hat, ist unten. Es verwendet das XMLPaket, um xmlValueden richtigen Knoten zu finden. Ich musste den layerParameter von readOGRauf den Namen des einen Ordners in der kml-Datei setzen. Wenn ich den layerParameter auf die der kml-Datei setze, erhalte ich den gleichen Fehler, den RobinLovelace oben beschrieben hat.

Unten sind viele Codezeilen dargestellt, die nur zeigen, wie die verschiedenen Knotenebenen des kml-Dokuments angezeigt werden. Ich denke, das wird etwas anders sein, abhängig von der Quelle des kml. Sie sollten jedoch die gleiche Logik verwenden können, um den richtigen Parameterwert zu ermitteln.

Außerdem habe ich eine Liste von KML - Dateien , so dass es leicht in eine Funktion gemacht werden könnten , die in einem genommen werden konnte lapply- do.callPaar. Dadurch könnten Daten aus einer langen Liste von kml-Dateien abgerufen werden. Oder viele Unterordner in einer einzelnen kml-Datei können anscheinend readOGRnicht mit mehreren Unterordnern in einer kml-Datei umgehen.

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)
Lehm
quelle
0

Ich weiß nicht, ob ich meine vorherige Antwort hätte ändern sollen. Vielleicht, aber das deckt einige Dinge nicht in dieser Antwort ab, also habe ich beschlossen, es zu verlassen.

Wie auch immer, der folgende Code funktioniert gut für mich. Es sucht nach allen xmlNodes in der kml-Datei, die als "Ordner" bezeichnet werden, und setzt den layerParameter readOGRdarauf xmlValue. Getestet auf Arbeitsverzeichnis mit ungefähr 6 unterschiedlichen kml Akten. Die Ausgabe ist eine Liste der importierten SpatialDataFrames-Objekte. Jeder SpatialDataFrame kann einfach aus der Liste ausgewählt werden.

Kml-Dateien mit mehreren Ordnerknoten werden immer noch nicht angesprochen. Diese Funktion kann jedoch problemlos mit einer anderen verschachtelten applyFunktion hinzugefügt werden.

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