マイコン宇宙講座-太陽系惑星の位置表示

指定した日付の太陽系9惑星の軌道上の位置を表示します。計算は指定した日付の惑星の軌道要素からサブルーチンkeplerから離心近点離角を計算します。さらに真近点離角と動径を求め、グラフィック上位置に変換します。基本的にはこういった流れです(実際はもう少し複雑)。マイコン宇宙講座はPC-8001のグラフィックを利用して描いています。ですが、解像度としては160×100であり、現状のHDグラフィックだととても小さすぎてしまいます。そこで、これを640×400に補正して表示することにします。

メインルーチン m31.py

# m31.py
# マイコン宇宙講座
# 3-1 太陽系の惑星位置表示プログラム
from PIL import Image, ImageDraw, ImageFont
from tkinter.constants import SOLID
import tkinter as tk
import math
import lib


M2PI = 2.0 * math.pi

# 太陽系を表示する
def display_solor_system():
    global photo

    std = editbox.get()

    # 入力されていない時は何にもしない
    if std != '':
        dy, dt = std.split(',')
        dy = float(dy)
        dt = float(dt)

        jd, yy, mm, dd, hh, ms, ss = lib.mjd(dy, dt)

        drawing_solor_system(jd, yy, mm, dd, lib.T)

        # 保存された画像を表示
        photo = tk.PhotoImage(file='./images/solor.png')
        canvas.create_image(1, 1, image=photo, anchor=tk.NW)

        # テキストボックスの中身をクリア
        editbox.delete(0, tk.END)

    # 日付と時刻の入力欄にフォーカスをセット
    editbox.focus_set()


# 太陽系を描く
def drawing_solor_system(jd, yy, mm, dd, T):
    img = Image.new('RGB', (642, 402), (0, 0, 0))
    draw = ImageDraw.Draw(img)

    # フォント名は実行環境に合わせて変更すること
    # TrueTypeの等幅フォント名を指定する
    font1 = ImageFont.truetype('TakaoGothic.ttf', 16)
    font2 = ImageFont.truetype('TakaoGothic.ttf', 12)

    # 太陽と惑星のカラーパレット
    planet_color = [
        '#ff0000',
        '#FFD7A0',
        '#FAAF3C',
        '#37AAE1',
        '#FF5050',
        '#B9BEB4',
        '#C8A05F',
        '#A0C8FA',
        '#3232E1',
        '#E6B96E'
    ]

    # 画面補正値
    pwx = 4
    pwy = 4

    # 画面上の太陽の位置
    mx = 412
    my = 200
    sx = 110
    sy = 110

    # フレームを描く
    draw.rectangle((0, 0, 640, 400))
    draw.line((224, 0, 224, 400))
    draw.line((0, 220, 224, 220))

    # Sun
    draw.ellipse((mx - 4, my - 4, mx + 4, my + 4), fill=(255, 0, 0))  # メイン画面
    draw.ellipse((sx - 4, sy - 4, sx + 4, sy + 4), fill=(255, 0, 0))  # サブ画面

    # 春分点の方向(メイン画面)
    for ln in range(418, 620, 10):
        draw.line((ln, 200, ln + 5, 200))
    draw.line((625, 200, 620, 195))
    draw.line((625, 200, 620, 205))

    # 春分点の方向(サブ画面)
    for ln in range(114, 210, 5):
        draw.line((ln, 110, ln + 2, 110))
    draw.line((214, 110, 209, 105))
    draw.line((214, 110, 209, 115))

    # タイトルの表示
    draw.text((400, 380), 'SOLOR SYSTEM', font=font1)

    # 年月日の表示
    datestr = str('%4d年 %2d月 %2d日' % (yy, mm, dd))
    draw.text((65, 230), datestr, font=font2)

    # 惑星名とシンボルを描画
    draw.text((10, 270), 'P', font=font2)
    draw.text((10, 285), 'L SUN------(   ) MERCURY------(   )', font=font2)
    draw.text((10, 300), 'A VENUS----(   ) EARTH--------(   )', font=font2)
    draw.text((10, 315), 'N MARS-----(   ) JUPITER------(   )', font=font2)
    draw.text((10, 330), 'E SATURN---(   ) URANUS-------(   )', font=font2)
    draw.text((10, 345), 'T NEPTUNE--(   ) PLUTO--------(   )', font=font2)
    draw.text((10, 360), 'S', font=font2)

    # 太陽
    drawing_planet_symbol(0, 91, 291, planet_color[0], draw)
    # 水星
    drawing_planet_symbol(1, 204, 291, planet_color[1], draw)
    # 金星
    drawing_planet_symbol(2, 91, 306, planet_color[2], draw)
    # 地球
    drawing_planet_symbol(3, 204, 306, planet_color[3], draw)
    # 火星
    drawing_planet_symbol(4, 91, 321, planet_color[4], draw)
    # 木星
    drawing_planet_symbol(5, 204, 321, planet_color[5], draw)
    # 土星
    drawing_planet_symbol(6, 91, 336, planet_color[6], draw)
    # 天王星
    drawing_planet_symbol(7, 204, 336, planet_color[7], draw)
    # 海王星
    drawing_planet_symbol(8, 91, 351, planet_color[8], draw)
    # 冥王星
    drawing_planet_symbol(9, 204, 351, planet_color[9], draw)

    # 惑星の位置計算
    t1 = jd - 33281.92334
    t1 = t1 * (2.737909288e-5 + 1.260132857e-17 * t1)
    t2 = t1 * t1

    for pn in range(1, 10):
        e, m, p, n, i, a, rd = lib.mean_elements(pn, t1, t2)
        ec = e
        mo = M2PI * (m / (M2PI) - int(m / (M2PI)))

        ss, cc, ff = lib.kepler(mo, ec)

        b = a * math.sqrt(1 - ec * ec)
        ss = b * ss
        cc = a * ff

        tt = lib.quadrant(ss, cc)

        v = tt
        r = math.sqrt(ss * ss + cc * cc)
        pp = n + p
        vv = v + pp
        r0 = 15
        if pn > 4:
            r0 = 0.9
        rr = r0 * r
        x = rr * math.cos(vv)
        y = rr * math.sin(vv)
        y = -y
        if pn <= 4:
            # 水星-火星はサブ画面へ
            x2 = int((x * pwx) + sx)
            y2 = int((y * pwy) + sy - 4)
        else:
            # 木星-冥王星はメイン画面へ
            x2 = int((x * pwx) + mx)
            y2 = int((y * pwy) + my)

        # 惑星を描く
        drawing_planet_symbol(pn, x2, y2, planet_color[pn], draw)

    # 画像を保存
    # 必ずPNG形式で保存
    img.save('./images/solor.png')


