PHPで住所・ランドマークから最寄り駅を求める

(1/1)
HeartRails Geo API」は、路線/駅名データ等の地理情報を無償の WebAPI として提供している。
これと、Google や Yahoo!JAPAN の地図サービスをマッシュアップすることで、住所やランドマークから最寄り駅を求める PHP プログラムを作ってみることにする。

(2019 年 5 月 8 日)地理院地図、OpenStreetMap も利用できるようにした。
(2019 年 3 月 16 日)Yahoo! JavaScript マップも利用できるようにした。

目次

サンプル・プログラムの実行例

PHPで住所・ランドマークから最寄り駅を求める
Google マップ表示

サンプル・プログラム

圧縮ファイルの内容
stationsearch.phpサンプル・プログラム本体。
pahooGeoCode.php住所・緯度・経度に関わるクラス pahooGeoCode。
使い方は「PHPで住所・ランドマークから最寄り駅を求める」「PHPで住所・ランドマークから緯度・経度を求める」などを参照。include_path が通ったディレクトリに配置すること。
サンプル・プログラムは 2 つの機能を持つ。
1 つは、そのまま実行すると、検索キーワードを入力する画面を表示する機能――[検索]ボタンを押下することで、検索結果を表示する。
もう 1 つは、前述の変数を GET 渡しで呼び出すことで、検索結果のみを表示する機能である。たとえば「東京都千代田区皇居外苑1-1」の最寄り駅を検索する場合は、URL で 'stationsearch.php?query=%93%8C%8B%9E%93s%90%E7%91%E3%93c%8B%E6%8Dc%8B%8F%8AO%89%91%82P%81%7C%82P' と指定してやればよい。

サンプル・プログラムの流れ

PHPで住所・ランドマークから最寄り駅を求める

準備:pahooGeoCode クラス

0035: class pahooGeoCode {
0036:     var $items;      //検索結果格納用
0037:     var $error;      //エラーフラグ
0038:     var $hits;       //検索ヒット件数
0039:     var $webapi; //直前に呼び出したWebAPI URL
0040: 
0041:     //Google Cloud Platform APIキー
0042:     //https://cloud.google.com/maps-platform/
0043:     //※Google Maps APIを利用しないのなら登録不要
0044:     var $GOOGLE_API_KEY_1 = '**************************';   //HTTPリファラ用
0045:     var $GOOGLE_API_KEY_2 = '**************************';   //IP制限用
0046: 
0047:     //Yahoo! JAPAN Webサービス アプリケーションID
0048:     //https://e.developer.yahoo.co.jp/register
0049:     //※Yahoo! JAPAN Webサービスを利用しないのなら登録不要
0050:     var $YAHOO_APPLICATION_ID = '*****************************';

地図描画や住所検索を行うために、クラスファイル "pahooGeoCode.php" を使用する。組み込み関数  require_once  を使って読めるディレクトリに配置する。ディレクトリは、設定ファイル php.ini に記述されているオプション設定 include_path に設定しておく。
クラスについては「PHP でクラスを使ってテキストの読みやすさを調べる」を参照されたい。

地図や住所検索として Google を利用するのであれば、Google Cloud Platform API キー が必要で、その入手方法は「Google Cloud Platform - WebAPI の登録方法」を、Yahoo!JAPAN を利用するのであれば、Yahoo! JAPAN Web サービス アプリケーション IDが必要で、その入手方法は「Google Cloud Platform - WebAPI の登録方法」を、それぞれ参照されたい。

準備:地図サービスの選択

0031: //地図描画サービスの選択
0032: //    0:Google
0033: //    1:Yahoo!JAPAN
0034: //    2:地理院地図・OSM
0035: define('MAPSERVICE', 0);
0036: 
0037: //住所検索サービスの選択
0038: //    0:Google
0039: //    1:Yahoo!JAPAN
0040: //   11:HeartRails Geo API
0041: define('GEOSERVICE', 0);
0042: 
0043: //逆ジオコーディングサービスの選択
0044: //    0:Google
0045: //    1:Yahoo!JAPAN
0046: //   11:HeartRails Geo API
0047: //   21:簡易ジオコーディングサービス
0048: define('REVGEOSERVICE', 21);

表示する地図は、Google マップ、[Yahoo!マップ]blue]、地理院地図・オープンストリートマップ(OSM)から選べる。あらかじめ、定数 MAPSERVIC に値を設定すること。
住所検索サービスは、GoogleYahoo!JAPANHeartRails Geo APIから選べる。あらかじめ、定数 GEOSERVICE に値を設定すること。
逆ジオコーディングサービスは、GoogleYahoo!JAPANHeartRails Geo API簡易ジオコーディングサービスから選べる。あらかじめ、定数 REVGEOSERVICE に値を設定すること。
PHPで住所・ランドマークから最寄り駅を求める
Yahoo!マップ
PHPで住所・ランドマークから最寄り駅を求める
地理院地図(淡色)
PHPで住所・ランドマークから最寄り駅を求める
OpenStreetMap

