目次
サンプル・プログラムの実行例
サンプル・プログラム
stationsearch.php | サンプル・プログラム本体。 |
pahooGeoCode.php | 住所・緯度・経度に関わるクラス pahooGeoCode。 使い方は「PHPで住所・ランドマークから最寄り駅を求める」「PHPで住所・ランドマークから緯度・経度を求める」などを参照。include_path が通ったディレクトリに配置すること。 |
pahooInputData.php | データ入力に関わる関数群。 使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。 |
バージョン | 更新日 | 内容 |
---|---|---|
4.5.0 | 2023/07/14 | 検索キーの最小・最大長の指定 |
4.4.0 | 2023/07/02 | 国土地理院ジオコーディングAPIを追加 |
4.3 | 2021/02/20 | PHP8対応,Yahoo! JavaScriptマップ廃止 |
4.2 | 2020/03/21 | OSM Nominatim Search API追加 |
4.1 | 2019/05/11 | 地理院地図、OpenStreetMapも利用できるようにした |
バージョン | 更新日 | 内容 |
---|---|---|
6.3.1 | 2023/07/09 | bug-fix |
6.3.0 | 2023/07/02 | getPointsGSI()追加 |
6.2.0 | 2023/07/02 | ip2address()追加 |
6.1.0 | 2022/12/30 | ip2address()追加 |
6.0.4 | 2022/12/13 | PHP8.2対応 |
バージョン | 更新日 | 内容 |
---|---|---|
1.3.0 | 2023/07/11 | roundFloat() 追加 |
1.2.0 | 2023/04/22 | exitIfLessVersion() 追加 |
1.1.2 | 2023/02/05 | validString() 修正 |
1.11 | 2022/07/03 | isCommandLine() 修正 |
1.1 | 2022/06/04 | getValidString() 修正 |
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' と指定してやればよい。
サンプル・プログラムの流れ
準備:pahooGeoCode クラス
37: class pahooGeoCode {
38: var $items; //検索結果格納用
39: var $error; //エラー・フラグ
40: var $errmsg; //エラー・メッセージ
41: var $hits; //検索ヒット件数
42: var $webapi; //直前に呼び出したWebAPI URL
43:
44: //Google Cloud Platform APIキー
45: //https://cloud.google.com/maps-platform/
46: //※Google Maps APIを利用しないのなら登録不要
47: var $GOOGLE_API_KEY_1 = '**************************'; //HTTPリファラ用
48: var $GOOGLE_API_KEY_2 = '**************************'; //IP制限用
49:
50: //Yahoo! JAPAN Webサービス アプリケーションID
51: //https://e.developer.yahoo.co.jp/register
52: //※Yahoo! JAPAN Webサービスを利用しないのなら登録不要
53: var $YAHOO_APPLICATION_ID = '*****************************';
クラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」を参照されたい。
地図や住所検索として Google を利用するのであれば、Google Cloud Platform APIキー が必要で、その入手方法は「Google Cloud Platform - WebAPIの登録方法」を、Yahoo!JAPAN を利用するのであれば、Yahoo! JAPAN Webサービス アプリケーションIDが必要で、その入手方法は「Yahoo!JAPAN デベロッパーネットワーク - WebAPIの登録方法」を、それぞれ参照されたい。
準備:地図サービスの選択
56: //地図描画サービスの選択
57: // 0:Google
58: // 2:地理院地図・OSM
59: define('MAPSERVICE', 2);
60:
61: //住所検索サービスの選択
62: // 0:Google
63: // 1:Yahoo!ジオコーダAPI
64: // 11:HeartRails Geo API
65: // 12:OSM Nominatim Search API
66: // 13:国土地理院ジオコーディングAPI
67: define('GEOSERVICE', 1);
68:
69: //逆ジオコーディングサービスの選択
70: // 0:Google
71: // 1:Yahoo!JAPAN
72: // 11:HeartRails Geo API
73: // 21:簡易ジオコーディングサービス
74: define('REVGEOSERVICE', 1);
住所検索サービスは、Google、Yahoo!JAPAN、HeartRails Geo APIから選べる。あらかじめ、定数 GEOSERVICE に値を設定すること。
逆ジオコーディングサービスは、Google、Yahoo!JAPAN、HeartRails Geo API、簡易ジオコーディングサービスから選べる。あらかじめ、定数 REVGEOSERVICE に値を設定すること。
HeartRails Geo API 最寄駅検索
これらの API は、入力パラメータ(IN)は GET 渡しで、出力結果(OUT)は XML で戻るという形である。
- エリア情報取得 API
- 都道府県情報取得 API
- 市区町村情報取得 API
- 町域情報取得 API
- 最寄駅情報取得 API
- 郵便番号による住所検索 API
- 緯度経度による住所検索 API
- キーワードによる住所検索 API
- 「エリア名」 「市区町村名」 「町域名」 の連結コンボボックス
- 「都道府県名」 「市区町村名」 「町域名」 の連結コンボボックス
- 「郵便番号」 による住所検索フォーム
URL |
---|
https://express.heartrails.com/api/xml |
フィールド名 | 要否 | 内 容 |
---|---|---|
method | 必須 | メソッド名:getStation(固定) |
x | 必須 | 最寄り駅を取得したい場所の経度(世界測地系)。 |
y | 必須 | 最寄り駅を取得したい場所の緯度(世界測地系)。 |
解説:HeartRails Geo APIの呼び出し
221: /**
222: * HeartRails Express のURLを取得する
223: * @param double $lat 緯度(世界測地系)
224: * @param double $lng 経度(世界測地系)
225: * @return string URL
226: */
227: function getURL_Heartrails($lat, $lng) {
228: $res = "http://express.heartrails.com/api/xml?method=getStations&x={$lng}&y={$lat}";
229:
230: return $res;
231: }
233: /**
234: * HeartRails Express API から必要な情報を配列に格納する
235: * @param double $latitude 緯度(世界測地系)
236: * @param double $longitude 経度(世界測地系)
237: * @return array(ヒットした施設数, メッセージ, APIのURL)
238: * @return int ヒット数
239: */
240: function getResults_Heartrails($latitude, $longitude, &$items) {
241: //受信データの要素名
242: $tbl = array(
243: 'name', //最寄駅名
244: 'prev', //前の駅名 (始発駅の場合は null)
245: 'next', //次の駅名 (終着駅の場合は null)
246: 'x', //最寄駅の経度 (世界測地系)
247: 'y', //最寄駅の緯度 (世界測地系)
248: 'distance', //指定の場所から最寄駅までの距離 (精度は 10 m)
249: 'postal', //最寄駅の郵便番号
250: 'prefecture', //最寄駅の存在する都道府県名
251: 'line' //最寄駅の存在する路線名
252: );
253:
254: $url = $this->getURL_Heartrails($latitude, $longitude); //リクエストURL
255: $cnt = 1;
256:
257: //PHP4用; DOM XML利用
258: if (! $this->isphp5over()) {
259: if (($dom = $this->read_xml($url)) == NULL) {
260: return array(FALSE, 'WebAPIのトラブルです.', FALSE);
261: }
262: $resultset = $dom->get_elements_by_tagname('response');
263: $results = $resultset[0]->get_elements_by_tagname('station');
264: //検索結果取りだし
265: foreach ($results as $element) {
266: foreach ($tbl as $name) {
267: $node = $element->get_elements_by_tagname($name);
268: if ($node != NULL) {
269: $items[$cnt][$name] = (string)$node[0]->get_content();
270: }
271: }
272: $items[$cnt]['id'] = $this->num2alpha($cnt);
273: $items[$cnt]['title'] = $items[$cnt]['name'];
274: $items[$cnt]['longitude'] = $items[$cnt]['x'];
275: $items[$cnt]['latitude'] = $items[$cnt]['y'];
276: $items[$cnt]['description'] =<<< EOT
277: {$items[$cnt]['name']} ({$items[$cnt]['line']}){$items[$cnt]['distance']}
278: EOT;
279: $cnt++;
280: }
281:
282: //PHP5用; SimpleXML利用
283: } else {
284: $response = simplexml_load_file($url);
285: //レスポンス・チェック
286: if (isset($response->station) == FALSE) {
287: return array(FALSE, 'WebAPIのトラブルです.', FALSE);
288: }
289: //検索結果取りだし
290: foreach ($response->station as $element) {
291: foreach ($tbl as $name) {
292: if (isset($element->$name)) {
293: $items[$cnt][$name] = (string)$element->$name;
294: }
295: }
296: $items[$cnt]['id'] = $this->num2alpha($cnt);
297: $items[$cnt]['title'] = $items[$cnt]['name'];
298: $items[$cnt]['longitude'] = $items[$cnt]['x'];
299: $items[$cnt]['latitude'] = $items[$cnt]['y'];
300: $items[$cnt]['description'] =<<< EOT
301: {$items[$cnt]['name']} ({$items[$cnt]['line']}){$items[$cnt]['distance']}
302: EOT;
303: $cnt++;
304: }
305: }
306:
307: return array($cnt - 1, '', $url);
308: }
Googleマップ描画
862: /**
863: * Googleマップを描く
864: * @param string $id マップID
865: * @param float $latitude 中心座標:緯度(世界測地系)
866: * @param float $longitude 中心座標:経度(世界測地系)
867: * @param string $type マップタイプ:HYBRID/ROADMAP/SATELLITE/TERRAIN
868: * @param int $zoom 拡大率
869: * @param string $call イベント発生時にコールする関数(省略可)
870: * @param array $items 地点情報(省略可能)
871: * string title タイトル
872: * string description 情報ウィンドウに表示する内容(HTML文)
873: * float latitude 緯度
874: * float longitude 経度
875: * string icon アイコンURL
876: * @param string $call2 追加スクリプト(省略可)
877: * @param int $max_width 情報ウィンドウの最大幅(省略時:200)
878: * @param array $offset アイコンから情報ウィンドウのオフセット位置(省略時:0,0)
879: * @return string Googleマップのコード
880: */
881: function drawGMap($id, $latitude, $longitude, $type, $zoom, $call=NULL, $items=NULL, $call2=NULL, $max_width=200, $offset=NULL) {
882: $key = $this->GOOGLE_API_KEY_1;
883: $call = ($call != NULL) ? $call . '()' : '';
884: if (! is_array($offset)) {
885: $offset = array(0, 0);
886: }
887:
888: $code =<<< EOD
889: <script src="https://maps.googleapis.com/maps/api/js?key={$key}&callback=initMap&region=JP" async defer></script>
890: <script>
891: function initMap() {
892: var map = new google.maps.Map(document.getElementById('{$id}'), {
893: center: new google.maps.LatLng({$latitude}, {$longitude}),
894: zoom: {$zoom},
895: mapTypeId: google.maps.MapTypeId.{$type},
896: mapTypeControl: true,
897: scaleControl: true
898: });
899:
900: map.addListener('dragend', getPointData);
901: map.addListener('zoom_changed', getPointData);
902: map.addListener('maptypeid_changed', getPointData);
903:
904: //イベント発生時の地図情報を取得・格納
905: function getPointData() {
906: var point = map.getCenter();
907: //経度
908: if (document.getElementById("longitude") != null) {
909: document.getElementById("longitude").value = point.lng();
910: }
911: //緯度
912: if (document.getElementById("latitude") != null) {
913: document.getElementById("latitude").value = point.lat();
914: }
915: //ズーム
916: if (document.getElementById("zoom") != null) {
917: document.getElementById("zoom").value = map.getZoom();
918: }
919: //地図タイプ
920: if (document.getElementById("type") != null) {
921: var type_g = map.getMapTypeId();
922: var types = {"roadmap":"地図", "satellite":"航空写真", "hybrid":"ハイブリッド", "terrain":"地形図" };
923: for (key in types) {
924: if (key == type_g) {
925: document.getElementById("type").value = key;
926: break;
927: }
928: }
929: }
930: {$call}
931: }
932:
933: EOD;
934: //地点情報
935: if ($items != NULL) {
936: foreach ($items as $i=>$item) {
937: if ($i > 999) break; //最大999箇所まで
938: $mark = (string)sprintf('%03d', $i);
939: //アイコン
940: $mark2 = ($i <= 26) ? $this->num2alpha($i) : 'Z';
941: $icon = isset($item['icon']) ? $item['icon'] :
942: "https://www.google.com/mapfiles/marker{$mark2}.png";
943: list($icon_width, $icon_height) = getimagesize($icon);
944: if (isset($item['label']) && ($item['label'] != '')) {
945: $ss =<<< EOD
946: icon: {
947: url: 'https://www.pahoo.org/common/space.gif'
948: },
949: label: {
950: text: '{$item['label']}',
951: color: '{$item['label_color']}',
952: fontSize: '{$item['label_size']}px',
953: fontWeight: '{$item['label_weight']}'
954: }
955:
956: EOD;
957: } else {
958: $ss =<<< EOD
959: icon: {
960: url: '{$icon}',
961: size: new google.maps.Size({$icon_width}, {$icon_height}),
962: origin: new google.maps.Point(0, 0),
963: anchor: new google.maps.Point({$icon_width} / 2, {$icon_height})
964: }
965:
966: EOD;
967: }
968: $code .=<<< EOD
969: var marker_{$mark} = new google.maps.Marker({
970: position: new google.maps.LatLng({$item['latitude']}, {$item['longitude']}),
971: map: map,
972: {$ss},
973: title: '{$item['title']}',
974: zIndex: 100
975: });
976:
977: EOD;
978: if (isset($item['description'])) {
979: $code .=<<< EOD
980: var infowindow_{$mark} = new google.maps.InfoWindow({
981: content: '{$item['description']}',
982: maxWidth: {$max_width},
983: pixelOffset: new google.maps.Size({$offset[0]}, {$offset[1]})
984: });
985: marker_{$mark}.addListener('click', function() {
986: infowindow_{$mark}.open(map, marker_{$mark});
987: });
988:
989: EOD;
990: }
991: }
992: }
993: //追加関数
994: if ($call2 != NULL) {
995: $code .=<<< EOD
996: {$call2}
997:
998: EOD;
999: }
1000: $code .=<<< EOD
1001: }
1002: </script>
1003:
1004: EOD;
1005:
1006: return $code;
1007: }
まずソースとして、"https://maps.googleapis.com/maps/api/js" を読み込む。このとき、パラメータ key に Google 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マップの標準的なアルファベットアイコンによってマーキングする。
地理院地図・OSM描画
ここでは、このタイル画像を繋げて、Googleマップのようなユーザー・インターフェースを提供する無償のJavaScriptライブラリ Leaflet を利用することにする。
あわせて、自由に利用できる世界地図作成プロジェクト「OpenStreetMap」(OSM)が提供する地図タイルも利用できるようにした。
1834: /**
1835: * Leafletによるマップ描画
1836: * @param string $id マップID
1837: * @param float $latitude 中心座標:緯度(世界測地系)
1838: * @param float $longitude 中心座標:経度(世界測地系)
1839: * @param string $type マップタイプ:GSISTD/GSIPALE/GSIBLANK/GSIPHOTO/OSM
1840: * @param int $zoom 拡大率
1841: * @param string $call イベント発生時にコールする関数(省略可)
1842: * @param array $items 地点情報(省略可能)
1843: * string description 情報ウィンドウに表示する内容(HTML文)
1844: * float latitude 緯度
1845: * float longitude 経度
1846: * string icon アイコンURL
1847: * @param string $call2 追加スクリプト(省略可)
1848: * @param int $max_width 情報ウィンドウの最大幅(省略時:200)
1849: * @param array $offset アイコンから情報ウィンドウのオフセット位置(省略時:NULL)
1850: * @param array $overlays オーバーレイ:GSIELEV/GSIFAULT/GSIFLOOD
1851: * @return string Leafletマップのコード
1852: */
1853: function drawLeaflet($id, $latitude, $longitude, $type, $zoom, $call=NULL, $items=NULL, $call2=NULL, $max_width=200, $offset=NULL, $overlays=NULL) {
1854:
1855: if (! is_array($offset)) {
1856: $offset = array(0, 0);
1857: }
1858: //デフォルト・オーバーレイ
1859: $addoverlay = '';
1860: if ($overlays != NULL) {
1861: foreach ($overlays as $overlay) {
1862: $addoverlay .=<<< EOD
1863: {$overlay}.addTo(map);
1864:
1865: EOD;
1866: }
1867: }
1868:
1869: $code =<<< EOD
1870: <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
1871: <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
1872: <script>
1873: window.onload = function() {
1874: let map = L.map('{$id}',{zoomControl:false});
1875: map.setView([{$latitude}, {$longitude}], {$zoom});
1876: L.control.scale({
1877: maxWidth: 200,
1878: position: 'bottomright',
1879: imperial: false
1880: }).addTo(map);
1881: L.control.zoom({position:'topleft'}).addTo(map);
1882:
1883: //地理院地図:標準地図
1884: let GSISTD = new L.tileLayer(
1885: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
1886: {
1887: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1888: minZoom: 0,
1889: maxZoom: 18,
1890: name: 'GSISTD'
1891: });
1892: //地理院地図:淡色地図
1893: let GSIPALE = new L.tileLayer(
1894: 'https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png',
1895: {
1896: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1897: minZoom: 2,
1898: maxZoom: 18,
1899: name: 'GSIPALE'
1900: });
1901: //地理院地図:白地図
1902: let GSIBLANK = new L.tileLayer(
1903: 'https://cyberjapandata.gsi.go.jp/xyz/blank/{z}/{x}/{y}.png',
1904: {
1905: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1906: minZoom: 5,
1907: maxZoom: 14,
1908: name: 'GSIBLANK'
1909: });
1910: //地理院地図:写真
1911: let GSIPHOTO = new L.tileLayer(
1912: 'https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg',
1913: {
1914: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1915: minZoom: 2,
1916: maxZoom: 18,
1917: name: 'GSIPHOTO'
1918: });
1919: //OpenStreetMap
1920: let OSM = new L.tileLayer(
1921: 'https://tile.openstreetmap.jp/{z}/{x}/{y}.png',
1922: {
1923: attribution: "© <a href='https://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors",
1924: minZoom: 0,
1925: maxZoom: 18,
1926: name: 'OSM'
1927: });
1928:
1929: //baseMapsオブジェクトにタイル設定
1930: let baseMaps = {
1931: "地理院地図" : GSISTD,
1932: "淡色地図" : GSIPALE,
1933: "白地図" : GSIBLANK,
1934: "写真地図" : GSIPHOTO,
1935: "オープンストリートマップ" : OSM
1936: };
1937:
1938: //地理院地図:色別標高図(オーバーレイ)
1939: let GSIELEV = new L.tileLayer(
1940: 'https://cyberjapandata.gsi.go.jp/xyz/relief/{z}/{x}/{y}.png',
1941: {
1942: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1943: opacity: 0.6,
1944: minZoom: 5,
1945: maxZoom: 15,
1946: name: 'GSIELEV'
1947: });
1948: //地理院地図:活断層図(オーバーレイ)
1949: let GSIFAULT = new L.tileLayer(
1950: 'https://cyberjapandata.gsi.go.jp/xyz/afm/{z}/{x}/{y}.png',
1951: {
1952: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1953: opacity: 0.6,
1954: minZoom: 11,
1955: maxZoom: 16,
1956: name: 'GSIFAULT'
1957: });
1958: //地理院地図:治水地形分類図 更新版(オーバーレイ)
1959: let GSIFLOOD = new L.tileLayer(
1960: 'https://cyberjapandata.gsi.go.jp/xyz/lcmfc2/{z}/{x}/{y}.png',
1961: {
1962: attribution: "<a href='https://maps.gsi.go.jp/development/ichiran.html' target='_blank'>地理院タイル</a>",
1963: opacity: 0.6,
1964: minZoom: 11,
1965: maxZoom: 16,
1966: name: 'GSIFLOOD'
1967: });
1968:
1969: //baseMapsオブジェクトにオーバーレイ設定
1970: let overlayMaps = {
1971: "色別標高図" : GSIELEV,
1972: "活断層図" : GSIFAULT,
1973: "治水地形分類図" : GSIFLOOD,
1974: };
1975:
1976: //layersコントロールにbaseMapsオブジェクトを設定して地図に追加
1977: L.control.layers(baseMaps, overlayMaps).addTo(map);
1978: {$type}.addTo(map);
1979: {$addoverlay}
1980:
1981: //イベント追加
1982: map.on('viewreset', getPointData);
1983: map.on('zoomend', getPointData);
1984: map.on('baselayerchange', getPointData);
1985: map.on('overlayadd', getPointData);
1986: map.on('overlayremove', getPointData);
1987: map.on('move', getPointData);
1988:
1989: //イベント発生時の地図情報を取得・格納
1990: function getPointData() {
1991: let pos = map.getCenter();
1992: //経度
1993: if (document.getElementById('longitude') != null) {
1994: document.getElementById('longitude').value = pos.lng;
1995: }
1996: //緯度
1997: if (document.getElementById('latitude') != null) {
1998: document.getElementById('latitude').value = pos.lat;
1999: }
2000: //ズーム
2001: if (document.getElementById('zoom') != null) {
2002: document.getElementById('zoom').value = map.getZoom();
2003: }
2004: //タイプ
2005: if (document.getElementById('type') != null) {
2006: for (let k in baseMaps) {
2007: if (map.hasLayer(baseMaps[k])) {
2008: document.getElementById('type').value = baseMaps[k].options.name;
2009: }
2010: }
2011: }
2012: //オーバーレイ
2013: if (document.getElementById('overlays') != null) {
2014: let str = '';
2015: let cnt = 0;
2016: for (let k in overlayMaps) {
2017: if (map.hasLayer(overlayMaps[k])) {
2018: if (cnt > 0) str += ',';
2019: str += overlayMaps[k].options.name;
2020: cnt++;
2021: }
2022: }
2023: document.getElementById('overlays').value = str;
2024: }
2025: {$call}
2026: }
2027:
2028:
2029: EOD;
2030: //地点情報
2031: if ($items != NULL) {
2032: foreach ($items as $i=>$item) {
2033: if ($i > 999) break; //最大999箇所まで
2034: $mark = (string)sprintf('%03d', $i);
2035: //アイコン
2036: $mark2 = ($i <= 26) ? $this->num2alpha($i) : 'Z';
2037: $icon = isset($item['icon']) ? $item['icon'] :
2038: "https://www.google.com/mapfiles/marker{$mark2}.png";
2039: list($icon_width, $icon_height) = getimagesize($icon);
2040: $offx = round($icon_width / 2);
2041: $offy = $icon_height;
2042: $info = isset($item['description']) ? "marker_{$mark}.bindPopup('{$item['description']}', {maxWidth: {$max_width}, offset: [{$offset[0]}, {$offset[1]}] });" : '';
2043:
2044: //アイコン・ラベル
2045: if (isset($item['label']) && ($item['label'] != '')) {
2046: $ss =<<< EOD
2047: let icon_{$mark} = new L.divIcon({
2048: html: '<span style="color:{$item['label_color']}; font-size:{$item['label_size']}px; font-weight:{$item['label_weight']}; white-space:nowrap;">{$item['label']}</span>',
2049: iconSize: [0, 0],
2050: iconAnchor: [{$item['label_size']}, {$item['label_size']}],
2051: });
2052:
2053: EOD;
2054: //通常アイコン
2055: } else {
2056: $ss =<<< EOD
2057: let icon_{$mark} = new L.icon({
2058: iconUrl: '{$icon}',
2059: iconAnchor: [{$offx}, {$offy}]
2060: });
2061: EOD;
2062: }
2063: $code .=<<< EOD
2064: {$ss}
2065: let marker_{$mark} = new L.Marker([{$item['latitude']}, {$item['longitude']}], {icon: icon_{$mark}}).addTo(map);
2066: {$info}
2067:
2068: EOD;
2069: }
2070: }
2071: //追加関数
2072: if ($call2 != NULL) {
2073: $code .=<<< EOD
2074: {$call2}
2075:
2076: EOD;
2077: }
2078: $code .=<<< EOD
2079: }
2080: </script>
2081:
2082: EOD;
2083:
2084: return $code;
2085: }
まずソースとして、UNPKG から Leaflet 本体とスタイルシートを読み込む。
Leaflet のレイヤを切り替えることで、以下の地図が切り替わるようにしてある。
- 地理院地図:標準地図
- 地理院地図:淡色地図
- 地理院地図:白地図
- 地理院地図:写真
- OpenStreetMap
マップのドラッグ、ズーム変更、レイヤ変更のイベントを拾って(on)、getPointDate() 関数を呼び出す。
このユーザー関数内で、緯度・経度、ズーム値を、IDで示されるオブジェクトに格納する。hidden属性のテキストボックスに格納することを想定している。
これにより、ページ切替が起きても地図の状態を保持できる。
また、本メソッドに JavaScriptを引数 $call として渡してやれば、getPointDate() 関数内で追加で呼び出す。
引数 $items を渡してやれば、地図上にマーキングする。
$items は2次元配列で、1次元目は地点番号(1以上)、2次元目は情報の種類である。たとえば $items[3]['description'] には、地点番号3の情報ウィンドウに表示する内容HTMLを代入する。
$items には、マッピングするアイコンURLも指定可能である。指定しない場合は、Googleマップの標準的なアルファベットアイコンによってマーキングする。
動的マップ描画
2458: /**
2459: * 地図サービスを利用してJavaScriptマップを描く
2460: *
2461: * @param string $id マップID
2462: * @param float $latitude 中心座標:緯度(世界測地系)
2463: * @param float $longitude 中心座標:経度(世界測地系)
2464: * @param string $type マップタイプ
2465: * Googleの場合 HYBRID/ROADMAP/SATELLITE/TERRAIN
2466: * Yahoo!JAPANの場合 NORMAL/PHOTO/B1/OSM
2467: * @param int $zoom 拡大率
2468: * @param string $call イベント発生時にコールする関数(省略可)
2469: * @param array $items 地点情報(省略可能)
2470: * string title タイトル(Yahoo!では無効)
2471: * string description 情報ウィンドウに表示する内容(HTML文)
2472: * float latitude 緯度
2473: * float longitude 経度
2474: * string icon アイコンURL
2475: * string label アイコン・ラベル(省略可能)
2476: * string label_size アイコン・ラベルのサイズ(省略可能)
2477: * string label_weight アイコン・ラベルの太さ(省略可能)
2478: * string label_color アイコン・ラベルの色(省略可能)
2479: * @param string $api 0:Google Maps JavaScript(省略時)
2480: * 2:地理院地図・OSM(Leaflet使用)
2481: * @param string $call2 追加スクリプト(省略可)
2482: * @param int $max_width 情報ウィンドウの最大幅(省略時:200)
2483: * @param array $offset アイコンから情報ウィンドウのオフセット位置(省略時:0,0)
2484: * @param array $overlays Leaflet用オーバーレイ
2485: * @return string JavaScriptマップのコード
2486: */
2487: function drawJSmap($id, $latitude, $longitude, $type, $zoom, $call=NULL, $items=NULL, $api=0, $call2=NULL, $max_width=200, $offset=NULL, $overlays=NULL) {
2488: //マップタイプの読み替え
2489: static $tbl1 = array('HYBRID'=>'PHOTO', 'ROADMAP'=>'NORMAL', 'SATELLITE'=>'PHOTO', 'TERRAIN'=>'PHOTO');
2490: static $tbl2 = array('NORMAL'=>'ROADMAP', 'PHOTO'=>'SATELLITE', 'B1'=>'ROADMAP', 'OSM'=>'ROADMAP');
2491: static $tbl3 = array('HYBRID'=>'GSISTD', 'ROADMAP'=>'OSM', 'SATELLITE'=>'GSIPHOTO', 'TERRAIN'=>'GSIPHOTO');
2492: $type = strtoupper($type);
2493:
2494: switch ($api) {
2495: //Google Maps JavaScript
2496: case 0;
2497: $type = isset($tbl2[$type]) ? $tbl2[$type] : $type;
2498: $js = $this->drawGMap($id, $latitude, $longitude, $type, $zoom, $call, $items, $call2, $max_width, $offset);
2499: break;
2500: /** 2020年10月31日サービス終了
2501: //Yahoo! JavaScriptマップ
2502: case 1:
2503: $type = isset($tbl1[$type]) ? $tbl1[$type] : $type;
2504: $js = $this->drawYOLPmap($id, $latitude, $longitude, $type, $zoom, $call, $items, $call2);
2505: break;
2506: **/
2507: //地理院地図・OSM(Leaflet使用)
2508: case 2:
2509: $type = isset($tbl3[$type]) ? $tbl3[$type] : $type;
2510: $js = $this->drawLeaflet($id, $latitude, $longitude, $type, $zoom, $call, $items, $call2, $max_width, $offset, $overlays);
2511: break;
2512: }
2513:
2514: return $js;
2515: }
マップタイプについては、相互に読み替えができるようにしてある。
その他の WebAPI
pahooGeoCode::getAddress3 が呼び出すWebAPIについては、「PHPで緯度・経度から住所を求める」を参照のこと。
活用例
参考サイト
- HeartRails Geo API
- Google Maps JavaScript API
- 【重要】 YOLP Web APIにおける一部API・SDKの提供終了お知らせ:Yahoo! Open Local Platform (YOLP)
- Leaflet
- 地理院タイル:国土地理院
- OpenStreetMap
- PHPで住所・ランドマークから緯度・経度を求める:ぱふぅ家のホームページ
- PHPで緯度・経度から住所を求める:ぱふぅ家のホームページ
- 地図・住所から「最寄り駅」をグーグルマップで探す:みんなの知識 ちょっと便利帳
これと、Googleや地理院地図、オープンストリートマップの地図サービスをクラウド連携することで、住所やランドマークから最寄り駅を求めるPHPプログラムを作ってみることにする。
なお、Yahoo! JavaScriptマップは、2020年(令和2年)10月31日をもってサービスを終了しており利用できない。
(2023年7月14日)検索キーの最小・最大長が指定できるようにした.
(2023年7月2日)国土地理院ジオコーディングAPIを利用できるようにした.