diff --git a/pom.xml b/pom.xml index d35d3e3..03d8801 100644 --- a/pom.xml +++ b/pom.xml @@ -12,6 +12,13 @@ 17 17 + + + + 724rocks + https://git.724.rocks/api/packages/Minecon724/maven + + @@ -26,5 +33,30 @@ test + + + + + org.apache.maven.plugins + maven-release-plugin + 3.0.1 + + + + + + + 724rocks + https://git.724.rocks/api/packages/Minecon724/maven + + + 724rocks + https://git.724.rocks/api/packages/Minecon724/maven + + + + + scm:git:https://git.724.rocks/Minecon724/wtapi.git + \ No newline at end of file diff --git a/release.properties b/release.properties new file mode 100644 index 0000000..5e6aa36 --- /dev/null +++ b/release.properties @@ -0,0 +1,21 @@ +#release configuration +#Thu May 30 13:53:21 CEST 2024 +completedPhase=check-poms +exec.pomFileName=pom.xml +exec.snapshotReleasePluginAllowed=false +pinExternals=false +preparationGoals=clean verify +project.scm.eu.m724\:wtapi.developerConnection=scm\:git\:https\://git.724.rocks/Minecon724/wtapi.git +project.scm.eu.m724\:wtapi.tag=HEAD +projectVersionPolicyConfig=${projectVersionPolicyConfig}\n +projectVersionPolicyId=default +pushChanges=true +releaseStrategyId=default +remoteTagging=true +scm.branchCommitComment=@{prefix} prepare branch @{releaseLabel} +scm.commentPrefix=[maven-release-plugin] +scm.developmentCommitComment=@{prefix} prepare for next development iteration +scm.releaseCommitComment=@{prefix} prepare release @{releaseLabel} +scm.rollbackCommitComment=@{prefix} rollback the release of @{releaseLabel} +scm.tagNameFormat=@{project.artifactId}-@{project.version} +scm.url=scm\:git\:https\://git.724.rocks/Minecon724/wtapi.git diff --git a/src/main/java/eu/m724/wtapi/object/Severity.java b/src/main/java/eu/m724/wtapi/object/Severity.java new file mode 100644 index 0000000..e6ad4fd --- /dev/null +++ b/src/main/java/eu/m724/wtapi/object/Severity.java @@ -0,0 +1,5 @@ +package eu.m724.wtapi.object; + +public enum Severity { + LIGHT, MODERATE, HEAVY +} diff --git a/src/main/java/eu/m724/wtapi/object/WeatherState.java b/src/main/java/eu/m724/wtapi/object/WeatherState.java deleted file mode 100644 index 066f372..0000000 --- a/src/main/java/eu/m724/wtapi/object/WeatherState.java +++ /dev/null @@ -1,5 +0,0 @@ -package eu.m724.wtapi.object; - -public enum WeatherState { - CLEAR, RAIN, THUNDER -} diff --git a/src/main/java/eu/m724/wtapi/provider/impl/openweathermap/OWMResponseConverter.java b/src/main/java/eu/m724/wtapi/provider/impl/openweathermap/OWMResponseConverter.java new file mode 100644 index 0000000..5426a7e --- /dev/null +++ b/src/main/java/eu/m724/wtapi/provider/impl/openweathermap/OWMResponseConverter.java @@ -0,0 +1,230 @@ +package eu.m724.wtapi.provider.impl.openweathermap; + +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +import eu.m724.wtapi.object.Coordinates; +import eu.m724.wtapi.object.Severity; +import eu.m724.wtapi.object.Weather; + +public class OWMResponseConverter { + static Weather convert(JsonObject json) { + Weather weather = new Weather(); + + int conditionCode = json.getAsJsonArray("weather") + .get(0).getAsJsonObject() + .getAsJsonPrimitive("id").getAsInt(); + + switch (conditionCode) { + // Group 2xx: Thunderstorm + case 200: // thunderstorm with light rain + weather.thunderstormSeverity = Severity.LIGHT; + weather.rainSeverity = Severity.LIGHT; + break; + case 201: // thunderstorm with rain + weather.thunderstormSeverity = Severity.MODERATE; + weather.rainSeverity = Severity.MODERATE; + break; + case 202: // thunderstorm with heavy rain + weather.thunderstormSeverity = Severity.HEAVY; + weather.rainSeverity = Severity.HEAVY; + break; + case 210: // light thunderstorm + weather.thunderstormSeverity = Severity.LIGHT; + break; + case 211: // thunderstorm + weather.thunderstormSeverity = Severity.MODERATE; + break; + case 212: // heavy thunderstorm + weather.thunderstormSeverity = Severity.HEAVY; + break; + case 221: // ragged thunderstorm + weather.thunderstormSeverity = Severity.MODERATE; + break; + case 230: // thunderstorm with light drizzle + weather.thunderstormSeverity = Severity.LIGHT; + weather.drizzleSeverity = Severity.LIGHT; + break; + case 231: // thunderstorm with drizzle + weather.thunderstormSeverity = Severity.MODERATE; + weather.drizzleSeverity = Severity.MODERATE; + break; + case 232: // thunderstorm with heavy drizzle + weather.thunderstormSeverity = Severity.HEAVY; + weather.drizzleSeverity = Severity.HEAVY; + break; + + // Group 3xx: Drizzle + case 300: // light intensity drizzle + weather.drizzleSeverity = Severity.LIGHT; + break; + case 301: // drizzle + weather.drizzleSeverity = Severity.MODERATE; + break; + case 302: // heavy intensity drizzle + weather.drizzleSeverity = Severity.HEAVY; + break; + case 310: // light intensity drizzle rain + weather.drizzleSeverity = Severity.LIGHT; + break; + case 311: // drizzle rain + weather.drizzleSeverity = Severity.MODERATE; + break; + case 312: // heavy intensity drizzle rain + weather.drizzleSeverity = Severity.HEAVY; + break; + case 313: // shower rain and drizzle + weather.rainSeverity = Severity.MODERATE; + weather.drizzleSeverity = Severity.MODERATE; + weather.shower = true; + break; + case 314: // heavy shower rain and drizzle + weather.rainSeverity = Severity.HEAVY; + weather.drizzleSeverity = Severity.HEAVY; + weather.shower = true; + break; + case 321: // shower drizzle + weather.drizzleSeverity = Severity.MODERATE; + weather.shower = true; + break; + + // Group 5xx: Rain + case 500: // light rain + weather.rainSeverity = Severity.LIGHT; + break; + case 501: // moderate rain + weather.rainSeverity = Severity.MODERATE; + break; + case 502: // heavy intensity rain + weather.rainSeverity = Severity.HEAVY; + break; + case 503: // very heavy rain + weather.rainSeverity = Severity.HEAVY; + break; + case 504: // extreme rain + weather.rainSeverity = Severity.HEAVY; + break; + case 511: // freezing rain + weather.rainSeverity = Severity.MODERATE; + break; + case 520: // light intensity shower rain + weather.rainSeverity = Severity.LIGHT; + weather.shower = true; + break; + case 521: // shower rain + weather.rainSeverity = Severity.MODERATE; + weather.shower = true; + break; + case 522: // heavy intensity shower rain + weather.rainSeverity = Severity.HEAVY; + weather.shower = true; + break; + case 531: // ragged shower rain + weather.rainSeverity = Severity.MODERATE; + weather.shower = true; + break; + + // Group 6xx: Snow + case 600: // light snow + weather.snowSeverity = Severity.LIGHT; + break; + case 601: // snow + weather.snowSeverity = Severity.MODERATE; + break; + case 602: // heavy snow + weather.snowSeverity = Severity.HEAVY; + break; + case 611: // sleet + weather.sleetSeverity = Severity.MODERATE; + break; + case 612: // light shower sleet + weather.sleetSeverity = Severity.LIGHT; + weather.shower = true; + break; + case 613: // shower sleet + weather.sleetSeverity = Severity.MODERATE; + weather.shower = true; + break; + case 615: // light rain and snow + weather.rainSeverity = Severity.LIGHT; + weather.snowSeverity = Severity.LIGHT; + break; + case 616: // rain and snow + weather.rainSeverity = Severity.MODERATE; + weather.snowSeverity = Severity.MODERATE; + break; + case 620: // light shower snow + weather.snowSeverity = Severity.LIGHT; + weather.shower = true; + break; + case 621: // shower snow + weather.snowSeverity = Severity.MODERATE; + weather.shower = true; + break; + case 622: // heavy shower snow + weather.snowSeverity = Severity.HEAVY; + weather.shower = true; + break; + } + + weather.coordinates = + new Coordinates( + json.getAsJsonObject("coord").getAsJsonPrimitive("lon").getAsDouble(), + json.getAsJsonObject("coord").getAsJsonPrimitive("lat").getAsDouble() + ); + + weather.temperature = json + .getAsJsonObject("main") + .getAsJsonPrimitive("temp") + .getAsFloat(); + + weather.temperatureApparent = json + .getAsJsonObject("main") + .getAsJsonPrimitive("feels_like") + .getAsFloat(); + + weather.windSpeed = json + .getAsJsonObject("wind") + .getAsJsonPrimitive("speed") + .getAsFloat(); + + JsonPrimitive pri = json + .getAsJsonObject("wind") + .getAsJsonPrimitive("gust"); + + if (pri != null) + weather.windSpeed = pri.getAsFloat(); + + weather.humidity = json + .getAsJsonObject("main") + .getAsJsonPrimitive("humidity") + .getAsInt() / 100f; + + weather.cloudiness = json + .getAsJsonObject("clouds") + .getAsJsonPrimitive("all") + .getAsInt() / 100f; + + weather.sunrise = json + .getAsJsonObject("sys") + .getAsJsonPrimitive("sunrise") + .getAsLong(); + + weather.sunset = json + .getAsJsonObject("sys") + .getAsJsonPrimitive("sunrise") + .getAsLong(); + + weather.city = json + .getAsJsonPrimitive("name") + .getAsString(); + + weather.description = json + .getAsJsonArray("weather") + .get(0).getAsJsonObject() + .getAsJsonPrimitive("description") + .getAsString(); + + return weather; + } +} diff --git a/src/main/java/eu/m724/wtapi/provider/impl/openweathermap/OpenWeatherMapProvider.java b/src/main/java/eu/m724/wtapi/provider/impl/openweathermap/OpenWeatherMapProvider.java new file mode 100644 index 0000000..c91a9a3 --- /dev/null +++ b/src/main/java/eu/m724/wtapi/provider/impl/openweathermap/OpenWeatherMapProvider.java @@ -0,0 +1,113 @@ +package eu.m724.wtapi.provider.impl.openweathermap; + +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.net.http.HttpResponse.BodyHandlers; +import java.util.ArrayList; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +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.WeatherProvider; +import eu.m724.wtapi.provider.exception.AuthorizationException; +import eu.m724.wtapi.provider.exception.ProviderException; +import eu.m724.wtapi.provider.exception.QuotaExceededException; +import eu.m724.wtapi.provider.exception.ServerProviderException; + +public class OpenWeatherMapProvider extends WeatherProvider { + private String requestUrlFormat; + private String apiKey; + + public OpenWeatherMapProvider(String apiKey) { + this.apiKey = apiKey; + this.requestUrlFormat = "https://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&appid=" + apiKey; + } + + @Override + public void init() throws ProviderException { + CompletableFuture weatherFuture = + getWeather(new Coordinates(0, 0)); + + try { + weatherFuture.get(); + } catch (InterruptedException e) { + throw new ProviderException("unexpected interruptedexception"); + } catch (ExecutionException e) { + throw (ProviderException) e.getSuppressed()[0]; // TODO + } + } + + @Override + public CompletableFuture getWeather(Coordinates coordinates) { + String url = String.format( + requestUrlFormat, coordinates.latitude, coordinates.longitude); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create(url)) + .build(); + + CompletableFuture> responseFuture = + HttpClient.newBuilder() + .proxy(ProxySelector.getDefault()).build() + .sendAsync(request, BodyHandlers.ofString()); + + CompletableFuture weatherFuture = + responseFuture.thenApply((response) -> { + int code = response.statusCode(); + + if (code == 401) + throw new AuthorizationException("invalid api key"); + if (code == 429) + throw new QuotaExceededException("ratelimited", 1000); + if (code != 200) + throw new ServerProviderException(String.format("status code %d", code)); + + JsonObject jsonResponse = + JsonParser.parseString(response.body()) + .getAsJsonObject(); + + Weather weather = OWMResponseConverter.convert(jsonResponse); + + return weather; + }); + + return weatherFuture; + } + + @Override + public CompletableFuture getWeatherBulk(Coordinates[] coordinateses) { + ArrayList> weatherFutures = new ArrayList<>(); + + for (Coordinates coordinates : coordinateses) { + weatherFutures.add(getWeather(coordinates)); + } + + CompletableFuture weathersFuture = + CompletableFuture.allOf( + weatherFutures.toArray(new CompletableFuture[0])) + .thenApply(v -> + weatherFutures.stream() + .map(CompletableFuture::join) + .toArray(Weather[]::new)); + + return weathersFuture; + } + + @Override + public int getQuotaHourly() { + return 1370; + } + + @Override + public int getBulkLimit() { + return 1; + } + +} diff --git a/src/test/java/eu/m724/wtapi/CoordinateTest.java b/src/test/java/eu/m724/wtapi/CoordinateTest.java new file mode 100644 index 0000000..5869512 --- /dev/null +++ b/src/test/java/eu/m724/wtapi/CoordinateTest.java @@ -0,0 +1,20 @@ +package eu.m724.wtapi; + +import org.junit.Test; + +import eu.m724.wtapi.object.Coordinates; + +public class CoordinateTest { + @Test + public void testCoordinates() { + Coordinates coordinates = new Coordinates(-91.1, 180.01); + + System.out.println(coordinates.longitude); + + assert coordinates.latitude == 88.9; + assert coordinates.longitude == -179.99; + + coordinates = new Coordinates(-91.1, 180.1); + assert coordinates.longitude != -179.9; // TODO + } +} diff --git a/src/test/java/eu/m724/wtapi/TestOWM.java b/src/test/java/eu/m724/wtapi/TestOWM.java new file mode 100644 index 0000000..2bffb8f --- /dev/null +++ b/src/test/java/eu/m724/wtapi/TestOWM.java @@ -0,0 +1,47 @@ +package eu.m724.wtapi; + +import static org.junit.Assert.assertNotNull; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import org.junit.Test; + +import eu.m724.wtapi.object.Coordinates; +import eu.m724.wtapi.object.Weather; +import eu.m724.wtapi.provider.WeatherProvider; +import eu.m724.wtapi.provider.impl.openweathermap.OpenWeatherMapProvider; + +public class TestOWM { + @Test + public void openWeatherMapTest() throws InterruptedException, ExecutionException { + String apiKey = "f3198d2a4ae3076e0aa47c821a253447"; + + WeatherProvider provider = new OpenWeatherMapProvider(apiKey); + + provider.init(); + + CompletableFuture weatherFuture = + provider.getWeather(new Coordinates(53.2232, -4.2008)); + + Weather weather = weatherFuture.get(); + assertNotNull(weather); + + System.out.printf("current weather in %s: %s\n", weather.city, weather.description); + + CompletableFuture weatherBulkFuture = + provider.getWeatherBulk( + new Coordinates[] { + new Coordinates(54.6606714, -3.3827237), + new Coordinates(47.5705952, -53.5556464), + new Coordinates(34.2073721, -84.1402857), + }); + + Weather[] weathers = weatherBulkFuture.get(); + assert weathers.length == 3; + + for (Weather weather1 : weathers) { + System.out.printf("current weather in %s: %s\n", weather1.city, weather1.description); + } + } +}