HeartRails Geo API 最寄駅検索

HeartRails Geo API」は、2015 年(平成 27 年)9 月現在、以下の API を無償公開している。
これらの API は、入力パラメータ(IN)は GET 渡しで、出力結果(OUT)は XML で戻るという形である。
  • エリア情報取得 API
  • 都道府県情報取得 API
  • 市区町村情報取得 API
  • 町域情報取得 API
  • 最寄駅情報取得 API
  • 郵便番号による住所検索 API
  • 緯度経度による住所検索 API
  • キーワードによる住所検索 API
  • 「エリア名」 「市区町村名」 「町域名」 の連結コンボボックス
  • 「都道府県名」 「市区町村名」 「町域名」 の連結コンボボックス
  • 「郵便番号」 による住所検索フォーム
WebAPIのURL
URL
http://express.heartrails.com/api/xml

入力パラメータ
フィールド名 要否 内  容
method 必須 メソッド名:getStation(固定)
x 必須 最寄り駅を取得したい場所の経度(世界測地系)。
y 必須 最寄り駅を取得したい場所の緯度(世界測地系)。
応答データ(xml) response station name 駅名 line 路線名 distance 検索地点からの距離 x 経度 y 緯度 prefecture 都道府県 postal 郵便番号 next 次の駅 prev 前の駅

解説:HeartRails Geo APIの呼び出し

0229: /**
0230:  * HeartRails Express のURLを取得する
0231:  * @param double $lat 緯度(世界測地系)
0232:  * @param double $lng 経度(世界測地系)
0233:  * @return string URL
0234: */
0235: function getURL_Heartrails($lat$lng) {
0236:     $res = "http://express.heartrails.com/api/xml?method=getStations&x={$lng}&y={$lat}";
0237: 
0238:     return $res;
0239: }

0241: /**
0242:  * HeartRails Express API から必要な情報を配列に格納する
0243:  * @param double $latitude  緯度(世界測地系)
0244:  * @param double $longitude 経度(世界測地系)
0245:  * @return array(ヒットした施設数, メッセージ, APIのURL)
0246:  * @return int ヒット数
0247: */
0248: function getResults_Heartrails($latitude$longitude, &$items) {
0249: //受信データの要素名
0250: $tbl = array(
0251:     'name',            //最寄駅名
0252:     'prev',            //前の駅名 (始発駅の場合は null
0253:     'next',            //次の駅名 (終着駅の場合は null
0254:     'x',            //最寄駅の経度 (世界測地系)
0255:     'y',            //最寄駅の緯度 (世界測地系)
0256:     'distance',    //指定の場所から最寄駅までの距離 (精度は 10 m)
0257:     'postal',        //最寄駅の郵便番号 
0258:     'prefecture',    //最寄駅の存在する都道府県名
0259:     'line'         //最寄駅の存在する路線名
0260: );
0261: 
0262:     $url = $this->getURL_Heartrails($latitude$longitude);   //リクエストURL
0263:     $cnt = 1;
0264: 
0265: //PHP4用; DOM XML利用
0266:     if (! isphp5over()) {
0267:         if (($dom = $this->read_xml($url)) == NULL) {
0268:             return array(FALSE, 'WebAPIのトラブルです.', FALSE);
0269:         }
0270:         $resultset = $dom->get_elements_by_tagname('response');
0271:         $results = $resultset[0]->get_elements_by_tagname('station');
0272:         //検索結果取りだし
0273:         foreach ($results as $element) {
0274:             foreach ($tbl as $name) {
0275:                 $node = $element->get_elements_by_tagname($name);
0276:                 if ($node != NULL) {
0277:                     $items[$cnt][$name] = (string)$node[0]->get_content();
0278:                 }
0279:             }
0280:             $items[$cnt]['id']        = $this->num2alpha($cnt);
0281:             $items[$cnt]['title']     = $items[$cnt]['name'];
0282:             $items[$cnt]['longitude'] = $items[$cnt]['x'];
0283:             $items[$cnt]['latitude']  = $items[$cnt]['y'];
0284: $items[$cnt]['description'] =<<< EOD
0285: {$items[$cnt]['name']}&nbsp;({$items[$cnt]['line']}){$items[$cnt]['distance']}
0286: EOD;
0287:             $cnt++;
0288:         }
0289: 
0290: //PHP5用; SimpleXML利用
0291:     } else {
0292:         $response = simplexml_load_file($url);
0293:         //レスポンス・チェック
0294:         if (isset($response->station) == FALSE) {
0295:             return array(FALSE, 'WebAPIのトラブルです.', FALSE);
0296:         }
0297:         //検索結果取りだし
0298:         foreach ($response->station as $element) {
0299:             foreach ($tbl as $name) {
0300:                 if (isset($element->$name)) {
0301:                     $items[$cnt][$name] = (string)$element->$name;
0302:                 }
0303:             }
0304:             $items[$cnt]['id']        = $this->num2alpha($cnt);
0305:             $items[$cnt]['title']     = $items[$cnt]['name'];
0306:             $items[$cnt]['longitude'] = $items[$cnt]['x'];
0307:             $items[$cnt]['latitude']  = $items[$cnt]['y'];
0308: $items[$cnt]['description'] =<<< EOD
0309: {$items[$cnt]['name']}&nbsp;({$items[$cnt]['line']}){$items[$cnt]['distance']}
0310: EOD;
0311:             $cnt++;
0312:         }
0313:     }
0314: 
0315:     return array($cnt - 1, '', $url);
0316: }

