PHPで地図上に恵方を表示

(1/1)
PHP で二十四節気・七十二候一覧を作成」では、毎年の節分の日を計算するプログラムを作成した。節分の日に恵方の方角を向いて恵方巻きを無言で食べると良いとされるが、この恵方は、年によって方角が変化する。そこで今回は、PHP を使い、指定した年の恵方の方角を地図上に矢印で描く PHP プログラムを作ってみることにする。

目次

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

PHPで地図上に恵方を表示
Googleマップ

サンプル・プログラムのダウンロード

圧縮ファイルの内容
eho.phpサンプル・プログラム本体
pahooGeoCode.php住所・緯度・経度に関わるクラス pahooGeoCode。
使い方は「PHPで住所・ランドマークから最寄り駅を求める」などを参照。include_path が通ったディレクトリに配置すること。
pahooCalendar.php暦計算クラス pahooCalendar。
暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。

恵方とは

恵方ロールケーキを食べる男の子のイラスト
節分の日に恵方の方角を向いて恵方巻きを無言で食べると良いとされるが、この恵方は、年によって方角が変化する。

もともとは、正月に各家庭で迎える歳神 (としがみ) 様がやって来る方角を意味していた。旧暦では立春が正月にあたるので、その前日の節分に、歳神様を迎える準備をする。
陰陽道が普及すると、恵方の方角に、その年の歳徳神 (としとくじん) (恵方神)がいて、祟り神が巡ってこない良い方角とされた。
恵方は、その年の十干 (じっかん) によって決まる。十干は、甲・乙・丙・丁・戊・己・庚・辛・壬・癸の 10 の要素が割り当てられている。つまり、年号を 10 で割った余りで、その年の十干を知ることができる。
恵方は 4種類しかなく、たとえば甲の年と己の年の恵方は同じである。
十干と恵方の対応を整理すると、下表のようになる。
十干 西暦の下1桁 恵方
4 東北東微東 75度
5 西南西微西 255度
6 南南東微南 165度
7 北北西微北 345度
8 南南東微南 165度
9 東北東微東 75度
0 西南西微西 255度
1 南南東微南 165度
2 北北西微北 345度
3 南南東微南 165度

解説:pahooCalendarクラス

0076: //暦計算クラス:include_pathが通ったディレクトリに配置
0077: require_once('pahooCalendar.php');

0351: //pahooCalendarクラス
0352: $pcl = new pahooCalendar();
0353: $pcl->setLanguage('jp');

恵方の方位角の計算は、ユーザークラス "pahooCalendar" に分離している。
まず、クラスファイル "pahooCalendar.php" を  require_once  し、オブジェクトを生成する。

準備:pahooGeoCode クラス

0073: //住所・緯度・経度に関わるクラス:include_pathが通ったディレクトリに配置
0074: require_once('pahooGeoCode.php');

0349: //pahooGeoCodeクラス
0350: $pgc = new pahooGeoCode();

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

Yahoo! JAPAN Web サービスを利用するには Yahoo! JAPAN Web サービス アプリケーション IDが必要で、その入手方法は「Yahoo!JAPAN デベロッパーネットワーク - WebAPI の登録方法」を参照されたい。
また、地図として Google マップを利用するのであれば、Google Cloud Platform API キー が必要で、その入手方法は「Google Cloud Platform - WebAPI の登録方法」を参照されたい。また、Google マップで経路探索を行う場合は、DirectionsService APIを有効にする必要がある。課金率が高いので留意されたい。

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

0034: //地図サービス(WebAPI)の選択
0035: //    0:Google
0036: //    2:地理院地図・OSM
0037: define('MAPSERVICE', 2);

表示する地図は、Google マップと地理院地図・ OSM から選べる。
あらかじめ、定数 MAPSERVICE に値を設定すること。

準備:住所検索サービス(WebAPI)の選択

0039: //住所検索サービスの選択
0040: //    0:Google
0041: //    1:Yahoo!JAPAN
0042: //   11:HeartRails Geo API
0043: //   12:OSM Nominatim Search API
0044: define('GEOSERVICE', 0);

