サンプル・プログラムの実行例
サンプル・プログラムのダウンロード
address2geo | サンプル・プログラム本体。 |
pahooGeoCode.js | 住所・緯度・経度に関わるクラス pahooGeoCode。 |
サンプル・プログラムの流れ
続いて、2つのボタン押下によって、各々 exec, clear のアクションを発生する。
execは、キーワードから緯度・経度を求めるもので、詳細は後述する。
clearは、初期化してからexecと同じ処理を行う。
Google Geocoding API による緯度・経度変換
URL |
---|
https://maps.google.com/maps/api/geocode/json |
フィールド名 | 要否 | 内 容 |
---|---|---|
key | 必須 | Google APIを利用するためのアプリケーション・キー(後述) |
address | 必須 | 検索キーワード。住所、駅名、ランドマークなど(UTF-8) |
language | 省略可能 | 使用言語。【例】ja |
region | 省略可能 | 地域コード。【例】JP |
bounds | 省略可能 | 境界ボックスの南西と北東の角の緯度・経度で指定。【例】34.172684,-118.604794|34.236144,-118.500938 |
フィールド名 | 内 容 | |||
---|---|---|---|---|
results[] | address_components[] | long_name | 住所 | |
short_name | 住所(短縮) | |||
types[] | 住所の種類 | |||
formatted_address | 住所(宛先表記) | |||
geometry | location | lat | 緯度 | |
lng | 経度 | |||
location_type | 取得結果の精度 | |||
viewport | southwest | ビューポートの南西の座標 | ||
northeast | ビューポートの北東の座標 | |||
bounds | southwest | 境界ボックスの南西の座標 | ||
northeast | 境界ボックスの北東の座標 | |||
place_id | Place ID |
準備、クラスについて
「Google Maps Platform」にアクセスし、「使ってみる」ボタンをクリックすると無料で取得できる。
0014: class pahooGeoCode {
0015:
0016: /**
0017: * コンストラクタ
0018: * @param Function callbackコールバック関数
0019: * @returnなし
0020: */
0021: constructor(callback) {
0022: this.items = new Array(); //検索結果格納用
0023: this.pp = new Object(); //各種パラメータ格納用
0024: this.pp.error = false; //エラーフラグ
0025: this.pp.errmsg = ''; //エラーメッセージ
0026: this.pp.hits = 0; //検索ヒット件数
0027: this.pp.webapi = '';
0028:
0029: //Google API KEY
0030: //https://cloud.google.com/maps-platform/
0031: this.GOOGLE_API_KEY = '***********************************';
0032:
0033: //Google Maps APIのスクリプト読み込み
0034: var key = this.GOOGLE_API_KEY;
0035: var fn = (typeof callback != 'undefined') ? '&callback=' + callback : '';
0036: var url = 'https://maps.googleapis.com/maps/api/js?key=' + key + fn + '®ion=JP';
0037: var script = document.createElement('script');
0038: script.src = url;
0039: script.async = true;
0040: document.head.appendChild(script);
0041: }
先ほど取得したアプリケーションキーをメンバ変数 GOOGLE_API_KEY に記入する。
クラス pahooGeoCode が GoogleMaps API を呼び出すとき、このアプリケーションを使う。
メンバ変数を用いるときは、このようにコンストラクタの中で初期化しておく。
後半は、GoogleMaps API のスクリプトを動的に読み込む処理である。コンストラクタに引数 callback が渡されていれば、これをコールバック関数としてAPIを呼び出す。
解説:Google Geocoding API
0044: /**
0045: * Google Geocoding API(V3) のURLを取得する(JSON)
0046: * @param String query検索キーワード(UTF-8)
0047: * @return String URL呼び出しURL
0048: */
0049: getURL_GeoCodeAPI_V3(query) {
0050: var key = this.GOOGLE_API_KEY;
0051: var url = 'https://maps.googleapis.com/maps/api/geocode/json?key=' + key + '&language=ja®ion=JP&address=' + encodeURI(query);
0052: return url;
0053: }
0054:
0055: /**
0056: * Google Geocoding API(V3) を用いて住所・駅名の緯度・経度を求める
0057: * @param String query 検索キーワード
0058: * @param Array items[] {latitude:緯度, longitude:経度, address:住所}
0059: * @param Object pp{error:エラーフラグ,errmsg:エラーメッセージ,
0060: * hits:ヒット件数,webapi:WebAPIのURL}
0061: * @param Function callback コールバック関数(非同期処理用)
0062: * @returnなし
0063: */
0064: getPointsV3_all(query, items, pp, callback) {
0065: var url = this.getURL_GeoCodeAPI_V3(query); //リクエストURL
0066: pp.webapi = url;
0067: var n = 0;
0068: var obj = new Object();
0069:
0070: //JSONデータ取得
0071: var req = new XMLHttpRequest();
0072: req.onreadystatechange = function() {
0073: if (req.readyState == 4 && req.status == 200) {
0074: pahooGeoCode.getPointsV3_callback(req.responseText, items, pp, callback);
0075: }
0076: };
0077: try {
0078: req.open('GET', url, true);
0079: req.send(null);
0080: } catch(e) {
0081: pp.error = true;
0082: pp.errmsg = 'エラー:Google Geocoding APIが呼び出せない.';
0083: pp.hits = 0;
0084: pahooGeoCode.getPointsV3_callback(req.responseText, items, pp, callback);
0085: }
0086: }
0087:
0088: /**
0089: * Google Geocoding API(V3) のコールバック関数(非同期処理用)
0090: * @param String results APIのリターン値
0091: * @param Array items[] {latitude:緯度, longitude:経度, address:住所}
0092: * @param Object pp{error:エラーフラグ,errmsg:エラーメッセージ,
0093: * hits:ヒット件数,webapi:WebAPIのURL}
0094: * @param Function callback コールバック関数(非同期処理用)
0095: * @returnなし
0096: */
0097: static getPointsV3_callback(results, items, pp, callback) {
0098: if (pp.error == false) {
0099: var n = 0;
0100: var json = JSON.parse(results);
0101: //レスポンス・チェック
0102: if (json.status.match(/ok/i) != null) {
0103: json.results.forEach(function(val) {
0104: var obj = new Object();
0105: obj.latitude = val.geometry.location.lat;
0106: obj.longitude = val.geometry.location.lng;
0107: obj.address = val.formatted_address;
0108: items.push(obj);
0109: n++;
0110: });
0111: pp.error = false;
0112: pp.errmsg = '';
0113: pp.hits = n;
0114: } else if (json.status.match(/ZERO_RESULTS/i) != null) {
0115: pp.error = true;
0116: pp.errmsg = 'エラー:結果がない.';
0117: pp.hits = 0;
0118: } else {
0119: pp.error = true;
0120: pp.errmsg = 'エラー:' + json.error_message;
0121: pp.hits = 0;
0122: }
0123: }
0124: callback(items, pp);
0125: }
メソッド getPointsV3_all は、Google Geocoding API の呼び出しを行う。呼び出しは XMLHttpRequest 関数によって行う。
XMLHttpRequest は非同期なデータ通信を実現するための関数である。非同期とはどういうことかというと、WebAPIが値を戻す間に、JavaScriptの処理は先へ進むということである。
WebAPIが何らかの反応を起こすと通信が発生し、onreadystatechange イベントが発生する。これをキャッチアップして、適切な処理を行うのがコールバック関数である。ここではメソッド getPointsV3_callback をコールバック関数としている。
逆に言うと、コールバック関数が呼び出されるまで、JSONには正しい値が入っていないと言うことである。
よって、JSONから必要なデータを取り込むのは、コールバック関数の方で行う。
さらに、getPointsV3_callback から引数で指定された callback 関数を呼び出す――ここでは、address2get.html の方に定義したユーザー関数 searchKeyword_callback をコールする――ことで、結果の一覧表作成とエラー表示までを行う。
なお、コールバック関数は“関数”でなければならないが、静的メソッドでも呼び出しが可能である。
ただし、静的メソッドであるため、ここからプロパティを参照できない。このため、データの受け渡しに、引数を使っている。
PHPは、原則としてシングルスレットであるから非同期であることをさほど意識しないで済むが、このようにJavaScriptでは非同期であることを意識し、イベント駆動式のコーディングを行わなければならない。
解説:Google Geocoding API(JS版)
0127: /**
0128: * Google Geocoding API(V3) を用いて住所・駅名の緯度・経度を求める(JS版)
0129: * @param String query 検索キーワード
0130: * @param Array items[] {latitude:緯度, longitude:経度, address:住所}
0131: * @param Object pp{error:エラーフラグ,errmsg:エラーメッセージ,
0132: * hits:ヒット件数,webapi:WebAPIのURL}
0133: * @param Function callback コールバック関数(非同期処理用)
0134: * @returnなし
0135: */
0136: getPointsV3_all_JS(query, items, pp, callback) {
0137: const geocoder = new google.maps.Geocoder();
0138: geocoder.geocode({
0139: 'address': query,
0140: 'region': 'jp'
0141: },
0142: function(results, status) {
0143: //成功
0144: if (status == google.maps.GeocoderStatus.OK) {
0145: pahooGeoCode.getPointsV3_callback(results, items, pp, callback);
0146: //エラー処理
0147: } else if (status == google.maps.GeocoderStatus.ZERO_RESULTS) {
0148: pp.error = true;
0149: pp.errmsg = '結果がない.';
0150: pahooGeoCode.getPointsV3_callback(results, items, pp, callback);
0151: } else {
0152: pp.error = true;
0153: pp.errmsg = 'API呼び出しでトラブルが起きた.';
0154: pahooGeoCode.getPointsV3_callback(results, items, pp, callback);
0155: }
0156: });
0157: }
0158:
0159: /**
0160: * Google Geocoding API(V3) のコールバック関数(JS版)
0161: * @param Array results[] APIのリターン値
0162: * @param Array items[] {latitude:緯度, longitude:経度, address:住所}
0163: * @param Object pp{error:エラーフラグ,errmsg:エラーメッセージ,
0164: * hits:ヒット件数,webapi:WebAPIのURL}
0165: * @param Function callback コールバック関数(非同期処理用)
0166: * @returnなし
0167: */
0168: static getPointsV3_callback_JS(results, items, pp, callback) {
0169: var n = 0;
0170: if (pp.error == false) {
0171: results.forEach(function(val) {
0172: var obj = new Object();
0173: obj.latitude = parseFloat(val.geometry.location.lat());
0174: obj.longitude = parseFloat(val.geometry.location.lng());
0175: obj.address = val.formatted_address;
0176: items.push(obj);
0177: n++;
0178: });
0179: //エラー処理
0180: if (n == 0) {
0181: pp.error = true;
0182: pp.errmsg = 'エラー:検索結果がない';
0183: pp.hits = 0;
0184: } else {
0185: pp.error = false;
0186: pp.errmsg = '';
0187: pp.hits = n;
0188: }
0189: }
0190: callback(items, pp);
0191: }
解説:住所から緯度・経度を求める
0193: /**
0194: * 緯度経度文字列を分解する
0195: * @param String str 緯度経度文字列
0196: * @return Object {longitude:緯度, latitude:経度}
0197: */
0198: parse_geo(str) {
0199: var re = /E(\d+)\.(\d+)\.(\d+)\.(\d+)N(\d+)\.(\d+)\.(\d+)\.(\d+)/i;
0200: var arr = str.match(re);
0201: for (var i = 1; i < parseInt(arr.length); i++) {
0202: arr[i] = parseInt(arr[i]);
0203: }
0204: var res = {
0205: longitude: arr[1] + arr[2] / 60 + arr[3] / 3600 + arr[4] / 36000,
0206: latitude: arr[5] + arr[6] / 60 + arr[7] / 3600 + arr[8] / 36000
0207: }
0208: return res;
0209: }
0210:
0211: /**
0212: * Google Geocoding API(V3) を用いて住所・駅名の緯度・経度を検索
0213: * @param String query検索キーワード(UTF-8)
0214: * @param Function callback コールバック関数
0215: * @returnなし
0216: */
0217: searchPoint(query, callback) {
0218: this.items = []; //結果を空にする
0219: this.hits = 0;
0220:
0221: //緯度・経度表記
0222: var re = /E(\d+)\.(\d+)\.(\d+)\.(\d+)N(\d+)\.(\d+)\.(\d+)\.(\d+)/i;
0223: if (query.match(re) != null) {
0224: var obj = new Object();
0225: var res = this.parse_geo(query);
0226: obj.latitude = parseFloat(res.latitude);
0227: obj.longitude = parseFloat(res.longitude);
0228: obj.address = '';
0229: this.items.push(obj);
0230: this.hits = 1;
0231: callback(this.items);
0232:
0233: //Google Geocoding API使用
0234: } else {
0235: this.getPointsV3_all(query, this.items, this.pp, callback); //検索実行
0236: }
0237: }
メソッド searchPoint が、Google Geocoding API を利用し、住所やランドマーク名から緯度・経度を求める。
解説:初期化処理
0011: <!DOCTYPE html>
0012: <html lang="ja">
0013: <head>
0014: <meta charset="UTF-8">
0015: <title></title>
0016: <meta name="author" content="studio pahoo" />
0017: <meta name="copyright" content="studio pahoo" />
0018: <meta name="ROBOTS" content="NOINDEX,NOFOLLOW" />
0019: <meta http-equiv="pragma" content="no-cache">
0020: <meta http-equiv="cache-control" content="no-cache">
0021: <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.js"></script>
0022: <script src="./pahooGeoCode.js"></script>
0023:
0024: <script>
0025: //初期設定
0026: $(function() {
0027: //プログラム・タイトル
0028: TITLE = '住所・ランドマークを緯度・経度に変換(JS版)';
0029:
0030: //初期値
0031: DEF_QUERY = '東京駅';
0032:
0033: //クラスpahooGeoCode
0034: PGC = new pahooGeoCode('searchKeyword');
0035:
0036: //画面の初期値設定
0037: var refere = 'https://www.pahoo.org/e-soul/webtech/js01/js01-09-01.shtm';
0038: document.title = TITLE;
0039: $('#title').html(TITLE + ' <span style="font-size:small;">' + getLastModified() + '版</span>');
0040: $('#refere').html('参考サイト:<a href="' + refere +'">' + refere + '</a>');
0041: init();
0042: });
0043:
Ajaxを使った初期設定では、デフォルト入力値 def_query、参照URL referer、タイトルなどを、各要素に代入する。
解説:住所・ランドマークを緯度・経度に変換
0065: /**
0066: * 住所・ランドマーク検索
0067: * @paramなし
0068: * @returnなし
0069: */
0070: function searchKeyword() {
0071: var query = String(document.getElementById('query').value);
0072: PGC.searchPoint(query, searchKeyword_callback);
0073: }
0074:
0075: /**
0076: * 住所・ランドマーク検索時のコールバック関数
0077: * @param Array items[] {latitude:緯度, longitude:経度, address:住所}
0078: * @param Object pp{error:エラーフラグ,errmsg:エラーメッセージ,
0079: * hits:ヒット件数,webapi:WebAPIのURL}
0080: * @returnなし
0081: */
0082: function searchKeyword_callback(items, pp) {
0083: if (PGC.pp.error == false) {
0084: $('#latitude').val(items[0].latitude);
0085: $('#longitude').val(items[0].longitude);
0086: makeTable(); //緯度・経度一覧
0087: }
0088: $('#errmsg').html(PGC.pp.errmsg);
0089: }
0090:
0091: /**
0092: * 緯度・経度一覧を作成する
0093: * @paramなし
0094: * @returnなし
0095: */
0096: function makeTable() {
0097: n = PGC.items.length;
0098: var html = `
0099: <table>
0100: <tr class="index">
0101: <th>No.</th>
0102: <th>住所</th>
0103: <th>緯度</th>
0104: <th>経度</th>
0105: </tr>
0106: `;
0107: for (var key = 0; key < n; key++) {
0108: var html = html + `
0109: <tr>
0110: <td class="number">${key + 1}</td>
0111: <td class="text">${PGC.items[key].address}</td>
0112: <td class="number">${PGC.items[key].latitude}</td>
0113: <td class="number">${PGC.items[key].longitude}</td>
0114: </tr>
0115: `;
0116: }
0117: htlm = html + "</table>\n";
0118: $('#results').html(html);
0119: }
コールバック関数 searchKeyword_callback は、APIから得られた緯度・経度をユーザー関数 makeTable によって一覧表表示する。
makeTable 関数では、PHPのヒアドキュメントに相当するテンプレート文字列が JavaScript ES6(ES2015) から利用できるようになったので、これを活用している。
参考サイト
- PHPでGoogle等を利用して住所から緯度・経度を求める:ぱふぅ家のホームページ
- ジオコーディング:GoogleマップAPIプログラミング解説
そこで今回は、「PHPでGoogle等を利用して住所から緯度・経度を求める」で紹介したPHPサーバサイド・プラグラムを、JavaScriptを使ってクライアント・サイドに移植してみる。