HeartRails Geo 最寄駅情報取得 APIの使い方は、前回と同じである。応答メッセージの処理は、PHP4 では DOM XML を、PHP5 以降では SimpleXML を使っている。

Googleマップ描画

Google Maps JavaScript API は、JavaScript を使って Google マップを動的に描画するための WebAPI である。

0803: /**
0804:  * Googleマップを描く
0805:  * @param string $id        マップID
0806:  * @param float $latitude  中心座標:緯度(世界測地系)
0807:  * @param float $longitude 中心座標:経度(世界測地系)
0808:  * @param string $type      マップタイプ:HYBRID/ROADMAP/SATELLITE/TERRAIN
0809:  * @param int    $zoom      拡大率
0810:  * @param string $call      イベント発生時にコールする関数(省略可)
0811:  * @param array  $items     地点情報(省略可能)
0812:  *                  string title       タイトル
0813:  *                  string description 情報ウィンドウに表示する内容(HTML文)
0814:  *                  float latitude    緯度
0815:  *                  float longitude   経度
0816:  *                  string icon        アイコンURL
0817:  * @param string $call2     追加スクリプト(省略可)
0818:  * @return string Googleマップのコード
0819: */
0820: function drawGMap($id$latitude$longitude$type$zoom$call=NULL$items=NULL$call2=NULL) {
0821:     $key = $this->GOOGLE_API_KEY_1;
0822:     $call = ($call != NULL) ? $call . '()' : '';
0823: $code =<<< EOD
0824: <script src="https://maps.googleapis.com/maps/api/js?key={$key}&amp;callback=initMap&amp;region=JP" async defer></script>
0825: <script>
0826: function initMap() {
0827:     var map = new google.maps.Map(document.getElementById('{$id}'), {
0828:         center: new google.maps.LatLng({$latitude}, {$longitude}),
0829:         zoom: {$zoom},
0830:         mapTypeId: google.maps.MapTypeId.{$type},
0831:         mapTypeControl: true,
0832:         scaleControl: true
0833:     });
0834: 
0835:     map.addListener('dragend', getPointData);
0836:     map.addListener('zoom_changed', getPointData);
0837:     map.addListener('maptypeid_changed', getPointData);
0838: 
0839:     //イベント発生時の地図情報を取得・格納
0840:     function getPointData() {
0841:         var point = map.getCenter();
0842:         //経度
0843:         if (document.getElementById("longitude") != null) {
0844:             document.getElementById("longitude").value = point.lng();
0845:         }
0846:         //緯度
0847:         if (document.getElementById("latitude") != null) {
0848:             document.getElementById("latitude").value = point.lat();
0849:         }
0850:         //ズーム
0851:         if (document.getElementById("zoom") != null) {
0852:             document.getElementById("zoom").value = map.getZoom();
0853:         }
0854:         //地図タイプ
0855:         if (document.getElementById("type") != null) {
0856:             var type_g = map.getMapTypeId();
0857:             var types = {"roadmap":"地図", "satellite":"航空写真", "hybrid":"ハイブリッド", "terrain":"地形図" };
0858:             for (key in types) {
0859:                 if (key == type_g) {
0860:                     document.getElementById("type").value = key;
0861:                     break;
0862:                 }
0863:             }
0864:         }
0865:         {$call}
0866:     }
0867: 
0868: EOD;
0869:     //地点情報
0870:     if ($items != NULL) {
0871:         foreach ($items as $i=>$item) {
0872:             if ($i > 26)    break;       //'Z'を超えたらスキップ
0873:             //アイコン
0874:             $mark = $this->num2alpha($i);
0875:             $icon = isset($item['icon']) ? $item['icon'] : 
0876:                         "http://www.google.com/mapfiles/marker{$mark}.png";
0877:             list($icon_width$icon_height) = getimagesize($icon);
0878: $code .=<<< EOD
0879: var icon_{$mark} = {
0880:     url: '{$icon}',
0881:     size: new google.maps.Size({$icon_width}, {$icon_height}),
0882:     origin: new google.maps.Point(0, 0),
0883:     anchor: new google.maps.Point({$icon_width} / 2, {$icon_height})
0884: };
0885: var marker_{$mark} = new google.maps.Marker({
0886:     position: new google.maps.LatLng({$item['latitude']}, {$item['longitude']}),
0887:     map: map,
0888:     icon: icon_{$mark},
0889:     title: '{$item['title']}',
0890:     zIndex: 100
0891: });
0892: 
0893: EOD;
0894:             if (isset($item['description'])) {
0895: $code .=<<< EOD
0896: var infowindow_{$mark} = new google.maps.InfoWindow({
0897:     content: '{$item['description']}',
0898:     maxWidth: 200
0899: });
0900: marker_{$mark}.addListener('click', function() {
0901:     infowindow_{$mark}.open(map, marker_{$mark});
0902: });
0903: 
0904: EOD;
0905:             }
0906:         }
0907:     }
0908:     //追加関数
0909:     if ($call2 != NULL) {
0910: $code .=<<< EOD
0911: {$call2}
0912: 
0913: EOD;
0914:     }
0915: $code .=<<< EOD
0916: }
0917: </script>
0918: 
0919: EOD;
0920: 
0921:     return $code;
0922: }

