[enhancement] - Add support for remaining 4 Moon Phases
mslalith opened this issue · 15 comments
Currently MoonPhase.Phase
supports
- NEW_MOON
- FULL_MOON
- FIRST_QUARTER
- LAST_QUARTER.
Add support for remaining Moon Phases
- WAXING_CRESCENT
- WAXING_GIBBOUS
- WANING_GIBBOUS
- WANING_CRESCENT
Also can you add the functionality where we can find the MoonPhase.Phase
of given date
Adding the four remaining moon phases is easy. I will do that.
But you can already give a concrete number for other moon phases:
MoonPhase mp = MoonPhase.compute()
.phase(45.0) // 45° = Waxing Crescent
.execute();
The phase is given in degrees, where 0.0 = New Moon, 180.0 = Full Moon.
About the moon phase of a given date, you can use MoonIllumination
to compute the phase of a concrete date:
MoonIllumination mi = MoonIllumination.compute()
.now()
.execute();
System.out.println(mi.getPhase());
The returned phase is also in degrees, as mentioned above.
Missing phases are now added, see commit bbc68b5. It will be available in the next release.
When I mean by moon phase of a given date is that sometimes a moon phase (NEW, FULL) will long for 2 or more days, if we were to use breakpoints to find moon phase, then we may get NEW/FULL moon for both the days.
If MoonPhase
can return that, this will help solve this confusion
In my case, I'm first checking whether today is NEW_MOON
if not for FULL_MOON
if those both are false then I'm using breakpoints to get moon phase
Each of these angular positions is an instant in time. The date assigned to that instant will depend on the local time zone.
You can define a window (what astrologers would call an "orb") in which you want to consider the moon to be in a phase, e.g. full moon is 170 to 190 degrees. But then that window might extend over more than one day.
@isomeme you mean to say that I have to use breakpoints to confirm the moon phase for the day?
To be honest, I don't know what you mean with breakpoints.
As @isomeme said, a moon phase does not long for days, but is a short instant that this library calculates with a precision of about a minute. For example, the next full moon will be almost precisely on June 24th 2021 18:38:41 UTC.
To get the exact date of the next full moon, you can use a code like this:
MoonPhase mp = MoonPhase.compute()
.now()
.timezone("UTC")
.phase(Phase.FULL_MOON)
.execute();
LocalDate dateOfFullMoon = mp.getTime().toLocalDate();
System.out.println(dateOfFullMoon);
The result depends on your time zone, so you should give it using .timezone()
, otherwise your system's timezone will be used.
The starting time of the window may be important to you. As I mentioned above, the next full moon will be on June 24th 2021 18:38:41 UTC. If you invoke the example on June 24th, 2021 12:00:00 UTC, the June's full moon is still in the future, so the example will give June 24th 2021 as result. However if you invoke the example on June 24th, 2021 20:00:00 UTC, the June's full moon is now in the past, and the result will be the full moon of July. If you would rather get the June's full moon, you should use .today()
instead of .now()
. Then the calculation window then starts on midnight instead of the current time.
Thinking about what @isomeme said... If you consider "full moon" to be between, say, 170 to 190 degrees, you can still simply calculate when this full moon window (or is it "orb"?) is starting and ending:
MoonPhase mpStart = MoonPhase.compute()
.now()
.timezone("UTC")
.phase(170.0) // start of full moon window
.execute();
MoonPhase mpEnd = MoonPhase.compute()
.now()
.timezone("UTC")
.phase(190.0) // end of full moon window
.execute();
Is it that what you are looking for?
When calculating the next full moon (or any other phase) using .today()
, be aware that two users running that calculation at the same moment in different timezones can get answers a month apart. This problem led the Roman Catholic Church (and later other denominations) to invent a fake Moon to ensure that everyone would agree on the date of Easter. As a major calendar nerd, this is one of my favorite stories.
This is how I'm calculating today's moon phase
private fun findLunarPhase(moonIllumination: MoonIllumination): LunarPhase {
val maxNewMoon = 0.025
val maxCrescentMoon = 0.45
val illumination = moonIllumination.fraction
val positionAngle = moonIllumination.phase
return when {
isTodayTheMoon(MoonPhase.Phase.NEW_MOON) -> NEW_MOON
isTodayTheMoon(MoonPhase.Phase.FULL_MOON) -> FULL_MOON
isTodayTheMoon(MoonPhase.Phase.FIRST_QUARTER) -> FIRST_QUARTER
isTodayTheMoon(MoonPhase.Phase.LAST_QUARTER) -> LAST_QUARTER
maxNewMoon < illumination && illumination <= maxCrescentMoon -> {
if (0 <= positionAngle && positionAngle < 180) WANING_CRESCENT else WAXING_CRESCENT
}
else -> {
if (0 <= positionAngle && positionAngle < 180) WANING_GIBBOUS else WAXING_GIBBOUS
}
}
}
Is there any better way to do this with the library?
Ah, I think I understood now... You won't need the moonIllumination.fraction
for that. All you need is the .phase
.
for (i in 0..31) {
val mi = MoonIllumination.compute()
.today()
.plusDays(i)
.execute()
val angle = mi.phase + 180.0
val phase = when {
angle < 22.5 -> "New Moon"
angle < 67.5 -> "Waxing Crescent"
angle < 112.5 -> "First Quarter"
angle < 157.5 -> "Waxing Gibbous"
angle < 202.5 -> "Full Moon"
angle < 247.5 -> "Waning Gibbous"
angle < 292.5 -> "Last Quarter"
angle < 337.5 -> "Waning Crescent"
else -> "New Moon"
}
println("%2d: %3.1f° -> %s".format(i, angle, phase))
}
.phase
ranges between -180° and 180°, so it does not match the phases that are used in the MoonPhase.Phase
enumeration. It's a design weakness that has historical reasons. Changing it would break the API. (Edit) When I add 180.0
to the mi.phase
, it will match the angles of the enumeration.
However, I will add a new method to MoonIllumination
that will return one of the MoonPhase.Phase
enum values.
See commits 38786f6 and 5490c88. You can use something like this now:
for (i in 0..31) {
val mi = MoonIllumination.compute()
.today()
.plusDays(i)
.execute()
val phase = mi.closestPhase
println("%2d: %s".format(i, phase))
}
phase
contains the closest matching MoonPhase.Phase
enum value.
Does that help you?
@shred yeah very much, thanks ❤️
I'm new to Astronomy and I want to learn more about Moon & Calendar, can anyone point me in a direction.
@mslalith Thank you for your feedback. I will prepare a new release today.
I learned most of the things I needed to write this library from the book "Astronomy on the Personal Computer" by Oliver Montenbruck and Thomas Pfleger. A classic recommendation is "Astronomical Algorithms" by Jean Meeus. Both books focus on the mathematical aspect though. I had to learn a lot more than I had expected when I started this library. Sometimes it almost made me go crazy (especially when time zones come into play). But I enjoyed it. It is a very interesting topic.
I'd also like to add another trivia. The German language is said to have a word for almost everything. But there are no words for "waxing crescent", "waning gibbous" and so on, we actually only talk about four moon phases. I realized that this was the reason why I missed to add those four phases. It just never came to my mind that there are more than four of them. 😉
The new methods and constants are released in v3.5, and also backported to v2.12. The new versions should be available at Maven Central in the next hours.