Moon Phase Details Factory #
The MoonPhaseDetailsFactory builds a complete MoonPhaseOverviewModel from an existing AstrologicalSubjectModel. While the basic LunarPhaseModel attached to every subject provides the Sun-Moon angle, phase name, and emoji, this factory enriches that data into a full lunar context suitable for UI display, API responses, or detailed reports.
What It Provides #
| Section | Data |
|---|---|
| Moon Summary | Phase name, emoji, major phase label, waxing/waning stage, illumination percentage, age in days, lunar cycle progress, Sun and Moon zodiac signs |
| Illumination Details | Numeric percentage, visible fraction (0-1), phase angle in degrees |
| Upcoming Phases | Last and next occurrence of New Moon, First Quarter, Full Moon, Last Quarter (precise Swiss Ephemeris timing) |
| Next Lunar Eclipse | Date, timestamp, eclipse type (Total, Partial, Penumbral) |
| Sun Info | Sunrise, sunset, solar noon, day length, apparent altitude and azimuth |
| Next Solar Eclipse | Date, timestamp, eclipse type (Total, Annular, Partial, Hybrid) |
| Location | Latitude, longitude, default-location flag |
Usage #
from kerykeion import AstrologicalSubjectFactory, MoonPhaseDetailsFactory
# 1. Create an astrological subject
subject = AstrologicalSubjectFactory.from_birth_data(
"Alice", 1990, 6, 15, 12, 0,
lng=12.4964, lat=41.9028, tz_str="Europe/Rome",
online=False,
)
# 2. Generate the moon phase overview
overview = MoonPhaseDetailsFactory.from_subject(subject)
# 3. Access the data
print(f"Phase: {overview.moon.phase_name} {overview.moon.emoji}")
print(f"Illumination: {overview.moon.illumination}")
print(f"Stage: {overview.moon.stage}")
print(f"Major Phase: {overview.moon.major_phase}")
print(f"Age: {overview.moon.age_days} days")
Expected Output:
Phase: Waxing Crescent 🌒
Illumination: 2%
Stage: waxing
Major Phase: New Moon
Age: 1 days
API Reference #
MoonPhaseDetailsFactory.from_subject(...) #
@classmethod
def from_subject(
cls,
subject: AstrologicalSubjectModel,
*,
using_default_location: bool = False,
location_precision: int = 0,
) -> MoonPhaseOverviewModel
| Parameter | Type | Default | Description |
|---|---|---|---|
subject |
AstrologicalSubjectModel |
Required | An astrological subject with Sun, Moon, and time/location data. |
using_default_location |
bool |
False |
Whether the location comes from a default configuration (metadata for API consumers). |
location_precision |
int |
0 |
Optional precision indicator for the location coordinates. |
Returns: MoonPhaseOverviewModel with all sections populated.
Accessing Nested Data #
The returned MoonPhaseOverviewModel provides structured access to all data:
overview = MoonPhaseDetailsFactory.from_subject(subject)
# Moon summary
moon = overview.moon
if moon.zodiac:
print(f"Zodiac: Sun in {moon.zodiac.sun_sign}, Moon in {moon.zodiac.moon_sign}")
# Illumination details
if moon.detailed and moon.detailed.illumination_details:
illum = moon.detailed.illumination_details
print(f"Visible fraction: {illum.visible_fraction:.4f}")
print(f"Phase angle: {illum.phase_angle:.2f}°")
# Upcoming phases
if moon.detailed and moon.detailed.upcoming_phases:
phases = moon.detailed.upcoming_phases
if phases.full_moon and phases.full_moon.next:
print(f"Next Full Moon: {phases.full_moon.next.datestamp}")
if phases.new_moon and phases.new_moon.last:
print(f"Last New Moon: {phases.new_moon.last.datestamp}")
# Eclipses
if moon.next_lunar_eclipse:
print(f"Next Lunar Eclipse: {moon.next_lunar_eclipse.datestamp}")
print(f"Type: {moon.next_lunar_eclipse.type}")
# Sun info
if overview.sun:
sun = overview.sun
print(f"Sunrise: {sun.sunrise_timestamp}")
print(f"Sunset: {sun.sunset_timestamp}")
print(f"Day length: {sun.day_length}")
if sun.position:
print(f"Sun altitude: {sun.position.altitude:.2f}°")
if sun.next_solar_eclipse:
print(f"Next Solar Eclipse: {sun.next_solar_eclipse.datestamp}")
JSON Serialization #
The overview model is fully serializable via Pydantic:
# Full JSON (excluding None fields for compact output)
print(overview.model_dump_json(exclude_none=True, indent=2))
# Python dict
data = overview.model_dump(exclude_none=True)
Generating a Report #
The ReportGenerator accepts MoonPhaseOverviewModel directly:
from kerykeion import ReportGenerator
report = ReportGenerator(overview)
report.print_report()
This produces a formatted ASCII table report with sections for Moon Summary, Illumination Details, Upcoming Phases, Eclipses, Sun Info, and Location. See the Report Module documentation for details.
Precision and Accuracy #
- Phase timings: Binary search with Swiss Ephemeris converges to ~1 second precision for all major phase events.
- Illumination formula: Standard
k = 0.5 * (1 - cos(angle))applied to the Sun-Moon ecliptic separation. - Lunar age: Computed from the actual last New Moon timestamp (not a synodic-month approximation).
- Eclipse search: Uses
swe.sol_eclipse_when_globandswe.lun_eclipse_whenfor the next global eclipse of each type. - Sunrise/sunset: Computed via
swe.rise_transwith standard atmospheric refraction corrections.
Edge Cases #
- Polar regions: When the Sun does not rise or set (polar day/night), sunrise and sunset fields will be
None. - Missing lunar phase: If the subject was created with
calculate_lunar_phase=False, the moon summary will contain onlyNonefields. - No coordinates: If the subject has no
lat/lng, sun times and position will beNone, and location fields will be empty.
Need this in production? Use the Astrologer API for hosted calculations, charts, and AI interpretations - no server setup required. Learn more →