将KML文件转换为CSV并在rgdal和sf软件包以及Python在线演示中出现错误

问题描述 投票:1回答:1

我在R和Python中解析此KML文件的层时遇到困难。我提供了一个链接,可以从我的Dropbox下载文件。此文件最初与我共享。但是,有人告诉我文件起源于Distilleries Fighting Covid,但我不知道如何找到或获取它。

我想要的是提取所有图层,并最终将它们分成各自的csv文件。我要检索的节点是名称,地址,城市,州,邮政编码。我得到的最接近的信息来自堆栈文章Read multiple layers of KML file using 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”)中的错误:找不到任何功能另外:警告消息:在ogrFIDs(dsn = dsn,层=层):未找到任何特征

现在,我能够读取lyr变量中存储的图层。

下面的代码将产生一个清单7。

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

接下来,我尝试使用以下代码从他那里拉一层:

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

这导致以下错误和警告(与上面相同):

readOGR(“ Distilleries and Hospitals.kml”,“ Distilleries”)中的错误:找不到任何功能另外:警告消息:在ogrFIDs(dsn = dsn,层=层):未找到任何特征

最后,我尝试通过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 File at Dropbox for Download (~28mb)

编辑1:从下面的注释中可以看出,该文件中的图层为空。如果那是正确的,那么问题是,如何从该文件中将所需的数据获取到CSV文件中。

编辑2:进一步调查KML文档,看来我的所有信息都可以在placemark标签(...)中找到。但是,我不确定如何提取该数据。这是最终目标。如果这些不是层,那么如果有人可以指出我的方向来解决这个问题,那就太好了。再次感谢您的所有帮助。

编辑3数据摘录和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',''))

运行此命令时,我得到的第一条记录的第一行地址很完美,但同时也出现以下错误:

AttributeError:'NoneType'对象没有属性'strip'

我相信这是因为在第一个记录中,地址2为空。因此,我认为这实际上是尝试从扩展数据中一次提取所有内容,这也不是我想要的。

我遇到的真正困难是拉<Data name = "..."> ... </Data>字段。

这是我在XML / KML解析上的第一个小技巧,因此,我将不胜感激。我真的不知道下一步该怎么做。

结束文件将是CSV文件,其标题为:名称,地址1,地址2,城市,州,邮政编码。老实说,我也很好,只是摆脱了地址2。拥有并不重要。

如果您需要进一步的澄清,请询问。预先感谢您的宝贵时间。

python r kml sf rgdal
1个回答
0
投票

由于KML文件是XML文件,请考虑XSLT,这是一种专用语言,旨在将XML文件转换为不同的XML,HTML甚至CSV格式。

两个带有lxml的Python和带有xslt的R(扩展到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>

Online Demo

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
© www.soinside.com 2019 - 2024. All rights reserved.