Преобразование файла KML в CSV и наличие ошибок в пакетах rgdal и sf, а также Python - PullRequest
1 голос
/ 06 апреля 2020

У меня проблемы с анализом слоев этого файла KML в R и Python. Я включил ссылку для загрузки файла из моего Dropbox. Этот файл был предоставлен мне в оригинале. Тем не менее, мне говорят, что файл начинается с Distilleries Fighting Covid , но я не мог понять, как его найти или добраться до него.

Я хочу извлечь все слои и в конечном итоге разделить их на свои csv файлы. Узлы, которые я хочу получить: имя, адрес, город, штат, почтовый индекс. Самое близкое, что я получил с этим, от стекового поста Чтение нескольких слоев файла KML с использованием R .

Для этой первой попытки мой код выглядит следующим образом:

library(rgdal)
allKmlLayers <- function(kmlfile){
  lyr <- ogrListLayers(kmlfile)
  mykml <- list()
  for (i in 1:length(lyr)){
    mykml[i] <- readOGR(kmlfile, lyr[i])
  }
  names(mykml) <- lyr
  return(mykml)
}

kmlfile <- "Distilleries and Hospitals.kml"
mykml <- allKmlLayers(kmlfile)

Однако при этом я получаю следующую ошибку и предупреждение:

Ошибка в readOGR («Distilleries and Hospitals.kml», «Distilleries»):
функции не найдены. Дополнительно: Предупреждение: В ogrFID (dsn = dsn, layer = layer): функции не найдены

Теперь я могу прочитать слои, хранящиеся в переменной lyr.

Приведенный ниже код выдаст список из 7.

lyr <- ogrListLayers("Distilleries and Hospitals.kml")

Далее я попытался просто потянуть из одного слоя со следующим кодом:

mykml <- readOGR("Distilleries and Hospitals.kml", "Distilleries")

Это привело к следующей ошибке и предупреждению (как указано выше):