住所やワンドマークから緯度・軽度を検索する WebAPI を選ぶことができる。
あらかじめ、定数 GEOSERVICE に値を設定すること。

準備:各種パラメータ

0053: //マップの表示サイズ(単位:ピクセル)
0054: define('MAP_WIDTH',  600);
0055: define('MAP_HEIGHT', 400);
0056: //マップID
0057: define('MAPID', 'map_id');
0058: 
0059: //初期値
0060: define('DEF_LONGITUDE', 139.766667);  //地図中心(経度)
0061: define('DEF_LATITUDE',  35.681111);      //    (緯度)
0062: define('DEF_QUERY',     '東京駅');        //検索クエリ
0063: define('DEF_TYPE',     'roadmap');        //マップタイプ
0064: define('DEF_ZOOM',     12);         //ズーム
0065: define('DEF_CAT',     'address');        //カテゴリ(Yahoo!)
0066: define('LINE_COLOR', '#FF0000');       //恵方の色
0067: define('LINE_OPACITY', 0.6);         //恵方の透明度
0068: define('LINE_WEIGHT', 5);              //恵方の太さ
0069: define('LINE_LENGTH', 5000);           //恵方の全長(メートル)
0070: define('ARROW_ANGLE', 20);         //恵方:矢印の角度
0071: define('ARROW_LENGTH', 0.1);         //恵方:矢の長さ(全長に対する比率)

地図や矢印の表示レイアウトを決めるための各種パラメータである。変更不可としているもの以外は、任意の値を設定できる。

解説:恵方の方位角を求める

0740: /**
0741:  * 恵方の方位角を求める
0742:  * @param int $year 西暦年
0743:  * @return float 恵方の方位角(北を0度として時計回り)
0744: */
0745: function eho($year) {
0746:     static $table = array(
0747:         255,        //庚年(西南西微西)
0748:         165,        //辛年(南南東微南)
0749:         345,        //壬年(北北西微北)
0750:         165,        //癸年(南南東微南)
0751:          75,        //甲年(東北東微東)
0752:         255,        //乙年(西南西微西)
0753:         165,        //丙年(南南東微南)
0754:         345,        //丁年(北北西微北)
0755:         165         //戊年(南南東微南)
0756:     );
0757:     $i = $year % 10;
0758: 
0759:     return $table[$i];
0760: }

恵方の方位角を求めるには、前述の「恵方とは」で紹介した一覧を配列として用意しておき、西暦年を 10 で割った剰余と紐付けてやる。

解説:地図上に矢印を描く

0202: /**
0203:  * 地図上に矢印を描くスクリプトを生成
0204:  * @param   object $pgc       pahooGeoCodeクラス
0205:  * @param float  $lat, $lng 開始点の緯度・経度
0206:  * @param float  $angle     方位角(北を0度として時計回り)
0207:  * @param int    $api   0:Google Maps JavaScript(省略時)
0208:  *                       11:地理院地図・OSM(Leaflet使用)
0209:  * @return string JavaScript
0210: */
0211: function jsArrow($pgc$latitude$longitude$angle$api=0) {
0212:     //本線
0213:     list($lat1$lng1) = $pgc->getPointAngle($longitude$latitude$angleLINE_LENGTH);
0214:     $points[0]['latitude']  = $latitude;
0215:     $points[0]['longitude'] = $longitude;
0216:     $points[1]['latitude']  = $lat1;
0217:     $points[1]['longitude'] = $lng1;
0218:     $js = $pgc->jsLine($pointsLINE_COLORLINE_OPACITYLINE_WEIGHTMAPSERVICE);
0219: 
0220:     //矢印
0221:     $a = $angle - 180 - ARROW_ANGLE;
0222:     if ($a < 0)     $a += 360;
0223:     list($lat2$lng2) = $pgc->getPointAngle($lng1$lat1$aLINE_LENGTH * ARROW_LENGTH);
0224:     $points[0]['latitude']  = $lat1;
0225:     $points[0]['longitude'] = $lng1;
0226:     $points[1]['latitude']  = $lat2;
0227:     $points[1]['longitude'] = $lng2;
0228:     $js .= $pgc->jsLine($pointsLINE_COLORLINE_OPACITYLINE_WEIGHTMAPSERVICE);
0229: 
0230:     $a = $angle - 180 + ARROW_ANGLE;
0231:     if ($a < 0)     $a += 360;
0232:     list($lat3$lng3) = $pgc->getPointAngle($lng1$lat1$aLINE_LENGTH * ARROW_LENGTH);
0233:     $points[1]['latitude']  = $lat3;
0234:     $points[1]['longitude'] = $lng3;
0235:     $js .= $pgc->jsLine($pointsLINE_COLORLINE_OPACITYLINE_WEIGHTMAPSERVICE);
0236: 
0237:     return $js;
0238: }