メソッド drawGMap は、Google マップを描画するための JavaScript を生成する。
まずソースとして、"https://maps.googleapis.com/maps/api/js" を読み込む。このとき、パラメータ keyGoogle Cloud Platform API キー を渡してやる必要がある。
また、コールバック関数として initMap() を呼び出し、このユーザー関数内で初期化を行う。

マップのドラッグ、ズーム変更、マップタイプ変更のイベントを拾って(addListner)、getPointDate() 関数を呼び出す。
このユーザー関数内で、緯度・経度、ズーム値、マップタイプを、ID で示されるオブジェクトに格納する。hidden 属性のテキストボックスに格納することを想定している。
これにより、ページ切替が起きても地図の状態を保持できる。
また、本メソッドに JavaScript を引数 $call として渡してやれば、getPointDate() 関数内で追加で呼び出す。

引数 $items を渡してやれば、地図上にマーキングする。
$items は 2 次元配列で、1 次元目は地点番号(1 以上)、2 次元目は情報の種類である。たとえば $items[3]['description'] には、地点番号 3 の情報ウィンドウに表示する内容 HTML を代入する。
$items には、マッピングするアイコン URL も指定可能である。指定しない場合は、Google マップの標準的なアルファベットアイコンによってマーキングする。

Yahoo!JAPANマップ描画

Yahoo! JavaScript マップ描画 は、JavaScript を使って Yahoo!マップを動的に描画するための WebAPI である。

