Skip to content

Interpolate #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ public class AstronomicalCalendar implements Cloneable {
*/
private AstronomicalCalculator astronomicalCalculator;

private final TimeZone timeZoneUTC = TimeZone.getTimeZone("UTC");

/**
* The getSunrise method returns a <code>Date</code> representing the
* {@link AstronomicalCalculator#getElevationAdjustment(double) elevation adjusted} sunrise time. The zenith used
Expand Down Expand Up @@ -625,7 +627,7 @@ protected Date getDateFromTime(double time, SolarEvent solarEvent) {
double calculatedTime = time;

Calendar adjustedCalendar = getAdjustedCalendar();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Calendar cal = Calendar.getInstance(timeZoneUTC);
cal.clear();// clear all fields
cal.set(Calendar.YEAR, adjustedCalendar.get(Calendar.YEAR));
cal.set(Calendar.MONTH, adjustedCalendar.get(Calendar.MONTH));
Expand Down
17 changes: 10 additions & 7 deletions src/main/java/com/kosherjava/zmanim/util/GeoLocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ public class GeoLocation implements Cloneable {
/** constant for milliseconds in an hour (3,600,000) */
private static final long HOUR_MILLIS = MINUTE_MILLIS * 60;

private static final double PI2 = Math.PI * 2;
private static final double PI_4 = Math.PI / 4;

/**
* Method to return the elevation in Meters <b>above</b> sea level.
*
Expand Down Expand Up @@ -441,7 +444,7 @@ private double vincentyInverseFormula(GeoLocation location, int formula) {
double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);

double lambda = L;
double lambdaP = 2 * Math.PI;
double lambdaP = PI2;
double iterLimit = 20;
double sinLambda = 0;
double cosLambda = 0;
Expand Down Expand Up @@ -510,10 +513,10 @@ private double vincentyInverseFormula(GeoLocation location, int formula) {
*/
public double getRhumbLineBearing(GeoLocation location) {
double dLon = Math.toRadians(location.getLongitude() - getLongitude());
double dPhi = Math.log(Math.tan(Math.toRadians(location.getLatitude()) / 2 + Math.PI / 4)
/ Math.tan(Math.toRadians(getLatitude()) / 2 + Math.PI / 4));
double dPhi = Math.log(Math.tan(Math.toRadians(location.getLatitude()) / 2 + PI_4)
/ Math.tan(Math.toRadians(getLatitude()) / 2 + PI_4));
if (Math.abs(dLon) > Math.PI)
dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
dLon = dLon > 0 ? -(PI2 - dLon) : (PI2 + dLon);
return Math.toDegrees(Math.atan2(dLon, dPhi));
}