Ошибка в readOGR ("Ликероводочные заводы и больницы". kml "," Distilleries "):
функции не найдены. Дополнительно: Предупреждение: в ogrFID (dsn = dsn, layer = layer): функции не найдены

Наконец, я попытался использовать аналогичный подход с lapply с использованием пакета sf.

library(sf)
kmlfile <- "Distilleries and Hospitals.kml"
mykml <- lapply(lyr, function(i) st_read(kmlfile, i))
names(mykml) <- lyr

Я получаю 7 0x3 списков со нет никакой информации.

Любая помощь с этим была бы замечательной.

И последнее замечание: если вы все же получите файл с веб-сайта, обратите внимание, что рядом с конец файла, где R не будет читать файл (по крайней мере, не для меня) из-за специальных символов. Ошибка скажет вам, где это находится при использовании функции sf.

Спасибо за ваше время на это.

Файл KML в Dropbox для скачивания (~ 28 МБ)

Редактировать 1: Из комментария, оставленного ниже, кажется, что слои в этом файле пусты. Если это точно, то вопрос в том, как мне получить необходимые данные из этого файла в файл CSV.

Редактировать 2: Дальнейшее изучение документа KML, похоже, что вся моя информация может можно найти в тегах placemark (...). Однако я не уверен, как вытащить эти данные. Это конечная цель. Если это не слои, то было бы здорово, если бы кто-то мог указать мне, как решить эту проблему. Опять же, я хочу заранее поблагодарить вас за всю вашу помощь.

Редактировать 3 Data Excerpt и Python Попытка: я вручную манипулировал файлом, чтобы удалить все, что меня не особо интересует в долгое время Ниже приведена небольшая выдержка из файла. В нем перечислены первые три компании.

<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
  <Document>
    <Folder>
      <name>Distilleries</name>
      <Placemark>
        <name>Bomb City Enterprises</name>
        <description><![CDATA[Address: 306 S Cleveland St<br>Address Line2: <br>City: Amarillo<br>Location: Alabama<br>State_Abbrev: AL<br>Postal Code: 79102<br>unnamed (1): <br>unnamed (2): <br>unnamed (3): <br>Updated 2020-04-12 20:30:13.383810: ]]></description>
        <ExtendedData>
          <Data name="Address">
            <value>306 S Cleveland St</value>
          </Data>
          <Data name="Address Line2">
            <value/>
          </Data>
          <Data name="City">
            <value>Amarillo</value>
          </Data>
          <Data name="Location">
            <value>Alabama</value>
          </Data>
          <Data name="State_Abbrev">
            <value>AL</value>
          </Data>
          <Data name="Postal Code">
            <value>79102</value>
          </Data>
          <Data name="unnamed (1)">
            <value/>
          </Data>
          <Data name="unnamed (2)">
            <value/>
          </Data>
          <Data name="unnamed (3)">
            <value/>
          </Data>
          <Data name="Updated 2020-04-12 20:30:13.383810">
            <value/>
          </Data>
        </ExtendedData>
      </Placemark>
      <Placemark>
        <name>Cahaba Brewing Company</name>
        <address>4500 5th Ave. S building C Birmingham Alabama AL 35222</address>
        <description><![CDATA[Address: 4500 5th Ave. S<br>Address Line2: building C<br>City: Birmingham<br>Location: Alabama<br>State_Abbrev: AL<br>Postal Code: 35222<br>unnamed (1): <br>unnamed (2): <br>unnamed (3): <br>Updated 2020-04-12 20:30:13.383810: ]]></description>
        <styleUrl>#icon-1517-0288D1</styleUrl>
        <ExtendedData>
          <Data name="Address">
            <value>4500 5th Ave. S</value>
          </Data>
          <Data name="Address Line2">
            <value>building C</value>
          </Data>
          <Data name="City">
            <value>Birmingham</value>
          </Data>
          <Data name="Location">
            <value>Alabama</value>
          </Data>
          <Data name="State_Abbrev">
            <value>AL</value>
          </Data>
          <Data name="Postal Code">
            <value>35222</value>
          </Data>
          <Data name="unnamed (1)">
            <value/>
          </Data>
          <Data name="unnamed (2)">
            <value/>
          </Data>
          <Data name="unnamed (3)">
            <value/>
          </Data>
          <Data name="Updated 2020-04-12 20:30:13.383810">
            <value/>
          </Data>
        </ExtendedData>
      </Placemark>
      <Placemark>
        <name>Redmont Distilling Company</name>
        <address>4550 5th Ave South building N Birmingham Alabama AL 35222</address>
        <description><![CDATA[Address: 4550 5th Ave South<br>Address Line2: building N<br>City: Birmingham<br>Location: Alabama<br>State_Abbrev: AL<br>Postal Code: 35222<br>unnamed (1): <br>unnamed (2): <br>unnamed (3): <br>Updated 2020-04-12 20:30:13.383810: ]]></description>
        <styleUrl>#icon-1517-0288D1</styleUrl>
        <ExtendedData>
          <Data name="Address">
            <value>4550 5th Ave South</value>
          </Data>
          <Data name="Address Line2">
            <value>building N</value>
          </Data>
          <Data name="City">
            <value>Birmingham</value>
          </Data>
          <Data name="Location">
            <value>Alabama</value>
          </Data>
          <Data name="State_Abbrev">
            <value>AL</value>
          </Data>
          <Data name="Postal Code">
            <value>35222</value>
          </Data>
          <Data name="unnamed (1)">
            <value/>
          </Data>
          <Data name="unnamed (2)">
            <value/>
          </Data>
          <Data name="unnamed (3)">
            <value/>
          </Data>
          <Data name="Updated 2020-04-12 20:30:13.383810">
            <value/>
          </Data>
        </ExtendedData>
      </Placemark>
      <Placemark>

Поскольку мне не повезло с R, я добавил свою попытку Python ниже. Я надеюсь. Однако с добавленными данными, если кто-то сможет сделать это в R, я тоже буду этому рад.

Первое, что я пытаюсь получить - это имя. Затем из расширенного раздела данных я, в конечном счете, ищу адрес 1, адрес 2, город, аббревиатуру штата и почтовый индекс. У меня все хорошо, если я все получу, если в нем будет пустое поле, в котором нет данных. Например, адрес 2 часто пуст, просто верните пустое поле и продолжайте двигаться, чтобы при объединении списков все выстраивалось в линию.

В приведенном ниже примере делается попытка получить только имя и строку адреса 1. Я думаю, что , если я смогу получить это, тогда я смогу расширить его до конца.

Дополнительный код, который я попробовал, приведен ниже:

import xml.etree.ElementTree as et

doc = et.parse(filename)
nmsp = '{http://www.opengis.net/kml/2.2}'

name = []
address1 = []

for pm in doc.iterfind('.//{0}Placemark'.format(nmsp)):
    print(pm.find('{0}name'.format(nmsp)).text)
    name.append(pm.find('{0}name'.format(nmsp)).text)
    for adr1 in pm.iterfind('{0}ExtendedData//{0}value'.format(nmsp)):
        address1.append(adr1.text.strip().replace('\n',''))
        print(adr1.text.strip().replace('\n',''))

Когда я запускаю это, я получаю первую запись с первой адресной строкой 1, но я также получаю следующую ошибку:

AttributeError: объект 'NoneType' не имеет атрибута 'strip'

Я считаю, что это потому, что в первой записи Адрес 2 пуст. Поэтому я полагаю, что это попытка фактически извлечь все сразу из расширенных Данных, что тоже не то, что я хочу.

Реальная трудность, с которой я сталкиваюсь, - это тянуть поля <Data name = "..."> ... </Data>.

Это мой первый треск при разборе XML / KML, поэтому я бы очень признателен за любую помощь. Я действительно не имею ни малейшего понятия, что попробовать дальше на этом этапе.

Конечный файл будет CSV-файлом с заголовками: имя, адрес 1, адрес 2, город, штат, почтовый индекс. Честно говоря, я тоже в порядке, просто избавившись от адреса 2. Это не критично.

Если вам нужны дополнительные разъяснения, просто спросите. Заранее спасибо за ваше время.

1 Ответ

1 голос
/ 16 апреля 2020

Поскольку файлы KML представляют собой XML файлы, рассмотрим XSLT , язык специального назначения, предназначенный для преобразования файлов XML в различные форматы XML, HTML, даже в CSV.

Оба Python с lxml и R с xslt (расширенный пакет до xml2) могут запускать сценарии XSLT 1.0.

XSLT (сохранить как .xsl, специальный. xml файл)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:doc="http://www.opengis.net/kml/2.2">
  <xsl:output indent="yes" method="text" encoding="UTF-8"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/doc:kml">
    <xsl:copy>
      <xsl:text>Name,Address 1,Address 2,City,State,Zip&#xa;</xsl:text>
      <xsl:apply-templates select="descendant::doc:Placemark"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="doc:Placemark">
    <xsl:copy>
      <xsl:value-of select="concat(doc:name, ',',
                                   doc:ExtendedData/doc:Data[@name='Address'], ',',
                                   doc:ExtendedData/doc:Data[@name='Address Line2'], ',',
                                   doc:ExtendedData/doc:Data[@name='City'], ',',
                                   doc:ExtendedData/doc:Data[@name='Location'], ',',
                                   doc:ExtendedData/doc:Data[@name='Postal Code'])"/>
      <xsl:text>&#xa;</xsl:text>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Онлайн-демонстрация

