localSolarEclipse function
Local circumstances of solar eclipse eclipse for a place at
geodetic latitude and longitude (degrees, east positive) and
height above sea level in metres; null if the eclipse is not seen
there at all (the penumbra misses the place).
The Moon is a sphere with the radii of the NASA Five Millennium Canon (moonRadiusRatio, and moonRadiusRatioUmbra for the second and third contacts), so the limb profile is ignored: contacts II and III may differ from observed ones by a second or two. Polar motion is ignored.
deltaTSeconds overrides the ΔT model (it sets the Earth's
orientation at a given moment of Terrestrial Time), e.g. to reproduce
predictions made with a different ΔT.
Implementation
LocalSolarEclipse? localSolarEclipse(
Ephemeris ephemeris,
SolarEclipse eclipse, {
required double latitude,
required double longitude,
double height = 0,
double? deltaTSeconds,
}) {
final observer = Observer(
latitude: latitude,
longitude: longitude,
height: height,
);
_Local at(double t) =>
_Local(_shadow(ephemeris, t), observer, t, deltaTSeconds);
// Maksimum lokalne: najmniejsza odległość obserwatora od osi cienia.
// Całe zaćmienie trwa najwyżej ok. 6 h, więc ±0,2 doby wystarcza.
final start = eclipse.jdTT - 0.2, end = eclipse.jdTT + 0.2;
final tMax = _minimize((t) => at(t).m, start, end);
final max = at(tMax);
if (max.m >= max.l1) return null;
// Obserwator na brzegu półcienia (I, IV) i cienia (II, III).
double outer(double t) {
final l = at(t);
return l.m - l.l1;
}
double inner(double t) {
final l = at(t);
return l.m - l.l2.abs();
}
final first = _root(outer, start, tMax);
final fourth = _root(outer, tMax, end);
double? second, third;
if (max.m < max.l2.abs()) {
second = _root(inner, first, tMax);
third = _root(inner, tMax, fourth);
}
SolarEclipseContact contact(double t) {
final local = at(t);
final sun = horizontalPosition(
ephemeris,
Naif.sun,
observer,
t,
deltaTSeconds: deltaTSeconds,
);
// Kierunek od obserwatora do osi cienia na płaszczyźnie podstawowej
// = kierunek od środka Słońca do środka Księżyca na niebie.
final p = math.atan2(local.u, local.v) / _degree;
return SolarEclipseContact(
jdTT: t,
altitude: sun.altitude,
azimuth: sun.azimuth,
positionAngle: p < 0 ? p + 360 : p,
);
}
// W jednostkach promienia Słońca: Księżyc ma promień r, odległość
// środków c = 2m/(L1 + L2) (c = 1 + r przy m = L1, c = |1 − r| przy
// m = |L2|).
final sum = max.l1 + max.l2;
final r = (max.l1 - max.l2) / sum;
final c = 2 * max.m / sum;
return LocalSolarEclipse(
type: second == null
? SolarEclipseType.partial
: max.l2 < 0
? SolarEclipseType.total
: SolarEclipseType.annular,
firstContact: contact(first),
secondContact: second == null ? null : contact(second),
thirdContact: third == null ? null : contact(third),
maximum: contact(tMax),
fourthContact: contact(fourth),
magnitude: (max.l1 - max.m) / sum,
obscuration: _overlap(r, c),
diameterRatio: r,
);
}