1212: /**
1213:  * Yahoo! JavaScriptマップを描く
1214:  * @param string $id        マップID
1215:  * @param float $latitude  中心座標:緯度(世界測地系)
1216:  * @param float $longitude 中心座標:経度(世界測地系)
1217:  * @param string $type      マップタイプ:NORMAL/PHOTO/B1/OSM
1218:  *                  ※注意:マップタイプ変更イベントをキャッチアップできない
1219:  * @param int    $zoom      拡大率
1220:  * @param string $call      イベント発生時にコールする関数(省略可)
1221:  * @param array  $items     地点情報(省略可能)
1222:  *                  string description 情報ウィンドウに表示する内容(HTML文)
1223:  *                  float latitude    緯度
1224:  *                  float longitude   経度
1225:  *                  string icon        アイコンURL
1226:  * @param string $call2     追加スクリプト(省略可)
1227:  * @return string Yahoo! JavaScriptマップのコード
1228: */
1229: function drawYOLPmap($id$latitude$longitude$type$zoom$call=NULL$items=NULL$call2=NULL) {
1230:     $appid = $this->YAHOO_APPLICATION_ID;
1231: 
1232: $code =<<< EOD
1233: <script src="https://map.yahooapis.jp/js/V1/jsapi?appid={$appid}"></script>
1234: <script>
1235: window.onload = function() {
1236:     var ymap = new Y.Map("{$id}", {
1237:         configure : {
1238:             doubleClickZoom : true,
1239:             scrollWheelZoom : true,
1240:             singleClickPan : true,
1241:             dragging : true
1242:         }
1243:     });
1244:     var control1 = new Y.LayerSetControl();
1245:     ymap.addControl(control1);
1246:     var control2 = new Y.ZoomControl();
1247:     ymap.addControl(control2);
1248:     ymap.drawMap(new Y.LatLng($latitude$longitude), $zoom, Y.LayerSetId.{$type});
1249: 
1250:     ymap.bind('moveend',  getPointData);
1251:     ymap.bind('zoomend',  getPointData);
1252: 
1253:     //イベント発生時の地図情報を取得・格納
1254:     function getPointData() {
1255:         var point = ymap.getCenter();
1256:         //経度
1257:         if (document.getElementById("longitude") != null) {
1258:             document.getElementById("longitude").value = point.lng();
1259:         }
1260:         //緯度
1261:         if (document.getElementById("latitude") != null) {
1262:             document.getElementById("latitude").value = point.lat();
1263:         }
1264:         //ズーム
1265:         if (document.getElementById("zoom") != null) {
1266:             document.getElementById("zoom").value = ymap.getZoom();
1267:         }
1268:         {$call}
1269:     }
1270: 
1271: 
1272: EOD;
1273:     //地点情報
1274:     if ($items != NULL) {
1275:         foreach ($items as $i=>$item) {
1276:             if ($i > 26)    break;       //'Z'を超えたらスキップ
1277:             //アイコン
1278:             $mark = $this->num2alpha($i);
1279:             $icon = isset($item['icon']) ? $item['icon'] : 
1280:                         "http://www.google.com/mapfiles/marker{$mark}.png";
1281:             $info  = isset($item['description']) ? "marker_{$mark}.bindInfoWindow('{$item['description']}');" : '';
1282: 
1283: $code .=<<< EOD
1284:     var icon_{$mark} = new Y.Icon('{$icon}');
1285:     var marker_{$mark} = new Y.Marker(new Y.LatLng({$item['latitude']}, {$item['longitude']}), {icon: icon_{$mark}});
1286:     {$info}
1287:     ymap.addFeature(marker_{$mark});
1288: 
1289: EOD;
1290:         }
1291:     }
1292:     //追加関数
1293:     if ($call2 != NULL) {
1294: $code .=<<< EOD
1295: {$call2}
1296: 
1297: EOD;
1298:     }
1299: $code .=<<< EOD
1300: }
1301: </script>
1302: 
1303: EOD;
1304: 
1305:     return $code;
1306: }

メソッド drawYOLPmap は、Yahoo!マップを描画するための JavaScript を生成する。
まずソースとして、"https://map.yahooapis.jp/js/V1/jsapi" を読み込む。このとき、パラメータ $appidYahoo! JAPAN Web サービス アプリケーション ID を渡してやる必要がある。

マップのドラッグ、ズーム変更のイベントを拾って(bind)、getPointDate() 関数を呼び出す。
このユーザー関数内で、緯度・経度、ズーム値を、ID で示されるオブジェクトに格納する。hidden 属性のテキストボックスに格納することを想定している。
これにより、ページ切替が起きても地図の状態を保持できる。
また、本メソッドに JavaScript を引数 $call として渡してやれば、getPointDate() 関数内で追加で呼び出す。

