Membaca file KML ke R?

42

Saya sedang bekerja dengan file .kml besar (hingga 10 Gb) dan perlu cara yang efisien untuk membacanya menjadi R. Sampai sekarang saya telah mengonversinya menjadi shapefile melalui QGIS dan kemudian kembali ke R dengan readShapePoly dan readOGR (yang terakhir , omong-omong, ~ 1000 lebih cepat dari yang sebelumnya). Saya idealnya ingin memotong tahap perantara QGIS karena rumit dan lambat.

Bagaimana cara membaca file .kml secara langsung?

Saya melihat ini bisa juga dilakukan dengan readOGR . Sayangnya, saya tidak bisa melihat bagaimana menerapkan contoh yang dikerjakan (setelah persiapan panjang file .kml:) xx <- readOGR(paste(td, "cities.kml", sep="/"), "cities"). Tampaknya "kota" di sini adalah nama objek spasial.

Roger Bivand mengakui bahwa "Bagaimana seseorang menemukan nama ini tidak jelas, karena driver KML di OGR membutuhkannya untuk mengakses file. Salah satu kemungkinan adalah:

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

"

Tapi ini juga tidak berhasil untukku. Berikut ini file uji .kml untuk dicoba. Dengan itu di direktori kerja saya, readOGR("x.kml", "id")menghasilkan pesan kesalahan ini:

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

Dan system(paste("ogrinfo", "x.kml"), intern=TRUE)menghasilkan:

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

, yang saya tidak mengerti.

Apakah getKMLcoordinates{maptools} menjadi alternatif yang valid?

Saya juga sudah mencoba ini:

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

Koordinat dihasilkan dengan benar, tetapi upaya saya untuk mengubahnya kembali menjadi objek poligon gagal dengan pesan berikut:

Error in SpatialPolygons(tkml, proj4string = CRS("+init=epsg:3857")) : 
  cannot get a slot ("area") from an object of type "double"
RobinLovelace
sumber
1
Anda bisa mendapatkan lapisan dalam kml menggunakan fungsi rgdal ogrListLayers.
Mario Becerra

Jawaban:

37

Untuk membaca KML dengan driver OGR, Anda berikan nama file dan nama layer.

Komentar Roger adalah bahwa nama layer disembunyikan dalam file KML, dan kecuali Anda tahu bagaimana KML dibuat, Anda tidak dapat menyimpulkan nama layer dari nama file KML.

Melihat contoh KML Anda, saya dapat melihat:

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

Yang memberitahu saya nama layer adalah x, tidak id, dan sebagainya:

> 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

berfungsi dengan baik.

Sekarang, Anda bisa mencoba dan mendapatkan nama dengan parsing KML sebagai XML menggunakan parser R XML, atau Anda dapat mungkin mencoba membacanya di R sebagai file teks sampai Anda menemukan tag nama.

Pendekatan lain adalah menjalankan program ogrinfo baris perintah yang memuntahkan nama layer dari file 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)

di sini menunjukkan ada lapisan poligon yang disebut x.

Spacedman
sumber
Terima kasih atas jawaban Anda Spaced - langsung menyelesaikan masalah. Ini penjelasan yang jelas seperti ini yang membuat saya suka bertukar tumpukan! Satu pertanyaan 'poin bonus': dapatkah saya menggunakan perintah yang sama untuk membaca dalam sekumpulan data (misalnya 1 juta poligon pertama)? Kalau tidak, akan terlihat untuk membagi kml besar dengan program eksternal.
RobinLovelace
2
KML menjadi XML tidak benar-benar dirancang untuk akses acak. Solusi nyata adalah dengan memasukkan data spasial Anda ke dalam basis data spasial, dan memiliki beberapa indeks spasial untuk kecepatan. Lihat PostGIS.
Spacedman
OK rencana bagus - Saya telah memberi tahu klien bahwa PostGIS adalah jalan ke depan untuk data sebesar itu, dan saya yakin itu adalah pilihan yang tepat untuk hal-hal yang ingin ia lakukan. Alasan bagus bagi saya untuk mempelajarinya dengan benar!
RobinLovelace
Ada juga ekstensi spasial ke sqlite , basis data berbasis file, yang tidak mengharuskan Anda untuk menginstal layanan dan memerlukan konfigurasi yang lebih sedikit daripada PostGIS.
Frank
anehnya systemdi R dibutuhkan path.expandpada ~untuk ogrinfobekerja, meskipun bekerja dengan baik di jalan tidak dikembangkan pada baris perintah (MacOS, Sys.which('ogrinfo')dan which ogrinfokembali jalur yang sama)
MichaelChirico
5

Jika Anda ingin melakukan cara alternatif menggunakan maptool, ini harusnya berfungsi:

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

Kuncinya di sini adalah Anda harus melalui beberapa langkah untuk membuat kelas poligon spasial.

Terlihat
sumber
hai @ Seen, saya sudah mencoba pendekatan Anda tetapi tampaknya tidak berhasil? Saya memiliki kesalahan: Kesalahan dalam Polygon (tkml): coords harus berupa matriks dua kolom> head (tkml) [[1]] [1] -87.88141 30.49800 dan saya memilikinya sebagai daftar .. menurut Anda apakah itu boleh diubah daftar koordinat ke matriks? tahnks!
maycca
1

Tidak tahu apakah ini masih menjadi masalah bagi orang lain, tapi saya berlari berputar-putar untuk sementara waktu dengan ini. Apa yang akhirnya berhasil untuk saya ada di bawah ini. Ia menggunakan XMLpaket untuk mendapatkan xmlValuedari simpul yang tepat. Saya harus mengatur layerparameter readOGRke nama salah satu folder dalam file kml. Ketika saya mengatur layerparameter ke file kml, saya akan mendapatkan kesalahan yang sama seperti yang dijelaskan oleh RobinLovelace di atas.

Yang ditunjukkan di bawah ini adalah banyak baris kode yang hanya menunjukkan cara melihat berbagai tingkat simpul dari dokumen kml. Saya pikir ini akan sedikit berbeda tergantung pada sumber kml. Tetapi Anda harus dapat menggunakan logika yang sama untuk menentukan nilai parameter yang benar.

Juga, saya membuat sebuah daftar file kml sehingga bisa dengan mudah dibuat menjadi fungsi yang dapat dimasukkan ke dalam lapply- do.callpasangan. Ini kemudian bisa menarik data dari daftar panjang file kml. Atau, banyak subfolder dalam file kml tunggal karena tampaknya readOGRtidak dapat menangani beberapa subfolder dalam file 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)
Tanah liat
sumber
0

Tidak tahu apakah saya harus mengubah jawaban saya sebelumnya. Mungkin, tetapi itu mencakup beberapa hal yang tidak ada dalam jawaban ini, jadi saya memutuskan untuk meninggalkannya.

Bagaimanapun, kode di bawah ini berfungsi dengan baik untuk saya. Itu mencari semua xmlNodes dalam file kml yang disebut "Folder" dan kemudian menetapkan layerparameter readOGRuntuk itu xmlValue. Diuji pada direktori kerja dengan sekitar 6 file kml terpisah. Output adalah daftar objek SpatialDataFrames yang diimpor. Setiap SpatialDataFrame dapat dengan mudah diatur dari daftar.

Masih tidak membahas file kml dengan beberapa folder Folder. Tetapi fitur itu dapat dengan mudah ditambahkan dengan applyfungsi bersarang lainnya .

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)
Tanah liat
sumber