我在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。拥有并不重要。
如果您需要进一步的澄清,请询问。预先感谢您的宝贵时间。
由于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
</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>
</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