将 KML 文件转换为 CSV 并在 rgdal 和 sf 包以及 Python 中出现错误
Converting a KML file to CSV and having errors with rgdal and sf packages as well as Python
我在 R 和 Python 中解析此 KML 文件的图层时遇到困难。我已经包含了一个 link 来从我的 Dropbox 下载文件。此文件最初是与我共享的。但是,我被告知该文件源自 Distilleries Fighting Covid,但我不知道如何找到它或获取它。
我想要的是提取所有层并最终将它们分离到它们自己的 csv
文件中。我要检索的节点是名称、地址、城市、州、邮编。我得到的最接近的是来自堆栈 post 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)
但是,这样做时,我收到以下错误和警告:
Error in readOGR("Distilleries and Hospitals.kml", "Distilleries") :
no features found In addition: Warning message: In ogrFIDs(dsn = dsn,
layer = layer) : no features found
现在,我可以读取存储在 lyr 变量中的层。
下面的代码将生成一个 7 的列表。
lyr <- ogrListLayers("Distilleries and Hospitals.kml")
接下来,我尝试使用以下代码从他的一层中拉出:
mykml <- readOGR("Distilleries and Hospitals.kml", "Distilleries")
这导致了以下错误和警告(同上):
Error in readOGR("Distilleries and Hospitals.kml", "Distilleries") :
no features found In addition: Warning message: In ogrFIDs(dsn = dsn,
layer = layer) : no features found
最后,我尝试使用与 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 通常是空的,只是 return 一个空字段并继续移动,这样当我合并列表时,所有内容都排成一行。
下面的示例只尝试获取名称和地址行 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' object has no attribute 'strip'
我相信这是因为在第一条记录中,地址 2 是空的。因此,我相信这是在尝试从扩展数据中同时提取所有内容,这也不是我想要的。
我遇到的真正困难是拉 <Data name = "..."> ... </Data>
个字段。
这是我第一次尝试 XML/KML 解析,如有任何帮助,我将不胜感激。在这一点上,我真的不知道下一步该尝试什么。
最终文件将是一个包含 headers 的 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>
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
我在 R 和 Python 中解析此 KML 文件的图层时遇到困难。我已经包含了一个 link 来从我的 Dropbox 下载文件。此文件最初是与我共享的。但是,我被告知该文件源自 Distilleries Fighting Covid,但我不知道如何找到它或获取它。
我想要的是提取所有层并最终将它们分离到它们自己的 csv
文件中。我要检索的节点是名称、地址、城市、州、邮编。我得到的最接近的是来自堆栈 post 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)
但是,这样做时,我收到以下错误和警告:
Error in readOGR("Distilleries and Hospitals.kml", "Distilleries") :
no features found In addition: Warning message: In ogrFIDs(dsn = dsn, layer = layer) : no features found
现在,我可以读取存储在 lyr 变量中的层。
下面的代码将生成一个 7 的列表。
lyr <- ogrListLayers("Distilleries and Hospitals.kml")
接下来,我尝试使用以下代码从他的一层中拉出:
mykml <- readOGR("Distilleries and Hospitals.kml", "Distilleries")
这导致了以下错误和警告(同上):
Error in readOGR("Distilleries and Hospitals.kml", "Distilleries") :
no features found In addition: Warning message: In ogrFIDs(dsn = dsn, layer = layer) : no features found
最后,我尝试使用与 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 通常是空的,只是 return 一个空字段并继续移动,这样当我合并列表时,所有内容都排成一行。
下面的示例只尝试获取名称和地址行 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' object has no attribute 'strip'
我相信这是因为在第一条记录中,地址 2 是空的。因此,我相信这是在尝试从扩展数据中同时提取所有内容,这也不是我想要的。
我遇到的真正困难是拉 <Data name = "..."> ... </Data>
个字段。
这是我第一次尝试 XML/KML 解析,如有任何帮助,我将不胜感激。在这一点上,我真的不知道下一步该尝试什么。
最终文件将是一个包含 headers 的 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>
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