对地址进行地理编码以在 KML 中使用

Mano Marks,Google 地理位置团队
撰写时间:2007 年 12 月
更新时间:2013 年 12 月

目标

本教程面向熟悉脚本语言的开发者,旨在帮助他们了解如何使用 Google Geocoding API 对地址进行地理编码,并将其纳入 KML 文件中。虽然代码示例以 Python 呈现,但可以相当轻松地将其调整为大多数其他编程语言。

地理编码是将地址转换为一组纬度/经度坐标的过程,这样便可在地图上指示地址。您可能需要对地址进行地理编码,然后将其直接放入 KML 文件中。例如,当您在表单中输入数据并生成 KML 文件以响应请求时,这种情况很常见。这些 KML 文件可以存储在数据库或文件系统中,也可以返回到连接到您的文件的 NetworkLink 中。请注意,使用此技术时,您必须遵守 Geocoding API 的服务条款,因为结果的存储时间以及每天可进行地理编码的元素数量都存在一些限制。

本教程将介绍如何使用 Python 将字符串“1600 Amphitheatre Pkwy, Mountain View, CA 94043”转换为以下内容:

<?xml version='1.0' encoding='UTF-8'?> 
<kml xmlns='http://earth.google.com/kml/2.2'>
<Document>
<Placemark>
<description>1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA</description>
<Point>
<coordinates>-122.081783,37.423111,0</coordinates>
</Point>
</Placemark>
</Document>
</kml>

创建 KML 文档

KML 是一种 XML 标记语言,因此我们可以使用 Python 的内置 xml.dom.minidom 函数来创建 KML 文档。Python 的 minidom 是 DOM 实现,而大多数编程语言都支持 DOM,因此此流程应可轻松移植到其他编程语言。具体步骤如下所示:

  1. 使用 Python 的 xml.dom.minidom.Document() 创建文档。
  2. 使用 createElementNS. 创建根 <kml> 元素
  3. 使用 appendChild 将其附加到文档。
  4. 使用 createElement 创建 Document 元素。
  5. 使用 appendChild 将其附加到 <kml> 元素。
  6. 针对每个地址,使用 createElement 创建一个 <Placemark> 元素,并将其附加到 Document 元素。然后,创建一个 <description> 元素,为其分配地址值,并将其附加到 <Placemark> 元素。
  7. 创建 <Point> 元素,添加子 <coordinates> 元素,并将其附加到 <Placemark> 元素。
  8. 将地址发送到 Maps API 地理编码器,该地理编码器会以 JSON 或 XML 格式发送响应。 使用 urllib.urlopen() 检索文件并将其读入字符串。
  9. 解析响应并提取经度和纬度元素。
  10. <coordinates> 元素中创建一个文本节点,并将经度/纬度字符串分配为该节点的值。
  11. 将 KML 文档写入文本文件。

Python 代码示例

请注意,以下示例代码使用的是虚拟 mapsKey 变量,您需要将此密钥替换为自己的密钥

以下是使用 Python 2.7 进行地理编码并输出 JSON 的示例代码:

import urllib
import xml.dom.minidom
import json 

def geocode(address, sensor=False):
 # This function queries the Google Maps API geocoder with an
 # address. It gets back a csv file, which it then parses and
 # returns a string with the longitude and latitude of the address.

 # This isn't an actual maps key, you'll have to get one yourself.
 # Sign up for one here: https://code.google.com/apis/console/
  mapsKey = 'abcdefgh'
  mapsUrl = 'https://maps.googleapis.com/maps/api/geocode/json?address='
     
 # This joins the parts of the URL together into one string.
  url = ''.join([mapsUrl,urllib.quote(address),'&sensor=',str(sensor).lower()])
#'&key=',mapsKey])
  jsonOutput = str(urllib.urlopen(url).read ()) # get the response 
  # fix the output so that the json.loads function will handle it correctly
  jsonOutput=jsonOutput.replace ("\\n", "")
  result = json.loads(jsonOutput) # converts jsonOutput into a dictionary 
  # check status is ok i.e. we have results (don't want to get exceptions)
  if result['status'] != "OK": 
    return ""
  coordinates=result['results'][0]['geometry']['location'] # extract the geometry 
  return str(coordinates['lat'])+','+str(coordinates['lng'])

def createKML(address, fileName):
 # This function creates an XML document and adds the necessary
 # KML elements.

  kmlDoc = xml.dom.minidom.Document()
  
  kmlElement = kmlDoc.createElementNS('http://earth.google.com/kml/2.2','kml')

  kmlElement = kmlDoc.appendChild(kmlElement)

  documentElement = kmlDoc.createElement('Document')
  documentElement = kmlElement.appendChild(documentElement)

  placemarkElement = kmlDoc.createElement('Placemark')
  
  descriptionElement = kmlDoc.createElement('description')
  descriptionText = kmlDoc.createTextNode(address)
  descriptionElement.appendChild(descriptionText)
  placemarkElement.appendChild(descriptionElement)
  pointElement = kmlDoc.createElement('Point')
  placemarkElement.appendChild(pointElement)
  coorElement = kmlDoc.createElement('coordinates')

  # This geocodes the address and adds it to a  element.
  coordinates = geocode(address)
  coorElement.appendChild(kmlDoc.createTextNode(coordinates))
  pointElement.appendChild(coorElement)

  documentElement.appendChild(placemarkElement)

  # This writes the KML Document to a file.
  kmlFile = open(fileName, 'w')
  kmlFile.write(kmlDoc.toprettyxml(' '))  
  kmlFile.close()

if __name__ == '__main__':
  createKML('1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA', 'google.kml')

其他注意事项

地址解析请求计时

地理编码请求将受地理编码器的每日最大查询速率限制。如需详细了解这些限制,请参阅 Google Geocoding API 文档。为确保您不会过于快速地向地理编码器发送查询,您可以在每个地理编码请求之间指定延迟时间。您可以在每次收到 OVER_QUERY_LIMIT 状态时增加此延迟时间,并使用 while 循环来确保您已成功对地址进行地理编码,然后再迭代到下一个地址。

更改基础国家/地区

地理编码器经过编程,可根据来源网域调整结果的偏差。 例如,在 maps.google.com 的搜索框中输入“syracuse”会地理编码纽约州的“Syracuse”市,而在 maps.google.it(意大利网域)中输入相同的查询内容会找到西西里岛的“Siracusa”市。如果您通过 HTTP 地理编码将该查询发送到 maps.google.it 而不是 maps.google.com,也会获得相同的结果。您可以通过修改上述示例代码中的 mapsUrl 变量来实现这一点。如需详细了解区域偏向,请参阅 Geocoding API 文档。

注意:您无法向不存在的 maps.google.* 服务器发送请求,因此请确保国家/地区网域存在,然后再将地理编码查询重定向到该网域。如需了解各个国家/地区对地理编码的支持情况,请参阅这篇帖子

总结

使用上述代码,您现在可以使用 Python 对地址进行地理编码,从中创建 KML <Placemark>,并将其保存到磁盘。如果您发现自己每天需要地理编码的地址数量超过了限制,或者 Google 地理编码器无法覆盖您感兴趣的区域,请考虑使用其他地理编码网络服务。

现在您已了解如何对地址进行地理编码,接下来可以查看以下文章:在 Google Mashup 编辑器中使用 KML使用 PHP 和 MySQL 创建 KML。如果您对此教程有任何问题或疑问,请在 Stack Overflow 论坛中发帖咨询。