New feature: sunrise and sunset calculation

This commit is contained in:
Minecon724 2024-09-24 18:34:07 +02:00
parent d2e812a70e
commit c670ba88bd
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
6 changed files with 189 additions and 0 deletions

View file

@ -0,0 +1,27 @@
package eu.m724.wtapi.object;
import java.time.Duration;
import java.time.LocalDate;
public class Twilight {
/**
* The date this object contains times for
*/
public final LocalDate date;
/**
* Time of sunrise relative to UTC midnight of the date
*/
public final Duration sunrise;
/**
* Time of sunset relative to UTC midnight of the date
*/
public final Duration sunset;
public Twilight(LocalDate date, Duration sunrise, Duration sunset) {
this.date = date;
this.sunrise = sunrise;
this.sunset = sunset;
}
}

View file

@ -0,0 +1,51 @@
package eu.m724.wtapi.provider.twilight;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import java.time.Duration;
import java.time.LocalDate;
import static java.lang.Math.*;
public class SimpleTwilightTimeProvider extends TwilightTimeProvider {
@Override
public Twilight calculateTwilightTime(LocalDate date, Coordinates coordinates) {
int dayOfYear = date.getDayOfYear() - 1; // -1 because we have to start from zero
// 2 * PI / 365 = 0.01721420632103996
double fractionalYear = 0.01721420632103996 * dayOfYear;
double equationOfTime = 229.18 * (0.000075
+ 0.001868 * cos(fractionalYear)
- 0.032077 * sin(fractionalYear)
- 0.014615 * cos(2 * fractionalYear)
- 0.040849 * sin(2 * fractionalYear));
double declination = 0.006918
- 0.399912 * cos(fractionalYear)
+ 0.070257 * sin(fractionalYear)
- 0.006758 * cos(2 * fractionalYear)
+ 0.000907 * sin(2 * fractionalYear)
- 0.002697 * cos(3 * fractionalYear)
+ 0.00148 * sin(3 * fractionalYear);
double latRad = toRadians(coordinates.latitude);
// 90.833 deg = 1.5853349194640094 rad
double n1 = cos(1.5853349194640094) / (cos(latRad) * cos(declination));
double n2 = tan(latRad) * tan(declination);
double hourAngle = acos(n1 - n2);
double hourAngleDeg = 4 * toDegrees(hourAngle);
// sunrise = 720 - 4 * (coordinates.longitude + hourAngleDeg) - equationOfTime
// sunset = 720 - 4 * (coordinates.longitude - hourAngleDeg) - equationOfTime
double solarNoon = 720 - 4 * coordinates.longitude - equationOfTime;
double sunrise = solarNoon - hourAngleDeg;
double sunset = solarNoon + hourAngleDeg;
return new Twilight(
date,
Duration.ofMillis((long) (sunrise * 60000)),
Duration.ofMillis((long) (sunset * 60000))
);
}
}

View file

@ -0,0 +1,22 @@
package eu.m724.wtapi.provider.twilight;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import java.time.LocalDate;
// TODO make this an interface? also consider new name
/**
* Twilight refers to sunset and sunrise time<br>
* Is it the correct term? I don't know
*/
public abstract class TwilightTimeProvider {
/**
* Calculates sunrise and sunset for provided coordinates
*
* @param date UTC date
* @param coordinates Coordinates of the observer
* @return {@link Twilight}
*/
public abstract Twilight calculateTwilightTime(LocalDate date, Coordinates coordinates);
}

View file

@ -0,0 +1,46 @@
package eu.m724.wtapi.twilight;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import eu.m724.wtapi.provider.twilight.SimpleTwilightTimeProvider;
import eu.m724.wtapi.provider.twilight.TwilightTimeProvider;
import org.junit.Test;
import java.time.LocalDate;
public class ApproximateTwilightTimeTest {
/**
* Acceptable discrepancy in minutes
*/
public static final int ACCEPTABLE_DIFFERENCE = 10;
@Test
public void approximateTest() {
TwilightTimeProvider provider = new SimpleTwilightTimeProvider();
testLocation(provider, 26, 6, 2023, 53.123394, 23.0864867, 122, 1139);
testLocation(provider, 13, 11, 2040, 45.432427, -122.3899276, 907, 1481);
testLocation(provider, 23, 3, 2021, 55.5024161, 9.6801853, 315, 1061);
testLocation(provider, 6, 8,1990, -72.012117, 2.5240873, 600, 832);
// TODO this is broken so fix
//testLocation(provider, 6, 9,2021, 82.498665, -62.3458366, 74, 1351); // date not on purpose as this was the first sunset in that location. first sunset since 5th april
}
private void testLocation(TwilightTimeProvider provider, int day, int month, int year, double latitude, double longitude, int actualSunrise, int actualSunset) {
LocalDate date = LocalDate.of(year, month, day);
Coordinates coordinates = new Coordinates(latitude, longitude);
Twilight twilight = provider.calculateTwilightTime(date, coordinates);
System.out.println();
System.out.println(date);
System.out.println(coordinates.latitude + " " + coordinates.longitude);
System.out.println("Calculated sunrise: " + date.atStartOfDay().plus(twilight.sunrise));
System.out.println("Actual sunrise: " + date.atStartOfDay().plusMinutes(actualSunrise));
assert Math.abs(twilight.sunrise.toMinutes() - actualSunrise) < ACCEPTABLE_DIFFERENCE;
System.out.println("Calculated sunset: " + date.atStartOfDay().plus(twilight.sunset));
System.out.println("Actual sunset: " + date.atStartOfDay().plusMinutes(actualSunset));
assert Math.abs(twilight.sunset.toMinutes() - actualSunset) < ACCEPTABLE_DIFFERENCE;
}
}

View file

@ -0,0 +1,20 @@
package eu.m724.wtapi.twilight;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import eu.m724.wtapi.provider.twilight.TwilightTimeProvider;
import java.time.Duration;
import java.time.LocalDate;
public class MockTwilightTimeProvider extends TwilightTimeProvider {
@Override
public Twilight calculateTwilightTime(LocalDate date, Coordinates coordinates) {
int time = (int) (coordinates.latitude + coordinates.longitude);
return new Twilight(
date,
Duration.ofMinutes(-time),
Duration.ofMinutes(time)
);
}
}

View file

@ -0,0 +1,23 @@
package eu.m724.wtapi.twilight;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import eu.m724.wtapi.provider.twilight.TwilightTimeProvider;
import org.junit.Test;
import java.time.LocalDate;
public class MockTwilightTimeTest {
@Test
public void testTwilight() {
TwilightTimeProvider provider = new MockTwilightTimeProvider();
LocalDate date = LocalDate.of(2077, 4, 20);
Coordinates coordinates = new Coordinates(52.4796012, 62.1847245);
Twilight twilight = provider.calculateTwilightTime(date, coordinates);
assert twilight.date.equals(date);
assert twilight.sunrise.getSeconds() == -6840;
assert twilight.sunset.getSeconds() == 6840;
}
}