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

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

(2023年2月3日)表記改訂:バクムーン→バックムーン,スタージャンムーン→スタージョンムーン
(2023年1月14日)数値入力にSpinner、pahooInputDataを導入した。
(2022年12月26日)満月の日を取得するアルゴリズムを見直した。

目次

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

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

サンプル・プログラム

圧縮ファイルの内容
supermoon.phpサンプル・プログラム本体。
pahooCalendar.php暦・潮位計算クラス pahooCalendar。
暦・潮位計算クラスの使い方は「PHPで二十四節気・七十二候一覧を作成」「PHPで月齢を計算」「PHPで日出没・月出没・月齢・潮を計算」「PHPで潮位を計算する」などを参照。include_path が通ったディレクトリに配置すること。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
supermoon.php 更新履歴
バージョン 更新日 内容
1.5.0 2023/01/14 数値入力にSpinner、pahooInputData導入
1.4.0 2023/01/09 pahooCalendarクラスの計算精度を向上
1.3.0 2021/01/04 満月の日を取得するアルゴリズムを見直した
1.2 2021/01/04 PHP8対応,満月の呼び名等を追加
1.1 2018/01/03 明るさの比などを一覧表示
pahooCalendar.php 更新履歴
バージョン 更新日 内容
4.5.1 2025/05/31 deg2ddmm(), deg2hhmm() 不具合修正
4.5.0 2024/03/17 ヒジュラ暦メソッドを追加
4.4.1 2024/03/17 getCabinetOfficeHolidayTable() -- bug-fix
4.4.0 2024/02/25 内閣府の祝日表を参照できるようにした
4.3.2 2023/02/11 getSolarTerm72() 表記改訂:水澤腹堅→水沢腹堅
pahooInputData.php 更新履歴
バージョン 更新日 内容
1.8.1 2025/03/15 validRegexPattern() -- debug
1.8.0 2024/11/12 validRegexPattern() 追加
1.7.0 2024/10/09 validURL() validEmail() 追加
1.6.0 2024/10/07 isButton() -- buttonタグに対応
1.5.0 2024/01/28 exitIfExceedVersion() 追加

準備:初期値など

supermoon.php

  36: //指定できる西暦年の範囲
  37: define('MIN_YEAR', 1901);
  38: define('MAX_YEAR', 2099);
  39: 
  40: //表示言語(jp:日本語, en:英語, en3:英語略記)
  41: define('LANGUAGE', 'jp');
  42: 
  43: //世界時からの時差(日本標準時)
  44: define('UTCDIFF', +9.0);
  45: 
  46: //計算期間(月)
  47: define('CALC_TERM', 12);
  48: 
  49: //視半径が最小/最大の時の背景色
  50: define('MIN_COLOR', '88EEFF');
  51: define('MAX_COLOR', 'FFEE88');
  52: 
  53: //Spinner - jQuery UI を使用するかどうか
  54: define('USESPINNER', TRUE);
  55: 
  56: //数値増減クリックで即判定するかどうか(TRUE:即判定,FALE:判定ボタンを用意)
  57: define('ONCHANGE', FALSE);
  58: 
  59: //表示幅(単位:ピクセル)
  60: define('WIDTH', 600);
  61: 
  62: //require_once()で呼ぶファイルはinclude_pathが通っているフォルダに配置すること。
  63: //暦計算クラス
  64: require_once('pahooCalendar.php');
  65: 
  66: //データ入力に関わる関数群
  67: require_once('pahooInputData.php');

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

月齢、月の視半径、各種カレンダー計算は、ユーザークラス "pahooCalendar" に用意したメソッド群を利用する。
オブジェクト生成時に、表示言語として "ja"(日本語)を、世界時との時差としてユーザー定義の定数 UTCDIFF を渡しておく。こうすることで、pahooCalendar クラスが出力する文字列は日本語に、日時計算は時差 UTCDIFF がある前提で計算を行う。

数値入力に jQuery UISpinner を使用している。設定でOFFにすることもできる。Spinner の使い方については、「Spinner - jQuery UI - PHPで素数かどうか判定」をご覧いただきたい。

入力した数値の取得とバリデーションチェックに、ユーザー定義クラス群 "pahooInputData.php" を利用している。

準備:次の満月の日時を求める

月と太陽の位置関係
月と太陽の位置関係
満月は、月と太陽の黄経差が180度になる瞬間である。
一方、月齢は朔の日(新月の日)を第1日とする日数であるから、月齢15が必ずしも満月の日になるとは限らない。

pahooCalendar.php

1898: /**
1899:  * 指定した日時の次の満月の日時を求める(日時はローカル時間).
1900:  * 太陽と月の黄緯差180度になる日時を近似計算で求める.
1901:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-50-01.shtm
1902:  * @param   int $year, $month, $day  年月日(グレゴリオ暦)
1903:  * @param   float $hour, $min, $sec 時分秒(ローカル時間)
1904:  * @param   float $tdiff UTCとの時差;NULLの時はTDIFF参照
1905:  * @return  array($year, $month, $day, $hour, $min, $sec)  日時(ローカル時間)
1906: */
1907: function next_fullmoon($year, $month, $day, $hour, $min, $sec, $tdiff=NULL) {
1908:     $dif = 29.55;       //一朔望月(近似計算の指標)
1909:     $jd  = $this->Gregorian2JD($year, $month, $day, $hour, $min, $sec, $tdiff);
1910:     $jdc = (float)$jd;
1911: 
1912:     //ニュートン法による近似計算
1913:     for ($i = 1$i < 25$i++) {
1914:         list($year, $month, $day, $hour, $min, $sec) = $this->JD2Gregorian($jdc, $tdiff);
1915:         $ym = $this->longitude_moon($year, $month, $day, $hour, $min, $sec, $tdiff);
1916:         $ys = $this->longitude_sun($year, $month, $day, $hour, $min, $sec, $tdiff);
1917: 
1918:         //太陽と月の黄緯差180度を目指す.
1919:         $dy = $ym - $ys - 180.0;
1920: 
1921:         //引き込み範囲に入ったら補正する.
1922:         if ($dy <-360.0) {
1923:             $dy +360.0;
1924:         } else if ($dy >0.0) {
1925:             $dy -360.0;
1926:         }
1927: 
1928:         //誤差内に入ったらループを脱出する.
1929:         if ($dy >0.0 && $dy <0.0001break;
1930:         if ($dy < -0.0 && $dy >-0.0001)    break;
1931: 
1932:         $dd = $dy / $dif;
1933:         $jdc -$dd;
1934:     }
1935: 
1936:     return array($year, $month, $day, $hour, $min, $sec);
1937: }

そこで、指定した日時から次に、月と太陽の黄経差が180度になる日時を求めるメソッド next_fullmoon を用意した。
太陽の視黄経の計算メソッド longitude_sun と月の視黄経の計算メソッド longitude_moon はすでに説明したとおりだが、黄経値から年月日を計算する逆関数は存在しないため、近似計算によって求めている。このため、国立天文台が発表する時刻と数分のズレがある。

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

pahooCalendar.php

1711: /**
1712:  * J2000.0からの経過年数における月の視差(度)を求める。
1713:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-44-01.shtm
1714:  * @param   float $jy 2000.0からの経過年数
1715:  * @return  float 月の視差(度)
1716: */
1717: function __dif_moon($jy) {
1718:     $p_moon  =  0.0003 * sin(deg2rad($this->__angle(227.0  +  4412.0   * $jy)));
1719:     $p_moon +=  0.0004 * sin(deg2rad($this->__angle(194.0  +  3773.4   * $jy)));
1720:     $p_moon +=  0.0005 * sin(deg2rad($this->__angle(329.0  +  8545.4   * $jy)));
1721:     $p_moon +=  0.0009 * sin(deg2rad($this->__angle(100.0  + 13677.3   * $jy)));
1722:     $p_moon +=  0.0028 * sin(deg2rad($this->__angle(  0.0  +  9543.98  * $jy)));
1723:     $p_moon +=  0.0078 * sin(deg2rad($this->__angle(325.7  +  8905.34  * $jy)));
1724:     $p_moon +=  0.0095 * sin(deg2rad($this->__angle(190.7  +  4133.35  * $jy)));
1725:     $p_moon +=  0.0518 * sin(deg2rad($this->__angle(224.98 +  4771.989 * $jy)));
1726:     $p_moon +=  0.9507 * sin(deg2rad($this->__angle(90.0)));
1727: 
1728:     return $p_moon;
1729: }

pahooCalendar.php

1731: /**
1732:  * 指定した日時における月の視差(度)を求める(日時はローカル時間)。
1733:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-44-01.shtm
1734:  * @param   int $year, $month, $day  グレゴリオ暦による年月日
1735:  * @param   float $hour, $min, $sec 時分秒(ローカル時間)
1736:  * @param   float $tdiff UTCとの時差;NULLの時はTDIFF
1737:  * @return  float 月の視差
1738: */
1739: function dif_moon($year, $month, $day, $hour, $min, $sec, $tdiff=NULL) {
1740:     $jy = $this->Gregorian2JY($year, $month, $day, $hour, $min, $sec, $tdiff);
1741: 
1742:     return $this->__dif_moon($jy);
1743: }

pahooCalendar.php

1745: /**
1746:  * 指定した日時における月の視半径(度)を求める(日時はローカル時間)。
1747:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-50-01.shtm
1748:  * @param   int $year, $month, $day  グレゴリオ暦による年月日
1749:  * @param   float $hour, $min, $sec 時分秒(ローカル時間)
1750:  * @param   float $tdiff UTCとの時差;NULLの時はTDIFF
1751:  * @return  float 月の視半径(度)
1752: */
1753: function rad_moon($year, $month, $day, $hour, $min, $sec, $tdiff=NULL) {
1754:     $dif = $this->dif_moon($year, $month, $day, $hour, $min, $sec, $tdiff);
1755:     $rad = asin(0.2725 * sin(deg2rad($dif)));
1756: 
1757:     return rad2deg($rad);
1758: }

月の視半径 rad は、下記の計算式により月の視差から求めることができる。
これを rad_moon として実装している。
$$ rad = sin^{-1}(0.2725 \times sin(dif)) $$

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

supermoon.php

 201: /**
 202:  * ある期間に起きる満月と視半径を計算
 203:  * @param   int $year  開始年(西暦)
 204:  * @param   int $month 開始月
 205:  * @param   int $term  計算期間(月)
 206:  * @param   array $items 計算結果を格納する配列
 207:  * @return  int 満月の回数
 208: */
 209: function calcSuperMoon($year, $month, $term, &$items) {
 210:     //暦計算に関わるクラス
 211:     $pcl = new pahooCalendar(LANGUAGE, UTCDIFF);
 212: 
 213:     $i = $cnt = 0;
 214:     $day = 1;
 215:     while ($i < $term) {
 216:         //次の満月の日時を求める
 217:         list($year1, $month1, $day1, $hour1, $min1, $sec1) =$pcl->next_fullmoon($year, $month, $day, 0, 0, 0);
 218:         $items[$cnt]['year']  = $year1;
 219:         $items[$cnt]['month'] = $month1;
 220:         $items[$cnt]['day']   = $day1;
 221:         $items[$cnt]['week']  = $pcl->getWeekString($year1, $month1, $day1);
 222:         $items[$cnt]['hour']  = $hour1;
 223:         $items[$cnt]['min']   = $min1 + round($sec1 / 60.0, 0);
 224:         //月の視半径
 225:         $items[$cnt]['rad'] = $pcl->rad_moon($year1, $month1, $day1, $hour1, $min1, $sec1);
 226:         //満月の呼び名
 227:         $items[$cnt]['name']  = $pcl->getFullMoonNickname($month1);
 228:         //次のパラメータを用意する
 229:         $cnt++;
 230:         $year = $year1;
 231:         if ($month < $month1)    $i++;
 232:         $month = $month1;
 233:         $day = $day1 + 20.0;    //次の計算基準は26日後
 234:         if ($day > $pcl->getDaysInMonth($year, $month)) {
 235:             $day = 1;
 236:             $month++;
 237:             $i++;
 238:             if ($month > 12) {
 239:                 $month = 1;
 240:                 $year++;
 241:             }
 242:         }
 243:     }
 244:     //オブジェクト解放
 245:     $pcl = NULL;
 246: 
 247:     return $cnt;
 248: }

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

解説:満月の呼び名

pahooCalendar.php

1979: /**
1980:  * 指定した月の満月の呼び名を求める.
1981:  * 参考サイト https://www.pahoo.org/e-soul/webtech/php02/php02-50-01.shtm
1982:  * @param   int $month  月
1983:  * @return  string 満月の呼び名
1984: */
1985: function getFullMoonNickname($month) {
1986:     $table_jp = array(
1987: 'ウルフムーン',
1988: 'スノームーン',
1989: 'ワームムーン',
1990: 'ピンクムーン',
1991: 'フラワームーン',
1992: 'ストロベリームーン',
1993: 'バックムーン',         //または「バクムーン」.発音はbʌkだが,buckskinをバックスキンと読むことから,ここでは「バクムーン」にした.
1994: 'スタージョンムーン',   //または「スタージャンムーン」.Twitterでアンケートをとて「スタージョンムーン」にした. https://twitter.com/papa_pahoo/status/1620259072871395328
1995: 'ハーベストムーン',
1996: 'ハンターズムーン',
1997: 'ビーバームーン',
1998: 'コールドムーン'
1999: );
2000: 
2001:     $table_en = array(
2002: 'Wolf Moon',
2003: 'Snow Moon',
2004: 'Worm Moon',
2005: 'Pink Moon',
2006: 'Flower Moon',
2007: 'Strawberry Moon',
2008: 'Buck Moon',
2009: 'Sturgeon Moon',
2010: 'Harvest Moon',
2011: 'Hunter\#x27;s Moon',
2012: 'Beaver Moon',
2013: 'Cold Moon'
2014: );
2015: 
2016:     $month = (int)$month;
2017:     if (($month >1&& ($month << 12)) {
2018:         if ($this->language == 'jp') {
2019:             $name = $table_jp[$month - 1];
2020:         } else {
2021:             $name = $table_en[$month - 1];
2022:         }
2023:     } else {
2024:         $name = '';
2025:     }
2026: 
2027:     return $name;
2028: }

メソッド getFullMoonNickname を呼び出すと、指定したつきの満月の呼び名を日本語または英語で返す。

7月の Buck Moonbuk(雄ジカ)は発音はbʌkだが、buckskinをバックスキンと読むことから、ここでは「バクムーン」にした。
8月の Sturgeon Moonsturgeon(チョウザメ)の発音はstɚːdʒənだが、Wikipedia日本語版にある Sturgeon をもつ人名の表記は揺れている。
  • ウィリアム・スタージャン - イギリスの物理学者。
  • シオドア・スタージョン - アメリカのSF作家。
  • ニコラ・スタージョン - スコットランドの政治家。
  • ローリン・S・スタージョン - アメリカの映画監督
また、日本気象協会はスタージャンムーンを使っている。そこでTwitterでアンケートをとり、ここでは「スタージョンムーン」にした。

解説:一覧表作成

supermoon.php

 251: /**
 252:  * 満月一覧から表示用HTMLを求める.
 253:  * @param   array $items 計算結果
 254:  * @return  string HTML文字列
 255: */
 256: function makeTable($items) {
 257:     //最大・最小の視半径を求める
 258:     $minrad = 999;
 259:     $minid = -1;
 260:     $maxrad = 0;
 261:     $maxid = -1;
 262:     foreach ($items as $key=>$item) {
 263:         if ($item['rad'< $minrad) {
 264:             $minrad = $item['rad'];
 265:             $minid = $key;
 266:         }
 267:         if ($item['rad'> $maxrad) {
 268:             $maxrad = $item['rad'];
 269:             $maxid = $key;
 270:         }
 271:     }
 272: 
 273:     //一覧表作成
 274:     $width = WIDTH;
 275:     $outstr =<<< EOT
 276: <table style="width:{$width};">
 277: <tr><th>年月日・時</th><th colspan="2">月の視半径<br /><span class="comp">(最小半径を100%)</span></th><th>明るさの比<br /><span class="comp">(最小半径を100%)</span></th><th>呼び名</th></tr>
 278: 
 279: EOT;
 280:     foreach ($items as $key=>$item) {
 281:         $ymd = sprintf("%04d年%02d月%02d日(%s) %02d:%02d",
 282:             $item['year'], $item['month'], $item['day'], $item['week'], $item['hour'], $item['min']);
 283:         $mm = intval($item['rad'* 60);
 284:         $s1 = intval(($item['rad'* 60 - $mm* 60);
 285:         $s2 = ($item['rad'* 60 - $mm - $s1 / 60* 60 * 100;
 286:         $rad = sprintf("%2d'%02d\".%02d", $mm, $s1, $s2);
 287:         $rat = sprintf("%3.1f", $item['rad'] / $minrad * 100);
 288:         $brg = sprintf("%3.1f", pow($item['rad'] / $minrad, 2* 100);
 289: 
 290:         //背景色
 291:         if ($key == $minid) {
 292:             $color = ' style="background-color:#' . MIN_COLOR . ';"';
 293:         } else if ($key == $maxid) {
 294:             $color = ' style="background-color:#' . MAX_COLOR . ';"';
 295:         } else {
 296:             $color = '';
 297:         }
 298: 
 299:         $outstr .=<<< EOT
 300: <tr{$color}>
 301: <td>{$ymd}</td>
 302: <td>{$rad}</td>
 303: <td>{$rat}%</td>
 304: <td>{$brg}%</td>
 305: <td>{$item['name']}</td>
 306: </tr>
 307: 
 308: EOT;
 309:     }
 310:     $outstr .=<<< EOT
 311: </table>
 312: </body>
 313: 
 314: EOT;
 315: 
 316:     return $outstr;
 317: }

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

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

活用例

みんなの知識 ちょっと便利帳」では、このサンプル・プログラムを活用し、「スーパームーンを調べる」というサービスを提供している。ご活用をありがとうございます。

参考サイト

(この項おわり)
header