对地址进行地理编码,以便在 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 的极简性是 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, NY”这个城市进行地理编码,而对于 maps.google.it(意大利的网域)输入相同的查询,将查找西西里岛上的“Siracusa”。通过修改上述示例代码中的 mapsUrl 变量,通过 HTTP 地理编码将该查询发送到 maps.google.it(而非 maps.google.com)可以获得相同的结果。如需详细了解区域自定义调整,请参阅 Geocoding API 文档。

注意:您无法向不存在的 Maps.google.* 服务器发送请求,因此在将地理编码查询重定向到国家/地区网域之前,请确保该国家/地区存在。如需按国家/地区提供的地理编码支持,请查看这篇博文

总结

使用上面的代码,您现在可以使用 Python 对地址进行地理编码,根据它创建 KML <Placemark>,并将其保存到磁盘。如果您发现每天需要地理编码的地址数量超出限制,或者 Google 地理编码器未涵盖您感兴趣的区域,则可以考虑使用其他地理编码网络服务。

现在,您已了解如何对地址进行地理编码,请查看有关在 Google 混搭编辑器中使用 KML使用 PHP 和 MySQL 创建 KML 的文章。如果您对本教程有任何疑问或问题,请在 Stack Overflow 论坛中发帖。