2008 年 3 月
目标
本教程概述了如何使用 Python 基于逗号分隔值 (CSV) 数据创建 KML。CSV 数据是当今最常用的文件格式之一。大多数电子表格和数据库都可以读取和写入 CSV 文件。您可以在文本编辑器中编辑这种简单格式。Python 等许多编程语言都有用于读取和写入 CSV 文件的特殊库。因此,它成为交换大量数据的理想媒介。
虽然本教程中的代码示例使用 Python 编写,但它们可以适应大多数其他编程语言。本教程使用在 KML 中对地址进行地理编码中的代码将地址转换为经度/纬度坐标。它还使用 KML 2.2 中的新 <ExtendedData>
元素,并利用添加自定义数据中所述的球形框模板。因此,Google 地图或其他使用 KML 的应用都不支持生成的 KML,但可对代码进行调整,以生成与地图兼容的 KML。
示例数据
在本教程中,将 google-addresses.csv 文件用作 CSV 文件示例。此文件包含美国各 Google 办事处的所有地址、电话号码和传真号码。文件内容如下:
Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax Headquarters,1600 Amphitheatre Parkway,,,Mountain View,CA,94043,650-253-0000,650-253-0001 New York Sales & Engineering Office,76 Ninth Avenue,,,New York,NY,10011,212-565-0000,212-565-0001 Ann Arbor Sales Office,201 South Division Street,,,Ann Arbor,MI,48104,734-332-6500,734-332-6501 Atlanta Sales & Engineering Office,10 10th Street NE,,,Atlanta,GA,30309,404-487-9000,404-487-9001 Boulder Sales & Engineering Office,2590 Pearl St.,,,Boulder,CO,80302,303-245-0086,303-535-5592 Cambridge Sales & Engineering Office,5 Cambridge Center,,,Cambridge,MA,02142,617-682-3635,617-249-0199 Chicago Sales & Engineering Office,20 West Kinzie St.,,,Chicago,IL,60610,312-840-4100,312-840-4101 Coppell Sales Office,701 Canyon Drive,,,Coppell,TX,75019,214-451-4000,214-451-4001 Detroit Sales Office,114 Willits Street,,,Birmingham,MI,48009,248-351-6220,248-351-6227 Irvine Sales & Engineering Office,19540 Jamboree Road,,,Irvine,CA,92612,949-794-1600,949-794-1601 Pittsburgh Engineering Office,4720 Forbes Avenue,,,Pittsburgh,PA,15213,, Santa Monica Sales & Engineering Office,604 Arizona Avenue,,,Santa Monica,CA,90401,310-460-4000,310-309-6840 Seattle Engineering Office,720 4th Avenue,,,Kirkland,WA,98033,425-739-5600,425-739-5601 Seattle Sales Office,501 N. 34th Street,,,Seattle,WA,98103,206-876-1500,206-876-1501 Washington D.C. Public Policy Office,1001 Pennsylvania Avenue NW,,,Washington,DC,20004,202-742-6520,
请注意每行如何是一系列文本字符串(以英文逗号分隔)。每个逗号均分隔一个字段;每行的逗号数量相同。第一行包含字段的名称,按顺序排列。例如,每行中的第一个文本块是“Office”字段,第二个是“Address1”等。Python 可以将该字段转换为 dicts
集合,称为 DictReader
,可让您逐步执行每一行。此代码示例需要您提前了解数据的结构,但您可以添加一些基本处理程序来动态传递字段结构。
解析 CSV 文件
Python 的 xml.dom.minidom
模块提供了用于创建 XML 文档的绝佳工具,由于 KML 是 XML,因此您将在本教程中广泛用到它。您可以使用 createElement
或 createElementNS
创建一个元素,然后使用 appendChild
附加到另一个元素。以下是解析 CSV 文件和创建 KML 文件的步骤。
- 将 Geocoding_for_KML.py 导入您的模块。
- 为 CSV 文件创建
DictReader
。DictReader
是dicts
的集合,每行一个集合。 - 使用 Python 的
xml.dom.minidom.Document()
创建文档。 - 使用
createElementNS.
创建根<kml>
元素 - 将其附加到文档
。
- 使用
createElement
创建一个<Document>
元素。 - 使用
appendChild
将它附加到<kml>
元素。 - 对于每一行,创建一个
<Placemark>
元素,并将其附加到<Document>
元素。 - 对于每一行中的每一列,创建一个
<ExtendedData>
元素,并将其附加到您在第 8 步中创建的<Placemark>
元素。 - 创建一个
<Data>
元素,并将其附加到<ExtendedData>
元素。 为<Data>
元素指定名称属性,并使用setAttribute
为其分配列名称。 - 创建一个
<value>
元素并将其附加到<Data>
元素。创建一个文本节点,并使用createTextNode
为其分配列值。将文本节点附加到<value>
元素。 - 创建一个
<Point>
元素并将其附加到<Placemark>
元素。创建一个<coordinates>
元素并将其附加到<Point>
元素。 - 从行中提取地址,使其成为采用以下格式的单个字符串:Address1,Address2,City,State,Zip。因此,第一行将为
1600 Amphitheater Parkway,,Mountain View,CA,94043
。各播放列表之间可以存在逗号。请注意,为此,您需要事先了解 CSV 文件的结构以及哪些列构成地址。 - 对地址进行地理编码,使用对 KML 中使用的地址进行地理编码中所述的 Geocoding_for_KML.py 代码。这将返回一个字符串,即该位置的经度和纬度。
- 创建一个文本节点,并为其分配第 14 步中坐标的值,然后将其附加到
<coordinates>
元素。 - 将 KML 文档写入文件。
- 如果您将列名称列表作为参数传递给脚本,则脚本将按此顺序添加元素。如果我们不考虑元素的顺序,可以使用
dict.keys()
生成list
。不过,dict.keys()
不会保留文档中的原始顺序。如需使用此参数,请以逗号分隔列表的形式传入字段名称列表,如下所示:python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax
Python 代码示例
使用 Python 2.2 基于 CSV 文件创建 KML 文件的示例代码如下所示。您也可以在此处下载。
import geocoding_for_kml
import csv
import xml.dom.minidom
import sys
def extractAddress(row):
# This extracts an address from a row and returns it as a string. This requires knowing
# ahead of time what the columns are that hold the address information.
return '%s,%s,%s,%s,%s' % (row['Address1'], row['Address2'], row['City'], row['State'], row['Zip'])
def createPlacemark(kmlDoc, row, order):
# This creates aelement for a row of data.
# A row is a dict.
placemarkElement = kmlDoc.createElement('Placemark')
extElement = kmlDoc.createElement('ExtendedData')
placemarkElement.appendChild(extElement)
# Loop through the columns and create a element for every field that has a value.
for key in order:
if row[key]:
dataElement = kmlDoc.createElement('Data')
dataElement.setAttribute('name', key)
valueElement = kmlDoc.createElement('value')
dataElement.appendChild(valueElement)
valueText = kmlDoc.createTextNode(row[key])
valueElement.appendChild(valueText)
extElement.appendChild(dataElement)
pointElement = kmlDoc.createElement('Point')
placemarkElement.appendChild(pointElement)
coordinates = geocoding_for_kml.geocode(extractAddress(row))
coorElement = kmlDoc.createElement('coordinates')
coorElement.appendChild(kmlDoc.createTextNode(coordinates))
pointElement.appendChild(coorElement)
return placemarkElement
def createKML(csvReader, fileName, order):
# This constructs the KML document from the CSV file.
kmlDoc = xml.dom.minidom.Document()
kmlElement = kmlDoc.createElementNS('http://earth.google.com/kml/2.2', 'kml')
kmlElement.setAttribute('xmlns','http://earth.google.com/kml/2.2')
kmlElement = kmlDoc.appendChild(kmlElement)
documentElement = kmlDoc.createElement('Document')
documentElement = kmlElement.appendChild(documentElement)
# Skip the header line.
csvReader.next()
for row in csvReader:
placemarkElement = createPlacemark(kmlDoc, row, order)
documentElement.appendChild(placemarkElement)
kmlFile = open(fileName, 'w')
kmlFile.write(kmlDoc.toprettyxml(' ', newl = '\n', encoding = 'utf-8'))
def main():
# This reader opens up 'google-addresses.csv', which should be replaced with your own.
# It creates a KML file called 'google.kml'.
# If an argument was passed to the script, it splits the argument on a comma
# and uses the resulting list to specify an order for when columns get added.
# Otherwise, it defaults to the order used in the sample.
if len(sys.argv) >1: order = sys.argv[1].split(',')
else: order = ['Office','Address1','Address2','Address3','City','State','Zip','Phone','Fax']
csvreader = csv.DictReader(open('google-addresses.csv'),order)
kml = createKML(csvreader, 'google-addresses.kml', order)
if __name__ == '__main__':
main()
已创建 KML 示例
下面显示的是此脚本创建的 KML 示例。
请注意某些 <value>
元素中只有空格。这是因为该字段中没有任何数据您也可以点击此处下载完整示例。
<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
<Document>
<Placemark>
<ExtendedData>
<Data name="Office">
<value>
Headquarters
</value>
</Data>
<Data name="Address1">
<value>
1600 Amphitheater Parkway
</value>
</Data>
<Data name="City">
<value>
Mountain View
</value>
</Data>
<Data name="State">
<value>
CA
</value>
</Data>
<Data name="Zip">
<value>
94043
</value>
</Data>
<Data name="Phone">
<value>
650-253-0000
</value>
</Data>
<Data name="Fax">
<value>
650-253-0001
</value>
</Data>
</ExtendedData>
<Point>
<coordinates>
-122.081783,37.423111
</coordinates>
</Point>
</Placemark>
...
屏幕截图
以下屏幕截图显示的是该 KML 文件在 Google 地球中的样子。
由于每个 <Placemark>
元素都没有 <BalloonStyle><text>
和 <description>
元素,因此提示框会默认为使用 <Data>
元素进行绘制。
地理编码注意事项
“在 KML 中使用地理编码地址”中提到了这一点,但需要反复说明。地理编码请求将受到地理编码器的最大查询率和每天 15000 次查询(基于您的 IP)的约束。此外,如果地理编码器查询速度超出其处理速度,便会返回状态代码 620
。(此处提供了状态代码的完整列表)。为了确保向地理编码器发送查询时不会过快,您可以指定每个地理编码请求之间的延迟时间。每次收到 620
状态时,您可以增加此延迟时间,并使用 while
循环来确保在对下一个地址进行地理编码之前已成功对某个地址进行了地理编码。这意味着,如果您的 CSV 文件非常大,您可能需要修改地理编码代码,或者跟踪地标的创建速度,如果速度太快,则可以跟踪其创建速度。
总结
现在,您可以使用 Python 根据 CSV 文件创建 KML 文件。使用提供的代码,KML 文件只能在 Google 地球中工作。您可以使用 <description>
(而非 <ExtendedData>
)对其进行修改,使其同时适用于 Google 地图和 Google 地球。此外,您还可以轻松地将此代码示例转换为提供 XML 支持的任何其他编程语言。
现在,您已将所有 CSV 文件转换为 KML 文件,您可能需要查看其他 KML 文章,例如使用 PHP 和 MySQL 创建 KML 以及扩展数据中的 Google 开发者指南文章添加自定义数据。