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

42

Я работаю с огромными файлами .kml (до 10 Гб) и мне нужен эффективный способ чтения их в R. До сих пор я конвертировал их в шейп-файлы через 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"
RobinLovelace
источник
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.

Spacedman
источник
Спасибо за ваш ответ Spaced - решил проблему сразу. Это четкое объяснение, которое заставляет меня любить обмен стека! Один вопрос «бонусного балла»: могу ли я использовать одну и ту же команду для чтения подмножества данных (например, первых 1 миллиона полигонов)? В противном случае будем искать разделить огромные кмлс с помощью внешней программы.
RobinLovelace
2
KML, будучи XML, на самом деле не предназначен для произвольного доступа. Реальное решение - поместить ваши пространственные данные в пространственную базу данных и иметь некоторые пространственные индексы для скорости. Проверьте PostGIS.
Spacedman
Хорошо, хороший план - я сказал клиенту, что 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"))

Ключевым моментом здесь является то, что вам нужно пройти пару шагов, чтобы создать класс пространственного многоугольника.

Видели
источник
Привет @Seen, я попробовал твой подход, но, кажется, не работает? У меня есть ошибка: Ошибка в многоугольнике (tkml): координаты должны быть двухколоночной матрицей> head (tkml) [[1]] [1] -87.88141 30.49800 и т.д. У меня есть список ... Как вы думаете, его нормально конвертировать? список координат для матрицы? танкс!
Maycca
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)
глина
источник