# 惑星のシンボルを描く
def drawing_planet_symbol(p, x, y, color, draw):
    draw.ellipse((x - 4, y - 4, x + 4, y + 4), fill=color)
    if p == 6:
        draw.line((x - 8, y, x + 8, y), fill=color)


# ユリウス日から年月日を返す
def display_date(jd, t, draw, fonts):
    yy, mm, dd = lib.jdate(jd, t)
    # 年月日の表示
    datestr = str('%4d年 %2d月 %2d日' % (yy, mm, dd))
    draw.text((65, 230), datestr, font=fonts)


# メイン
root = tk.Tk()
root.resizable(False, False)
root.geometry('642x440')
root.title('マイコン宇宙講座 - 太陽系の惑星位置表示プログラム')

canvas = tk.Canvas(root, width=640, height=400, bg='black')
canvas.pack(anchor=tk.NW)

label = tk.Label(root, text='DATE AND TIME(JST):')
label.place(x=10, y=410)

editbox = tk.Entry(root, relief=SOLID)
editbox.place(x=155, y=408, width=140, height=24)

button = tk.Button(root, text='表示', width=6, relief=SOLID, cursor='hand1', command=display_solor_system)
button.place(x=480, y=407)

button = tk.Button(root, text='閉じる', width=6, relief=SOLID, cursor='hand1', command=root.destroy)
button.place(x=558, y=407)

# 日付と時刻の入力欄にフォーカスをセット
editbox.focus_set()

root.mainloop()

例題 2000年1月1日9時00分00秒(JST)における太陽系9惑星の位置を表示してみよう。

DATE AND TIME(JST)に20000101,090000と入力して、[表示]をクリックすると、その日の太陽系9惑星の軌道上の位置が描かれます。

2000年1月1日の惑星の位置
2000年1月1日の惑星の位置