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

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

(2021 年 1 月 4 日)PHP8 対応,満月の呼び名,スーパームーンの色分け機能を追加した。

目次

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

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

サンプル・プログラム

圧縮ファイルの内容
supermoon.phpサンプル・プログラム本体。
pahooCalendar.php暦計算クラス pahooCalendar。
暦計算クラスの使い方は「PHPで日出没・月出没・月齢・潮を計算」を参照。include_path が通ったディレクトリに配置すること。

準備:外部クラスなど

0036: //表示幅(単位:ピクセル)
0037: define('WIDTH', 600);
0038: 
0039: //計算期間(月)
0040: define('CALC_TERM', 12);
0041: 
0042: //世界時からの時差
0043: define('UTCDIFF', 9);
0044: 
0045: //指定できる西暦年の範囲
0046: define('MIN_YEAR', 1948);
0047: define('MAX_YEAR', 2099);
0048: 
0049: //視半径が最小/最大の時の背景色
0050: define('MIN_COLOR', '88EEFF');
0051: define('MAX_COLOR', 'FFEE88');
0052: 
0053: //暦計算クラス
0054: require_once('pahooCalendar.php');

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

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

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

0941: /**
0942:  * 月の視差
0943:  * @param double $jy 2000.0からの経過年数
0944:  * @return double 月の視差
0945: */
0946: function __dif_moon($jy) {
0947:     $p_moon  =  0.0003 * sin(deg2rad($this->__angle(227.0  +  4412.0   * $jy)));
0948:     $p_moon +=  0.0004 * sin(deg2rad($this->__angle(194.0  +  3773.4   * $jy)));
0949:     $p_moon +=  0.0005 * sin(deg2rad($this->__angle(329.0  +  8545.4   * $jy)));
0950:     $p_moon +=  0.0009 * sin(deg2rad($this->__angle(100.0  + 13677.3   * $jy)));
0951:     $p_moon +=  0.0028 * sin(deg2rad($this->__angle(  0.0  +  9543.98  * $jy)));
0952:     $p_moon +=  0.0078 * sin(deg2rad($this->__angle(325.7  +  8905.34  * $jy)));
0953:     $p_moon +=  0.0095 * sin(deg2rad($this->__angle(190.7  +  4133.35  * $jy)));
0954:     $p_moon +=  0.0518 * sin(deg2rad($this->__angle(224.98 +  4771.989 * $jy)));
0955:     $p_moon +=  0.9507 * sin(deg2rad($this->__angle(90.0)));
0956: 
0957:     return $p_moon;
0958: }

0960: /**
0961:  * 月の視差
0962:  * @param int $year, $month, $day  グレゴリオ暦による年月日
0963:  * @param double $hour, $min, $sec 時分秒(世界時)
0964:  * @return double 月の視差
0965: */
0966: function dif_moon($year$month$day$hour$min$sec) {
0967:     $jy = $this->Gregorian2JY($year$month$day$hour$min$sec);
0968: 
0969:     return $this->__dif_moon($jy);
0970: }

0972: /**
0973:  * 月の視半径(視差から算出)
0974:  * @param int $year, $month, $day  グレゴリオ暦による年月日
0975:  * @param double $hour, $min, $sec 時分秒(世界時)
0976:  * @return double 月の視半径(度)
0977: */
0978: function rad_moon($year$month$day$hour$min$sec) {
0979:     $dif = $this->dif_moon($year$month$day$hour$min$sec);
0980:     $rad = asin(0.2725 * sin(deg2rad($dif)));
0981: 
0982:     return rad2deg($rad);
0983: }

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

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

