Как загрузить все поля / ExtendedData (а не только «имя» и «описание») из слоя KML в R

Я работал над загрузкой файлов KML в R, чтобы создавать веб-карты с помощью Leaflet / Shiny. Импорт довольно прост (с использованием этого образца KML):

library(rgdal)

sampleKml <- readOGR("D:/KML_Samples.kml", layer = ogrListLayers("D:/KML_Samples.kml")[1])

В этом примере ogrListLayers втягивает все слои kml, а я подмножество только первого элемента / слоя. Очень просто.

Проблема в том, что при использовании этого метода для чтения слоев KML используются только два поля: «Имя» и «Описание», как показано ниже:

> sampleKml <- readOGR("D:/KML_Samples.kml", layer = ogrListLayers("D:/KML_Samples.kml")[1])
OGR data source with driver: KML 
Source: "D:/KML_Samples.kml", layer: "Placemarks"
with 3 features
It has 2 fields
> sampleKml@data
                Name                                                                                  Description
1   Simple placemark Attached to the ground. Intelligently places itself at the height of the underlying terrain.
2 Floating placemark                                                  Floats a defined distance above the ground.
3 Extruded placemark                                              Tethered to the ground by a customizable "tail" 

Таким образом, R читает слой KML как SpatialPointsDataFrame с 3 функциями (3 разные точки) и двумя полями (столбцы). Однако, когда я загружаю слой в QGIS и читаю его таблицу атрибутов, есть много полей в дополнение к имени и описанию, видел здесь.

Насколько я могу судить, «имя» и «описание» являются метками KML, а любые дополнительные данные считаются расширенными данными. Я хочу импортировать эти расширенные данные вместе с данными меток.

Есть ли способ вытащить ВСЕ эти поля / атрибуты слоя KML в R? Желательно с readOGR(), но я открыт для всех предложений.


person Lauren    schedule 31.08.2017    source источник
comment
Пока я ничего не знаю о ГИС или KML, попробуйте поиграть с аргументами в readOGR даже с подробным, чтобы увидеть, появляются ли какие-либо соответствующие сообщения.   -  person Parfait    schedule 01.09.2017
comment
Кроме того, я проверил вашу ссылку в валидаторе KML Google. и проходит, однако с рекомендациями по совместимости с широким спектром программ чтения каналов: Метка должна содержать атрибут id. Это важно, если вы хотите напрямую ссылаться на функции. Вот ссылка отчет   -  person Parfait    schedule 01.09.2017
comment
Что вы хотите делать с данными после того, как вы поместите свой KML в R? Я прошу b / c, что некоторые поля, показанные на вашем снимке экрана QGIS, являются стандартными полями, которые даже не записаны в файл KML (например, отметка времени, начало, конец), и в KML есть много других полей, которые не виден QGIS (например: LookAt и все его дочерние элементы). Вы, наверное, не хотите / не нуждаетесь в них все? Является ли этот образец файла (с сайта разработчиков KML) фактическим KML, который вы хотите проанализировать, или у вас есть другой файл, который содержит пары имя / значение в тегах ExtendedData (или отображается в виде таблицы во всплывающих подсказках)?   -  person Christiaan Adams    schedule 05.09.2017
comment
@ChristiaanAdams Я хочу использовать ExtendedData! Я отредактирую свой пост, чтобы уточнить (я обнаружил термин «расширенные данные» только после того, как опубликовал этот вопрос). Мой настоящий KML-файл предназначен для тропических циклонов, и я хочу использовать такие поля, как дата / время, скорость ветра и т. Д.   -  person Lauren    schedule 05.09.2017
comment
Отлично, наверное, это облегчает задачу. Если данные, которые вам нужны, на самом деле находятся в KML в виде пар имя / значение ExtendedData, вы сможете довольно легко их проанализировать. Извините, я не могу помочь с кодом R. Просто будьте осторожны, потому что существует МНОГО файлов KML, которые были сгенерированы инструментами экспорта KML в ArcGIS ESRI, где таблица данных в всплывающих сообщениях представляет собой просто большой двоичный объект HTML и не хранится в парах имя / значение ExtendedData, так что разобрать его намного сложнее. Поскольку ваш образец файла не содержит ExtendedData, я предлагаю предоставить тот, который содержит, чтобы специалисты R могли помочь.   -  person Christiaan Adams    schedule 06.09.2017
comment
Хотя эта проблема еще не решена, похоже, что это связано с проблемой совместимости между библиотекой libkml и Windows.   -  person Lauren    schedule 29.11.2017
comment
Кроме того, это очень по теме для gis.stackexchange.com, пожалуйста, похожие вопросы?   -  person Spacedman    schedule 04.08.2018


