為 KML 使用的地址進行地理編碼

Google 地理位置團隊 Mano Marks
撰寫日期: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 Geocoder,後者會以 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」這個城市。如果透過 HTTP 地理編碼將該查詢傳送至 maps.google.it,而非 maps.google.com,您會得到相同的結果。如要這麼做,請修改上述範例程式碼中的 mapsUrl 變數。如要進一步瞭解區域偏向,請參閱 Geocoding API 說明文件。

注意:您無法將要求傳送至不存在的 maps.google.* 伺服器,因此請務必先確認國家/地區網域存在,再將地理編碼查詢重新導向至該網域。如要瞭解各國家/地區的地理編碼支援情形,請參閱這篇文章

結論

使用上述程式碼,您現在可以透過 Python 對地址進行地理編碼、從中建立 KML <Placemark>,並儲存至磁碟。如果發現每天需要地理編碼的地址數量超過限制,或是 Google 地理編碼器未涵蓋您感興趣的區域,請考慮使用其他地理編碼網路服務。

現在您已瞭解如何將地址轉換為地理編碼,請參閱「在 Google Mashup Editor 中使用 KML」和「使用 PHP 和 MySQL 建立 KML」一文。如果您對本教學課程有任何問題,請前往 Stack Overflow 論壇提問。