0169: /**
0170:  * ある期間に起きる満月と視半径を計算
0171:  * @param int $year  開始年(西暦)
0172:  * @param int $month 開始月
0173:  * @param int $term  計算期間(月)
0174:  * @param array $items 計算結果を格納する配列
0175:  * @return int 満月の回数
0176: */
0177: function calcSuperMoon($year$month$term, &$items) {
0178:     $pcl = new pahooCalendar();
0179:     $pcl->setLanguage('jp');
0180: 
0181:     $cnt = 0;
0182:     for ($i = 0; $i < $term$i++) {
0183:         $day = 1;
0184:         while ($day <= $pcl->getDaysInMonth($year$month)) {
0185:             $m1 = $pcl->moon_age($year$month$day, -UTCDIFF, 0, 0);
0186:             $m2 = $pcl->moon_age($year$month$day, 23-UTCDIFF, 59, 59);
0187:             if ($m1 <= 15.0 && $m2 > 15.0) {
0188:                 for ($hour = -UTCDIFF$hour < 24-UTCDIFF$hour++) {
0189:                     $m2 = $pcl->moon_age($year$month$day$hour, 0, 0);
0190:                     if ($m2 > 15.0)  break;
0191:                 }
0192:                 //月の視半径
0193:                 $items[$cnt]['rad'] = $pcl->rad_moon($year$month$day$hour, 0, 0);
0194:                 $items[$cnt]['age']   = $m2;
0195:                 $items[$cnt]['year']  = $year;
0196:                 $items[$cnt]['month'] = $month;
0197:                 $items[$cnt]['day']   = $day;
0198:                 $items[$cnt]['week']  = $pcl->getWeekString($year$month$day);
0199:                 $items[$cnt]['hour']  = $hour + UTCDIFF;
0200:                 $items[$cnt]['name']  = $pcl->getFullMoonNickname($month);
0201:                 $cnt++;
0202:             }
0203:             $day++;
0204:         }
0205:         //次の月
0206:         $month++;
0207:         if ($month > 12) {
0208:             $year++;
0209:             $month = 1;
0210:         }
0211:     }
0212: 
0213:     return $cnt;
0214: }

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

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

解説:一覧表作成

0217: /**
0218:  * 一覧表作成
0219:  * @param array $items 計算結果
0220:  * @return string HTML文字列
0221: */
0222: function makeTable($items) {
0223:     //最大・最小の視半径を求める
0224:     $minrad = 999;
0225:     $minid = -1;
0226:     $maxrad = 0;
0227:     $maxid = -1;
0228:     foreach ($items as $key=>$item) {
0229:         if ($item['rad'] < $minrad) {
0230:             $minrad = $item['rad'];
0231:             $minid = $key;
0232:         }
0233:         if ($item['rad'] > $maxrad) {
0234:             $maxrad = $item['rad'];
0235:             $maxid = $key;
0236:         }
0237:     }
0238: 
0239:     //一覧表作成
0240:     $width = WIDTH;
0241: $outstr =<<< EOT
0242: <table style="width:{$width};">
0243: <tr><th>年月日・時</th><th colspan="2">月の視半径<br /><span class="comp">(最小半径を100%)</span></th><th>明るさの比<br /><span class="comp">(最小半径を100%)</span></th><th>呼び名</th></tr>
0244: 
0245: EOT;
0246:     foreach ($items as $key=>$item) {
0247:         $ymd = sprintf("%04d年%02d月%02d日(%s) %02d時",
0248:             $item['year'], $item['month'], $item['day'], $item['week'], $item['hour']);
0249:         $mm = intval($item['rad'] * 60);
0250:         $s1 = intval(($item['rad'] * 60 - $mm) * 60);
0251:         $s2 = ($item['rad'] * 60 - $mm - $s1 / 60) * 60 * 100;
0252:         $rad = sprintf("%2d'%02d\".%02d", $mm$s1$s2);
0253:         $rat = sprintf("%3.1f", $item['rad'] / $minrad * 100);
0254:         $brg = sprintf("%3.1f", pow($item['rad'] / $minrad, 2) * 100);
0255: 
0256:         //背景色
0257:         if ($key == $minid) {
0258:             $color = ' style="background-color:#' . MIN_COLOR . ';"';
0259:         } else if ($key == $maxid) {
0260:             $color = ' style="background-color:#' . MAX_COLOR . ';"';
0261:         } else {
0262:             $color = '';
0263:         }
0264: 
0265: $outstr .=<<< EOT
0266: <tr{$color}>
0267: <td>{$ymd}</td>
0268: <td>{$rad}</td>
0269: <td>{$rat}%</td>
0270: <td>{$brg}%</td>
0271: <td>{$item['name']}</td>
0272: </tr>
0273: 
0274: EOT;
0275:     }
0276: $outstr .=<<< EOT
0277: </table>
0278: 
0279: EOT;
0280: 
0281:     return $outstr;
0282: }

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

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

参考サイト

(この項おわり)
header