Expand All @@ -529,16 +532,16 @@ public double getRhumbLineDistance(GeoLocation location) {
double earthRadius = 6378137; // Earth's radius in meters (WGS-84)
double dLat = Math.toRadians(location.getLatitude()) - Math.toRadians(getLatitude());
double dLon = Math.abs(Math.toRadians(location.getLongitude()) - Math.toRadians(getLongitude()));
double dPhi = Math.log(Math.tan(Math.toRadians(location.getLatitude()) / 2 + Math.PI / 4)
/ Math.tan(Math.toRadians(getLatitude()) / 2 + Math.PI / 4));
double dPhi = Math.log(Math.tan(Math.toRadians(location.getLatitude()) / 2 + PI_4)
/ Math.tan(Math.toRadians(getLatitude()) / 2 + PI_4));
double q = dLat / dPhi;

if (!(Math.abs(q) <= Double.MAX_VALUE)) {
q = Math.cos(Math.toRadians(getLatitude()));
}
// if dLon over 180° take shorter rhumb across 180° meridian:
if (dLon > Math.PI) {
dLon = 2 * Math.PI - dLon;
dLon = PI2 - dLon;
}
double d = Math.sqrt(dLat * dLat + q * q * dLon * dLon);
return d * earthRadius;
Expand Down
17 changes: 10 additions & 7 deletions src/main/java/com/kosherjava/zmanim/util/GeoLocationUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public class GeoLocationUtils {
*/
private static int FINAL_BEARING = 2;

private static final double PI2 = Math.PI * 2;
private static final double PI_4 = Math.PI / 4;

/**
* Calculate the <a href="http://en.wikipedia.org/wiki/Great_circle">geodesic</a> initial bearing between this Object and
* a second Object passed to this method using <a href="http://en.wikipedia.org/wiki/Thaddeus_Vincenty">Thaddeus
Expand Down Expand Up @@ -127,7 +130,7 @@ private static double vincentyFormula(GeoLocation location, GeoLocation destinat
double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);

double lambda = L;
double lambdaP = 2 * Math.PI;
double lambdaP = PI2;
double iterLimit = 20;
double sinLambda = 0;
double cosLambda = 0;
Expand Down Expand Up @@ -211,10 +214,10 @@ private static double vincentyFormula(GeoLocation location, GeoLocation destinat
public static double getRhumbLineBearing(GeoLocation location, GeoLocation destination) {
double dLon = Math.toRadians(destination.getLongitude() - location.getLongitude());
double dPhi = Math.log(Math.tan(Math.toRadians(destination.getLatitude())
/ 2 + Math.PI / 4)
/ Math.tan(Math.toRadians(location.getLatitude()) / 2 + Math.PI / 4));
/ 2 + PI_4)
/ Math.tan(Math.toRadians(location.getLatitude()) / 2 + PI_4));
if (Math.abs(dLon) > Math.PI)
dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
dLon = dLon > 0 ? -(PI2 - dLon) : (PI2 + dLon);
return Math.toDegrees(Math.atan2(dLon, dPhi));
}

Expand All @@ -232,16 +235,16 @@ public static double getRhumbLineDistance(GeoLocation location, GeoLocation dest
double earthRadius = 6378137; // Earth's radius in meters (WGS-84)
double dLat = Math.toRadians(location.getLatitude()) - Math.toRadians(destination.getLatitude());
double dLon = Math.abs(Math.toRadians(location.getLongitude()) - Math.toRadians(destination.getLongitude()));
double dPhi = Math.log(Math.tan(Math.toRadians(location.getLatitude()) / 2 + Math.PI / 4)
/ Math.tan(Math.toRadians(destination.getLatitude()) / 2 + Math.PI / 4));
double dPhi = Math.log(Math.tan(Math.toRadians(location.getLatitude()) / 2 + PI_4)
/ Math.tan(Math.toRadians(destination.getLatitude()) / 2 + PI_4));
double q = dLat / dPhi;

if (!(Math.abs(q) <= Double.MAX_VALUE)) {
q = Math.cos(Math.toRadians(destination.getLatitude()));
}
// if dLon over 180° take shorter rhumb across 180° meridian:
if (dLon > Math.PI) {
dLon = 2 * Math.PI - dLon;
dLon = PI2 - dLon;
}
double d = Math.sqrt(dLat * dLat + q * q * dLon * dLon);
return d * earthRadius;
Expand Down
164 changes: 157 additions & 7 deletions src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java
Original file line number Diff line number Diff line change
Expand Up @@ -284,11 +284,13 @@ private static double getEquationOfTime(double julianCenturies) {
double geomMeanAnomalySun = getSunGeometricMeanAnomaly(julianCenturies);
double y = Math.tan(Math.toRadians(epsilon) / 2.0);
y *= y;
double sin2l0 = Math.sin(2.0 * Math.toRadians(geomMeanLongSun));
double sinm = Math.sin(Math.toRadians(geomMeanAnomalySun));
double cos2l0 = Math.cos(2.0 * Math.toRadians(geomMeanLongSun));
double sin4l0 = Math.sin(4.0 * Math.toRadians(geomMeanLongSun));
double sin2m = Math.sin(2.0 * Math.toRadians(geomMeanAnomalySun));
double geomMeanLongSunRad = Math.toRadians(geomMeanLongSun);
double geomMeanAnomalySunRad = Math.toRadians(geomMeanAnomalySun);
double sin2l0 = Math.sin(2.0 * geomMeanLongSunRad);
double sinm = Math.sin(geomMeanAnomalySunRad);
double cos2l0 = Math.cos(2.0 * geomMeanLongSunRad);
double sin4l0 = Math.sin(4.0 * geomMeanLongSunRad);
double sin2m = Math.sin(2.0 * geomMeanAnomalySunRad);
double equationOfTime = y * sin2l0 - 2.0 * eccentricityEarthOrbit * sinm + 4.0 * eccentricityEarthOrbit * y
* sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * eccentricityEarthOrbit * eccentricityEarthOrbit * sin2m;
return Math.toDegrees(equationOfTime) * 4.0;
Expand All @@ -311,7 +313,8 @@ private static double getEquationOfTime(double julianCenturies) {
private static double getSunHourAngle(double latitude, double solarDeclination, double zenith, SolarEvent solarEvent) {
double latRad = Math.toRadians(latitude);
double sdRad = Math.toRadians(solarDeclination);
double hourAngle = (Math.acos(Math.cos(Math.toRadians(zenith)) / (Math.cos(latRad) * Math.cos(sdRad))
double zRad = Math.toRadians(zenith);
double hourAngle = (Math.acos(Math.cos(zRad) / (Math.cos(latRad) * Math.cos(sdRad))
- Math.tan(latRad) * Math.tan(sdRad)));

if (solarEvent == SolarEvent.SUNSET) {
Expand Down Expand Up @@ -493,19 +496,166 @@ private static double getSunRiseSetUTC(Calendar calendar, double latitude, doubl
double equationOfTime = getEquationOfTime(tnoon);
double solarDeclination = getSunDeclination(tnoon);
double hourAngle = getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
if (Double.isNaN(hourAngle)) {
hourAngle = interpolateHourAngle1(julianDay, latitude, longitude, zenith, solarEvent);
if (Double.isNaN(hourAngle)) {
return Double.NaN;
}
}
double delta = longitude - Math.toDegrees(hourAngle);
double timeDiff = 4 * delta;
double timeUTC = 720 + timeDiff - equationOfTime;

// Second pass includes fractional Julian Day in gamma calc
double newt = getJulianCenturiesFromJulianDay(julianDay + timeUTC / 1440.0);
equationOfTime = getEquationOfTime(newt);

solarDeclination = getSunDeclination(newt);
hourAngle = getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
if (Double.isNaN(hourAngle)) {
hourAngle = interpolateHourAngle2(julianDay, latitude, longitude, zenith, solarEvent);
if (Double.isNaN(hourAngle)) {
return Double.NaN;
}
}
delta = longitude - Math.toDegrees(hourAngle);
timeDiff = 4 * delta;
timeUTC = 720 + timeDiff - equationOfTime;
return timeUTC;
}

private static double getSunHourAngle1(double julianDay, double latitude, double longitude, double zenith, SolarEvent solarEvent) {
double noonmin = getSolarNoonMidnightUTC(julianDay, longitude, SolarEvent.NOON);
double tnoon = getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440.0);
double solarDeclination = getSunDeclination(tnoon);
return getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
}

private static double getSunHourAngle2(double julianDay, double latitude, double longitude, double zenith, SolarEvent solarEvent) {
double noonmin = getSolarNoonMidnightUTC(julianDay, longitude, SolarEvent.NOON);
double tnoon = getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440.0);

// First calculates sunrise and approximate length of day
double equationOfTime = getEquationOfTime(tnoon);
double solarDeclination = getSunDeclination(tnoon);
double hourAngle = getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
if (Double.isNaN(hourAngle)) {
return Double.NaN;
}
double delta = longitude - Math.toDegrees(hourAngle);
double timeDiff = 4 * delta;
double timeUTC = 720 + timeDiff - equationOfTime;

// Second pass includes fractional Julian Day in gamma calc
double newt = getJulianCenturiesFromJulianDay(julianDay + timeUTC / 1440.0);

solarDeclination = getSunDeclination(newt);
return getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
}

/**
* Use linear interpolation to calculate a missing angle.
*/
private static double interpolateHourAngle1(double julianDay, double latitude, double longitude, double zenith, SolarEvent solarEvent) {
double hourAngle;
double x1 = 0;
double y1 = 0;
double x2 = 0;
double y2 = 0;

double d = julianDay - 1;
final double dayFirst = Math.max(julianDay - 366, 1);
while (d >= dayFirst) {
hourAngle = getSunHourAngle1(d, latitude, longitude, zenith, solarEvent);

if (!Double.isNaN(hourAngle)) {
x1 = d;
y1 = hourAngle;
break;
}
d--;
}

d = julianDay + 1;
final double dayLast = julianDay + 366;
while (d <= dayLast) {
hourAngle = getSunHourAngle1(d, latitude, longitude, zenith, solarEvent);

if (!Double.isNaN(hourAngle)) {
if (x1 == 0) {
x1 = d;
y1 = hourAngle;
d++;
continue;
}
x2 = d;
y2 = hourAngle;
break;
}
d++;
}

if ((x1 == 0) || (x2 == 0)) {
return Double.NaN;
}
double dx = x2 - x1;
if (dx == 0) {
return Double.NaN;
}
double dy = y2 - y1;
return y1 + ((julianDay - x1) * dy / dx);
}

/**
* Use linear interpolation to calculate a missing angle.
*/
private static double interpolateHourAngle2(double julianDay, double latitude, double longitude, double zenith, SolarEvent solarEvent) {
double hourAngle;
double x1 = 0;
double y1 = 0;
double x2 = 0;
double y2 = 0;

double d = julianDay - 1;
final double dayFirst = Math.max(julianDay - 366, 1);
while (d >= dayFirst) {
hourAngle = getSunHourAngle2(d, latitude, longitude, zenith, solarEvent);

if (!Double.isNaN(hourAngle)) {
x1 = d;
y1 = hourAngle;
break;
}
d--;
}

d = julianDay + 1;
final double dayLast = julianDay + 366;
while (d <= dayLast) {
hourAngle = getSunHourAngle2(d, latitude, longitude, zenith, solarEvent);

if (!Double.isNaN(hourAngle)) {
if (x1 == 0) {
x1 = d;
y1 = hourAngle;
d++;
continue;
}
x2 = d;
y2 = hourAngle;
break;
}
d++;
}

if ((x1 == 0) || (x2 == 0)) {
return Double.NaN;
}
double dx = x2 - x1;
if (dx == 0) {
return Double.NaN;
}
double dy = y2 - y1;
return y1 + ((julianDay - x1) * dy / dx);
}
}
Loading
Loading