Skip to content

Instantly share code, notes, and snippets.

@takada-at
Created July 24, 2025 15:13
Show Gist options
  • Save takada-at/a6b43933a3ee5b66a8ffe5461fa7a0d4 to your computer and use it in GitHub Desktop.
Save takada-at/a6b43933a3ee5b66a8ffe5461fa7a0d4 to your computer and use it in GitHub Desktop.
# /// 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