Ответы (1)


TL;DR

Основная проблема заключается в отсутствии библиотеки LibKML для Windows. Мое решение извлекает данные непосредственно из KML с помощью функции.

Проблема

Я столкнулся с той же проблемой, и после некоторого поиска в Google выяснилось, что это как-то связано с LibKML и Windows. Выполнение одного и того же кода на моем компьютере с Ubuntu дало разные результаты, а именно ExtendedData была получена при загрузке сохраненного файла KML.

library(rgdal)
library(dplyr)
poly_df<-data.frame(x=c(1,1,0,0),y=c(1,0,0,1))
poly<-poly_df %>% 
  Polygon %>% 
  list %>% 
  Polygons(ID="1") %>% 
  list %>% 
  SpatialPolygons(proj4string = CRS("+init=epsg:4326")) %>% 
  SpatialPolygonsDataFrame(data=data.frame(test="this is a test"))

writeOGR(poly,"test.kml",driver="KML",layer="poly")
poly2<-readOGR("test.kml")
poly2@data

Если кому-то удастся построить LibKML [1], он сможет загружать файлы KML с расширенными данными [2].

В Windows LibKML необходимо создавать с помощью Visual Studio 2005 [1]. Эта версия Visual Studio больше не поддерживается [3]. В [3] user2889419 предоставляет ссылку на версию 2005 г.
Я загрузил и установил версию, но сборка LibKML в конечном итоге завершилась неудачно с множеством ошибок и предупреждений (некоторые файлы не существуют). Это было то место, где меня остановили, потому что я вышел из зоны комфорта, но хотел поделиться результатами своей погони.

Решение в R

Мое решение - прочитать KML напрямую, а затем извлечь ExtendedData при загрузке пространственного объекта через readOGR rgdal. Я предполагаю, что readOGR запускается поверх файла, как и моя процедура извлечения. Затем оба объединяются, и на выходе получается SpatialPolygonsDataFrame.
Сначала у меня были проблемы с извлечением узлов из файлов KML, потому что я не знал о концепции пространств имен [4]. (Отредактировал следующую функцию, потому что у меня возникли проблемы с файлами KML другого происхождения.)

readKML <- function(file,keep_name_description=FALSE,layer,...) {
  # Set keep_name_description = TRUE to keep "Name" and "Description" columns
  #   in the resulting SpatialPolygonsDataFrame. Only works when there is
  #   ExtendedData in the kml file.

  sp_obj<-readOGR(file,layer,...)
  xml1<-read_xml(file)
  if (!missing(layer)) {
    different_layers <- xml_find_all(xml1, ".//d1:Folder") 
    layer_names <- different_layers %>% 
      xml_find_first(".//d1:name") %>% 
      xml_contents() %>% 
      xml_text()

    selected_layer <- layer_names==layer
    if (!any(selected_layer)) stop("Layer does not exist.")
    xml2 <- different_layers[selected_layer]
  } else {
    xml2 <- xml1
  }

  # extract name and type of variables

  variable_names1 <- 
    xml_find_first(xml2, ".//d1:ExtendedData") %>% 
    xml_children() 

  while(variable_names1 %>% 
        xml_attr("name") %>% 
        is.na() %>% 
        any()&variable_names1 %>%
        xml_children() %>% 
        length>0) variable_names1 <- variable_names1 %>%
    xml_children()

  variable_names <- variable_names1 %>%
    xml_attr("name") %>% 
    unique()

  # return sp_obj if no ExtendedData is present
  if (is.null(variable_names)) return(sp_obj)

  data1 <- xml_find_all(xml2, ".//d1:ExtendedData") %>% 
    xml_children()

  while(data1 %>%
        xml_children() %>% 
        length>0) data1 <- data1 %>%
    xml_children()

  data <- data1 %>% 
    xml_text() %>% 
    matrix(.,ncol=length(variable_names),byrow = TRUE) %>% 
    as.data.frame()

  colnames(data) <- variable_names

  if (keep_name_description) {
    sp_obj@data <- data
  } else {
    try(sp_obj@data <- cbind(sp_obj@data,data),silent=TRUE)
  }
  sp_obj
}

Старая версия: извлечение через ReadLines.

Мое решение - прочитать KML напрямую, а затем извлечь ExtendedData при загрузке пространственного объекта через readOGR rgdal. Я предполагаю, что readOGR запускается поверх файла, как и моя процедура извлечения. Затем оба объединяются, и на выходе получается SpatialPolygonsDataFrame.

library(tidyverse)
library(rgdal)

readKML<-function(file,keep_name_description=FALSE,...) {
  # Set keep_name_description = TRUE to keep "Name" and "Description" columns 
  #   in the resulting SpatialPolygonsDataFrame. Only works when there is 
  #   ExtendedData in the kml file.

  if (!grepl("\\.kml$",file)) stop("File is not a KML file.")
  if (!file.exists(file)) stop("File does not exist.")
  map<-readOGR(file,...)

  f1<-readLines(file)

  # get positions of ExtendedData in document
  exdata_position<-grep("ExtendedData",f1) %>% 
    matrix(ncol=2,byrow = TRUE) %>% 
    apply(1,function(x) {
      pos<-x[1]:x[2]
      pos[2:(length(pos)-1)]
    }) %>% 
    t %>% 
    as.data.frame

  # if there is no ExtendedData return SpatialPolygonsDataFrame
  if (ncol(exdata_position)==0) return(map)

  # Get Name of different columns
  extract1<-f1[exdata_position[1,] %>% 
                 unlist]  
  names_of_data<-extract1 %>% 
    strsplit("name=\"") %>%
    lapply(function(x) strsplit(x[[2]],split="\"") ) %>%
    unlist(recursive = FALSE) %>%
    lapply(function(x) return(x[1])) %>% 
    unlist

  # Extract Extended Data
  dat<-lapply(seq(nrow(exdata_position)),function(x) {
    extract2<-f1[exdata_position[x,] %>% 
                   unlist]  
    extract2 %>% 
      strsplit(">") %>%
      lapply(function(x) strsplit(x[[2]],split="<") ) %>% unlist(recursive = FALSE) %>%
      lapply(function(x) return(x[1])) %>% 
      unlist %>% 
      matrix(nrow=1) %>% 
      as.data.frame
  }) %>% 
    do.call(rbind,.)

  # Rename columns
  colnames(dat)<-names_of_data

  # Check if Name and Description should be dropped
  if (keep_name_description) {
    map@data<-cbind(map@data,dat)
  } else {
    map@data<-dat
  }
  map
}

[1] https://github.com/google/libkml/wiki/Building-and-installing-libkml
[2] https://github.com/r-spatial/sf/issues/499
[3] Где скачать Visual Studio Express 2005?
[4] Анализ XML в R: неправильные пространства имен

person Sebastian    schedule 02.08.2018
comment
В тестовом файле, на который вы указали ссылку в вопросе, нет слова ExtendedData. Также вам следует читать KML, используя пакет для чтения файлов XML. - person Spacedman; 03.08.2018
comment
Спасибо за ваш комментарий! Вы правы с XML, у меня еще не было времени углубляться в него. Вы правы, что в тестовом файле нет ExtendedData, но вопрос был в том, есть ли способ вытащить ВСЕ эти поля / атрибуты слоя KML в R? который включает ExtendedData. Я должен уточнить, что я предоставляю код для создания test.kml, в котором есть ExtendedData, а затем демонстрирую, возможно, менее предпочтительный, но рабочий способ его извлечения. - person Sebastian; 04.08.2018