PHPでスーパームーンを計算する

(1/1)
2018 年(平成 30 年)1 月 2 日の満月は、月が地球に最も近づき大きく見えることから「スーパームーン」と呼ばれる。
PHP で月齢を計算」で月齢を求めるプログラムを作ったが、今回は満月(月齢15)の時の月の大きさ(視半径)を計算するプログラムを作ってみることにする。

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

PHPでスーパームーンを計算する
月の平均視半径は 15'32"58(1'は 1 度の 60 分の 1、1"はさらに 60 分の 1)であるから、2018 年(平成 30 年)1 月 2 日のスーパームーン平均より半径が 7.7%も大きく見えることになる。

サンプル・プログラム

準備:外部クラスなど

0026: //計算期間(月)
0027: define('CALC_TERM', 12);
0028: 
0029: //世界時からの時差
0030: define('UTCDIFF', 9);
0031: 
0032: //暦計算クラス
0033: require_once('pahooCalendar.php');

あらかじめ、計算期間(月)と世界時からの時差を定数として定義しておく。

月齢、月の視半径、各種カレンダー計算は、ユーザークラス "pahooCalendar" に用意したメソッド群を利用する。
そこで、クラスファイル "pahooCalendar.php" を  require_once  し、オブジェクトを生成する。

解説:月の視半径を求める

0902: /**
0903:  * 月の視差
0904:  * @param double $jy 2000.0からの経過年数
0905:  * @return double 月の視差
0906: */
0907: function __dif_moon($jy) {
0908:     $p_moon  =  0.0003 * sin(deg2rad($this->__angle(227.0  +  4412.0   * $jy)));
0909:     $p_moon +=  0.0004 * sin(deg2rad($this->__angle(194.0  +  3773.4   * $jy)));
0910:     $p_moon +=  0.0005 * sin(deg2rad($this->__angle(329.0  +  8545.4   * $jy)));
0911:     $p_moon +=  0.0009 * sin(deg2rad($this->__angle(100.0  + 13677.3   * $jy)));
0912:     $p_moon +=  0.0028 * sin(deg2rad($this->__angle(  0.0  +  9543.98  * $jy)));
0913:     $p_moon +=  0.0078 * sin(deg2rad($this->__angle(325.7  +  8905.34  * $jy)));
0914:     $p_moon +=  0.0095 * sin(deg2rad($this->__angle(190.7  +  4133.35  * $jy)));
0915:     $p_moon +=  0.0518 * sin(deg2rad($this->__angle(224.98 +  4771.989 * $jy)));
0916:     $p_moon +=  0.9507 * sin(deg2rad($this->__angle(90.0)));
0917: 
0918:     return $p_moon;
0919: }
0920: 
0921: /**
0922:  * 月の視差
0923:  * @param int $year, $month, $day  グレゴリオ暦による年月日
0924:  * @param double $hour, $min, $sec 時分秒(世界時)
0925:  * @return double 月の視差
0926: */
0927: function dif_moon($year$month$day$hour$min$sec) {
0928:     $jy = $this->Gregorian2JY($year$month$day$hour$min$sec);
0929: 
0930:     return $this->__dif_moon($jy);
0931: }
0932: 
0933: /**
0934:  * 月の視半径(視差から算出)
0935:  * @param int $year, $month, $day  グレゴリオ暦による年月日
0936:  * @param double $hour, $min, $sec 時分秒(世界時)
0937:  * @return double 月の視半径(度)
0938: */
0939: function rad_moon($year$month$day$hour$min$sec) {
0940:     $dif = $this->dif_moon($year$month$day$hour$min$sec);
0941:     $rad = asin(0.2725 * sin(deg2rad($dif)));
0942: 
0943:     return rad2deg($rad);
0944: }

月の視半径 rad は、下記の計算式により月の視差から求めることができる。
これを rad_moon として実装している。
 mimetex 

解説:ある期間に起きる満月と視半径を計算

