Skip to content

Commit

Permalink
Noon and nadir is always computed for the next 24 hours only
Browse files Browse the repository at this point in the history
  • Loading branch information
shred committed Nov 27, 2017
1 parent f3e471a commit f291ca3
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 51 deletions.
38 changes: 26 additions & 12 deletions src/main/java/org/shredzone/commons/suncalc/MoonTimes.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ public final class MoonTimes {

private final Date rise;
private final Date set;
private final double ye;
private final boolean alwaysUp;
private final boolean alwaysDown;

private MoonTimes(Date rise, Date set, double ye) {
private MoonTimes(Date rise, Date set, boolean alwaysUp, boolean alwaysDown) {
this.rise = rise;
this.set = set;
this.ye = ye;
this.alwaysUp = alwaysUp;
this.alwaysDown = alwaysDown;
}

/**
Expand Down Expand Up @@ -104,6 +106,8 @@ public MoonTimes execute() {
Double rise = null;
Double set = null;
double ye = 0.0;
boolean alwaysUp = false;
boolean alwaysDown = false;

double y_minus = correctedMoonHeight(jd);

Expand Down Expand Up @@ -134,6 +138,11 @@ public MoonTimes execute() {
}
}

if (hour == 23 && rise == null && set == null) {
alwaysUp = ye >= 0.0;
alwaysDown = ye < 0.0;
}

if (rise != null && set != null) {
break;
}
Expand All @@ -144,7 +153,8 @@ public MoonTimes execute() {
return new MoonTimes(
rise != null ? jd.atHour(rise).getDate() : null,
set != null ? jd.atHour(set).getDate() : null,
ye);
alwaysUp,
alwaysDown);
}

/**
Expand Down Expand Up @@ -177,28 +187,32 @@ public Date getSet() {
}

/**
* {@code true} if the moon never rises/sets, but is always above the horizon that
* day. Always returns {@code false} if {@link Parameters#fullCycle()} is used.
* {@code true} if the moon never rises/sets, but is always above the horizon within
* the next 24 hours.
* <p>
* Note that {@link Parameters#fullCycle()} does not affect this result.
*/
public boolean isAlwaysUp() {
return rise == null && set == null && ye > 0.0;
return alwaysUp;
}

/**
* {@code true} if the moon never rises/sets, but is always below the horizon that
* day. Always returns {@code false} if {@link Parameters#fullCycle()} is used.
* {@code true} if the moon never rises/sets, but is always below the horizon within
* the next 24 hours.
* <p>
* Note that {@link Parameters#fullCycle()} does not affect this result.
*/
public boolean isAlwaysDown() {
return rise == null && set == null && ye <= 0.0;
return alwaysDown;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("MoonTimes[rise=").append(rise);
sb.append(", set=").append(set);
sb.append(", alwaysUp=").append(isAlwaysUp());
sb.append(", alwaysDown=").append(isAlwaysDown());
sb.append(", alwaysUp=").append(alwaysUp);
sb.append(", alwaysDown=").append(alwaysDown);
sb.append(']');
return sb.toString();
}
Expand Down
90 changes: 63 additions & 27 deletions src/main/java/org/shredzone/commons/suncalc/SunTimes.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@ public class SunTimes {
private final Date set;
private final Date noon;
private final Date nadir;
private final boolean alwaysUp;
private final boolean alwaysDown;

private SunTimes(Date rise, Date set, Date noon, Date nadir) {
private SunTimes(Date rise, Date set, Date noon, Date nadir, boolean alwaysUp,
boolean alwaysDown) {
this.rise = rise;
this.set = set;
this.noon = noon;
this.nadir = nadir;
this.alwaysUp = alwaysUp;
this.alwaysDown = alwaysDown;
}

/**
Expand Down Expand Up @@ -83,9 +88,8 @@ public static interface Parameters extends
Parameters twilight(double angle);

/**
* Checks only the next 24 hours. Rise, set, noon or nadir times can be
* {@code null} if the sun never reaches the point during one day (e.g. at
* solstice).
* Checks only the next 24 hours. Rise or set time can be {@code null} if the sun
* never reaches the point during one day (e.g. at solstice).
* <p>
* This is the default.
*
Expand All @@ -94,8 +98,8 @@ public static interface Parameters extends
Parameters oneDay();

/**
* Computes until rise, set, noon, and nadir times are found, even if the sun
* needs more than a day for it. This can considerably increase computation time.
* Computes until rise and set times are found, even if the sun needs more than a
* day for it. This can considerably increase computation time.
*
* @return itself
*/
Expand Down Expand Up @@ -259,6 +263,11 @@ public SunTimes execute() {
Double noon = null;
Double nadir = null;
double ye;
double lastXeAbs = Double.MAX_VALUE;
double noonXeAbs = Double.MAX_VALUE;
double nadirXeAbs = Double.MAX_VALUE;
double noonYe = 0.0;
double nadirYe = 0.0;

double y_minus = correctedSunHeight(jd);

Expand Down Expand Up @@ -289,16 +298,24 @@ public SunTimes execute() {
}
}

double xe = qi.getXe();
if (xe > -1.01 && xe < 1.01) {
if (ye < 0.0) {
nadir = xe + hour;
} else {
noon = xe + hour;
if (hour < 24) {
double xeAbs = Math.abs(qi.getXe());
if (xeAbs < lastXeAbs) {
double xeHour = qi.getXe() + hour;
if (qi.isMaximum() && xeAbs < noonXeAbs) {
noon = xeHour;
noonXeAbs = xeAbs;
noonYe = ye;
} else if (!qi.isMaximum() && xeAbs < nadirXeAbs) {
nadir = xeHour;
nadirXeAbs = xeAbs;
nadirYe = ye;
}
}
lastXeAbs = xeAbs;
}

if (rise != null && set != null && noon != null && nadir != null) {
if (hour >= 24 && rise != null && set != null) {
break;
}

Expand All @@ -308,8 +325,11 @@ public SunTimes execute() {
return new SunTimes(
rise != null ? jd.atHour(rise).getDate() : null,
set != null ? jd.atHour(set).getDate() : null,
noon != null ? jd.atHour(noon).getDate() : null,
nadir != null ? jd.atHour(nadir).getDate() : null);
jd.atHour(noon).getDate(),
jd.atHour(nadir).getDate(),
nadir == null || nadirYe > 0.0,
noon == null || noonYe < 0.0
);
}

/**
Expand All @@ -334,48 +354,64 @@ private double correctedSunHeight(JulianDate jd) {

/**
* Sunrise time. {@code null} if the sun does not rise that day.
* <p>
* Always returns a sunrise time if {@link Parameters#fullCycle()} was set.
*/
public Date getRise() {
return rise != null ? new Date(rise.getTime()) : null;
}

/**
* Sunset time. {@code null} if the sun does not set that day.
* <p>
* Always returns a sunset time if {@link Parameters#fullCycle()} was set.
*/
public Date getSet() {
return set != null ? new Date(set.getTime()) : null;
}

/**
* The time when the sun reaches its highest point. {@code null} if the sun never
* rises on that day.
* The time when the sun reaches its highest point within the next 24 hours.
* <p>
* Use {@link #isAlwaysDown()} to find out if the highest point is still below the
* twilight angle.
* <p>
* Note that {@link Parameters#fullCycle()} does not affect this result.
*/
public Date getNoon() {
return noon != null ? new Date(noon.getTime()) : null;
return new Date(noon.getTime());
}

/**
* The time when the sun reaches its lowest point. {@code null} if the sun never sets
* on that day.
* The time when the sun reaches its lowest point within the next 24 hours.
* <p>
* Use {@link #isAlwaysUp()} to find out if the lowest point is still above the
* twilight angle.
* <p>
* Note that {@link Parameters#fullCycle()} does not affect this result.
*/
public Date getNadir() {
return nadir != null ? new Date(nadir.getTime()) : null;
return new Date(nadir.getTime());
}

/**
* {@code true} if the sun never rises/sets, but is always above the twilight angle
* that day. Always returns {@code false} if {@link Parameters#fullCycle()} is used.
* within the next 24 hours.
* <p>
* Note that {@link Parameters#fullCycle()} does not affect this result.
*/
public boolean isAlwaysUp() {
return rise == null && set == null && noon != null;
return alwaysUp;
}

/**
* {@code true} if the sun never rises/sets, but is always below the twilight angle
* that day. Always returns {@code false} if {@link Parameters#fullCycle()} is used.
* within the next 24 hours.
* <p>
* Note that {@link Parameters#fullCycle()} does not affect this result.
*/
public boolean isAlwaysDown() {
return rise == null && set == null && nadir != null;
return alwaysDown;
}

@Override
Expand All @@ -385,8 +421,8 @@ public String toString() {
sb.append(", set=").append(set);
sb.append(", noon=").append(noon);
sb.append(", nadir=").append(nadir);
sb.append(", alwaysUp=").append(isAlwaysUp());
sb.append(", alwaysDown=").append(isAlwaysDown());
sb.append(", alwaysUp=").append(alwaysUp);
sb.append(", alwaysDown=").append(alwaysDown);
sb.append(']');
return sb.toString();
}
Expand Down
5 changes: 5 additions & 0 deletions src/site/markdown/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

This document will help you migrate your code to the latest _suncalc_ version.

## Version 2.1

* `SunTimes`'s `getNoon()` and `getNadir()` now always give a result, even if the sun stays below or above the twilight angle, respectively. To emulate the old behavior, use `isAlwaysUp()` and `isAlwaysDown()` (e.g. `Date noon = !sun.isAlwaysDown() ? sun.getNoon() : null`).
* At `SunTimes` and `MoonTimes`, the methods `isAlwaysUp()`, `isAlwaysDown()`, `getNoon()` and `getNadir()` ignore the `fullCycle` option now, and always consider the next 24 hours only.

## Version 2.0

> **NOTE:** Version 2.0 is a major rewrite. It uses different and (hopefully) more accurate formulae than the previous version. If you rely on reproducable results (e.g. in unit tests), be careful when upgrading. The results may differ up to several minutes between both versions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void testAlert() {
assertThat("rise", mt2.getRise(), DateMatcher.is("2017-07-14T05:45:33Z"));
assertThat("set", mt2.getSet(), DateMatcher.is("2017-07-14T11:26:12Z"));
assertThat("alwaysup", mt2.isAlwaysUp(), is(false));
assertThat("alwaysdown", mt2.isAlwaysDown(), is(false));
assertThat("alwaysdown", mt2.isAlwaysDown(), is(true));

MoonTimes mt3 = MoonTimes.compute().on(2017, 7, 14).utc().at(ALERT).execute();
assertThat("rise", mt3.getRise(), DateMatcher.is("2017-07-14T05:45:33Z"));
Expand All @@ -62,7 +62,7 @@ public void testAlert() {
MoonTimes mt5 = MoonTimes.compute().on(2017, 7, 18).utc().at(ALERT).fullCycle().execute();
assertThat("rise", mt5.getRise(), DateMatcher.is("2017-07-27T11:59:07Z"));
assertThat("set", mt5.getSet(), DateMatcher.is("2017-07-27T04:07:14Z"));
assertThat("alwaysup", mt5.isAlwaysUp(), is(false));
assertThat("alwaysup", mt5.isAlwaysUp(), is(true));
assertThat("alwaysdown", mt5.isAlwaysDown(), is(false));
}

Expand Down
25 changes: 15 additions & 10 deletions src/test/java/org/shredzone/commons/suncalc/SunTimesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,19 @@ public void testCologne() {
@Test
public void testAlert() {
SunTimes t1 = SunTimes.compute().at(ALERT).on(2017, 8, 10).utc().execute();
assertTimes(t1, null, null, "2017-08-10T16:12:47Z");
assertTimes(t1, null, null, "2017-08-10T16:12:47Z", true);

SunTimes t2 = SunTimes.compute().at(ALERT).on(2017, 9, 24).utc().execute();
assertTimes(t2, "2017-09-24T09:54:29Z", "2017-09-24T22:01:58Z", "2017-09-24T16:00:23Z");

SunTimes t3 = SunTimes.compute().at(ALERT).on(2017, 2, 10).utc().execute();
assertTimes(t3, null, null, null);
assertTimes(t3, null, null, "2017-02-10T16:25:05Z", false);

SunTimes t4 = SunTimes.compute().at(ALERT).on(2017, 8, 10).utc().fullCycle().execute();
assertTimes(t4, "2017-09-06T05:13:15Z", "2017-09-06T03:06:02Z", "2017-09-05T16:05:21Z");
assertTimes(t4, "2017-09-06T05:13:15Z", "2017-09-06T03:06:02Z", "2017-08-10T16:12:47Z", true);

SunTimes t5 = SunTimes.compute().at(ALERT).on(2017, 2, 10).utc().fullCycle().execute();
assertTimes(t5, "2017-02-27T15:24:18Z", "2017-02-27T17:23:46Z", "2017-02-27T16:23:41Z");
assertTimes(t5, "2017-02-27T15:24:18Z", "2017-02-27T17:23:46Z", "2017-02-10T16:25:05Z", false);

SunTimes t6 = SunTimes.compute().at(ALERT).on(2017, 9, 6).utc().oneDay().execute();
assertTimes(t6, "2017-09-06T05:13:15Z", "2017-09-06T03:06:02Z", "2017-09-06T16:04:59Z");
Expand Down Expand Up @@ -158,6 +158,10 @@ private Date createDate(int year, int month, int day, int hour, int minute) {
}

private void assertTimes(SunTimes t, String rise, String set, String noon) {
assertTimes(t, rise, set, noon, null);
}

private void assertTimes(SunTimes t, String rise, String set, String noon, Boolean alwaysUp) {
if (rise != null) {
assertThat("sunrise", t.getRise(), DateMatcher.is(rise));
} else {
Expand All @@ -170,14 +174,15 @@ private void assertTimes(SunTimes t, String rise, String set, String noon) {
assertThat("sunset", t.getSet(), is(nullValue()));
}

if (noon != null) {
assertThat("noon", t.getNoon(), DateMatcher.is(noon));
assertThat("noon", t.getNoon(), DateMatcher.is(noon));

if (alwaysUp != null) {
assertThat("always-down", t.isAlwaysDown(), is(!alwaysUp));
assertThat("always-up", t.isAlwaysUp(), is(alwaysUp));
} else {
assertThat("noon", t.getNoon(), is(nullValue()));
assertThat("always-down", t.isAlwaysDown(), is(false));
assertThat("always-up", t.isAlwaysUp(), is(false));
}

assertThat("always-down", t.isAlwaysDown(), is(rise == null && set == null && noon == null));
assertThat("always-up", t.isAlwaysUp(), is(rise == null && set == null && noon != null));
}

}

0 comments on commit f291ca3

Please sign in to comment.