Created
July 24, 2025 15:13
-
-
Save takada-at/a6b43933a3ee5b66a8ffe5461fa7a0d4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# /// script | |
# requires-python = ">=3.9" | |
# dependencies = [ | |
# "pyswisseph>=2.10.3.2", | |
# "pytz>=2025.2", | |
# ] | |
# /// | |
import pytz | |
import swisseph as swe | |
from datetime import datetime | |
import argparse | |
import random | |
def parse_arguments(): | |
"""コマンドライン引数を解析する""" | |
parser = argparse.ArgumentParser(description='ホロスコープを計算します') | |
parser.add_argument('datetime', nargs='?', help='日時 (YYYY-MM-DD HH:MM:SS 形式、デフォルト: 現在時刻)') | |
parser.add_argument('--random', action='store_true', help='ランダムな日時でホロスコープを生成') | |
return parser.parse_args() | |
def get_datetime(args): | |
"""引数に基づいて日時を取得する""" | |
jst = pytz.timezone('Asia/Tokyo') | |
if args.random: | |
year = random.randint(1900, 2100) | |
month = random.randint(1, 12) | |
day = random.randint(1, 28) | |
hour = random.randint(0, 23) | |
minute = random.randint(0, 59) | |
second = random.randint(0, 59) | |
return jst.localize(datetime(year, month, day, hour, minute, second)) | |
elif args.datetime: | |
try: | |
dt = datetime.strptime(args.datetime, '%Y-%m-%d %H:%M:%S') | |
return jst.localize(dt) | |
except ValueError: | |
try: | |
dt = datetime.strptime(args.datetime, '%Y-%m-%d') | |
return jst.localize(dt) | |
except ValueError: | |
print("エラー: 日時は 'YYYY-MM-DD HH:MM:SS' または 'YYYY-MM-DD' 形式で指定してください") | |
exit(1) | |
else: | |
utc_dt = datetime.now(pytz.utc) | |
return utc_dt.astimezone(jst) | |
def get_constants(): | |
"""定数を定義する""" | |
planets = { | |
'太陽': swe.SUN, | |
'月': swe.MOON, | |
'水星': swe.MERCURY, | |
'金星': swe.VENUS, | |
'火星': swe.MARS, | |
'木星': swe.JUPITER, | |
'土星': swe.SATURN, | |
'天王星': swe.URANUS, | |
'海王星': swe.NEPTUNE, | |
'冥王星': swe.PLUTO, | |
} | |
zodiac_signs = [ | |
('牡羊座♈', 0), | |
('牡牛座♉', 30), | |
('双子座♊', 60), | |
('蟹座♋', 90), | |
('獅子座♌', 120), | |
('乙女座♍', 150), | |
('天秤座♎', 180), | |
('蠍座♏', 210), | |
('射手座♐', 240), | |
('山羊座♑', 270), | |
('水瓶座♒', 300), | |
('魚座♓', 330) | |
] | |
return planets, zodiac_signs | |
def get_zodiac_sign(longitude, zodiac_signs): | |
"""黄道経度から星座を判定""" | |
sign_index = int(longitude / 30) | |
sign_name = zodiac_signs[sign_index][0] | |
degree_in_sign = longitude % 30 | |
return sign_name, degree_in_sign | |
def calculate_positions(jd, planets): | |
"""各天体の黄道経度を取得する""" | |
positions = {} | |
for name, planet_id in planets.items(): | |
lon = swe.calc_ut(jd, planet_id)[0][0] | |
positions[name] = lon | |
return positions | |
def calculate_houses(jd, latitude, longitude): | |
"""ハウス計算(プラシーダス方式)""" | |
houses, asc_mc = swe.houses(jd, latitude, longitude, b'P') | |
return houses, asc_mc | |
def get_house(longitude, cusps): | |
"""天体の黄道経度からハウス番号を判定""" | |
for i in range(12): | |
start = cusps[i] | |
end = cusps[(i + 1) % 12] | |
if start > end: | |
if longitude >= start or longitude < end: | |
return i + 1 | |
else: | |
if start <= longitude < end: | |
return i + 1 | |
return 1 | |
def calculate_planet_houses(positions, houses): | |
"""各天体のハウス位置を取得する""" | |
planet_houses = {} | |
for name, lon in positions.items(): | |
if name not in ['アセンダント', 'MC(天頂)']: | |
house_num = get_house(lon, houses) | |
planet_houses[name] = house_num | |
return planet_houses | |
def calculate_aspect(lon1, lon2): | |
"""2つの天体間のアスペクトを計算""" | |
diff = abs(lon1 - lon2) | |
if diff > 180: | |
diff = 360 - diff | |
aspects = { | |
'コンジャンクション(0°)': (0, 8), | |
'セクスタイル(60°)': (60, 6), | |
'スクエア(90°)': (90, 8), | |
'トライン(120°)': (120, 8), | |
'オポジション(180°)': (180, 8) | |
} | |
for aspect_name, (angle, orb) in aspects.items(): | |
if abs(diff - angle) <= orb: | |
return aspect_name, diff | |
return None, None | |
def find_aspects(positions): | |
"""主要アスペクトを検出する""" | |
aspects_found = [] | |
planet_names = list(positions.keys()) | |
for i in range(len(planet_names)): | |
for j in range(i + 1, len(planet_names)): | |
name1, name2 = planet_names[i], planet_names[j] | |
lon1, lon2 = positions[name1], positions[name2] | |
aspect, angle = calculate_aspect(lon1, lon2) | |
if aspect: | |
aspects_found.append({ | |
'planet1': name1, | |
'planet2': name2, | |
'aspect': aspect, | |
'angle': angle | |
}) | |
return aspects_found | |
def print_results(jst_dt, positions, houses, planet_houses, aspects_found, zodiac_signs): | |
"""結果を出力する""" | |
print(f"=== {jst_dt} ホロスコープ ===") | |
print("\n【天体位置】") | |
print(f"{'天体':<10} {'黄道経度':<10} {'星座':<10} {'度数':<8} {'ハウス'}") | |
print("-" * 60) | |
for body, lon in positions.items(): | |
sign_name, degree_in_sign = get_zodiac_sign(lon, zodiac_signs) | |
house_num = planet_houses.get(body, '-') | |
house_str = f'第{house_num}ハウス' if house_num != '-' else '-' | |
print(f"{body:<10} {lon:>8.2f}° {sign_name:<10} {degree_in_sign:>5.2f}° {house_str}") | |
print("\n【ハウスカスプ】") | |
print(f"{'ハウス':<10} {'黄道経度':<10} {'星座'}") | |
print("-" * 35) | |
for i in range(12): | |
house_name = f'第{i+1}ハウス' | |
cusp_lon = houses[i] | |
sign_name, degree_in_sign = get_zodiac_sign(cusp_lon, zodiac_signs) | |
print(f"{house_name:<10} {cusp_lon:>8.2f}° {sign_name} {degree_in_sign:>5.2f}°") | |
print("\n【主要アスペクト】") | |
if aspects_found: | |
print(f"{'天体1':<10} {'天体2':<10} {'アスペクト':<20} {'実際の角度'}") | |
print("-" * 55) | |
for asp in aspects_found: | |
print(f"{asp['planet1']:<10} {asp['planet2']:<10} {asp['aspect']:<20} {asp['angle']:>6.2f}°") | |
else: | |
print("主要アスペクトは検出されませんでした。") | |
def main(): | |
"""メイン処理""" | |
args = parse_arguments() | |
jst_dt = get_datetime(args) | |
utc_dt = jst_dt.astimezone(pytz.utc) | |
latitude = 35.6895 | |
longitude = 139.6917 | |
jd = swe.julday(utc_dt.year, utc_dt.month, utc_dt.day, | |
utc_dt.hour + utc_dt.minute / 60) | |
planets, zodiac_signs = get_constants() | |
positions = calculate_positions(jd, planets) | |
houses, asc_mc = calculate_houses(jd, latitude, longitude) | |
positions['アセンダント'] = asc_mc[0] | |
positions['MC(天頂)'] = asc_mc[1] | |
planet_houses = calculate_planet_houses(positions, houses) | |
aspects_found = find_aspects(positions) | |
print_results(jst_dt, positions, houses, planet_houses, aspects_found, zodiac_signs) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment