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

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

(2025年11月23日)pahooCalendar.next_fullmoonmメソッドの不具合修正

目次

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

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.6.1 2025/11/23 next_fullmoon: bug-fix
4.6.0 2025/10/08 getMidAutumnMoon() 追加
4.5.1 2025/05/31 deg2ddmm(), deg2hhmm() 不具合修正
4.5.0 2024/03/17 ヒジュラ暦メソッドを追加
4.4.1 2024/03/17 getCabinetOfficeHolidayTable() -- bug-fix
pahooInputData.php 更新履歴
バージョン 更新日 内容
2.0.1 2025/08/11 getParam() bug-fix
2.0.0 2025/08/11 pahooLoadEnv() 追加
1.9.0 2025/07/26 getParam() 引数に$trim追加
1.8.1 2025/03/15 validRegexPattern() debug
1.8.0 2024/11/12 validRegexPattern() 追加

準備:pahooInputData 関数群

PHPのバージョンや入力データのバリデーションなど、汎用的に使う関数群を収めたファイル "pahooInputData.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooInputData.php" を利用するが、常に最新のファイルを1つ配置すればよい。

また、各種クラウドサービスに登録したときに取得するアカウント情報、アプリケーションパスワードなどを登録した .pahooEnv ファイルから読み込む関数 pahooLoadEnv を備えている。こちらについては、「各種クラウド連携サービス(WebAPI)の登録方法」をご覧いただきたい。

準備:pahooCalendar クラス

pahooCalendar.php

  11: class pahooCalendar {
  12:     var $CONVERGE = 0.00005;            // 逐次近似計算収束判定値
  13:     var $ASTRO_REFRACT = 0.585556;      // 大気差
  14:     var $TDIFF = +9.0;          // 世界時との時差
  15:     var $error, $errmsg;        // エラーフラグ,エラーメッセージ
  16:     var $year, $month, $day;    // 西暦年月日
  17:     var $tblmoon;               // グレゴリオ暦=旧暦テーブル
  18:     var $language;              // 表示言語(jp:日本語, en:英語, en3:英語略記)
  19:     var $resolve2033;           // 旧暦2033年問題解決案 0:解決しない,1:案1,2:案2,3:対応案3  https://www.pahoo.org/e-soul/webtech/php02/php02-45-01.shtm#php_resolveLunarCalendar2033
  20:     var $pcc;                   // pahooCacheインスタンス
  21:     var $CabinetOfficeHolidayTables;    // 内閣府が公開している祝日表
  22:     const CABINETOFFICE_HOLIDAY_FILE = 'https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv';   // 内閣府が公開している祝日表URL
  23: 
  24: /**
  25:  * コンストラクタ
  26:  * @param   string $language 表示言語;省略時 jp
  27:  * @param   float  $tdiff    世界時との時差(省略時 +9.0;日本標準時)
  28:  * @param   object $pcc      pahooCacheインスタンス;省略時 NULL
  29:  *              インターネット経由で内閣府の国民の祝日を参照するときに指定
  30:  * @return  bool オブジェクト/FALSE:$tdiffが不正
  31: */
  32: function __construct($language='jp', $tdiff=+9.0, $pcc=NULL) {
  33:     $this->error  = FALSE;
  34:     $this->errmsg = '';
  35:     $this->year  = date('Y');
  36:     $this->month = date('n');
  37:     $this->day   = date('j');
  38:     $this->resolve2033 = 0;
  39:     $this->pcc = $pcc;
  40:     $this->CabinetOfficeHolidayTables = array();
  41: 
  42:     $this->setLanguage($language);
  43:     if ($this->setTimeDifference($tdiff) == FALSE) {
  44:         $this->error = TRUE;
  45:         $this->errmsg = 'illegal tdiff';
  46:     }
  47: }

西暦(グレゴリオ暦)計算やヒジュラ暦(イスラム暦)への変換、祝祭日、六曜、二十四節気の算出などの暦関係の計算、太陽や月の位置計算、潮位などを求めるクラス・ファイル "pahooCalendar.php" が同梱されているが、include_path が通ったディレクトリに配置してほしい。他のプログラムでも "pahooCalendar.php" を利用するが、常に最新のクラス・ファイルを1つ配置すればよい。

準備:初期値など

supermoon.php

  55: // 各種定数(START) ===========================================================
  56: 
  57: // 指定できる西暦年の範囲
  58: define('MIN_YEAR', 1901);
  59: define('MAX_YEAR', 2099);
  60: 
  61: // 表示言語(jp:日本語, en:英語, en3:英語略記)
  62: define('LANGUAGE', 'jp');
  63: 
  64: // 世界時からの時差(日本標準時)
  65: define('UTCDIFF', +9.0);
  66: 
  67: // 計算期間(月)
  68: define('CALC_TERM', 12);
  69: 
  70: // 視半径が最小/最大の時の背景色
  71: define('MIN_COLOR', '88EEFF');
  72: define('MAX_COLOR', 'FFEE88');
  73: 
  74: // Spinner - jQuery UI を使用するかどうか
  75: define('USESPINNER', TRUE);
  76: 
  77: // 数値増減クリックで即判定するかどうか(TRUE:即判定,FALE:判定ボタンを用意)
  78: define('ONCHANGE', FALSE);
  79: 
  80: // 表示幅(単位:ピクセル)
  81: define('WIDTH', 600);
  82: 
  83: // 各種定数(END) ===============================================================

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

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

数値入力に jQuery UISpinner を使用している。設定でOFFにすることもできる。Spinner の使い方については、「Spinner - jQuery UI - 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 < 23$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

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

ユーザー関数 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

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

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

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

活用例

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

参考サイト

(この項おわり)
header