0120: /**
0121:  * ある期間に起きる満月と視半径を計算
0122:  * @param int $year  開始年(西暦)
0123:  * @param int $month 開始月
0124:  * @param int $term  計算期間(月)
0125:  * @param array $items 計算結果を格納する配列
0126:  * @return int 満月の回数
0127: */
0128: function calcSuperMoon($year$month$term, &$items) {
0129:     $pcl = new pahooCalendar();
0130:     $pcl->setLanguage('jp');
0131: 
0132:     $cnt = 0;
0133:     for ($i = 0; $i < $term$i++) {
0134:         $day = 1;
0135:         while ($day <= $pcl->getDaysInMonth($year$month)) {
0136:             $m1 = $pcl->moon_age($year$month$day, -UTCDIFF, 0, 0);
0137:             $m2 = $pcl->moon_age($year$month$day, 23-UTCDIFF, 59, 59);
0138:             if ($m1 <= 15.0 && $m2 > 15.0) {
0139:                 for ($hour = -UTCDIFF$hour < 24-UTCDIFF$hour++) {
0140:                     $m2 = $pcl->moon_age($year$month$day$hour, 0, 0);
0141:                     if ($m2 > 15.0)  break;
0142:                 }
0143:                 //月の視半径
0144:                 $items[$cnt]['rad'] = $pcl->rad_moon($year$month$day$hour, 0, 0);
0145:                 $items[$cnt]['age']   = $m2;
0146:                 $items[$cnt]['year']  = $year;
0147:                 $items[$cnt]['month'] = $month;
0148:                 $items[$cnt]['day']   = $day;
0149:                 $items[$cnt]['week']  = $pcl->getWeekString($year$month$day);
0150:                 $items[$cnt]['hour']  = $hour + UTCDIFF;
0151:                 $cnt++;
0152:             }
0153:             $day++;
0154:         }
0155:         //次の月
0156:         $month++;
0157:         if ($month > 12) {
0158:             $year++;
0159:             $month = 1;
0160:         }
0161:     }
0162: 
0163:     return $cnt;
0164: }

ユーザー関数 calcSuperMoon は、指定した期間の間に起きる満月と視半径を計算し、配列 $items に格納する。

満月のタイミングは逆算できないので、力任せに毎日の月齢を計算し、15 を超えたタイミングをチェックしている。
ここで、ユーザークラス "pahooCalendar" は世界時をベースにしているため、世界時との時差(UTCDIFF)を参照し、それによってカレンダーの日付を補正している。

解説:一覧表作成

0167: /**
0168:  * 一覧表作成
0169:  * @param array $items 計算結果
0170:  * @return string HTML文字列
0171: */
0172: function makeTable($items) {
0173:     //最小の視半径を求める
0174:     $minrad = 999;
0175:     foreach ($items as $item) {
0176:         if ($item['rad'] < $minrad)    $minrad = $item['rad'];
0177:     }
0178: 
0179:     //一覧表作成
0180: $outstr =<<< EOT
0181: <table>
0182: <tr><th>年月日・時</th><th colspan="2">月の視半径<br /><span class="comp">(最小半径を100%)</span></th><th>明るさの比<br /><span class="comp">(最小半径を100%)</span></th></tr>
0183: 
0184: EOT;
0185:     foreach ($items as $item) {
0186:         $ymd = sprintf("%04d年%02d月%02d日(%s) %02d時",
0187:             $item['year'], $item['month'], $item['day'], $item['week'], $item['hour']);
0188:         $mm = intval($item['rad'] * 60);
0189:         $s1 = intval(($item['rad'] * 60 - $mm) * 60);
0190:         $s2 = ($item['rad'] * 60 - $mm - $s1 / 60) * 60 * 100;
0191:         $rad = sprintf("%2d'%02d\".%02d", $mm$s1$s2);
0192:         $rat = sprintf("%3.1f", $item['rad'] / $minrad * 100);
0193:         $brg = sprintf("%3.1f", pow($item['rad'] / $minrad, 2) * 100);
0194: $outstr .=<<< EOT
0195: <tr>
0196: <td>{$ymd}</td>
0197: <td>{$rad}</td>
0198: <td>{$rat}%</td>
0199: <td>{$brg}%</td>
0200: </tr>
0201: 
0202: EOT;
0203:     }
0204: $outstr .=<<< EOT
0205: </table>
0206: 
0207: EOT;
0208: 
0209:     return $outstr;
0210: }

ユーザー関数 makeTable は、calcSuperMoon の計算結果を格納した配列 $items を縦覧し、HTML のテーブルを作成してゆく。

冒頭で、期間中の最小半径を求める。
これを基準に、視半径の比率、明るさの比率(半径の 2 乗)を計算し、一覧表にしてゆく。

参考サイト

(この項おわり)
header