KML で使用する住所のジオコーディング

Mano Marks、Google Geo チーム
作成: 2007 年 12 月
更新: 2013 年 12 月

目標

このチュートリアルは、スクリプト言語に精通しており、Google Geocoding API を使用して住所をジオコーディングし、KML ファイルに組み込む方法を学びたいデベロッパーを対象としています。コードサンプルは Python で示されていますが、他のほとんどのプログラミング言語に簡単に適用できます。

ジオコーディングとは、住所を緯度と経度の座標のセットに変換するプロセスです。これにより、地図上に住所を表示できるようになります。住所をジオコーディングして KML ファイルに直接配置したい場合があります。たとえば、フォームにデータが入力され、リクエストに応じて KML ファイルを生成する場合などによくあります。これらの KML ファイルは、データベース、ファイル システムに保存するか、ファイルに接続する NetworkLink に返します。この手法を使用する際は、Geocoding API の利用規約を遵守する必要があります。結果を保存できる期間や、1 日にジオコーディングできる要素の数には制限があるためです。

このチュートリアルでは、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 に送信します。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')

その他の考慮事項

ジオコード化リクエストのタイミング

ジオコーディング リクエストには、ジオコーダの 1 日あたりの最大クエリレートの上限が適用されます。これらの上限の詳細については、Google Geocoding API のドキュメントをご覧ください。ジオコーダにクエリを送信する速度が速すぎないようにするには、各ジオコード リクエスト間の遅延を指定します。OVER_QUERY_LIMIT ステータスを受け取るたびにこの遅延を増やし、while ループを使用して、次のアドレスに反復処理を行う前にアドレスのジオコーディングが正常に完了したことを確認できます。

ベースの国を変更する

ジオコーダは、元のドメインに応じて結果をバイアスするようにプログラムされています。たとえば、maps.google.com の検索ボックスに「syracuse」と入力すると、ニューヨーク州の「Syracuse」という都市がジオコーディングされますが、maps.google.it(イタリアのドメイン)で同じクエリを入力すると、シチリア島の「Siracusa」という都市が検索されます。上記のサンプルコードの mapsUrl 変数を変更することで、maps.google.com ではなく maps.google.it に HTTP ジオコーディングでクエリを送信しても、同じ結果が得られます。地域バイアスの詳細については、Geocoding API のドキュメントをご覧ください。

注: 存在しない maps.google.* サーバーにリクエストを送信することはできません。そのため、ジオコーディング クエリをリダイレクトする前に、国別ドメインが存在することを確認してください。国別のジオコードのサポートについては、こちらの投稿をご覧ください。

まとめ

上記のコードを使用すると、Python を使用して住所をジオコーディングし、そこから KML <Placemark> を作成して、ディスクに保存できます。1 日にジオコーディングする必要がある住所の数が上限を超えている場合や、Google ジオコーダが対象とする地域が目的の地域と異なる場合は、追加のジオコーディング ウェブサービスの利用をご検討ください。

住所をジオコーディングする方法を理解したら、Google Mashup Editor で KML を使用するPHP と MySQL を使用して KML を作成するの記事をご覧ください。このチュートリアルについてご不明な点や問題がございましたら、Stack Overflow フォーラムに投稿してください。