PHPで月の満ち欠けを描画

(1/1)
PHPで日出没・月出没・月齢を計算」で月齢を求めるプログラムを紹介した。今回は、月齢を入力すると、月の満ち欠けの様子を画像として表示するPHPプログラムをつくってみることにする。

(2026年1月24日)PHP8.5対応:imagedestroyを使わない

目次

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

PHPで月の満ち欠けを描画

サンプル・プログラム

圧縮ファイルの内容
moonage.phpサンプル・プログラム本体。
fullmoon.webp満月画像。
pahooCalendar.php暦・潮位計算クラス pahooCalendar。
暦・潮位計算クラスの使い方は「PHPで二十四節気・七十二候一覧を作成」「PHPで月齢を計算」「PHPで日出没・月出没・月齢・潮を計算」「PHPで潮位を計算する」などを参照。include_path が通ったディレクトリに配置すること。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「PHPでGET/POSTでフォームから値を受け取る」「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
moonage.php 更新履歴
バージョン 更新日 内容
1.4.0 2026/01/24 PHP8.5対応:curl_closeを使わない
1.3.0 2023/01/16 現在の月齢をデフォルト値にする
1.2 2021/12/18 PHP8対応,リファラ・チェック改良
1.1 2015/12/19 改良
1.0 2015/12/13 初版
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() 追加
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

準備:初期値など

moonage.php

  56: // 各種定数(START) ===========================================================
  57: 
  58: // 満月の画像
  59: // PNG形式限定
  60: // 正方形で,領域いっぱいに真円の満月が描かれていること.
  61: // 背景は透明であること.
  62: define('FULLMOON', 'fullmoon.png');
  63: 
  64: // 影の透明度(0~127):0 は完全に不透明な状態。 127 は完全に透明な状態
  65: define('ALPHA', 30);
  66: 
  67: // 明部分を白色で塗るかどうか
  68: define('MOON_BRIGHT', FALSE);
  69: 
  70: // 画像データ保存機能を使うかどうか TRUE:使う,FALSE:使わない
  71: define('ISSAVE', FALSE);
  72: 
  73: // 画像を保存するフォルダ
  74: define('SAVE_PATH', './');
  75: 
  76: // 表示幅(ピクセル)
  77: define('WIDTH', 600);
  78: 
  79: // 表示言語(jp:日本語, en:英語, en3:英語略記)
  80: define('LANGUAGE', 'jp');
  81: 
  82: // 世界時からの時差(日本標準時)
  83: define('UTCDIFF', +9.0);
  84: 
  85: // 各種定数(END) ===============================================================

各種定数は変更可能である。

満月の写真があれば、それを定数 FULLMOON に指定する。
ただし、写真はPNG形式限定。大きさの制限はないが、正方形で、領域いっぱいに真円の満月が描かれていること。また、背景は透明であることが望ましい。
今回は、影の部分だけ半透明の黒で塗りつぶすだけだが、もし太陽光の当たっている明部分を白で塗りたければ、定数 MOON_BRIGHT を TRUE にする。
影や明部分の透明度を制御するアルファチャネルの値は定数 ALPHA に指定する。0 は完全に不透明な状態、127 は完全に透明な状態となる。

月齢ごとの満ち欠け画像を保存するフォルダを、定数 SAVE_PATH に指定する。

データ入力に関わる関数群は別ファイル "pahooInputData.php" に分離しており、include_path が通ったディレクトリに配置すること。
また、暦計算クラス・ファイル "pahooCalendar.php" もinclude_pathが通ったディレクトリに配置すること。

準備: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つ配置すればよい。

解説:指定した月齢の月の満ち欠けを描く

