Lire un fichier KML dans R?

42

Je travaille avec d’énormes fichiers .kml (jusqu’à 10 Gb) et j’ai besoin d’un moyen efficace de les lire en R. Jusqu’à présent, je les ai convertis en fichiers de formes via QGIS, puis en R avec readShapePoly et readOGR (ce dernier Soit dit en passant, est environ 1000 fois plus rapide que l’ancien). J'aimerais idéalement couper le stade intermédiaire de QGIS car il est lourd et lent.

Comment lire les fichiers .kml directement?

Je vois que cela peut également être fait avec readOGR . Malheureusement, je ne vois pas comment implémenter l'exemple travaillé (après une longue préparation du fichier .kml:) xx <- readOGR(paste(td, "cities.kml", sep="/"), "cities"). Il semble que "villes" soit le nom des objets spatiaux.

Roger Bivand admet que "la manière dont on découvre ce nom n’est pas évidente, car le pilote KML dans OGR en a besoin pour accéder au fichier. Une possibilité est:

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

"

Mais cela ne fonctionne pas pour moi non plus. Voici un fichier test .kml pour l'essayer. Avec cela dans mon répertoire de travail, readOGR("x.kml", "id")génère ce message d'erreur:

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

Et system(paste("ogrinfo", "x.kml"), intern=TRUE)génère:

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

, que je ne comprends tout simplement pas.

Est-ce que getKMLcoordinates{} maptools être une alternative valable?

J'ai aussi essayé ceci:

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

Les coordonnées sont générées correctement, mais ma tentative de les reconvertir en objet polygone a échoué avec le message suivant:

Error in SpatialPolygons(tkml, proj4string = CRS("+init=epsg:3857")) : 
  cannot get a slot ("area") from an object of type "double"
RobinLovelace
la source
1
Vous pouvez obtenir les couches dans le kml en utilisant la fonction ogrListLayers de rgdal.
Mario Becerra

Réponses:

37

Pour lire un fichier KML avec le pilote OGR, vous devez lui donner le nom du fichier et le nom de la couche.

Selon le commentaire de Roger, le nom de la couche est masqué dans le fichier KML. Si vous ne savez pas comment le fichier KML a été créé, vous ne pouvez pas en déduire le nom de la couche à partir du nom du fichier KML.

En regardant votre exemple KML, je peux voir:

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

Ce qui me dit que le nom de la couche est x, pas id, et ainsi:

> 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

fonctionne bien.

Maintenant, vous pouvez essayer d'obtenir le nom en analysant le fichier KML au format XML à l'aide d'un analyseur syntaxique R, ou vous pouvez éventuellement essayer de le lire dans R sous forme de fichier texte jusqu'à ce que vous trouviez la balise name.

L’autre approche consiste à exécuter le programme ogrinfo en ligne de commande qui crache les noms de couche d’un fichier 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)

ici montrant qu'il existe une couche de polygone appelée x.

Spacedman
la source
Merci pour votre réponse Spaced - a résolu le problème immédiatement. C'est une explication claire comme celle-là qui me fait aimer l'échange de pile! Une question «bonus»: puis-je utiliser la même commande pour lire un sous-ensemble de données (par exemple, le premier million de polygones)? Sinon cherchera à séparer les énormes kmls avec un programme externe.
RobinLovelace
2
Le format KML étant XML n'est pas vraiment conçu pour un accès aléatoire. La vraie solution est de mettre vos données spatiales dans une base de données spatiale et d’avoir des index spatiaux rapides. Découvrez PostGIS.
Spacedman
OK bon plan - J'ai dit au client que PostGIS était la voie à suivre pour de telles données massives et je suis convaincu que c'est la bonne option pour le genre de choses qu'il souhaite faire. Bonne excuse pour que je l'apprenne correctement!
RobinLovelace
Il existe également l' extension spatiale de sqlite , une base de données basée sur des fichiers, qui ne nécessite pas l'installation d'un service et nécessite moins de configuration que PostGIS.
Frank
étrangement systemdans la R nécessaire path.expandsur ~pour ogrinfotravailler, même si elle a bien fonctionné sur le chemin non déployée sur la ligne de commande (macOS, Sys.which('ogrinfo')et which ogrinforetourné les mêmes chemins)
MichaelChirico
5

Si vous souhaitez utiliser maptool de manière alternative, cela devrait fonctionner:

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 clé ici est que vous devez suivre quelques étapes pour créer une classe de polygone spatial.

Vu
la source
salut @Seen, j'ai essayé votre approche mais elle ne semble pas fonctionner? J'ai une erreur: Erreur dans le polygone (tkml): coords doit être une matrice à deux colonnes> head (tkml) [[1]] [1] -87.88141 30.49800 et j'en ai une liste .. pensez-vous que sa conversion est correcte liste des coordonnées à la matrice? tahnks!
Maycca
1

Je ne sais pas si c'est toujours un problème pour quelqu'un d'autre, mais je tournais en rond pendant un moment avec ça. Ce qui a finalement fonctionné pour moi est ci-dessous. Il utilise le XMLpaquet pour obtenir xmlValuele bon noeud. Je devais définir le layerparamètre de readOGRsur le nom de l'un des dossiers dans le fichier kml. Lorsque je règle le layerparamètre sur le fichier kml, j'obtiens la même erreur que RobinLovelace décrite ci-dessus.

Vous trouverez ci-dessous un grand nombre de lignes de code qui montrent uniquement comment afficher les différents niveaux de nœud du document kml. Je pense que ce sera légèrement différent en fonction de la source du kml. Mais vous devriez pouvoir utiliser la même logique pour déterminer la valeur de paramètre correcte.

En outre, j'ai créé une liste de fichiers kml afin qu'il puisse être facilement transformé en une fonction qui pourrait être mis dans une lapply- do.calldeux. Cela pourrait alors extraire une donnée d'une longue liste de fichiers kml. Ou bien, un grand nombre de sous-dossiers dans un fichier kml unique, car il semble readOGRne pas pouvoir gérer plusieurs sous-dossiers dans un fichier 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)
Argile
la source
0

Je ne sais pas si j'aurais dû modifier ma réponse précédente. Peut-être, mais cela couvre des choses qui ne sont pas dans cette réponse, alors j'ai décidé de la laisser.

Quoi qu'il en soit, le code ci-dessous fonctionne bien pour moi. Il recherche tous les xmlNodes dans le fichier kml qui s'appellent "Dossier", puis définit le layerparamètre de readOGRà xmlValue. Testé sur un répertoire de travail avec environ 6 fichiers kml distincts. La sortie est une liste d'objets SpatialDataFrames importés. Chaque SpatialDataFrame peut facilement être un sous-ensemble de la liste.

N'adresse toujours pas les fichiers kml avec plusieurs nœuds de dossiers. Mais cette fonctionnalité pourrait facilement être ajoutée à une autre applyfonction imbriquée .

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)
Argile
la source