Python

import lxml.etree as et

# INPUT XML AND XSL SOURCES
xml = et.parse('/path/to/Input.kml')
xsl = et.parse('/path/to/Script.xsl')

# RUN TRANSFORMATION
transformer = et.XSLT(xsl)
new_xml = transformer(xml)

# PRINT TO CONSOLE
print(new_xml)
# Name,Address 1,Address 2,City,State,Zip
# Bomb City Enterprises,306 S Cleveland St,,Amarillo,Alabama,79102
# Cahaba Brewing Company,4500 5th Ave. S,building C,Birmingham,Alabama,35222
# Redmont Distilling Company,4550 5th Ave South,building N,Birmingham,Alabama,35222

# SAVE TO FILE
with open('/path/to/Output.csv', 'wb') as f:
   f.write(new_xml)

R

library(xml2)
library(xslt)

# PARSE XML AND XSLT
doc <- read_xml('/path/toInput.kml')
style <- read_xml('/path/to/Script.xsl', package = "xslt")

# TRANSFORM NESTED INPUT INTO FLATTER OUTPUT
new_xml <- xslt::xml_xslt(doc, style)

# SAVE CSV
f <- file("/path/to/Output.csv")
    writeLines(new_xml, f)
close(f)

# BUILD DATA FRAME
final_df <- read.csv('/path/to/Output.csv')

#                         Name          Address.1  Address.2       City   State   Zip
# 1      Bomb City Enterprises 306 S Cleveland St              Amarillo Alabama 79102
# 2     Cahaba Brewing Company    4500 5th Ave. S building C Birmingham Alabama 35222
# 3 Redmont Distilling Company 4550 5th Ave South building N Birmingham Alabama 35222
...