package svg import ( "math" "time" "b612.me/astro/basic" eclipsecore "b612.me/astro/eclipse" ) type LunarEclipseType = eclipsecore.LunarEclipseType type LunarEclipseContactPoint = eclipsecore.LunarEclipseContactPoint type LunarEclipseInfo = eclipsecore.LunarEclipseInfo const ( LunarEclipseNone = eclipsecore.LunarEclipseNone LunarEclipsePenumbral = eclipsecore.LunarEclipsePenumbral LunarEclipsePartial = eclipsecore.LunarEclipsePartial LunarEclipseTotal = eclipsecore.LunarEclipseTotal ) func lunarEclipseInfoFromBasic(result basic.LunarEclipseResult, location *time.Location) LunarEclipseInfo { return LunarEclipseInfo{ Type: mapBasicLunarEclipseType(result.Type), PenumbralMagnitude: result.PenumbralMagnitude, UmbralMagnitude: result.Magnitude, PenumbralStart: ttJDEToTime(result.PenumbralStart, location), PartialStart: ttJDEToTime(result.PartialStart, location), TotalStart: ttJDEToTime(result.TotalStart, location), Maximum: ttJDEToTime(result.Maximum, location), TotalEnd: ttJDEToTime(result.TotalEnd, location), PartialEnd: ttJDEToTime(result.PartialEnd, location), PenumbralEnd: ttJDEToTime(result.PenumbralEnd, location), ContactPoints: lunarEclipseContactPointsFromBasic(result, location), HasPenumbral: result.HasPenumbral, HasPartial: result.HasPartial, HasTotal: result.HasTotal, } } func lunarEclipseContactPointsFromBasic( result basic.LunarEclipseResult, location *time.Location, ) []LunarEclipseContactPoint { if !result.HasPenumbral { return nil } contacts := []LunarEclipseContactPoint{ lunarEclipseContactPoint("P1", result.PenumbralStart, location, false), } if result.HasPartial { contacts = append(contacts, lunarEclipseContactPoint("U1", result.PartialStart, location, false)) } if result.HasTotal { contacts = append(contacts, lunarEclipseContactPoint("U2", result.TotalStart, location, true)) } if result.HasTotal { contacts = append(contacts, lunarEclipseContactPoint("U3", result.TotalEnd, location, true)) } if result.HasPartial { contacts = append(contacts, lunarEclipseContactPoint("U4", result.PartialEnd, location, false)) } contacts = append(contacts, lunarEclipseContactPoint("P4", result.PenumbralEnd, location, false)) return contacts } func lunarEclipseContactPoint( label string, ttJDE float64, location *time.Location, internalContact bool, ) LunarEclipseContactPoint { moonCenterPA := lunarEclipseMoonCenterPositionAngle(ttJDE) shadowCenterPA := normalizeDegree360(moonCenterPA + 180) contactPA := shadowCenterPA if internalContact { contactPA = moonCenterPA } return LunarEclipseContactPoint{ Label: label, Time: ttJDEToTime(ttJDE, location), ContactPositionAngle: contactPA, ContactClockwiseAngle: normalizeDegree360(360 - contactPA), MoonCenterPositionAngle: moonCenterPA, ShadowCenterPositionAngle: shadowCenterPA, } } func lunarEclipseMoonCenterPositionAngle(ttJDE float64) float64 { shadowRA, shadowDec := lunarEclipseShadowCenterRaDec(ttJDE) moonRA, moonDec := basic.HMoonTrueRaDec(ttJDE) return lunarEclipsePositionAngle(shadowRA, shadowDec, moonRA, moonDec) } func lunarEclipseShadowCenterRaDec(ttJDE float64) (float64, float64) { sunRA, sunDec := basic.HSunApparentRaDec(ttJDE) return normalizeDegree360(sunRA + 180), -sunDec } func lunarEclipsePositionAngle(fromRA, fromDec, toRA, toDec float64) float64 { dRA := (toRA - fromRA) * math.Pi / 180 fromDecRad := fromDec * math.Pi / 180 toDecRad := toDec * math.Pi / 180 angle := math.Atan2( math.Sin(dRA), math.Cos(fromDecRad)*math.Tan(toDecRad)-math.Sin(fromDecRad)*math.Cos(dRA), ) * 180 / math.Pi return normalizeDegree360(angle) } func mapBasicLunarEclipseType(eclipseType basic.LunarEclipseType) LunarEclipseType { switch eclipseType { case basic.LunarEclipsePenumbral: return LunarEclipsePenumbral case basic.LunarEclipsePartial: return LunarEclipsePartial case basic.LunarEclipseTotal: return LunarEclipseTotal default: return LunarEclipseNone } } func ttJDEToTime(ttJDE float64, location *time.Location) time.Time { if ttJDE == 0 { return time.Time{} } utcJDE := basic.TD2UT(ttJDE, false) return basic.JDE2DateByZone(utcJDE, location, false) } func timeToTTJDE(date time.Time) float64 { utcJDE := basic.Date2JDE(date.UTC()) return basic.TD2UT(utcJDE, true) } func normalizeDegree360(angle float64) float64 { angle = math.Mod(angle, 360) if angle < 0 { angle += 360 } return angle }