gen_trump_chart.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #!/usr/bin/env python3
  2. """Generate a natal chart PNG for Donald Trump."""
  3. import asyncio
  4. import sys
  5. import os
  6. import traceback
  7. sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
  8. from astro_mcp.ephemeris_client import call_sky_state, extract_bodies, extract_houses, extract_angles
  9. from astro_mcp import astrology
  10. from astro_mcp.chart_renderer import render_natal_wheel
  11. # Trump's birth data
  12. BIRTH_DATETIME = "1946-06-14T10:54:00-04:00"
  13. LATITUDE = 40.6501
  14. LONGITUDE = -73.7936
  15. ELEVATION = 10.0
  16. HOUSE_SYSTEM = "placidus"
  17. async def main():
  18. print("Calling ephemeris for Trump's birth data...")
  19. sys.stdout.flush()
  20. try:
  21. sky = await call_sky_state(
  22. datetime=BIRTH_DATETIME,
  23. lat=LATITUDE,
  24. lon=LONGITUDE,
  25. elevation=ELEVATION,
  26. geocentric=True,
  27. house_system=HOUSE_SYSTEM,
  28. )
  29. except Exception as e:
  30. traceback.print_exc()
  31. sys.exit(1)
  32. if "error" in sky:
  33. print(f"ERROR: {sky['error']}")
  34. sys.exit(1)
  35. raw_bodies = extract_bodies(sky)
  36. houses = extract_houses(sky)
  37. angles = extract_angles(sky)
  38. print(f"Got {len(raw_bodies)} bodies, {len(houses)} houses")
  39. sys.stdout.flush()
  40. # Build planet list
  41. planets = []
  42. for body in raw_bodies:
  43. ecl_lon = body.get("ecliptic_lon", 0.0)
  44. ecl_lat = body.get("ecliptic_lat", 0.0)
  45. speed_lon = body.get("speed_lon")
  46. zodiac = astrology.ecliptic_to_zodiac(ecl_lon)
  47. house = astrology.get_house_placement(ecl_lon, houses)
  48. planets.append({
  49. "body": body["body"],
  50. "sign": zodiac["sign"],
  51. "sign_abbreviation": zodiac["abbreviation"],
  52. "degree_within_sign": zodiac["degree"],
  53. "absolute_lon": zodiac["absolute_lon"],
  54. "ecliptic_lat": ecl_lat,
  55. "distance_au": body.get("distance_au", 0.0),
  56. "house": house,
  57. "retrograde": astrology.is_retrograde(speed_lon),
  58. })
  59. # Calculate aspects
  60. speed_lookup = {b["body"]: b.get("speed_lon") for b in raw_bodies}
  61. aspect_bodies = [
  62. {"name": p["body"], "lon": p["absolute_lon"], "speed_lon": speed_lookup.get(p["body"])}
  63. for p in planets
  64. ]
  65. aspects = astrology.compute_aspects(aspect_bodies, None)
  66. formatted_aspects = []
  67. for asp in aspects:
  68. formatted_aspects.append({
  69. "body1": asp["body1"],
  70. "body2": asp["body2"],
  71. "aspect": asp["aspect"],
  72. "orb": asp["orb"],
  73. "applying": asp["applying"],
  74. "exactness": asp["exactness"],
  75. })
  76. chart_data = {
  77. "input": {
  78. "birth_datetime": BIRTH_DATETIME,
  79. "latitude": LATITUDE,
  80. "longitude": LONGITUDE,
  81. "elevation": ELEVATION,
  82. "house_system": HOUSE_SYSTEM,
  83. "name": "Donald Trump",
  84. "birthplace": "New York, NY",
  85. },
  86. "chart_type": "natal",
  87. "planets": planets,
  88. "houses": houses,
  89. "aspects": formatted_aspects,
  90. "angles": angles,
  91. }
  92. print("Rendering natal wheel (bw, size=700)...")
  93. sys.stdout.flush()
  94. svg = render_natal_wheel(
  95. chart_data,
  96. style="modern",
  97. color_mode="bw",
  98. size=700,
  99. table_position="none",
  100. include_planets=False,
  101. include_houses=False,
  102. title="Donald Trump",
  103. )
  104. # Write SVG
  105. svg_path = "/home/shared/trump_natal_bw.svg"
  106. with open(svg_path, "w") as f:
  107. f.write(svg)
  108. print(f"SVG written to {svg_path}")
  109. # Convert to PNG
  110. import cairosvg
  111. png_path = "/home/shared/trump_natal_bw.png"
  112. cairosvg.svg2png(url=svg_path, write_to=png_path, scale=2)
  113. print(f"PNG written to {png_path}")
  114. # Print chart info
  115. asc = angles.get("ascendant", {})
  116. mc = angles.get("midheaven", {})
  117. print(f"\nASC: {asc.get('sign')} {asc.get('degree', 0):.2f}°")
  118. print(f"MC: {mc.get('sign')} {mc.get('degree', 0):.2f}°")
  119. print(f"\nPlanets:")
  120. for p in planets:
  121. retro = " Rx" if p["retrograde"] else ""
  122. print(f" {p['body']:12s} {p['sign']:12s} {p['degree_within_sign']:6.2f}° H{p['house']}{retro}")
  123. print(f"\nAspects ({len(formatted_aspects)}):")
  124. for a in formatted_aspects[:20]:
  125. print(f" {a['body1']:10s} {a['aspect']:12s} {a['body2']:10s} orb={a['orb']:.2f}°")
  126. asyncio.run(main())