恵方の方角へ向かって、地図上に矢印を描く。
矢印を 3 本の直線に分解して描くことを考える。方位角を示す本線と、その終点にぶら下がる矢印(2 本の短い線分)である。

本線は、eho 関数で得られた方位角へ向かって直線を描けばいい。始点と終点は緯度・経度で指定する必要があるので、始点から、方位角と距離を指定して終点を算出するユーザー関数 getPointAngle を用意した(後述)。

次に矢印部分であるが、これは、終点から見て、角度 +ARROW_ANGLE 方向、-ARROW_ANGLE 方向へ短い線分を描けばいい。線分の長さは、本線との比率 ARROW_LENGTH で計算している。

解説:方位角と距離を指定した地点の緯度・経度を求める

0647: /**
0648:  * ある地点から方位角と距離を指定した地点の緯度・経度を求める
0649:  * @param float $longitude 経度(世界測地系)
0650:  * @param float $latitude  緯度(世界測地系)
0651:  * @param float $angle     方位角(度)
0652:  * @param float $distance  距離(メートル)
0653:  * @return float array(緯度,経度)
0654: */
0655: function getPointAngle($longitude$latitude$angle$distance) {
0656:     $rad = 6378137.0;         //地球の半径(メートル)
0657:     $e2  = 6.69437999019758E-03;
0658: 
0659:     $wt  = sqrt(1.0 - $e2 * pow(sin($latitude * pi() / 180.0), 2));
0660:     $mt  = $rad * (1.0 - $e2) / pow($wt, 3);
0661:     $dit = $distance * cos($angle * pi() / 180.0) / $mt;
0662:     $i   = $latitude * pi() / 180.0 + $dit / 2;
0663:     $w   = sqrt(1.0 - $e2 * pow(sin($i), 2));
0664: 
0665:     //緯度
0666:     $m   = $rad * (1 - $e2) / pow($w, 3);
0667:     $di  = $distance * cos($angle * pi() / 180) / $m;
0668:     $lat = $latitude + $di * 180 / pi();
0669: 
0670:     //経度
0671:     $n   = $rad / $w;
0672:     $dk  = $distance * sin($angle * pi() / 180) / ($n * cos($i));
0673:     $lng = $longitude + $dk * 180 / pi();
0674: 
0675:     return array($lat$lng);
0676: }

ある地点から方位角と距離を指定した地点の緯度・経度を求めるユーザー関数が getPointAngle である。球面三角法の方程式を解くことで計算している。

解説:Google JavaScriptマップ用スクリプト生成(直線描画)

0984: /**
0985:  * 直線描画スクリプト:Googleマップ用
0986:  * @param array  $points  直線の座標配列
0987:  *                      [$n]['longitude'] 経度(世界測地系)
0988:  *                      [$n]['latitude']  緯度(世界測地系)
0989:  * @param string $color   描画色(省略時=#FF0000)
0990:  * @param float  $opacity 透明度(省略時=1)
0991:  * @param int    $weight  線の太さ(省略時=1)
0992:  * @return string JavaScript
0993: */
0994: function jsLine_Gmap($points$color='#FF0000', $opacity=1.0, $weight=1) {
0995:     $ss = '';
0996:     $cnt = 0;
0997:     foreach ($points as $pt) {
0998:         if ($cnt > 0)   $ss .= ",\n";
0999:         $ss .= "\t\tnew google.maps.LatLng({$pt['latitude']}, {$pt['longitude']})";
1000:         $cnt++;
1001:     }
1002: 
1003: $js =<<< EOT
1004:     var pt = [
1005: {$ss}
1006:     ];
1007:     var lines = new google.maps.Polyline({
1008:         map: map,
1009:         path: pt,
1010:         strokeColor: '{$color}',
1011:         strokeOpacity: {$opacity},
1012:         strokeWeight: {$weight}
1013:     });
1014:     lines.setMap(map);
1015: 
1016: EOT;
1017:     return $js;
1018: }

Google マップ上に直線(折れ線)を描くスクリプトを生成する。緯度・経度座標は配列で渡す。

解説:Leaflet用スクリプト生成(直線描画)

1849: /**
1850:  * 直線描画スクリプト:Leaflet用
1851:  * @param array  $points  直線の座標配列
1852:  *                      [$n]['longitude'] 経度(世界測地系)
1853:  *                      [$n]['latitude']  緯度(世界測地系)
1854:  * @param string $color   描画色(省略時=#FF0000)
1855:  * @param float  $opacity 透明度(省略時=1)
1856:  * @param int    $weight  線の太さ(省略時=1)
1857:  * @return string JavaScript
1858: */
1859: function jsLine_Leaflet($points$color='#FF0000', $opacity='1.0', $weight=1) {
1860:     $ss = '';
1861:     $cnt = 0;
1862:     foreach ($points as $pt) {
1863:         if ($cnt > 0)   $ss .= '';
1864:         $ss .= "[{$pt['latitude']}, {$pt['longitude']}]";
1865:         $cnt++;
1866:     }
1867: 
1868: $js =<<< EOT
1869:     L.polyline(
1870:         [{$ss}], {
1871:         'color':  '{$color}',
1872:         'opacity': {$opacity},
1873:         'weight':  {$weight}
1874:     }).addTo(map);
1875: 
1876: EOT;
1877:     return $js;
1878: }

Google マップ上に直線(折れ線)を描くスクリプトを生成する。緯度・経度座標は配列で渡す。

解説:マップ上に直線を描くスクリプト生成

2203: /**
2204:  * マップ上に直線を描くスクリプト生成
2205:  * @param array  $points  直線の座標配列
2206:  *                      [$n]['longitude'] 経度(世界測地系)
2207:  *                      [$n]['latitude']  緯度(世界測地系)
2208:  * @param string $color   描画色(省略時=#FF0000)
2209:  * @param float  $opacity 透明度(省略時=1)
2210:  * @param int    $weight  線の太さ(省略時=1)
2211:  * @param int    $api   0:Google Maps JavaScript(省略時)
2212:  *                       11:地理院地図・OSM(Leaflet使用)
2213:  * @return string JavaScript
2214: */
2215: function jsLine($points$color='#FF0000', $opacity='1.0', $weight=1, $api=0) {
2216:     switch ($api) {
2217:     //Google Maps JavaScript
2218:     case 0;
2219:         $js = $this->jsLine_Gmap($points$color$opacity$weight$api);
2220:         break;
2221:     //地理院地図・OSM(Leaflet使用)
2222:     case 2:
2223:         $js = $this->jsLine_Leaflet($points$color$opacity$weight$api);
2224:         break;
2225:     }
2226: 
2227:     return $js;
2228: }

jsLine_Gmap または jsLine_Leaflet を呼び出し、マップ上に直線を描くスクリプトを生成する。緯度・経度座標は配列で渡す。
#jsLine_Leaflet:title=jsLine_Leaflet] で生成したスクリプトは、drawJSmap メソッドの引数 $call に渡してやる。

参考サイト

(この項おわり)
header