moonage.php

 160: /**
 161:  * 指定した月齢の月の満ち欠けを描く.
 162:  * @param   double $age 月齢
 163:  * @return  object GDリソース
 164: */
 165: function drawMissingMoon($age) {
 166:     if ($age < 0 || $age > 30)       $age = 0;
 167: 
 168:     // 満月画像がなければ円を描画
 169:     if (! file_exists(FULLMOON)) {
 170:         $dd = 500;          // 直径
 171:         $rr = $dd / 2;      // 半径
 172:         $image = imagecreatetruecolor($dd, $dd);
 173:         // 背景透明化
 174:         $bgcolor = imagecolorallocate($image, 0xFF, 0xFF, 0xFF);// 背景色セット
 175:         imagefill($image, 0, 0, $bgcolor);
 176:         imagecolortransparent($image, $bgcolor);
 177:         // 円を描画
 178:         $color = imagecolorallocate($image, 0xFF, 0xFF, 0x7F);
 179:         imagefilledarc($image, $rr, $rr, $dd, $dd, 0, 360, $color, IMG_ARC_PIE);
 180: 
 181:     // 満月画像の読み込み
 182:     } else {
 183:         if (($arr = getimagesize(FULLMOON)) == FALSE)   return FALSE;
 184:         $dd = $arr[0];      // 直径
 185:         $rr = $dd / 2;      // 半径
 186:         // 画像読み込み
 187:         $image = imagecreatefrompng(FULLMOON);
 188:     }
 189: 
 190:     // 完全なアルファチャネル情報を保存するフラグをonにする
 191:     imagesavealpha($image, TRUE);
 192:     // カラー設定
 193:     $black = imagecolorallocatealpha($image, 0x00, 0x00, 0x00, ALPHA);
 194:     $white = imagecolorallocatealpha($image, 0xFF, 0xFF, 0xFF, ALPHA);
 195: 
 196:     // 影を描く
 197:     $x0 = $rr;
 198:     $y0 = $rr;
 199:     $th = $age / 14.765 * pi();
 200:     for ($y = -$rr$y <0$y++) {
 201:         $ac = acos($y / $rr);
 202:         $x2 = $rr * sin($ac);               // 円周
 203:         $x1 = $rr * cos($th* sin($ac);    // 月の形
 204:         if (($age > 0.5&& ($age < 29.5)) {
 205:             if ($age < 15.0) {
 206:                 $x3i = round($x0 + $x1);
 207:                 $x4i = round($x0 + $x2);
 208:                 $x5i = round($x0 - $x2);
 209:                 $y3i = round($y0 + $y);
 210:                 $y4i = round($y0 - $y);
 211:             } else {
 212:                 $x3i = round($x0 - $x1);
 213:                 $x4i = round($x0 - $x2);
 214:                 $x5i = round($x0 + $x2);
 215:                 $y3i = round($y0 + $y);
 216:                 $y4i = round($y0 - $y);
 217:             }
 218:             imageline($image, $x3i, $y3i, $x5i, $y3i, $black);
 219:             if (MOON_BRIGHT)    imageline($image, $x3i, $y3i, $x4i, $y3i, $white);
 220:             if ($y !0) {
 221:                 imageline($image, $x3i, $y4i, $x5i, $y4i, $black);
 222:                 if (MOON_BRIGHT) {
 223:                     imageline($image, $x3i, $y4i, $x4i, $y4i, $white);
 224:                 }
 225:             }
 226:         } else {
 227:             $x3i = round($x0 - $x2);
 228:             $x4i = round($x0 + $x2);
 229:             $y3i = round($y0 + $y);
 230:             $y4i = round($y0 - $y);
 231:             imageline($image, $x3i, $y3i, $x4i, $y3i, $black);
 232:             if ($y !0) {
 233:                 imageline($image, $x3i, $y4i, $x4i, $y4i, $black);
 234:             }
 235:         }
 236:     }
 237:     return $image;
 238: }

月の満ち欠けを描くのがユーザー関数 drawMissingMoon である。
冒頭で、 file_exists  を使い、満月画像 FULLMOON を探す。なければ円を描画する。

後半では、満月画像を読み込んで、影の部分を描画関数  imageline  を使って1ラインずつ塗りつぶしていく。
PHPで月の満ち欠けを描画
月の半径を $ R $、太陽-地球-月のなす角を $ \theta $ とすると、明部分の長さは $ R \ cos \ \theta $ であらわされる。
図1は、月の軌道と満ち欠けを示したものである。
遠方からやってくる太陽光線は直線になっているものと仮定する。ここで、太陽-地球-月のなす角をを $ \theta $ とする。

このときの月を拡大したのが図2である。
月の中心をOとすると、地球から観たときに明部分となっているのは、ACの部分である。
地球の中心をEとすると、$ \angle{AEC} = \theta = \angle{AOC} $ となる。
ここで、$ R = OA = OC $ であるので、$ AC = R \ cos \ \theta $ と計算できる。

$ R $ は、両極(緯度90°)で0、赤道(緯度0°)で月の半径と等しくなる。
月は真円であるので、月の緯度をγとするとRは $ R = sin \ \gamma $ で計算できる。

以上のことから、月の明部分の長さは $ sin \ \gamma \ cos \ \theta $ で計算できる。これを月の半径から減じたものが影の部分の長さになる。
$ \theta $ は月齢と同じ意味である。$ \theta = 180 ^\circ $(月齢15)を境界条件とし、月の南北で影の長さが同じであることから計算量を減らしたのがユーザー関数 draw_moonage の肝の部分となる。

参考サイト

(この項おわり)
header