引数 $items を渡してやれば、地図上にマーキングする。
$items は 2 次元配列で、1 次元目は地点番号(1 以上)、2 次元目は情報の種類である。たとえば $items[3]['description'] には、地点番号 3 の情報ウィンドウに表示する内容 HTML を代入する。
$items には、マッピングするアイコン URL も指定可能である。指定しない場合は、Google マップの標準的なアルファベットアイコンによってマーキングする。

地理院地図・OSM描画

国土地理院 は、基本測量結果や電子国土基本図、白地図、航空写真などを、各縮尺に応じて、256×256 ドットの分割画像である地理院タイルとして、URL を指定して直接参照できるようにしている。

ここでは、このタイル画像を繋げて、Google マップや Yahoo!JAPAN マップのようなユーザー・インターフェースを提供する無償の JavaScript ライブラリ Leaflet を利用することにする。

あわせて、自由に利用できる世界地図作成プロジェクト「OpenStreetMap」(OSM)が提供する地図タイルも利用できるようにした。

1391: /**
1392:  * Leafletによるマップ描画
1393:  * @param string $id        マップID
1394:  * @param float  $latitude  中心座標:緯度(世界測地系)
1395:  * @param float  $longitude 中心座標:経度(世界測地系)
1396:  * @param string $type      マップタイプ:GSISTD/GSIPALE/GSIBLANK/GSIPHOTO/OSM
1397:  * @param int    $zoom      拡大率
1398:  * @param string $call      イベント発生時にコールする関数(省略可)
1399:  * @param array  $items     地点情報(省略可能)
1400:  *                  string description 情報ウィンドウに表示する内容(HTML文)
1401:  *                  float  latitude    緯度
1402:  *                  float  longitude   経度
1403:  *                  string icon        アイコンURL
1404:  * @param string $call2     追加スクリプト(省略可)
1405:  * @return string Leafletマップのコード
1406: */
1407: function drawLeaflet($id$latitude$longitude$type$zoom$call=NULL$items=NULL$call2=NULL) {
1408: 
1409: $code =<<< EOD
1410: <link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" />
1411: <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>
1412: <script>
1413: window.onload = function() {
1414:     var map = L.map('{$id}',{zoomControl:false});
1415:     map.setView([{$latitude}, {$longitude}], {$zoom});
1416:     L.control.scale({
1417:         maxWidth: 200,
1418:         position: 'bottomright',
1419:         imperial: false
1420:     }).addTo(map);
1421:     L.control.zoom({position:'topleft'}).addTo(map);
1422: 
1423:     //地理院地図:標準地図
1424:     var GSISTD = new L.tileLayer(
1425:         'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
1426:         {
1427:             attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1428:             minZoom: 0,
1429:             maxZoom: 18,
1430:             name: 'GSISTD'
1431:         });
1432:     //地理院地図:淡色地図
1433:     var GSIPALE = new L.tileLayer(
1434:         'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
1435:         {
1436:             attribution: "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>",
1437:             minZoom: 2,
1438:             maxZoom: 18,
1439:             name: 'GSIPALE'
1440:         });
1441:     //地理院地図:白地図
1442:     var GSIBLANK = new L.tileLayer(
1443:         'https://cyberjapandata.gsi.go.jp/xyz/blank/{z}/{x}/{y}.png',
1444:         {
1445:             attribution: "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>",
1446:             minZoom: 5,
1447:             maxZoom: 14,
1448:             name: 'GSIBLANK'
1449:         });
1450:     //地理院地図:写真
1451:     var GSIPHOTO = new L.tileLayer(
1452:         'https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg',
1453:         {
1454:             attribution: "<a href='http://portal.cyberjapan.jp/help/termsofuse.html' target='_blank'>地理院タイル</a>",
1455:             minZoom: 2,
1456:             maxZoom: 18,
1457:             name: 'GSIPHOTO'
1458:         });
1459:     //OpenStreetMap
1460:     var OSM = new L.tileLayer(
1461:         'http://tile.openstreetmap.jp/{z}/{x}/{y}.png',
1462:         {
1463:             attribution: "© <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors",
1464:             minZoom: 0,
1465:             maxZoom: 18,
1466:             name: 'OSM'
1467:         });
1468: 
1469:     //baseMapsオブジェクトにタイル設定
1470:     var baseMaps = {
1471:         "地理院地図" : GSISTD,
1472:         "淡色地図" : GSIPALE,
1473:         "白地図" : GSIBLANK,
1474:         "写真地図" : GSIPHOTO,
1475:         "オープンストリートマップ" : OSM
1476:     };
1477: 
1478:     //layersコントロールにbaseMapsオブジェクトを設定して地図に追加
1479:     L.control.layers(baseMaps).addTo(map);
1480:     {$type}.addTo(map);
1481: 
1482:     //イベント追加
1483:     map.on('moveend', getPointData);
1484:     map.on('zoomend', getPointData);
1485:     map.on('baselayerchange', getPointData);
1486: 
1487:     //イベント発生時の地図情報を取得・格納
1488:     function getPointData() {
1489:         var pos = map.getCenter();
1490:         //経度
1491:         if (document.getElementById('longitude') != null) {
1492:             document.getElementById('longitude').value = pos.lng;
1493:         }
1494:         //緯度
1495:         if (document.getElementById('latitude') != null) {
1496:             document.getElementById('latitude').value = pos.lat;
1497:         }
1498:         //ズーム
1499:         if (document.getElementById('zoom') != null) {
1500:             document.getElementById('zoom').value = map.getZoom();
1501:         }
1502:         //タイプ
1503:         if (document.getElementById('type') != null) {
1504:             for (var k in baseMaps) {
1505:                 if (map.hasLayer(baseMaps[k])) {
1506:                     document.getElementById('type').value = baseMaps[k].options.name;
1507:                 }
1508:             }
1509:         }
1510:         {$call}
1511:     }
1512: 
1513: 
1514: EOD;
1515:     //地点情報
1516:     if ($items != NULL) {
1517:         foreach ($items as $i=>$item) {
1518:             if ($i > 26)    break;       //'Z'を超えたらスキップ
1519:             //アイコン
1520:             $mark = $this->num2alpha($i);
1521:             $icon = isset($item['icon']) ? $item['icon'] : 
1522:                         "http://www.google.com/mapfiles/marker{$mark}.png";
1523:             list($icon_width$icon_height) = getimagesize($icon);
1524:             $offx = round($icon_width / 2);
1525:             $offy = $icon_height;
1526:             $info  = isset($item['description']) ? $item['description'] : '';
1527: 
1528: $code .=<<< EOD
1529:     var icon_{$mark} = new L.icon({
1530:         iconUrl: '{$icon}',
1531:         iconAnchor: [{$offx}, {$offy}]
1532:     });
1533:     var marker_{$mark} = new L.Marker([{$item['latitude']}, {$item['longitude']}], {icon: icon_{$mark}}).addTo(map);
1534:     marker_{$mark}.bindPopup('{$info}', {maxWidth: 200})
1535: 
1536: EOD;
1537:         }
1538:     }
1539:     //追加関数
1540:     if ($call2 != NULL) {
1541: $code .=<<< EOD
1542: {$call2}
1543: 
1544: EOD;
1545:     }
1546: $code .=<<< EOD
1547: }
1548: </script>
1549: 
1550: EOD;
1551: 
1552:     return $code;
1553: }

