Partial update, not working

This commit is contained in:
Minecon724 2025-05-22 18:10:45 +02:00
commit dafb1d770a
Signed by: Minecon724
GPG key ID: A02E6E67AB961189
29 changed files with 415 additions and 262 deletions

View file

@ -1,21 +1,14 @@
package eu.m724.wtapi.object;
/**
* represents geographic coordinates
* contains fields latitude and longitude
* Represents coordinates
*
* @param latitude The latitude (-90-90)
* @param longitude The longitude (-180-180)
*/
public class Coordinates {
public final double latitude, longitude;
public String country, city; // TODO should it stay here?
public record Coordinates(double latitude, double longitude) {
public Coordinates(double latitude, double longitude) {
this.latitude = (((latitude + 90) % 180) + 180) % 180 - 90;
this.longitude = (((longitude + 180) % 360) + 360) % 360 - 180;
}
public Coordinates setAddress(String country, String city) {
this.country = country;
this.city = city;
return this;
}
}

View file

@ -0,0 +1,17 @@
package eu.m724.wtapi.object;
/**
* Represents a geolocation
*
* @param coordinates The coordinates
* @param city the city
* @param country The country name
* @param countryCode The ISO 3166-1 alpha-2 country code
*/
public record GeoLocation(
Coordinates coordinates,
String city,
String country,
String countryCode
) {
}

View file

@ -0,0 +1,24 @@
package eu.m724.wtapi.object;
import java.util.concurrent.TimeUnit;
public record Quota(
int maxRequests,
TimeUnit timeUnit
) {
public static Quota hourly(int maxRequestsHourly) {
return new Quota(maxRequestsHourly, TimeUnit.HOURS);
}
public static Quota daily(int maxRequestsDaily) {
return new Quota(maxRequestsDaily, TimeUnit.DAYS);
}
public int getMinuteQuota() {
return maxRequests / (int) timeUnit.toMicros(1);
}
public int getHourlyQuota() {
return maxRequests / (int) timeUnit.toHours(1);
}
}

View file

@ -1,5 +0,0 @@
package eu.m724.wtapi.object;
public enum Severity {
LIGHT, MODERATE, HEAVY
}

View file

@ -1,53 +1,33 @@
package eu.m724.wtapi.object;
import java.time.LocalDateTime;
/**
* contains weather conditions
* Represents current weather
*
* @param coordinates The coordinates
* @param timestamp The timestamp of this data
* @param raining Whether it's raining
* @param thundering Whether it's a thunderstorm
* @param snowing Whether it's snowing
* @param temperatureCelsius The temperature in Celsius
* @param temperatureApparentCelsius The apparent temperature (feels like) in Celsius
* @param relativeHumidityPercentage Percentage of relative humidity, 0.0-1.0
* @param cloudCoverPercentage Percentage of cloud cover (cloudiness), 0.0-1.0
*/
public class Weather {
public Coordinates coordinates;
public Severity rainSeverity, thunderstormSeverity, snowSeverity;
// secondary conditions
public Severity sleetSeverity, drizzleSeverity;
public boolean shower;
/**
* in kelvin
*/
public float temperature, temperatureApparent;
/**
* in meters per second
*/
public float windSpeed, windGust;
/**
* 0.0 - 1.0
*/
public float humidity, cloudiness;
/**
* as unix timestamp
*/
public long sunrise, sunset;
/**
* short name of weather
*/
public String description;
public boolean isRaining() {
return rainSeverity != null;
}
public boolean isThundering() {
return thunderstormSeverity != null;
}
public boolean isSnowing() {
return snowSeverity != null;
}
public record Weather(
Coordinates coordinates,
LocalDateTime timestamp,
boolean raining,
boolean thundering,
boolean snowing,
float temperatureCelsius,
float temperatureApparentCelsius,
float relativeHumidityPercentage,
float cloudCoverPercentage
) {
}

View file

@ -1,40 +1,68 @@
package eu.m724.wtapi.provider;
import eu.m724.wtapi.provider.exception.NoSuchProviderException;
import eu.m724.wtapi.provider.thunder.ThunderProvider;
import eu.m724.wtapi.provider.thunder.impl.blitzortung.BlitzortungProvider;
import eu.m724.wtapi.provider.twilight.impl.approximate.ApproximateTwilightTimeProvider;
import eu.m724.wtapi.provider.twilight.TwilightTimeProvider;
import eu.m724.wtapi.provider.weather.WeatherProvider;
import eu.m724.wtapi.provider.weather.impl.openweathermap.OpenWeatherMapProvider;
import eu.m724.wtapi.provider.weather.impl.openmeteo.OpenMeteoProvider;
public class Providers {
public static ThunderProvider getThunderProvider(String name, String apiKey) throws NoSuchProviderException {
/**
* Gets a thunder provider by name.<br>
* Currently: <code>blitzortung</code>
*
* @param name The provider name
* @param apiKey The API key, or <code>null</code> if not needed
* @return The thunder provider
* @throws NoSuchProviderException If invalid provider name
*/
public static ThunderProvider getThunderProvider(String name, String apiKey) {
switch (name.toLowerCase()) {
case "blitzortung":
return new BlitzortungProvider();
case "blitzortung" -> new BlitzortungProvider();
}
throw new NoSuchProviderException();
throw new NoSuchProviderException(name);
}
public static WeatherProvider getWeatherProvider(String name, String apiKey) throws NoSuchProviderException {
/**
* Gets a weather provider by name.<br>
* Currently: <code>openmeteo</code>
*
* @param name The provider name
* @param apiKey The API key, or <code>null</code> if not needed
* @return The weather provider
* @throws NoSuchProviderException If invalid provider name
*/
public static WeatherProvider getWeatherProvider(String name, String apiKey) {
switch (name.toLowerCase()) {
case "openweathermap":
return new OpenWeatherMapProvider(apiKey);
case "openmeteo" -> new OpenMeteoProvider();
}
throw new NoSuchProviderException();
throw new NoSuchProviderException(name);
}
public static TwilightTimeProvider getTwilightTimeProvider(String name, String apiKey) throws NoSuchProviderException {
/**
* Gets a twilight time provider by name.<br>
* Currently: <code>approximate</code>
*
* @param name The provider name
* @param apiKey The API key, or <code>null</code> if not needed
* @return The twilight time provider
* @throws NoSuchProviderException If invalid provider name
*/
public static TwilightTimeProvider getTwilightTimeProvider(String name, String apiKey) {
switch (name.toLowerCase()) {
case "approximate":
return new ApproximateTwilightTimeProvider();
case "approximate" -> new ApproximateTwilightTimeProvider();
}
throw new NoSuchProviderException();
throw new NoSuchProviderException(name);
}
public static class NoSuchProviderException extends RuntimeException {
public NoSuchProviderException(String provider) {
super(provider);
}
}
}

View file

@ -1,15 +0,0 @@
package eu.m724.wtapi.provider.exception;
/**
* when you specified an incorrect api key
*/
public class AuthorizationException extends ProviderException {
private static final long serialVersionUID = -2258293509429607176L;
public AuthorizationException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}

View file

@ -1,10 +0,0 @@
package eu.m724.wtapi.provider.exception;
/**
* thrown when there's no known provider with that name
*/
public class NoSuchProviderException extends Exception {
private static final long serialVersionUID = -2740598348303023762L;
}

View file

@ -0,0 +1,7 @@
package eu.m724.wtapi.provider.exception;
public class ProviderAuthorizationException extends ProviderException {
public ProviderAuthorizationException(String message) {
super(message);
}
}

View file

@ -3,11 +3,12 @@ package eu.m724.wtapi.provider.exception;
import java.util.concurrent.CompletionException;
public class ProviderException extends CompletionException {
private static final long serialVersionUID = -841882181122537157L;
public ProviderException(String message) {
super(message);
}
public ProviderException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,14 @@
package eu.m724.wtapi.provider.exception;
/**
* When unable to connect to the provider.
*/
public class ProviderRequestException extends ProviderException {
public ProviderRequestException(String message) {
super(message);
}
public ProviderRequestException(Throwable cause) {
super(cause);
}
}

View file

@ -1,22 +1,16 @@
package eu.m724.wtapi.provider.exception;
/**
* too many api requests, or ratelimited
*/
public class QuotaExceededException extends ProviderException {
private static final long serialVersionUID = 7550042052176034614L;
private int retryMs;
private final int retryInSeconds;
/**
*
* @param message
* @param retryMs SUGGESTION after how many ms to retry. it can be null
* Instantiates a new QuotaExceededException.
*
* @param retryInSeconds A <em>suggestion</em> after how many seconds to retry.
*/
public QuotaExceededException(String message, int retryMs) {
super(message);
this.retryMs = retryMs;
public QuotaExceededException(int retryInSeconds) {
super("Rate limited. Try again in " + retryInSeconds + "s");
this.retryInSeconds = retryInSeconds;
}
/**
@ -24,7 +18,7 @@ public class QuotaExceededException extends ProviderException {
* @return SUGGESTION after how many ms to retry. it can be null
*/
public int getRetryIn() {
return this.retryMs;
return this.retryInSeconds;
}
}

View file

@ -1,16 +0,0 @@
package eu.m724.wtapi.provider.exception;
/**
* when the provider is at fault
*/
public class ServerProviderException extends ProviderException {
private static final long serialVersionUID = 4461164912391786673L;
public ServerProviderException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}

View file

@ -62,7 +62,7 @@ class BlitzortungWebsocketClient extends WebSocketClient {
JsonParser.parseString(decode(message))
.getAsJsonObject();
long time = json.getAsJsonPrimitive("time").getAsLong() / 1000000;
long time = json.getAsJsonPrimitive("timestamp").getAsLong() / 1000000;
double lat = json.getAsJsonPrimitive("lat").getAsDouble();
double lon = json.getAsJsonPrimitive("lon").getAsDouble();

View file

@ -4,10 +4,11 @@ import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Twilight;
import java.time.LocalDate;
import java.time.LocalDateTime;
// TODO make this an interface? also consider new name
/**
* Twilight refers to sunset and sunrise time<br>
* Twilight refers to sunset and sunrise timestamp<br>
* Is it the correct term? I don't know
*/
public abstract class TwilightTimeProvider {
@ -19,4 +20,6 @@ public abstract class TwilightTimeProvider {
* @return {@link Twilight}
*/
public abstract Twilight calculateTwilightTime(LocalDate date, Coordinates coordinates);
public abstract double calculateSolarAltitude(LocalDateTime time, Coordinates coordinates);
}

View file

@ -21,8 +21,8 @@ public class ApproximateTwilightTimeProvider extends CacheableTwilightTimeProvid
double declination = cache.declination();
double equationOfTime = cache.equationOfTime();
double latRad = toRadians(coordinates.latitude);
double solarNoon = 720 - 4 * coordinates.longitude - equationOfTime;
double latRad = toRadians(coordinates.latitude());
double solarNoon = 720 - 4 * coordinates.longitude() - equationOfTime;
LocalDateTime solarNoonDateTime = cache.date().atStartOfDay().plusSeconds((long) (solarNoon * 60));
// 90.833 deg = 1.5853349194640094 rad
@ -94,15 +94,9 @@ public class ApproximateTwilightTimeProvider extends CacheableTwilightTimeProvid
public ApproximateTwilightTimeCache initializeCache(LocalDate date) {
double fractionalYear = getFractionalYear(date);
double equationOfTime = 229.18 * (0.000075
+ 0.001868 * cos(fractionalYear)
- 0.032077 * sin(fractionalYear)
- 0.014615 * cos(2 * fractionalYear)
- 0.040849 * sin(2 * fractionalYear));
return new ApproximateTwilightTimeCache(
date,
equationOfTime,
getEquationOfTime(fractionalYear),
getDeclination(fractionalYear)
);
}
@ -115,6 +109,14 @@ public class ApproximateTwilightTimeProvider extends CacheableTwilightTimeProvid
return 0.01721420632103996 * dayOfYear;
}
private double getEquationOfTime(double fractionalYear) {
return 229.18 * (0.000075
+ 0.001868 * cos(fractionalYear)
- 0.032077 * sin(fractionalYear)
- 0.014615 * cos(2 * fractionalYear)
- 0.040849 * sin(2 * fractionalYear));
}
private double getDeclination(double fractionalYear) {
return 0.006918
- 0.399912 * cos(fractionalYear)
@ -124,4 +126,24 @@ public class ApproximateTwilightTimeProvider extends CacheableTwilightTimeProvid
- 0.002697 * cos(3 * fractionalYear)
+ 0.00148 * sin(3 * fractionalYear);
}
@Override
public double calculateSolarAltitude(LocalDateTime time, Coordinates coordinates) {
double latRad = toRadians(coordinates.latitude());
double declination = getDeclination(getFractionalYear(time.toLocalDate()));
double hourAngle = toRadians(15 * (lst(time, coordinates.longitude()) - 12));
return asin(sin(declination) * sin(latRad) + cos(declination) * cos(latRad) * cos(hourAngle));
}
private double lst(LocalDateTime dateTime, double longitude) {
double hours = dateTime.toLocalTime().toSecondOfDay() / 3600.0;
return hours + longitude + longitude / 15 + getEquationOfTime(getFractionalYear(dateTime.toLocalDate()));
}
private double G_year(int year) {
double G_ref = 6.6768410277777778;
int year_ref = 2024;
return (G_ref + 0.0657098244 * (year - year_ref)) % 24;
}
}

View file

@ -1,8 +1,10 @@
package eu.m724.wtapi.provider.weather;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Quota;
import eu.m724.wtapi.object.Weather;
import eu.m724.wtapi.provider.exception.ProviderException;
@ -15,40 +17,12 @@ public abstract class WeatherProvider {
public abstract void init() throws ProviderException;
/**
* get weather for a single point
* @param coordinates
* @throws NullPointerException if coordinates is null
* @return a future that CAN throw {@link ProviderException}
* Get the current weather of a single or multiple points
*
* @param coordinates The coordinates
* @return A future that CAN throw {@link ProviderException}
*/
public abstract CompletableFuture<Weather> getWeather(Coordinates coordinates);
/**
* get weather for multiple points at bulk
* it can be called one at a time if the api doesn't support this or if there's too many
* @param coordinateses array of coordinates
* @throws IllegalArgumentException if array is empty
* @return a future that CAN throw {@link ProviderException}
*/
public abstract CompletableFuture<Weather[]> getWeatherBulk(Coordinates[] coordinateses);
/**
* get hourly quota that is max requests per hour
* @return hourly quota
*/
public abstract int getQuotaHourly();
/**
* how many coordinates in one bulk request
* this is because some apis don't support bulk or limit it
* @return amount of coordinates per bulk request
*/
public abstract int getBulkLimit();
/**
* estimates minimum delay between calls given last request
* @return milliseconds
*/
public int estimateDelay() {
return (int) Math.ceil(this.getQuotaHourly() / 2.0 / 60 / 60 / 1000);
}
public abstract CompletableFuture<Weather[]> getWeather(Coordinates... coordinates);
public abstract Quota getQuota();
}

View file

@ -0,0 +1,89 @@
package eu.m724.wtapi.provider.weather.impl.openmeteo;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Quota;
import eu.m724.wtapi.object.Weather;
import eu.m724.wtapi.provider.exception.ProviderException;
import eu.m724.wtapi.provider.exception.QuotaExceededException;
import eu.m724.wtapi.provider.exception.ProviderRequestException;
import eu.m724.wtapi.provider.weather.WeatherProvider;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class OpenMeteoProvider extends WeatherProvider {
@Override
public void init() throws ProviderException {
}
@Override
public CompletableFuture<Weather[]> getWeather(Coordinates... coordinates) {
String latitudes = Arrays.stream(coordinates)
.map(Coordinates::latitude)
.map(Number::toString)
.collect(Collectors.joining(","));
String longitudes = Arrays.stream(coordinates)
.map(Coordinates::longitude)
.map(Number::toString)
.collect(Collectors.joining(","));
String url = "https://api.open-meteo.com/v1/forecast?latitude=%s&longitude=%s&current=temperature_2m,relative_humidity_2m,rain,snowfall,apparent_temperature,cloud_cover,weather_code&timeformat=unixtime&wind_speed_unit=ms"
.formatted(latitudes, longitudes);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
CompletableFuture<HttpResponse<String>> responseFuture =
HttpClient.newBuilder().build()
.sendAsync(request, HttpResponse.BodyHandlers.ofString());
return responseFuture.thenApply((response) -> {
int code = response.statusCode();
JsonObject jsonResponse;
try {
jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject();
} catch (JsonParseException e) {
throw new ProviderRequestException(e);
}
if (code == 429) {
long now = System.currentTimeMillis();
int retryIn = -1;
String reason = jsonResponse.get("reason").getAsString();
if (reason.contains("Minutely")) {
retryIn = (int) (60 - (now % 60));
} else if (reason.contains("Daily")) {
retryIn = (int) (3600 - (now % 3600));
}
throw new QuotaExceededException(1000);
}
if (code != 200)
throw new ProviderRequestException("Status code: " + code);
return OpenMeteoResponseConverter.convert(jsonResponse);
});
}
@Override
public Quota getQuota() {
return Quota.daily(10000);
}
}

View file

@ -0,0 +1,53 @@
package eu.m724.wtapi.provider.weather.impl.openmeteo;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Weather;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
public class OpenMeteoResponseConverter {
static Weather[] convert(JsonElement jsonElement) {
Weather[] weathers;
if (jsonElement.isJsonArray()) {
weathers = jsonElement.getAsJsonArray().asList().stream()
.map(JsonElement::getAsJsonObject)
.map(OpenMeteoResponseConverter::convertSingle)
.toArray(Weather[]::new);
} else {
weathers = new Weather[] {
convertSingle(jsonElement.getAsJsonObject())
};
}
return weathers;
}
private static Weather convertSingle(JsonObject jsonObject) {
JsonObject current = jsonObject.getAsJsonObject("current");
Coordinates coordinates = new Coordinates(
jsonObject.get("latitude").getAsDouble(),
jsonObject.get("longitude").getAsDouble()
);
LocalDateTime timestamp = LocalDateTime.ofEpochSecond(current.get("time").getAsLong(), 0, ZoneOffset.UTC);
int weatherCode = current.get("weather_code").getAsInt();
boolean raining = current.get("rain").getAsDouble() > 0;
boolean thundering = weatherCode >= 90 && weatherCode < 100;
boolean snowing = current.get("snowfall").getAsDouble() > 0;
float temperatureCelsius = current.get("temperature").getAsFloat();
float temperatureApparentCelsius = current.get("apparent_temperature").getAsFloat();
float relativeHumidityPercentage = current.get("relative_humidity_2m").getAsInt() / 100f;
float cloudCoverPercentage = current.get("cloud_cover").getAsInt() / 100f;
return new Weather(coordinates, timestamp, raining, thundering, snowing, temperatureCelsius, temperatureApparentCelsius, relativeHumidityPercentage, cloudCoverPercentage);
}
}

View file

@ -9,6 +9,10 @@ import eu.m724.wtapi.object.Weather;
public class OWMResponseConverter {
static Weather convert(JsonObject json) {
Coordinates coordinates = new Coordinates(
json.getAsJsonObject("coord").getAsJsonPrimitive("lon").getAsDouble(),
json.getAsJsonObject("coord").getAsJsonPrimitive("lat").getAsDouble()
);
Weather weather = new Weather();
int conditionCode = json.getAsJsonArray("weather")
@ -178,12 +182,12 @@ public class OWMResponseConverter {
json.getAsJsonObject("coord").getAsJsonPrimitive("lat").getAsDouble()
).setAddress(country, city);
weather.temperature = json
weather.temperatureCelsius = json
.getAsJsonObject("main")
.getAsJsonPrimitive("temp")
.getAsFloat();
weather.temperatureApparent = json
weather.temperatureApparentCelsius = json
.getAsJsonObject("main")
.getAsJsonPrimitive("feels_like")
.getAsFloat();
@ -200,12 +204,12 @@ public class OWMResponseConverter {
if (pri != null)
weather.windSpeed = pri.getAsFloat();
weather.humidity = json
weather.relativeHumidity = json
.getAsJsonObject("main")
.getAsJsonPrimitive("humidity")
.getAsJsonPrimitive("relativeHumidityPercentage")
.getAsInt() / 100f;
weather.cloudiness = json
weather.cloudCover = json
.getAsJsonObject("clouds")
.getAsJsonPrimitive("all")
.getAsInt() / 100f;

View file

@ -6,21 +6,27 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Weather;
import eu.m724.wtapi.provider.exception.AuthorizationException;
import eu.m724.wtapi.provider.exception.ProviderAuthorizationException;
import eu.m724.wtapi.provider.exception.ProviderException;
import eu.m724.wtapi.provider.exception.QuotaExceededException;
import eu.m724.wtapi.provider.exception.ServerProviderException;
import eu.m724.wtapi.provider.exception.ProviderRequestException;
import eu.m724.wtapi.provider.weather.WeatherProvider;
import eu.m724.wtapi.provider.weather.impl.openmeteo.OpenMeteoProvider;
/**
* @deprecated
* Development will focus on {@link OpenMeteoProvider} instead.
*/
@Deprecated
public class OpenWeatherMapProvider extends WeatherProvider {
private String apiKey;
@ -32,56 +38,49 @@ public class OpenWeatherMapProvider extends WeatherProvider {
public void init() throws ProviderException {
CompletableFuture<Weather> weatherFuture =
getWeather(new Coordinates(0, 0));
try {
weatherFuture.join();
} catch (CompletionException e) {
throw (ProviderException) e;
}
}
@Override
public CompletableFuture<Weather> getWeather(Coordinates coordinates) {
weatherFuture.join();
}
private CompletableFuture<Weather> getWeather(Coordinates coordinates) {
String url = "https://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=%s"
.formatted(coordinates.latitude, coordinates.longitude, apiKey);
.formatted(coordinates.latitude(), coordinates.longitude(), apiKey);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
CompletableFuture<HttpResponse<String>> responseFuture =
HttpClient.newBuilder()
.proxy(ProxySelector.getDefault()).build()
.sendAsync(request, BodyHandlers.ofString());
CompletableFuture<Weather> weatherFuture =
responseFuture.thenApply((response) -> {
int code = response.statusCode();
if (code == 401)
throw new AuthorizationException("invalid api key");
throw new ProviderAuthorizationException("Invalid api key");
if (code == 429)
throw new QuotaExceededException("ratelimited", 1000);
throw new QuotaExceededException("Ratelimited", 1000);
if (code != 200)
throw new ServerProviderException(String.format("status code %d", code));
throw new ProviderRequestException("Status code: " + code);
JsonObject jsonResponse =
JsonParser.parseString(response.body())
.getAsJsonObject();
Weather weather = OWMResponseConverter.convert(jsonResponse);
return weather;
return OWMResponseConverter.convert(jsonResponse);
});
return weatherFuture;
}
@Override
public CompletableFuture<Weather[]> getWeatherBulk(Coordinates[] coordinateses) {
public CompletableFuture<Weather[]> getWeather(Coordinates... coordinates) {
ArrayList<CompletableFuture<Weather>> weatherFutures = new ArrayList<>();
for (Coordinates coordinates : coordinateses) {
for (Coordinates coordinates : coordinatesArray) {
weatherFutures.add(getWeather(coordinates));
}
@ -97,8 +96,8 @@ public class OpenWeatherMapProvider extends WeatherProvider {
}
@Override
public int getQuotaHourly() {
return 1370;
public int getQuota() {
return Duration.ofHours(1370);
}
@Override

View file

@ -9,14 +9,14 @@ public class CoordinateTest {
public void testCoordinates() {
Coordinates coordinates = new Coordinates(-91.1, 180.01);
System.out.println(coordinates.longitude);
System.out.println(coordinates.longitude());
assert coordinates.latitude == 88.9;
assert coordinates.longitude == -179.99;
assert coordinates.latitude() == 88.9;
assert coordinates.longitude() == -179.99;
coordinates = new Coordinates(-91.1, 180.1);
System.out.printf("Precision loss expected: %f\n", coordinates.longitude);
assert coordinates.longitude != -179.9; // TODO fix precision loss
System.out.printf("Precision loss expected: %f\n", coordinates.longitude());
assert coordinates.longitude() != -179.9; // TODO fix precision loss
assertNull(coordinates.city);
assertNull(coordinates.country);

View file

@ -25,7 +25,7 @@ public class BlitzortungTest {
provider.tick();
int size = coordinatesList.size();
if (size > 0)
System.out.printf("Last from tick: %f %f (total %d)\n", coordinatesList.get(size-1).latitude, coordinatesList.get(size-1).longitude, size);
System.out.printf("Last from tick: %f %f (total %d)\n", coordinatesList.get(size - 1).latitude(), coordinatesList.get(size - 1).longitude(), size);
Thread.sleep(25);
}

View file

@ -24,7 +24,7 @@ public class ThunderProviderTest {
provider.tick();
int size = coordinatesList.size();
if (size > 0)
System.out.printf("Last from tick: %f %f (total %d)\n", coordinatesList.get(size-1).latitude, coordinatesList.get(size-1).longitude, size);
System.out.printf("Last from tick: %f %f (total %d)\n", coordinatesList.get(size - 1).latitude(), coordinatesList.get(size - 1).longitude(), size);
Thread.sleep(20);
}
@ -33,6 +33,6 @@ public class ThunderProviderTest {
System.out.printf("Strikes in the last 1s: %d\n", coordinatesList.size());
System.out.printf("Latency: %dms\n", provider.getLatency());
assert coordinatesList.size() == 20; // TODO this is time sensitive and fails under loaded system. Also, the entire test is suboptimal
assert coordinatesList.size() == 20; // TODO this is timestamp sensitive and fails under loaded system. Also, the entire test is suboptimal
}
}

View file

@ -22,6 +22,10 @@ public class ApproximateTwilightTimeTest {
public void approximateTest() {
TwilightTimeProvider provider = new ApproximateTwilightTimeProvider();
double solarAltitude = provider.calculateSolarAltitude(LocalDateTime.now(), new Coordinates(51, 0));
System.out.println(Math.toDegrees(solarAltitude));
assert false;
// used https://gml.noaa.gov/grad/solcalc/index.html for reference values
testLocation(provider, 26, 6, 2023, 53.123394, 23.0864867, LocalDateTime.of(2023, 6, 26, 2, 2), LocalDateTime.of(2023, 6, 26, 18, 59), 0);
testLocation(provider, 13, 11, 2040, 45.432427, -122.3899276, LocalDateTime.of(2040, 11, 13, 15, 7), LocalDateTime.of(2040, 11, 14, 0, 41), 0);
@ -39,7 +43,7 @@ public class ApproximateTwilightTimeTest {
Twilight twilight = provider.calculateTwilightTime(date, coordinates);
System.out.println(date);
System.out.println(coordinates.latitude + " " + coordinates.longitude);
System.out.println(coordinates.latitude() + " " + coordinates.longitude());
System.out.println("Solar noon: " + twilight.solarNoon());
System.out.println("Calculated sunrise: " + twilight.sunrise());

View file

@ -4,15 +4,13 @@ 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;
import java.time.LocalDateTime;
import java.time.LocalTime;
public class MockTwilightTimeProvider extends TwilightTimeProvider {
@Override
public Twilight calculateTwilightTime(LocalDate date, Coordinates coordinates) {
int time = (int) (coordinates.latitude + coordinates.longitude);
int time = (int) (coordinates.latitude() + coordinates.longitude());
return new Twilight(
date,
date.atStartOfDay().plusSeconds(-time),
@ -21,4 +19,9 @@ public class MockTwilightTimeProvider extends TwilightTimeProvider {
false
);
}
@Override
public double calculateSolarAltitude(LocalDateTime time, Coordinates coordinates) {
return coordinates.latitude();
}
}

View file

@ -5,7 +5,7 @@ import eu.m724.wtapi.object.Coordinates;
import eu.m724.wtapi.object.Weather;
import eu.m724.wtapi.provider.exception.ProviderException;
import eu.m724.wtapi.provider.exception.QuotaExceededException;
import eu.m724.wtapi.provider.exception.ServerProviderException;
import eu.m724.wtapi.provider.exception.ProviderRequestException;
import eu.m724.wtapi.provider.weather.WeatherProvider;
public class MockWeatherProvider extends WeatherProvider {
@ -26,13 +26,13 @@ public class MockWeatherProvider extends WeatherProvider {
throw new NullPointerException("no coordinates passed");
System.out.printf("mock getting weather for %f, %f\n",
coordinates.latitude, coordinates.longitude);
coordinates.latitude(), coordinates.longitude());
CompletableFuture<Weather> completableFuture =
new CompletableFuture<>();
if (faulty)
completableFuture.completeExceptionally(new ServerProviderException("server is on vacation rn"));
completableFuture.completeExceptionally(new ProviderRequestException("Imagined server is down"));
else
completableFuture.complete(new Weather());
@ -40,13 +40,13 @@ public class MockWeatherProvider extends WeatherProvider {
}
@Override
public CompletableFuture<Weather[]> getWeatherBulk(Coordinates[] coordinateses) {
int len = coordinateses.length;
public CompletableFuture<Weather[]> getWeatherBulk(Coordinates[] coordinatesArray) {
int len = coordinatesArray.length;
if (len == 0)
throw new IllegalArgumentException("no coordinates passed");
System.out.printf("mock getting weather for multiple coords");
System.out.println("mock getting weather for multiple coords");
CompletableFuture<Weather[]> completableFuture =
new CompletableFuture<>();
@ -55,14 +55,14 @@ public class MockWeatherProvider extends WeatherProvider {
Weather[] weathers = new Weather[len];
for (int i=0; i<len; i++) {
if (coordinateses[i] == null)
throw new IllegalArgumentException("a coordinate is null");
if (coordinatesArray[i] == null)
throw new IllegalArgumentException("A coordinate is null");
weathers[i] = new Weather();
}
if (faulty)
completableFuture.completeExceptionally(new QuotaExceededException("im too lazy lmao", 60));
completableFuture.completeExceptionally(new QuotaExceededException("Quota exceeded", 60));
else
completableFuture.complete(weathers);
@ -70,7 +70,7 @@ public class MockWeatherProvider extends WeatherProvider {
}
@Override
public int getQuotaHourly() {
public int getQuota() {
return 5;
}

View file

@ -20,28 +20,18 @@ public class OpenWeatherMapTest {
WeatherProvider provider = new OpenWeatherMapProvider(apiKey);
provider.init();
CompletableFuture<Weather> weatherFuture =
provider.getWeather(new Coordinates(53.2232, -4.2008));
Weather weather = weatherFuture.get();
assertNotNull(weather);
System.out.printf("current weather in %s, %s: %s\n", weather.coordinates.city, weather.coordinates.country, weather.description);
CompletableFuture<Weather[]> weatherBulkFuture =
provider.getWeatherBulk(
new Coordinates[] {
new Coordinates(54.6606714, -3.3827237),
new Coordinates(47.5705952, -53.5556464),
new Coordinates(34.2073721, -84.1402857),
});
CompletableFuture<Weather[]> weathersFuture =
provider.getWeather(
new Coordinates(54.6606714, -3.3827237),
new Coordinates(47.5705952, -53.5556464),
new Coordinates(34.2073721, -84.1402857));
Weather[] weathers = weatherBulkFuture.get();
Weather[] weathers = weathersFuture.get();
assert weathers.length == 3;
for (Weather weather1 : weathers) {
System.out.printf("current weather in %s, %s: %s\n", weather.coordinates.city, weather.coordinates.country, weather1.description);
for (Weather weather : weathers) {
System.out.printf("Current weather in %s, %s: %s\n", weather.coordinates().city, weather.coordinates().country, weather.description);
}
}
}

View file

@ -22,7 +22,7 @@ public class ProviderTest {
WeatherProvider provider = new MockWeatherProvider(false);
provider.init();
assert provider.getQuotaHourly() == 5;
assert provider.getQuota() == 5;
assert provider.getBulkLimit() == 1;
CompletableFuture<Weather> weatherFuture =