Читання файлу KML в R?


42

Я працюю з величезними .kml файлами (до 10 Gb) і мені потрібен ефективний спосіб їх читання в R. До цих пір я перетворював їх у shapefiles через QGIS, а потім повертався в R з readShapePoly і readOGR (останній , до речі, на ~ 1000 швидше, ніж колишній). В ідеалі я хотів би припинити етап посередництва QGIS, оскільки це громіздко і повільно.

Як читати .kml файли безпосередньо?

Я бачу, це можна зробити і з readOGR . На жаль, я не бачу, як реалізувати відпрацьований приклад (після тривалої підготовки файлу .kml:) xx <- readOGR(paste(td, "cities.kml", sep="/"), "cities"). Здається, що "міста" тут - назва просторових об'єктів.

Роджер Біванд визнає, що "як виявити це ім'я не очевидно, оскільки драйвер KML в OGR потребує його для доступу до файлу. Одна з можливостей:

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

"

Але це також не працює для мене. Ось тестовий файл .kml, щоб його спробувати. З ним у моєму робочому каталозі readOGR("x.kml", "id")генерується це повідомлення про помилку:

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

І system(paste("ogrinfo", "x.kml"), intern=TRUE)генерує:

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

, чого я просто не розумію.

Чи буде getKMLcoordinates{maptools} вірною альтернативою?

Я також спробував це:

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

Координати генеруються правильно, але моя спроба перетворити їх назад в об'єкт багатокутника не вдалася із наступним повідомленням:

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

1
Ви можете отримати шари в kml за допомогою функції rgdal ogrListLayers.
Маріо Бесерра

Відповіді:


37

Щоб прочитати KML з драйвером OGR, ви даєте йому ім'я файлу та ім'я шару.

Коментар Роджера полягає в тому, що ім'я шару приховано у файлі KML, і, якщо ви не знаєте, як створено KML, ви не можете зробити висновок назви шару з імені файлу KML.

Переглядаючи ваш приклад KML, я бачу:

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

Що говорить мені, що назва шару - це xне id, і так:

> 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

працює чудово.

Тепер ви можете спробувати отримати ім'я, розібравши KML як XML за допомогою аналізатора R XML, або, можливо , ви можете спробувати прочитати його в R як текстовий файл, поки не знайдете тег імені.

Іншим підходом є запуск програми ogrinfo командного рядка, яка випилює назви шарів файлу 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)

тут показано, що існує багатокутний шар, який називається x.


Дякуємо за Вашу відповідь Пробіг - вирішено проблему відразу. Це чітке пояснення, як це, що змушує мене любити обмін стеками! Одне питання "бонусний бал": чи можу я використати ту саму команду для читання в підмножині даних (наприклад, перші 1 мільйон полігонів)? Інакше буде дивитись, щоб розділити величезні км із зовнішньою програмою.
RobinLovelace

2
KML, що є XML, насправді не розроблений для випадкового доступу. Справжнє рішення полягає в тому, щоб розмістити ваші просторові дані в просторовій базі даних і мати кілька просторових індексів для швидкості. Перевірте PostGIS.
Спайдермен

Добре хороший план - я сказав клієнту, що PostGIS - це шлях для отримання таких великих даних, і переконаний, що це правильний варіант для того, що він хоче робити. Хороший привід для мене, щоб це навчитися правильно!
RobinLovelace

Існує також просторове розширення sqlite - файлова база даних, яка не вимагає від вас встановлення послуги та вимагає меншої конфігурації, ніж PostGIS.
Френк

дивно systemв R потрібно path.expandна ~для ogrinfoдо роботи, навіть якщо він працював відмінно на нерозкрите шляху в командному рядку (MacOS, Sys.which('ogrinfo')і which ogrinfoповернулися ті ж шляху)
MichaelChirico

5

Якщо ви хочете зробити альтернативний спосіб за допомогою maptool, це має спрацювати:

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

Тут важливо пройти кілька кроків, щоб скласти просторовий клас багатокутника.


привіт @ Сенью, я спробував твій підхід, але, здається, це не працює? У мене помилка: Помилка в Polygon (tkml): координати повинні бути матрицею з двома стовпцями> head (tkml) [[1]] [1] -87.88141 30.49800 adn У мене є список .. Ви вважаєте, його нормально перетворити список координат матриці? танки!
майкка

1

Не знаю, чи це ще проблема для когось іншого, але я деякий час бігав по колах. Що нарешті для мене спрацювало, нижче. Він використовує XMLпакет, щоб потрапити на xmlValueправий вузол. Мені довелося встановити layerпараметр readOGRімені однієї з папок у файлі kml. Коли я встановлю layerпараметр до файлу kml, я отримаю ту саму помилку, що описана вище RobinLovelace.

Нижче показано багато рядків коду, які лише показують, як бачити різні рівні вузлів документа kml. Я думаю, це буде дещо по-іншому залежно від джерела кмл. Але ви повинні мати можливість використовувати ту саму логіку, щоб визначити правильне значення параметра.

Крім того , я створив список файлів KML так що це може бути легко перетворений в функцію , яка може бути поставлена в lapply- do.callпарах. Потім це може отримати дані з довгого списку файлів kml. Або багато підпапок у одному файлі kml, як здається, readOGRне можуть мати справу з декількома папками у файлі 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)

0

Не знаю, чи повинен я змінити свою попередню відповідь. Можливо, але це стосується деяких речей не в цій відповіді, тому я вирішив залишити це.

У всякому разі, код нижче працює для мене добре. Він шукає все XMLNodes в файлі KML , які називаються «Папка» , а потім встановлює layerпараметр readOGRдо цього xmlValue. Тестовано на робочому каталозі з приблизно 6 окремими файлами kml. Вихід - це список імпортованих об'єктів SpatialDataFrames. Кожен SpatialDataFrame може бути легко підмножений зі списку.

Досі не адресує файли kml з кількома вузлами папок. Але цю функцію можна легко додати за допомогою іншої вкладеної applyфункції.

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.