メソッド drawLeaflet は、地理院地図と OSM を描画するための JavaScript を生成する。
まずソースとして、UNPKG から Leaflet 本体とスタイルシートを読み込む。

Leaflet のレイヤを切り替えることで、以下の地図が切り替わるようにしてある。
  1. 地理院地図:標準地図
  2. 地理院地図:淡色地図
  3. 地理院地図:白地図
  4. 地理院地図:写真
  5. OpenStreetMap
この他、タイル形式の地図コンテンツがあれば、自由に追加できる。じつは Google マップもタイル形式で呼び出すことができるのだが、API コールしないと Google の規約違反となるため、実装してはいけない。

マップのドラッグ、ズーム変更、レイヤ変更のイベントを拾って(on)、getPointDate() 関数を呼び出す。
このユーザー関数内で、緯度・経度、ズーム値を、ID で示されるオブジェクトに格納する。hidden 属性のテキストボックスに格納することを想定している。
これにより、ページ切替が起きても地図の状態を保持できる。
また、本メソッドに JavaScript を引数 $call として渡してやれば、getPointDate() 関数内で追加で呼び出す。

引数 $items を渡してやれば、地図上にマーキングする。
$items は 2 次元配列で、1 次元目は地点番号(1 以上)、2 次元目は情報の種類である。たとえば $items[3]['description'] には、地点番号 3 の情報ウィンドウに表示する内容 HTML を代入する。
$items には、マッピングするアイコン URL も指定可能である。指定しない場合は、Google マップの標準的なアルファベットアイコンによってマーキングする。

