レベル: 中級 Jake Miles, Senior Developer, Conde Nast
2008年 9月 02日 Google のジオコーダ Web サービスについて調べましょう。Google のジオコーダは、住所を入力すると、その住所に関するデータを経度と緯度を含めて返します。この 2 回シリーズの記事では、そのデータを Google Maps API そして XSLT と組み合わせて Google マップや Google Earth にオーバーレイするためのデータを作成します。そのための例として不動産仲介業用のサンプル・アプリケーションを作成します。このアプリケーションでは、不動産仲介業者が HTML フォームから部屋の賃貸情報を入力すると、Google のジオコーダ Web サービスを使ってそれらの住所を経度と緯度に変換し、部屋の賃貸情報のデータベースから KML (Keyhole Markup Language) によるオーバーレイを作成します。第 1 回では、このアプリケーションの前半部分を作成します。つまりユーザーから部屋の賃貸情報を収集し、Google のジオコーダ Web サービスを使って住所を地理座標 (経度と緯度) に変換し、これらの座標を住所の情報と共にデータベースに保存します。
Google マップと Google Earth、そしてジオコーダ
 |
頻繁に使用する頭字語
- API: application programming interface
- HTML: Hypertext Markup Language
- HTTP: Hypertext Transfer Protocol
- KML: Keyhole Markup Language
- URL: Uniform Resource Locator
- XML: Extensible Markup Language
- XSLT: Extensible Stylesheet Transformations
|
|
Google マップは Web 上で普通に目にする地図技術となりました。Google マップでは、ある場所の地図を瞬時に表示することができ、その場所の周囲をパンしたりズームしたりすることができ、さらにはその場所を人間の目の高さで 360 度旋回して見ることもできます。Google Earth は地球上のさまざまな姿を 3D の写真で提供し、さまざまな高度で地球上の画像をパンしたりズームしたりすることができます。Google Maps API を利用すると、独自の Web ページに Google マップを埋め込むことができます。さらに Google Earth 上の座標などの地理情報を記述するために使われる XML 言語である KML を追加すると、その埋め込んだ Google マップ上に独自の視覚データやテキスト・データをオーバーレイすることができます。また KML データを Google Earth にインポートすれば、ユーザーによるパン操作やズーム操作に合わせて独自の 3D データを Google Earth に投影することもできます。
例えば Nine Inch Nails というアーティストは最新のアルバム「The Slip」をダウンロードでリリースしましたが、彼らは Google Earth と KML を使って場所ごとのダウンロード数のデータを公開しています (図 1)。
図 1. Nine Inch Nails のアルバム「The Slip」のダウンロード・データを Google Earth と KML を使って表示したもの
こうした方法で情報を公開するという素晴らしいコンセプトによって、Google の API と KML によってどんなことが可能になるのかがわかります。それぞれの棒の高さはその場所で記録されたダウンロード数を表しています。この棒を作成するには、(おそらく) そのダウンロードが行われた場所の経度と緯度、そして (最初は 0 として) その場所でのダウンロード回数に比例した高さを記述した KML による 1 行を作成しているはずです。
この視覚表現には非常に重要な詳細情報が 1 つ欠けており、ユーザーの住所、あるいは少なくとも郵便番号を Google Earth 上の地理座標に対応させる、ということができません。これはカスタムの KML データはすべて、経度と緯度、高度という座標を使って Google Earth 上に配置されるためです。
この問題を解決するために、Google は最近、ジオコーダという Web サービスを公開しました。このサービスは、住所を入力すると、(その住所の緯度と経度を含めて) その住所を可能な限り高い精度で表す KML データを返します。これらの座標を取得できさえすれば、ユーザーの想像力の許す限り任意のテキスト・データや視覚データを 2D の地図や 3D で現した地球の上にオーバーレイすることができます。
Google Maps API とジオコーダ・サービスを使い始めるための手順
Google Maps API と Google のジオコーダ Web サービスを使うためには、まず Google Maps API のキーにサインアップし (「参考文献」にリンクがあります)、API リクエストを発行する Web サイトの URL を指定する必要があります (サインアップできるキーと URL の数に制限はありません)。作成するページには、API キーと、Google マップを表示するために必要な JavaScript を含む HTML のスターター・ページを用意することになります。この JavaScript の中心となるのは、そのページがロードされると呼び出される JavaScript のload() 関数です (リスト 1)。
リスト 1. ページの中に Google マップを表示するための JavaScript 関数
function load() {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
}
}
|
この関数では、ブラウザーが Google マップを表示できることを確認してからマップ・オブジェクト (Gmap2) を作成し、ページ内で Google マップのコンテナーとして使用する HTML 要素 ("map") をこのオブジェクトに提供しています。さらに、このオブジェクトの中心を (GLatLng (緯度/経度) パラメーター・オブジェクトを使って) Google Earth 上のある座標に設定し、ズーム深さ (高さ) を13 に指定しています。図 2 は作成された地図を示しています。
図 2. スターター・ページによって表示された Google マップ
KML を使ってオーバーレイ・データを提供する
JavaScript による Google Maps API を使うことで、カスタム・データをオーバーレイすることができます。例えば、さまざまな場所にいくつかのブックマークを作成したり、あるいは色付きのポリゴンや直線をいくつかオーバーレイしたりすることができます。しかしもっと複雑なデータ (つまりもっと多くのデータ・ポイントを含むデータなど) の場合には、KML 文書を使う必要があります。KML 文書は、住所や Google Earth 上の 3D 座標、カスタムのテキスト・データや視覚データなど、Google Earth の地図や 3D モデルにオーバーレイされる位置情報データを指定することができます。
Google マップに KML データを提供する場合には、その KML を取得するために Google のサーバーがアクセスできる URL を使用する必要があります。つまりサーバー・サイドの KML を提供する必要があります。この記事のシリーズでは、サーバー・サイドの XSLT を使って情報のデータベースを KML ファイルに変換し、このファイルを Google マップと Google Earth に提供してカスタムのデータを表示します。
ジオコーダのデータをデータベースに保存する
住所の経度と緯度は Google マップと Google Earth を使う場合には便利ですが、経度と緯度を取得するためには Google のジオコーダ Web サービスを呼び出す必要があります。この呼び出しには少し時間がかかるため、データベースに住所を保存する場合には、その住所の経度と緯度も一緒に保存した方が適切です。そうすればデータベースにクエリーを実行して XML を直接生成することができ、XSLT を使って戻りデータを直接 KML に変換し、この KML を地図にオーバーレイすることができます。
これを実現するためにジオコーダ・サービスを呼び出す方法として、次の 2 つの選択肢があります。
- ジオコーダのサーバー・サイドを呼び出してから情報をデータベースに記録する方法
- Google が提供する JavaScript ライブラリーを使ってジオコーダを呼び出し、それからユーザーの住所情報を含むフォームを送信する方法
PHP からジオコーダを呼び出す
最初に PHP からジオコーダを呼び出し、その結果を SimpleXML モジュールを使ってトラバースします。不動産仲介業用に部屋の賃貸情報を記録するためのリクエスト・パラメーターを引数に取る recordListing() 関数では、住所を提供してジオコーダ・サービスを呼び出し、その結果得られた郵便番号と地理座標を引数のリクエスト・パラメーターと併せてデータベースに記録します。
リスト 2. recordListing() 関数 (PHP)
function recordListing($address, $aptNumber, $city, $state,
$aptType, $rent, $notes) {
$geocoder = new GoogleGeocoder(GOOGLE_MAPS_API_KEY);
$result = $geocoder->fetchAddress($address, $city, $state);
// use the geocoder to make sure the address is accurate enough to use
if ($result->getAccuracy() < GoogleGeocoderAccuracies::ADDRESS) {
throw new Exception ("Address does not have enough accuracy to record the listing.");
}
// store in the database
createListingInDb ($address, $aptNumber, $city, $state,
$result->getZipcode(),
$result->getLongitude(),
$result->getLatitude(),
$aptType, $rent, $notes);
}
|
リスト 2 のコードは、ジオコーダによる結果情報の別の使い方、つまりデータの完全性をチェックするための使い方を示しています。ジオコーダの結果には、入力された情報がどの程度詳細か、その程度を表す Accuracy レーティングが含まれています。ここでは、recordListing() がこの入力情報の詳細さの尺度 (下記のリスト 4 で詳細に説明します) を利用することで、その情報がデータベースに記録する上で十分詳細であるかを確認しています。
GoogleGeocoder クラス
コードを再利用可能なものにするために、Google のジオコーダへの呼び出しを GoogleGeocoder クラスの中にカプセル化します (リスト 3)。
リスト 3. GoogleGeocoder クラス (PHP)
class GoogleGeocoder {
private $apiKey;
private $googleGeocoderUrl = 'http://maps.google.com/maps/geo?';
public function __construct ($apiKey) {
$this->apiKey = $apiKey;
}
public function fetchAddress ($address, $city, $state) {
// encode address for google api call (plusses, commas)
$fullAddress = $this->encodeAddress ($address, $city, $state);
// create the geocoder API call
$geocoderCall =
$this->googleGeocoderUrl .
"q=$fullAddress" .
"&key=$this->apiKey" .
"&output=xml";
$result = file_get_contents ($geocoderCall);
return new GoogleGeocoderResult($result);
}
private function encodeAddress($address, $city, $state) {
return urlencode (join (", ", array ($address, $city, $state)));
}
}
|
GoogleGeocoder クラスは指定された住所を使って HTTP 経由でジオコーダ・サービスを呼び出します。このクラスは Google Maps API のキーを、HTTP 呼び出しに使用するコンストラクターの中で取得します。fetchAddress() はまず、呼び出しに適した形に住所をエンコードし、次にその呼び出しの URL を作成します (q パラメーターに住所を、key パラメーターに Google Maps API のキーを指定し、さらに output=xml を指定します)。この場合の他の出力オプションには json と csv があります。必要なものが経度と緯度のみであれば、csv は単純化されたカンマ区切りの応答を返します。最後に、この関数は file_get_contents() を使って URL を呼び出します。file_get_contents() は、サポートされているプロトコル (HTTP など) で始まるストリングが提供されていれば、URL をファイルのように読み取ります。
GoogleGeocoder サービスのレスポンス KML
GoogleGeocoder サービスは、その住所を記述する KML を返します。例えばこのサービスを「123 E. 34th St., New York, NY」という住所に対して呼び出すと、リスト 4 の XML が返されます。
リスト 4. 「123 E. 34th St., New York, NY」という住所に対するジオコーダのレスポンス XML
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.0">
<Response>
<name>123 E. 34th St., New York, NY</name>
<Status>
<code>200</code>
<request>geocode</request>
</Status>
<Placemark id="p1">
<address>123 E 34th St, New York, NY 10016, USA</address>
<AddressDetails Accuracy="8"
xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0">
<Country>
<CountryNameCode>US</CountryNameCode>
<AdministrativeArea>
<AdministrativeAreaName>NY</AdministrativeAreaName>
<SubAdministrativeArea>
<SubAdministrativeAreaName>Manhattan<
/SubAdministrativeAreaName>
<Locality>
<LocalityName>New York</LocalityName>
<Thoroughfare>
<ThoroughfareName>123 E 34th St<
/ThoroughfareName>
</Thoroughfare>
<PostalCode>
<PostalCodeNumber>10016<
/PostalCodeNumber>
</PostalCode>
</Locality>
</SubAdministrativeArea>
</AdministrativeArea>
</Country>
</AddressDetails>
<Point>
<coordinates>-73.980182,40.746595,0</coordinates>
</Point>
</Placemark>
</Response>
</kml>
|
このレスポンスには、成功 (コード 200)、あるいはいくつかのエラー・コードのうちの 1 つをレポートする Status が含まれています (「参考文献」にはジオコーダ・サービスのページにあるエラー・コード・テーブルへのリンクを挙げてあります)。Placemark 要素は呼び出しの中で指定される住所を表し、その住所を、世界中の住所を表現できる XAL (eXtensible Address Language) に従って構造化した表現で提供します。(XAL はさまざまな国々の多種多様な住所構造を処理しなければならないため、XAL による住所の構造は非常に風変わりなものに見えるかもしれません。)
AddressDetails 要素には、その XML がどの程度詳細に Google Earth 上で位置を表すかを示す Accuracy 属性も含まれています。例えば「New York, NY (ニューヨーク州ニューヨーク市)」のみを指定したとすると、Accuracy レーティングは低くなります。これはジオコーダが処理できる情報が少なく、その住所に関する詳細な情報 (郵便番号など) を提供できないためです。こうした場合には、ニューヨーク州内のある地点における正しい経度と緯度を提供することはできますが、それ以上詳細な情報を提供することはできません。Accuracy は結果に対する評価や信頼性の度合いではなく、その入力がどの程度詳細かを示す尺度なのです。
この XML レスポンスを容易に使用できるように、GoogleGeocoder は最終的に、XML のトラバースをカプセル化する GoogleGeocoderResult オブジェクトを作成します (リスト 5)。
リスト 5. GoogleGeocoderResult クラス (PHP)
class GoogleGeocoderResult {
public function __construct ($xml) {
$this->xml = new SimpleXMLElement($xml);
$this->resultCode = intval($this->xml->Response->Status->code);
if ($this->resultCode != 200) {
throw new GoogleGeocoderException ($this->getResultCode());
}
$this->placemark = $this->xml->Response->Placemark[0];
$this->accuracy = intval($this->placemark->AddressDetails['Accuracy']);
$coordinates = $this->placemark->Point->coordinates;
$coordinatesSplit = explode(",", $coordinates);
$this->longitude = $coordinatesSplit[0];
$this->latitude = $coordinatesSplit[1];
$this->altitude = $coordinatesSplit[2];
}
public function getZipcode() {
if ($this->accuracy < GoogleGeocoderAccuracies::POST_CODE) {
throw new Exception ("Address does not have enough accuracy for zipcode.");
}
return $this->placemark->AddressDetails->Country->AdministrativeArea->
SubAdministrativeArea->Locality->PostalCode->PostalCodeNumber;
}
|
GoogleGeocoderResult は、XML の構造に非常に容易にアクセスできるように、PHP の SimpleXML モジュールを使用します。SimpleXML は XML 文書の要素ツリーを PHP のオブジェクト・ツリーにマッピングし、サブ要素をプロパティーとして表現します。SimpleXMLElement は XML のストリングを取得し、その XML サブ要素を実質的に SimpleXMLElement オブジェクトの PHP プロパティーに変換します。対象の XML 要素が属性を含む場合には、PHP の連想配列 (配列の各キーが XML 要素の属性を表します) として SimpleXMLElement にアクセスすることもできます。
まず、GoogleGeocoderResult コンストラクターが以下のように SimpleXMLElement からレスポンスのステータス・コードを取得します。
$this->resultCode = intval($this->xml->Response->Status->code);
|
このコードの $this->xml の部分は、XML 文書から作成された SimpleXMLElement です。この要素の Response プロパティーにアクセスすると、この要素は KML レスポンスの中で Response 要素を表す SimpleXMLElement オブジェクトを返します (最上位レベルの SimpleXMLElement は XML ツリーの中で最上位レベルの KML 要素を表します)。ある要素が、同じ名前を持つサブ要素を複数持っている場合には、これらのサブ要素は配列のプロパティーに変換されます。例えばジオコーダのレスポンスが State 要素の中の複数のコードを返したとすると、最初のコードにアクセスするためには $this->xml->Response->Status->code[0] を使います。SimpleXMLElement をそのままの形で PHP ストリングに変換することもできるため、コード要素を直接 intval() に渡して数字に変換することができ、あるメソッドをそのコード要素に対して呼び出してテキスト値を取得する必要がありません。
このコンストラクターはレスポンス・コードを取得した後、そのレスポンス・コードが 200 ではない場合には、ジオコーダのエラー・コードを適切なエラー・メッセージに対応させる GoogleGeocoderException (サンプル・コードを参照) をスローします (エラー・メッセージは Google のサイトの参照テーブルから直接コピーしたものです)。
次にこのコンストラクターは、Google Earth 上で指定された場所を表現する Placemark 要素を (SimpleXMLElement オブジェクトとして) 保持します。必要な他のすべての情報は、この要素の配下にある要素から得られます。ここでは配列を使って Placemark にアクセスしていることに注目してください。ジオコーダのレスポンスは指定された住所に対する複数の Placemark を含んでいるかもしれませんが、SimpleXML はすべての要素に対して配列を使ってアクセスします。これは、その要素を配列と解釈するのか 1 つのオブジェクトと解釈するのか、SimpleXML が単に XML から判断することはできないためです。
次にこのコンストラクターは、レスポンスの Accuracy を取得します (AddressDetails 要素の Accuracy 属性を取得します)。
$this->accuracy = intval($this->placemark->AddressDetails['Accuracy']);
|
SimpleXMLElement によって、属性の値を SimpleXMLElement そのものの連想配列プロパティーとして利用できるようになります。つまり SimpleXMLElement は実質的に、ストリングであると同時に連想配列でもあり、そして同時に SimpleXMLElement オブジェクトでもあるということになります。そのため、こうしたスマートな構文で XML ツリーをトラバースできるのです。次のこのコンストラクターは、その位置の経度と緯度の座標を取得します。この座標は結果の中で、経度、緯度、高度の順に<coordinates>73.980182,40.746595,0</coordinates> のように現れます (住所を参照する場合には高度は常にゼロです)。
getZipcode() は郵便番号を取得するために、まずそのレスポンスが郵便番号を含めるほど十分に正確かどうかを GoogleGeocoderAccuracies クラスを使って確認します。(このクラスは Googleの参考資料 (「参考文献」を参照) にある Accuracy の値それぞれに対する定数を含んでいます)。
JavaScript からジオコーダを呼び出す
PHP からジオコーダを呼び出す代わりに、Google Maps API のライブラリーの中に用意されている GClientGeocoder という JavaScript を使ってジオコーダを呼び出すことができます。そしてレスポンス情報を使ってフォームの中に隠し入力を設定してからフォームをサーバーに送信します。リスト 6 はこれを行うための方法を示しています。
リスト 6. JavaScript からジオコーダを呼び出す
function onSubmitAddressForm() {
var addressInput = document.getElementById('addressInput');
var cityInput = document.getElementById('cityInput');
var stateInput = document.getElementById('stateInput');
var fullAddress = addressInput.value + ", " + cityInput.value + ", "
+ stateInput.value;
var geocoder = new GClientGeocoder();
geocoder.getLocations(fullAddress, submitAddressFromGeocoderResponse);
}
|
この方法は、add-address.php (「ダウンロード」を参照) の中にある HTML フォーム (送信用の address (番地)、city (市町村)、そして state (州) のテキスト入力を含んでいます) と、<button onclick="onSubmitAddressForm()">Submit</button> のようなボタンを使っています (このボタンがクリックされるとの onSubmitAddressForm() 関数が呼び出され、フォームの送信を処理します)。onSubmitAddressForm() 関数はフォームの要素から建物の address と city、state を取得し、それらを使って完全な住所を作成し、新しい GClientGeocoder オブジェクトを作成し、そしてこのオブジェクトの getLocations() メソッドを呼び出し、参照用の完全なアドレスと呼び出し対象の callback 関数をレスポンス・オブジェクトと共に渡します (リスト 7)。
リスト 7. JavaScript でジオコーダの callback レスポンスを使う
function submitAddressFromGeocoderResponse(response) {
if (! response || response.Status.code != 200) {
alert("Geocoder did not recognize address. Code = " + response.Status.code);
} else {
var coordinates = response.Placemark[0].Point.coordinates;
var longitude = coordinates[0];
var latitude = coordinates[1];
document.getElementById('longitudeInput').value = longitude;
document.getElementById('latitudeInput').value = latitude;
document.getElementById('addressForm').submit();
}
}
|
GClientGeocoder はジオコーダ・サービスのレスポンスを受信すると、submitAddressFromGeocoderResponse 関数を呼び出します。このレスポンスは JSON (JavaScript Object Notation) フォーマットです (JSON フォーマットについては「参考文献」を参照)。JSON は JavaScript から階層構造のデータを取得する際に XML に代わるものとして、次第に好まれるようになりつつあります。これは JSON が、先ほど示した XML のレスポンス・データに対応した同じ構造を持つ JavaScript オブジェクト・ツリーを、簡潔かつ人間が読み取れる形で明確に表現できるためです。JSON によるレスポンスの構造の例は Google の参考資料の中にあります (「参考文献」を参照)。1 つの住所に複数の Placemark が対応する場合があるため、Placemark 要素が実際には Placemark の配列であることに注意してください。
SubmitAddressFromGeocoderResponse は PHP コードの場合と同じようにレスポンスのステータス・コードをチェックし、SimpleXML を使った場合と同じオブジェクト構造を使って地理座標の値を取得します。次にそのページのフォームに隠し入力の値を設定し、そのフォームをプログラムによって送信し、データベースに保存します。
まとめ
これで、このアプリケーションの最初の部分は完成しました。ジオコーダ・サービスを呼び出し、地理座標の情報をデータベースに保存しました。第 2 回では、ストアード・プロシージャーを使い MySQL へのクエリーによって XML データを生成し、そのデータを XSLT を使って KML のオーバーレイ・データに変換し、そして Google Maps API を使って、その KML を Web サイトに埋め込まれた Google マップ上に表示します。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Part 1 example source code | google-maps-series-code.tar | 100KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
- developerWorks から直接ダウンロードできる IBM trial software を利用して皆さんの次期プロジェクトを構築してください。これらの試用版には DB2® や Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品が含まれています。
議論するために
著者について  | 
|  | Jake Miles は Conde Nast のシニア開発者であり、現在は Java、PHP、Adobe Flex、そして JavaScript を使って Facebook と Myspace、そして OpenSocial のアプリケーションに関する作業に従事しています。彼はプロの開発者として 10 年間働いており、しかも 10 歳の時から貪欲な学生であり、さまざまなものをいじってきました。彼はボランティアの教師もしています。 |
記事の評価
|