PHPで月の満ち欠けを描画

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

(2021年12月18日)PHP8対応,リファラ・チェック改良

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

PHPで月の満ち欠けを描画

目次

サンプル・プログラム

圧縮ファイルの内容
moonage.phpサンプル・プログラム本体。
fullmoon.png満月画像。

解説:準備

0040: //満月の画像
0041: // PNG形式限定
0042: // 正方形で,領域いっぱいに真円の満月が描かれていること.
0043: // 背景は透明であること.
0044: define('FULLMOON', 'fullmoon.png');
0045: 
0046: //影の透明度(0~127):0 は完全に不透明な状態。 127 は完全に透明な状態
0047: define('ALPHA', 30);
0048: 
0049: //明部分を白色で塗るかどうか
0050: define('MOON_BRIGHT', FALSE);
0051: 
0052: //画像を保存するフォルダ
0053: define('SAVE_PATH', './');

満月の写真があれば、それを定数 FULLMOON に指定する。
ただし、写真はPNG形式限定。大きさの制限はないが、正方形で、領域いっぱいに真円の満月が描かれていること。また、背景は透明であることが望ましい。

今回は、影の部分だけ半透明の黒で塗りつぶすだけだが、もし太陽光の当たっている明部分を白で塗りたければ、定数 MOON_BRIGHT を TRUE にする。

影や明部分の透明度を制御するアルファチャネルの値は定数 ALPHA に指定する。0 は完全に不透明な状態、127 は完全に透明な状態となる。

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

解説:月の満ち欠けを描く

0133: /**
0134:  * 月の満ち欠けを描く
0135:  * @param   double $age 月齢
0136:  * @return  object GDリソース
0137: */
0138: function draw_moonage($age) {
0139:     if ($age < 0 || $age > 30)      $age = 0;
0140: 
0141:     //満月画像がなければ円を描画
0142:     if (! file_exists(FULLMOON)) {
0143:         $dd = 500;           //直径
0144:         $rr = $dd / 2;       //半径
0145:         $image = imagecreatetruecolor($dd$dd);
0146:         //背景透明化
0147:         $bgcolor = imagecolorallocate($image0xFF0xFF0xFF);//背景色セット
0148:         imagefill($image, 0, 0, $bgcolor);
0149:         imagecolortransparent($image$bgcolor);
0150:         //円を描画
0151:         $color = imagecolorallocate($image0xFF0xFF0x7F);
0152:         imagefilledarc($image$rr$rr$dd$dd, 0, 360, $colorIMG_ARC_PIE);
0153: 
0154:     //満月画像の読み込み
0155:     } else {
0156:         if (($arr = getimagesize(FULLMOON)) == FALSE)   return FALSE;
0157:         $dd = $arr[0];       //直径
0158:         $rr = $dd / 2;       //半径
0159:         //画像読み込み
0160:         $image = imagecreatefrompng(FULLMOON);
0161:     }
0162: 
0163:     //完全なアルファチャネル情報を保存するフラグをonにする
0164:     imagesavealpha($imageTRUE);
0165:     //カラー設定
0166:     $black = imagecolorallocatealpha($image0x000x000x00ALPHA);
0167:     $white = imagecolorallocatealpha($image0xFF0xFF0xFFALPHA);
0168: 
0169:     //影を描く
0170:     $x0 = $rr;
0171:     $y0 = $rr;
0172:     $th = $age / 14.765 * pi();
0173:     for ($y = -$rr$y <= 0; $y++) {
0174:         $ac = acos($y / $rr);
0175:         $x2 = $rr * sin($ac);                // 円周
0176:         $x1 = $rr * cos($th) * sin($ac); // 月の形
0177:         if (($age > 0.5) && ($age < 29.5)) {
0178:             if ($age < 15.0) {
0179:                 $x3i = round($x0 + $x1);
0180:                 $x4i = round($x0 + $x2);
0181:                 $x5i = round($x0 - $x2);
0182:                 $y3i = round($y0 + $y);
0183:                 $y4i = round($y0 - $y);
0184:             } else {
0185:                 $x3i = round($x0 - $x1);
0186:                 $x4i = round($x0 - $x2);
0187:                 $x5i = round($x0 + $x2);
0188:                 $y3i = round($y0 + $y);
0189:                 $y4i = round($y0 - $y);
0190:             }
0191:             imageline($image$x3i$y3i$x5i$y3i$black);
0192:             if (MOON_BRIGHT)    imageline($image$x3i$y3i$x4i$y3i$white);
0193:             if ($y != 0) {
0194:                 imageline($image$x3i$y4i$x5i$y4i$black);
0195:                 if (MOON_BRIGHT) {
0196:                     imageline($image$x3i$y4i$x4i$y4i$white);
0197:                 }
0198:             }
0199:         } else {
0200:             $x3i = round($x0 - $x2);
0201:             $x4i = round($x0 + $x2);
0202:             $y3i = round($y0 + $y);
0203:             $y4i = round($y0 - $y);
0204:             imageline($image$x3i$y3i$x4i$y3i$black);
0205:             if ($y != 0) {
0206:                 imageline($image$x3i$y4i$x4i$y4i$black);
0207:             }
0208:         }
0209:     }
0210:     return $image;
0211: }

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

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

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

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

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

参考サイト

(この項おわり)
header