動的マップ描画

1810: /**
1811:  * 地図サービスを利用してJavaScriptマップを描く
1812:  *
1813:  * @param string $id        マップID
1814:  * @param float  $latitude  中心座標:緯度(世界測地系)
1815:  * @param float  $longitude 中心座標:経度(世界測地系)
1816:  * @param string $type      マップタイプ
1817:  *                              Googleの場合 HYBRID/ROADMAP/SATELLITE/TERRAIN
1818:  *                              Yahoo!JAPANの場合 NORMAL/PHOTO/B1/OSM
1819:  * @param int    $zoom      拡大率
1820:  * @param string $call      イベント発生時にコールする関数(省略可)
1821:  * @param array  $items     地点情報(省略可能)
1822:  *                  string title       タイトル(Yahoo!では無効)
1823:  *                  string description 情報ウィンドウに表示する内容(HTML文)
1824:  *                  float latitude    緯度
1825:  *                  float longitude   経度
1826:  *                  string icon        アイコンURL
1827:  * @param string $api   0:Google Maps JavaScript(省略時)
1828:  *                        1:Yahoo! JavaScriptマップ
1829:  *                       11:地理院地図・OSM(Leaflet使用)
1830:  * @param string $call2 追加スクリプト(省略可)
1831:  * @return string JavaScriptマップのコード
1832: */
1833: function drawJSmap($id$latitude$longitude$type$zoom$call=NULL$items=NULL$api=0, $call2=NULL) {
1834:     //マップタイプの読み替え
1835:     static $tbl1 = array('HYBRID'=>'PHOTO', 'ROADMAP'=>'NORMAL', 'SATELLITE'=>'PHOTO', 'TERRAIN'=>'PHOTO');
1836:     static $tbl2 = array('NORMAL'=>'ROADMAP', 'PHOTO'=>'SATELLITE', 'B1'=>'ROADMAP', 'OSM'=>'ROADMAP');
1837:     static $tbl3 = array('HYBRID'=>'gsistd', 'ROADMAP'=>'GSISTD', 'SATELLITE'=>'GSIPHOTO', 'TERRAIN'=>'GSIPHOTO');
1838:     $type = strtoupper($type);
1839: 
1840:     //Google使用
1841:     switch ($api) {
1842:     case 0;
1843:         $type = isset($tbl2[$type]) ? $tbl2[$type] : $type;
1844:         $js = $this->drawGMap($id$latitude$longitude$type$zoom$call$items$call2);
1845:         break;
1846:     case 1:
1847:         $type = isset($tbl1[$type]) ? $tbl1[$type] : $type;
1848:         $js = $this->drawYOLPmap($id$latitude$longitude$type$zoom$call$items$call2);
1849:         break;
1850:     case 2:
1851:         $type = isset($tbl3[$type]) ? $tbl3[$type] : $type;
1852:         $js = $this->drawLeaflet($id$latitude$longitude$type$zoom$call$items$call2);
1853:         break;
1854:     }
1855: 
1856:     return $js;
1857: }

メソッド drawJSmap は、引数 $api の値によって、Google マップ描画メソッド drawGMap、Yahoo!JAPAN マップ描画メソッド drawYOLPmap、地理院地図・ OSM 描画メソッド drawLeaflet のいずれかを呼び出す。
マップタイプについては、相互に読み替えができるようにしてある。

その他の WebAPI

pahooGeoCode::searchPoint3 が呼び出す WebAPI については、「PHP で住所・ランドマークから緯度・経度を求める」を参照のこと。
pahooGeoCode::getAddress3 が呼び出す WebAPI については、「PHP で緯度・経度から住所を求める」を参照のこと。

活用例

みんなの知識 ちょっと便利帳」では、「グーグルマップで見る京都の地下鉄路線図」からリンクする「地図・住所から「最寄り駅」をグーグルマップで探す - 初期設定:京都府庁周辺」や、「地図・住所から「最寄り駅」をグーグルマップで探す」で本プログラムを利用し、検索しやすいページを提供している。ありがとうございます。

参考サイト

(この項おわり)
header