From 7f0866ffa2ea70e3335aefd6e7130337540d453a Mon Sep 17 00:00:00 2001 From: Alexey Zinchenko Date: Sun, 1 May 2022 01:23:51 +0300 Subject: [PATCH] Implemented climatic forecast for 30 days. Some refactoring. --- README.md | 15 +- .../api/OpenWeatherMapClient.java | 38 ++- ...recastAtmosphericPressureDeserializer.java | 18 ++ .../ClimaticForecastCloudsDeserializer.java | 23 ++ .../ClimaticForecastLocationDeserializer.java | 54 ++++ .../ClimaticForecastRainDeserializer.java | 22 ++ .../ClimaticForecastSnowDeserializer.java | 22 ++ ...imaticForecastTemperatureDeserializer.java | 34 ++ .../ClimaticForecastWindDeserializer.java | 58 ++++ ...recastAtmosphericPressureDeserializer.java | 18 ++ .../DailyForecastCloudsDeserializer.java | 23 ++ .../DailyForecastLocationDeserializer.java | 54 ++++ .../daily/DailyForecastRainDeserializer.java | 22 ++ .../daily/DailyForecastSnowDeserializer.java | 22 ++ .../DailyForecastTemperatureDeserializer.java | 34 ++ .../ClimaticForecastResponseMapper.java | 84 +++++ .../mapper/CurrentWeatherResponseMapper.java | 21 +- .../mapper/DailyForecastResponseMapper.java | 167 +++------- .../api/mapper/GeocodingResponseMapper.java | 2 +- .../climatic/AtmosphericPressure.java | 98 ++++++ .../api/model/forecast/climatic/Forecast.java | 85 +++++ .../api/model/forecast/climatic/Location.java | 198 ++++++++++++ .../api/model/forecast/climatic/Rain.java | 97 ++++++ .../api/model/forecast/climatic/Snow.java | 97 ++++++ .../model/forecast/climatic/Temperature.java | 263 ++++++++++++++++ .../forecast/climatic/WeatherForecast.java | 295 ++++++++++++++++++ .../api/model/forecast/climatic/Wind.java | 146 +++++++++ .../forecast/daily/AtmosphericPressure.java | 2 +- .../api/model/forecast/daily/Forecast.java | 6 +- .../api/model/forecast/daily/Location.java | 2 +- .../api/model/forecast/daily/Rain.java | 22 +- .../api/model/forecast/daily/Snow.java | 18 +- .../model/forecast/daily/WeatherForecast.java | 56 +++- .../model/forecast/free/WeatherForecast.java | 1 - .../forecast/hourly/WeatherForecast.java | 1 - .../api/model/onecall/current/Daily.java | 2 +- .../api/model/onecall/current/Hourly.java | 5 +- .../onecall/historical/HistoricalWeather.java | 2 +- .../onecall/historical/HourlyHistorical.java | 5 +- ...limaticForecastAsyncRequestTerminator.java | 56 ++++ .../ClimaticForecastRequestCustomizer.java | 45 +++ .../ClimaticForecastRequestTerminator.java | 54 ++++ .../climatic/ClimaticForecastRequester.java | 20 ++ .../daily/DailyForecastRequestCustomizer.java | 2 +- .../daily/DailyForecastRequester.java | 2 +- .../DirectGeocodingRequestCustomizer.java | 2 - .../CurrentWeatherAsyncRequestTerminator.java | 2 +- .../CurrentWeatherRequestTerminator.java | 2 +- .../api/utils/RequestUtils.java | 2 +- .../ClimaticForecastResponseMapperTest.java | 126 ++++++++ .../DailyForecastResponseMapperTest.java | 137 ++++++++ .../mapper/GeocodingResponseMapperTest.java | 3 +- .../HourlyForecastResponseMapperTest.java | 3 +- .../AirPollutionDetailsUnitTest.java | 3 +- .../api/model/onecall/WindUnitTest.java | 2 +- .../current/DailyTemperatureUnitTest.java | 3 +- .../model/onecall/current/DailyUnitTest.java | 2 +- .../model/onecall/current/HourlyUnitTest.java | 5 +- .../historical/HistoricalWeatherUnitTest.java | 5 +- .../historical/HourlyHistoricalUnitTest.java | 5 +- .../api/model/weather/WeatherUnitTest.java | 1 - .../api/utils/TestMappingUtils.java | 12 + 62 files changed, 2402 insertions(+), 224 deletions(-) create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastAtmosphericPressureDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastCloudsDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastLocationDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastRainDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastSnowDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastTemperatureDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastWindDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastAtmosphericPressureDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastCloudsDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastLocationDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastRainDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastSnowDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastTemperatureDeserializer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapper.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/AtmosphericPressure.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Forecast.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Location.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Rain.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Snow.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Temperature.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/WeatherForecast.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Wind.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastAsyncRequestTerminator.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestCustomizer.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestTerminator.java create mode 100644 src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequester.java create mode 100644 src/test/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapperTest.java create mode 100644 src/test/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapperTest.java create mode 100644 src/test/java/com/github/prominence/openweathermap/api/utils/TestMappingUtils.java diff --git a/README.md b/README.md index 1004e28..47b6ca4 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ Free: * 5 day / 3-hour forecast * One Call API * Air pollution +* Geocoding API + +Paid: +* Hourly Forecast 4 days +* Daily Forecast 16 days +* Climatic 30 days Other: * Request timeout settings @@ -14,14 +20,15 @@ Other: ### Will be implemented later: Free: -* Geocoding API * Weather Stations * Weather Triggers Paid: -* Daily Forecast 16 days -* Hourly Forecast 4 days -* probably others... +* Climatic Forecast 30 days +* Bulk Downloading +* Solar Radiation API +* Global Weather Alerts / Push notifications +* Road Risk API ### Maven coordinates: diff --git a/src/main/java/com/github/prominence/openweathermap/api/OpenWeatherMapClient.java b/src/main/java/com/github/prominence/openweathermap/api/OpenWeatherMapClient.java index ec052c2..3981fd6 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/OpenWeatherMapClient.java +++ b/src/main/java/com/github/prominence/openweathermap/api/OpenWeatherMapClient.java @@ -26,6 +26,7 @@ import com.github.prominence.openweathermap.api.annotation.SubscriptionAvailabil import com.github.prominence.openweathermap.api.conf.TimeoutSettings; import com.github.prominence.openweathermap.api.request.RequestSettings; import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequester; +import com.github.prominence.openweathermap.api.request.forecast.climatic.ClimaticForecastRequester; import com.github.prominence.openweathermap.api.request.forecast.daily.DailyForecastRequester; import com.github.prominence.openweathermap.api.request.forecast.free.FiveDayThreeHourStepForecastRequester; import com.github.prominence.openweathermap.api.request.forecast.hourly.FourDaysHourlyForecastRequester; @@ -68,15 +69,6 @@ public class OpenWeatherMapClient { return new CurrentWeatherRequester(new RequestSettings(apiKey, timeoutSettings)); } - /** - * 5 Day / 3 Hour Forecast API. - * @return requester for retrieving 5 day/3-hour weather forecast information. - */ - @SubscriptionAvailability(plans = ALL) - public FiveDayThreeHourStepForecastRequester forecast5Day3HourStep() { - return new FiveDayThreeHourStepForecastRequester(new RequestSettings(apiKey, timeoutSettings)); - } - /** * Hourly forecast API. * @return requester for retrieving hourly weather forecast information for 4 days. @@ -86,6 +78,16 @@ public class OpenWeatherMapClient { return new FourDaysHourlyForecastRequester(new RequestSettings(apiKey, timeoutSettings)); } + /** + * One Call API. + * To get information about current weather, minute forecast for 1 hour, hourly forecast for 48 hours, daily forecast for 7 days and government weather alerts. + * @return requester for retrieving one call weather information. + */ + @SubscriptionAvailability(plans = ALL) + public OneCallWeatherRequester oneCall() { + return new OneCallWeatherRequester(new RequestSettings(apiKey, timeoutSettings)); + } + /** * Daily forecast API. * @return requester for retrieving daily weather forecast information for 16 days. @@ -96,13 +98,21 @@ public class OpenWeatherMapClient { } /** - * One Call API. - * To get information about current weather, minute forecast for 1 hour, hourly forecast for 48 hours, daily forecast for 7 days and government weather alerts. - * @return requester for retrieving one call weather information. + * Climatic forecast API. + * @return requester for retrieving climatic weather forecast information for 30 days. + */ + @SubscriptionAvailability(plans = { DEVELOPER, PROFESSIONAL, ENTERPRISE }) + public ClimaticForecastRequester climaticForecast30Days() { + return new ClimaticForecastRequester(new RequestSettings(apiKey, timeoutSettings)); + } + + /** + * 5 Day / 3 Hour Forecast API. + * @return requester for retrieving 5 day/3-hour weather forecast information. */ @SubscriptionAvailability(plans = ALL) - public OneCallWeatherRequester oneCall() { - return new OneCallWeatherRequester(new RequestSettings(apiKey, timeoutSettings)); + public FiveDayThreeHourStepForecastRequester forecast5Day3HourStep() { + return new FiveDayThreeHourStepForecastRequester(new RequestSettings(apiKey, timeoutSettings)); } /** diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastAtmosphericPressureDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastAtmosphericPressureDeserializer.java new file mode 100644 index 0000000..187c805 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastAtmosphericPressureDeserializer.java @@ -0,0 +1,18 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.climatic; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.climatic.AtmosphericPressure; + +import java.io.IOException; + +public class ClimaticForecastAtmosphericPressureDeserializer extends JsonDeserializer { + @Override + public AtmosphericPressure deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + return (AtmosphericPressure) AtmosphericPressure.withValue(jsonNode.get("pressure").asDouble()); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastCloudsDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastCloudsDeserializer.java new file mode 100644 index 0000000..95bf04c --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastCloudsDeserializer.java @@ -0,0 +1,23 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.climatic; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.Clouds; + +import java.io.IOException; + +public class ClimaticForecastCloudsDeserializer extends JsonDeserializer { + @Override + public Clouds deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final JsonNode cloudsNode = rootNode.get("clouds"); + if (cloudsNode != null) { + return Clouds.withValue((byte) cloudsNode.asInt()); + } + + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastLocationDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastLocationDeserializer.java new file mode 100644 index 0000000..97fe05d --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastLocationDeserializer.java @@ -0,0 +1,54 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.climatic; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.github.prominence.openweathermap.api.deserializer.CoordinatesDeserializer; +import com.github.prominence.openweathermap.api.model.Coordinates; +import com.github.prominence.openweathermap.api.model.forecast.climatic.Location; + +import java.io.IOException; + +import static com.github.prominence.openweathermap.api.utils.JsonDeserializationUtils.parseZoneOffset; + +public class ClimaticForecastLocationDeserializer extends JsonDeserializer { + private final ObjectMapper objectMapper = new ObjectMapper(); + + public ClimaticForecastLocationDeserializer() { + final SimpleModule module = new SimpleModule(); + module.addDeserializer(Coordinates.class, new CoordinatesDeserializer()); + objectMapper.registerModule(module); + } + + @Override + public Location deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final Location location = (Location) Location.withValues(rootNode.get("id").asInt(), rootNode.get("name").asText()); + + final JsonNode timezoneNode = rootNode.get("timezone"); + if (timezoneNode != null) { + location.setZoneOffset(parseZoneOffset(timezoneNode)); + } + + final JsonNode countryNode = rootNode.get("country"); + if (countryNode != null) { + location.setCountryCode(countryNode.asText()); + } + + final JsonNode coordNode = rootNode.get("coord"); + if (coordNode != null) { + location.setCoordinate(objectMapper.readValue(objectMapper.treeAsTokens(coordNode), Coordinates.class)); + } + + final JsonNode populationNode = rootNode.get("population"); + if (populationNode != null) { + location.setPopulation(populationNode.asLong()); + } + + return location; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastRainDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastRainDeserializer.java new file mode 100644 index 0000000..4af8413 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastRainDeserializer.java @@ -0,0 +1,22 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.climatic; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.climatic.Rain; + +import java.io.IOException; + +public class ClimaticForecastRainDeserializer extends JsonDeserializer { + @Override + public Rain deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final JsonNode rainNode = rootNode.get("rain"); + if (rainNode != null) { + return (Rain) Rain.withValue(rainNode.asDouble()); + } + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastSnowDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastSnowDeserializer.java new file mode 100644 index 0000000..b7ca77a --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastSnowDeserializer.java @@ -0,0 +1,22 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.climatic; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.climatic.Snow; + +import java.io.IOException; + +public class ClimaticForecastSnowDeserializer extends JsonDeserializer { + @Override + public Snow deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final JsonNode snowNode = rootNode.get("snow"); + if (snowNode != null) { + return (Snow) Snow.withValue(snowNode.asDouble()); + } + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastTemperatureDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastTemperatureDeserializer.java new file mode 100644 index 0000000..10d27f9 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastTemperatureDeserializer.java @@ -0,0 +1,34 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.climatic; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.climatic.Temperature; + +import java.io.IOException; + +public class ClimaticForecastTemperatureDeserializer extends JsonDeserializer { + @Override + public Temperature deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + + final Temperature temperature = new Temperature(); + final JsonNode tempNode = rootNode.get("temp"); + temperature.setMorning(tempNode.get("morn").asDouble()); + temperature.setDay(tempNode.get("day").asDouble()); + temperature.setEve(tempNode.get("eve").asDouble()); + temperature.setNight(tempNode.get("night").asDouble()); + temperature.setMin(tempNode.get("min").asDouble()); + temperature.setMax(tempNode.get("max").asDouble()); + + final JsonNode feelsLikeNode = rootNode.get("feels_like"); + temperature.setMorningFeelsLike(feelsLikeNode.get("morn").asDouble()); + temperature.setDayFeelsLike(feelsLikeNode.get("day").asDouble()); + temperature.setEveFeelsLike(feelsLikeNode.get("eve").asDouble()); + temperature.setNightFeelsLike(feelsLikeNode.get("night").asDouble()); + + return temperature; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastWindDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastWindDeserializer.java new file mode 100644 index 0000000..3f4fc96 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/climatic/ClimaticForecastWindDeserializer.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.deserializer.forecast.climatic; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.enums.UnitSystem; +import com.github.prominence.openweathermap.api.model.Wind; + +import java.io.IOException; + +public class ClimaticForecastWindDeserializer extends JsonDeserializer { + @Override + public Wind deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + final JsonNode windNode = p.getCodec().readTree(p); + final UnitSystem unitSystem = (UnitSystem) ctxt.findInjectableValue("unitSystem", null, null); + + JsonNode speedNode = windNode.get("speed"); + if (speedNode == null) { + speedNode = windNode.get("wind_speed"); + } + double speed = speedNode.asDouble(); + + final Wind wind = Wind.withValue(speed, unitSystem.getWindUnit()); + JsonNode degNode = windNode.get("deg"); + if (degNode == null) { + degNode = windNode.get("wind_deg"); + } + if (degNode != null) { + wind.setDegrees(degNode.asDouble()); + } + + return wind; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastAtmosphericPressureDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastAtmosphericPressureDeserializer.java new file mode 100644 index 0000000..42a5a84 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastAtmosphericPressureDeserializer.java @@ -0,0 +1,18 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.daily; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.daily.AtmosphericPressure; + +import java.io.IOException; + +public class DailyForecastAtmosphericPressureDeserializer extends JsonDeserializer { + @Override + public AtmosphericPressure deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode jsonNode = jsonParser.getCodec().readTree(jsonParser); + return (AtmosphericPressure) AtmosphericPressure.withValue(jsonNode.get("pressure").asDouble()); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastCloudsDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastCloudsDeserializer.java new file mode 100644 index 0000000..8b23c9f --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastCloudsDeserializer.java @@ -0,0 +1,23 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.daily; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.Clouds; + +import java.io.IOException; + +public class DailyForecastCloudsDeserializer extends JsonDeserializer { + @Override + public Clouds deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final JsonNode cloudsNode = rootNode.get("clouds"); + if (cloudsNode != null) { + return Clouds.withValue((byte) cloudsNode.asInt()); + } + + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastLocationDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastLocationDeserializer.java new file mode 100644 index 0000000..0297c2a --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastLocationDeserializer.java @@ -0,0 +1,54 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.daily; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.github.prominence.openweathermap.api.deserializer.CoordinatesDeserializer; +import com.github.prominence.openweathermap.api.model.Coordinates; +import com.github.prominence.openweathermap.api.model.forecast.daily.Location; + +import java.io.IOException; + +import static com.github.prominence.openweathermap.api.utils.JsonDeserializationUtils.parseZoneOffset; + +public class DailyForecastLocationDeserializer extends JsonDeserializer { + private final ObjectMapper objectMapper = new ObjectMapper(); + + public DailyForecastLocationDeserializer() { + final SimpleModule module = new SimpleModule(); + module.addDeserializer(Coordinates.class, new CoordinatesDeserializer()); + objectMapper.registerModule(module); + } + + @Override + public Location deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final Location location = (Location) Location.withValues(rootNode.get("id").asInt(), rootNode.get("name").asText()); + + final JsonNode timezoneNode = rootNode.get("timezone"); + if (timezoneNode != null) { + location.setZoneOffset(parseZoneOffset(timezoneNode)); + } + + final JsonNode countryNode = rootNode.get("country"); + if (countryNode != null) { + location.setCountryCode(countryNode.asText()); + } + + final JsonNode coordNode = rootNode.get("coord"); + if (coordNode != null) { + location.setCoordinate(objectMapper.readValue(objectMapper.treeAsTokens(coordNode), Coordinates.class)); + } + + final JsonNode populationNode = rootNode.get("population"); + if (populationNode != null) { + location.setPopulation(populationNode.asLong()); + } + + return location; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastRainDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastRainDeserializer.java new file mode 100644 index 0000000..9e2d604 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastRainDeserializer.java @@ -0,0 +1,22 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.daily; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.daily.Rain; + +import java.io.IOException; + +public class DailyForecastRainDeserializer extends JsonDeserializer { + @Override + public Rain deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final JsonNode rainNode = rootNode.get("rain"); + if (rainNode != null) { + return (Rain) Rain.withValue(rainNode.asDouble()); + } + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastSnowDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastSnowDeserializer.java new file mode 100644 index 0000000..3f8a46b --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastSnowDeserializer.java @@ -0,0 +1,22 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.daily; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.daily.Snow; + +import java.io.IOException; + +public class DailyForecastSnowDeserializer extends JsonDeserializer { + @Override + public Snow deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + final JsonNode snowNode = rootNode.get("snow"); + if (snowNode != null) { + return (Snow) Snow.withValue(snowNode.asDouble()); + } + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastTemperatureDeserializer.java b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastTemperatureDeserializer.java new file mode 100644 index 0000000..3384664 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/deserializer/forecast/daily/DailyForecastTemperatureDeserializer.java @@ -0,0 +1,34 @@ +package com.github.prominence.openweathermap.api.deserializer.forecast.daily; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.github.prominence.openweathermap.api.model.forecast.daily.Temperature; + +import java.io.IOException; + +public class DailyForecastTemperatureDeserializer extends JsonDeserializer { + @Override + public Temperature deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + final JsonNode rootNode = jsonParser.getCodec().readTree(jsonParser); + + final Temperature temperature = new Temperature(); + final JsonNode tempNode = rootNode.get("temp"); + temperature.setMorning(tempNode.get("morn").asDouble()); + temperature.setDay(tempNode.get("day").asDouble()); + temperature.setEve(tempNode.get("eve").asDouble()); + temperature.setNight(tempNode.get("night").asDouble()); + temperature.setMin(tempNode.get("min").asDouble()); + temperature.setMax(tempNode.get("max").asDouble()); + + final JsonNode feelsLikeNode = rootNode.get("feels_like"); + temperature.setMorningFeelsLike(feelsLikeNode.get("morn").asDouble()); + temperature.setDayFeelsLike(feelsLikeNode.get("day").asDouble()); + temperature.setEveFeelsLike(feelsLikeNode.get("eve").asDouble()); + temperature.setNightFeelsLike(feelsLikeNode.get("night").asDouble()); + + return temperature; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapper.java b/src/main/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapper.java new file mode 100644 index 0000000..4dcc37e --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapper.java @@ -0,0 +1,84 @@ +package com.github.prominence.openweathermap.api.mapper; + +import com.fasterxml.jackson.databind.InjectableValues; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.github.prominence.openweathermap.api.deserializer.HumidityDeserializer; +import com.github.prominence.openweathermap.api.deserializer.WeatherStateDeserializer; +import com.github.prominence.openweathermap.api.deserializer.forecast.climatic.*; +import com.github.prominence.openweathermap.api.enums.UnitSystem; +import com.github.prominence.openweathermap.api.model.Clouds; +import com.github.prominence.openweathermap.api.model.Humidity; +import com.github.prominence.openweathermap.api.model.WeatherState; +import com.github.prominence.openweathermap.api.model.Wind; +import com.github.prominence.openweathermap.api.model.forecast.climatic.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class ClimaticForecastResponseMapper extends AbstractMapper { + + public ClimaticForecastResponseMapper(UnitSystem unitSystem) { + objectMapper.setInjectableValues(new InjectableValues.Std().addValue("unitSystem", unitSystem != null ? unitSystem : UnitSystem.STANDARD)); + final SimpleModule module = new SimpleModule(); + module.addDeserializer(WeatherState.class, new WeatherStateDeserializer()); + module.addDeserializer(Temperature.class, new ClimaticForecastTemperatureDeserializer()); + module.addDeserializer(AtmosphericPressure.class, new ClimaticForecastAtmosphericPressureDeserializer()); + module.addDeserializer(Humidity.class, new HumidityDeserializer()); + module.addDeserializer(Clouds.class, new ClimaticForecastCloudsDeserializer()); + module.addDeserializer(Rain.class, new ClimaticForecastRainDeserializer()); + module.addDeserializer(Snow.class, new ClimaticForecastSnowDeserializer()); + module.addDeserializer(Wind.class, new ClimaticForecastWindDeserializer()); + module.addDeserializer(Location.class, new ClimaticForecastLocationDeserializer()); + objectMapper.registerModule(module); + } + + public Forecast mapToForecast(String json) { + Forecast forecast; + try { + final JsonNode root = objectMapper.readTree(json); + forecast = mapToForecast(root); + } catch (IOException e) { + throw new RuntimeException("Cannot parse Forecast response", e); + } + + return forecast; + } + + private Forecast mapToForecast(JsonNode root) throws IOException { + final Forecast forecast = new Forecast(); + forecast.setLocation(objectMapper.readValue(objectMapper.treeAsTokens(root.get("city")), Location.class)); + + final List forecasts = new ArrayList<>(); + + final JsonNode forecastListNode = root.get("list"); + for (JsonNode forecastNode : forecastListNode) { + forecasts.add(parseWeatherForecast(forecastNode)); + } + + forecast.setWeatherForecasts(forecasts); + + return forecast; + } + + private WeatherForecast parseWeatherForecast(JsonNode rootNode) throws IOException { + final WeatherForecast weatherForecast = new WeatherForecast(); + + weatherForecast.setForecastTime(parseDateTime(rootNode.get("dt"))); + weatherForecast.setSunriseTime(parseDateTime(rootNode.get("sunrise"))); + weatherForecast.setSunsetTime(parseDateTime(rootNode.get("sunset"))); + + weatherForecast.setWeatherStates(parseWeatherStates(rootNode.get("weather"))); + + weatherForecast.setTemperature(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Temperature.class)); + weatherForecast.setAtmosphericPressure(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), AtmosphericPressure.class)); + weatherForecast.setHumidity(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Humidity.class)); + weatherForecast.setClouds(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Clouds.class)); + weatherForecast.setWind(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Wind.class)); + weatherForecast.setRain(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Rain.class)); + weatherForecast.setSnow(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Snow.class)); + + return weatherForecast; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/mapper/CurrentWeatherResponseMapper.java b/src/main/java/com/github/prominence/openweathermap/api/mapper/CurrentWeatherResponseMapper.java index bbbc1b7..08a7968 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/mapper/CurrentWeatherResponseMapper.java +++ b/src/main/java/com/github/prominence/openweathermap/api/mapper/CurrentWeatherResponseMapper.java @@ -22,22 +22,21 @@ package com.github.prominence.openweathermap.api.mapper; -import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.InjectableValues; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.module.SimpleModule; import com.github.prominence.openweathermap.api.deserializer.*; import com.github.prominence.openweathermap.api.deserializer.weather.WeatherLocationDeserializer; import com.github.prominence.openweathermap.api.deserializer.weather.WeatherRainDeserializer; import com.github.prominence.openweathermap.api.deserializer.weather.WeatherSnowDeserializer; -import com.github.prominence.openweathermap.api.model.weather.*; import com.github.prominence.openweathermap.api.enums.UnitSystem; import com.github.prominence.openweathermap.api.model.*; +import com.github.prominence.openweathermap.api.model.weather.Location; +import com.github.prominence.openweathermap.api.model.weather.Rain; +import com.github.prominence.openweathermap.api.model.weather.Snow; +import com.github.prominence.openweathermap.api.model.weather.Weather; import java.io.IOException; -import java.time.Instant; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.TimeZone; /** * Official API response documentation: https://openweathermap.org/current#current_JSON. @@ -86,13 +85,7 @@ public class CurrentWeatherResponseMapper extends AbstractMapper { final JsonNode weatherArrayNode = rootNode.get("weather"); final Weather weather = new Weather(); - final List weatherStateList = new ArrayList<>(); - if (weatherArrayNode != null && weatherArrayNode.isArray()) { - for (JsonNode weatherNode : weatherArrayNode) { - weatherStateList.add(objectMapper.readValue(objectMapper.treeAsTokens(weatherNode), WeatherState.class)); - } - weather.setWeatherStates(weatherStateList); - } + weather.setWeatherStates(parseWeatherStates(weatherArrayNode)); weather.setTemperature(objectMapper.readValue(objectMapper.treeAsTokens(rootNode.get("main")), Temperature.class)); weather.setAtmosphericPressure(objectMapper.readValue(objectMapper.treeAsTokens(rootNode.get("main")), AtmosphericPressure.class)); diff --git a/src/main/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapper.java b/src/main/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapper.java index 99bb0e9..4af016b 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapper.java +++ b/src/main/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapper.java @@ -22,33 +22,41 @@ package com.github.prominence.openweathermap.api.mapper; +import com.fasterxml.jackson.databind.InjectableValues; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.github.prominence.openweathermap.api.deserializer.HumidityDeserializer; +import com.github.prominence.openweathermap.api.deserializer.WeatherStateDeserializer; +import com.github.prominence.openweathermap.api.deserializer.WindDeserializer; +import com.github.prominence.openweathermap.api.deserializer.forecast.daily.*; import com.github.prominence.openweathermap.api.enums.UnitSystem; import com.github.prominence.openweathermap.api.model.Clouds; -import com.github.prominence.openweathermap.api.model.Coordinates; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; import com.github.prominence.openweathermap.api.model.Wind; import com.github.prominence.openweathermap.api.model.forecast.daily.*; import java.io.IOException; -import java.time.Instant; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; -import java.util.TimeZone; /** * Official API response documentation: https://openweathermap.org/forecast16#JSON. */ -public class DailyForecastResponseMapper { - private final ObjectMapper objectMapper = new ObjectMapper(); - private final UnitSystem unitSystem; - +public class DailyForecastResponseMapper extends AbstractMapper { public DailyForecastResponseMapper(UnitSystem unitSystem) { - this.unitSystem = unitSystem; + objectMapper.setInjectableValues(new InjectableValues.Std().addValue("unitSystem", unitSystem != null ? unitSystem : UnitSystem.STANDARD)); + final SimpleModule module = new SimpleModule(); + module.addDeserializer(WeatherState.class, new WeatherStateDeserializer()); + module.addDeserializer(Temperature.class, new DailyForecastTemperatureDeserializer()); + module.addDeserializer(AtmosphericPressure.class, new DailyForecastAtmosphericPressureDeserializer()); + module.addDeserializer(Humidity.class, new HumidityDeserializer()); + module.addDeserializer(Clouds.class, new DailyForecastCloudsDeserializer()); + module.addDeserializer(Rain.class, new DailyForecastRainDeserializer()); + module.addDeserializer(Snow.class, new DailyForecastSnowDeserializer()); + module.addDeserializer(Wind.class, new WindDeserializer()); + module.addDeserializer(Location.class, new DailyForecastLocationDeserializer()); + objectMapper.registerModule(module); } /** @@ -63,7 +71,7 @@ public class DailyForecastResponseMapper { final JsonNode root = objectMapper.readTree(json); forecast = mapToForecast(root); } catch (IOException e) { - throw new RuntimeException("Cannot parse Forecast response"); + throw new RuntimeException("Cannot parse Forecast response", e); } return forecast; @@ -71,7 +79,7 @@ public class DailyForecastResponseMapper { private Forecast mapToForecast(JsonNode root) throws IOException { final Forecast forecast = new Forecast(); - forecast.setLocation(parseLocation(root.get("city"))); + forecast.setLocation(objectMapper.readValue(objectMapper.treeAsTokens(root.get("city")), Location.class)); final List forecasts = new ArrayList<>(root.get("cnt").asInt()); @@ -87,124 +95,25 @@ public class DailyForecastResponseMapper { private WeatherForecast parseWeatherForecast(JsonNode rootNode) throws IOException { final WeatherForecast weatherForecast = new WeatherForecast(); - final JsonNode weatherArrayNode = rootNode.get("weather"); - if (weatherArrayNode != null) { - final JsonNode weatherNode = weatherArrayNode.get(0); - weatherForecast.setWeatherState(parseWeatherState(weatherNode)); + + weatherForecast.setForecastTime(parseDateTime(rootNode.get("dt"))); + weatherForecast.setSunriseTime(parseDateTime(rootNode.get("sunrise"))); + weatherForecast.setSunsetTime(parseDateTime(rootNode.get("sunset"))); + + weatherForecast.setWeatherStates(parseWeatherStates(rootNode.get("weather"))); + + weatherForecast.setTemperature(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Temperature.class)); + weatherForecast.setAtmosphericPressure(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), AtmosphericPressure.class)); + weatherForecast.setHumidity(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Humidity.class)); + weatherForecast.setClouds(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Clouds.class)); + weatherForecast.setWind(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Wind.class)); + weatherForecast.setRain(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Rain.class)); + weatherForecast.setSnow(objectMapper.readValue(objectMapper.treeAsTokens(rootNode), Snow.class)); + + if (rootNode.has("pop")) { + weatherForecast.setProbabilityOfPrecipitation(rootNode.get("pop").asDouble()); } - final JsonNode mainNode = rootNode.get("main"); - weatherForecast.setTemperature(parseTemperature(mainNode)); - weatherForecast.setAtmosphericPressure(parsePressure(mainNode)); - weatherForecast.setHumidity(parseHumidity(mainNode)); - weatherForecast.setClouds(parseClouds(rootNode)); - weatherForecast.setWind(parseWind(rootNode)); - weatherForecast.setRain(parseRain(rootNode)); - weatherForecast.setSnow(parseSnow(rootNode)); - - weatherForecast.setForecastTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(rootNode.get("dt").asLong()), TimeZone.getDefault().toZoneId())); - return weatherForecast; } - - private WeatherState parseWeatherState(JsonNode weatherNode) throws IOException { - if (weatherNode == null) { - return null; - } - return objectMapper.readValue(objectMapper.treeAsTokens(weatherNode), WeatherState.class); - } - - private Temperature parseTemperature(JsonNode rootNode) { - final Temperature temperature = new Temperature(); - final JsonNode tempNode = rootNode.get("temp"); - temperature.setMorning(tempNode.get("morn").asDouble()); - temperature.setDay(tempNode.get("day").asDouble()); - temperature.setEve(tempNode.get("eve").asDouble()); - temperature.setNight(tempNode.get("night").asDouble()); - temperature.setMin(tempNode.get("min").asDouble()); - temperature.setMax(tempNode.get("max").asDouble()); - - final JsonNode feelsLikeNode = rootNode.get("feels_like"); - temperature.setMorningFeelsLike(feelsLikeNode.get("morn").asDouble()); - temperature.setDayFeelsLike(feelsLikeNode.get("day").asDouble()); - temperature.setEveFeelsLike(feelsLikeNode.get("eve").asDouble()); - temperature.setNightFeelsLike(feelsLikeNode.get("night").asDouble()); - - return temperature; - } - - private AtmosphericPressure parsePressure(JsonNode rootNode) { - return AtmosphericPressure.withValue(rootNode.get("pressure").asDouble()); - } - - private Humidity parseHumidity(JsonNode rootNode) { - return Humidity.withValue((byte) (rootNode.get("humidity").asInt())); - } - - private Wind parseWind(JsonNode root) throws IOException { - final JsonNode windNode = root.get("wind"); - return objectMapper.readValue(objectMapper.treeAsTokens(windNode), Wind.class); - } - - private Rain parseRain(JsonNode root) { - final JsonNode rainNode = root.get("rain"); - if (rainNode != null) { - final JsonNode threeHourNode = rainNode.get("3h"); - if (threeHourNode != null) { - return Rain.withThreeHourLevelValue(threeHourNode.asDouble()); - } - } - return null; - } - - private Snow parseSnow(JsonNode root) { - final JsonNode snowNode = root.get("snow"); - if (snowNode != null) { - final JsonNode threeHourNode = snowNode.get("3h"); - if (threeHourNode != null) { - return Snow.withThreeHourLevelValue(threeHourNode.asDouble()); - } - } - return null; - } - - private Clouds parseClouds(JsonNode rootNode) { - final JsonNode cloudsNode = rootNode.get("clouds"); - final JsonNode allValueNode = cloudsNode.get("all"); - if (allValueNode != null) { - return Clouds.withValue((byte) allValueNode.asInt()); - } - - return null; - } - - private Location parseLocation(JsonNode rootNode) { - final Location location = Location.withValues(rootNode.get("id").asInt(), rootNode.get("name").asText()); - - final JsonNode timezoneNode = rootNode.get("timezone"); - if (timezoneNode != null) { - location.setZoneOffset(ZoneOffset.ofTotalSeconds(timezoneNode.asInt())); - } - - final JsonNode countryNode = rootNode.get("country"); - if (countryNode != null) { - location.setCountryCode(countryNode.asText()); - } - - final JsonNode coordNode = rootNode.get("coord"); - if (coordNode != null) { - location.setCoordinate(parseCoordinate(coordNode)); - } - - return location; - } - - private Coordinates parseCoordinate(JsonNode rootNode) { - final JsonNode latitudeNode = rootNode.get("lat"); - final JsonNode longitudeNode = rootNode.get("lon"); - if (latitudeNode != null && longitudeNode != null) { - return Coordinates.of(latitudeNode.asDouble(), longitudeNode.asDouble()); - } - return null; - } } diff --git a/src/main/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapper.java b/src/main/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapper.java index 6f76190..135c772 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapper.java +++ b/src/main/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapper.java @@ -28,8 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.github.prominence.openweathermap.api.deserializer.GeocodingRecordDeserializer; import com.github.prominence.openweathermap.api.deserializer.ZipCodeGeocodingDeserializer; -import com.github.prominence.openweathermap.api.model.geocoding.ZipCodeGeocodingRecord; import com.github.prominence.openweathermap.api.model.geocoding.GeocodingRecord; +import com.github.prominence.openweathermap.api.model.geocoding.ZipCodeGeocodingRecord; import java.util.List; diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/AtmosphericPressure.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/AtmosphericPressure.java new file mode 100644 index 0000000..164a524 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/AtmosphericPressure.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2021 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + +import java.util.Objects; + +/** + * The AtmosphericPressure type represents atmospheric pressure value. + * Its value can only be a double in [0, +∞) range. + */ +public class AtmosphericPressure { + private static final String DEFAULT_UNIT = "hPa"; + + private double seaLevelValue; + + protected AtmosphericPressure() { + } + + /** + * Static method for {@link AtmosphericPressure} creation with value checking. + * @param seaLevelValue atmospheric pressure value. + * @return instantiated {@link AtmosphericPressure} object. + */ + public static AtmosphericPressure withValue(double seaLevelValue) { + final AtmosphericPressure atmosphericPressure = new AtmosphericPressure(); + atmosphericPressure.setSeaLevelValue(seaLevelValue); + return atmosphericPressure; + } + + /** + * Gets sea level value. + * + * @return the sea level value. + */ + public Double getSeaLevelValue() { + return seaLevelValue; + } + + /** + * Sets sea level value. + * + * @param seaLevelValue the sea level value. + * @throws IllegalArgumentException in case if provided value isn't in allowed range. + */ + public void setSeaLevelValue(double seaLevelValue) { + if (seaLevelValue < 0) { + throw new IllegalArgumentException("Atmospheric pressure value must be in [0, +∞) range."); + } + this.seaLevelValue = seaLevelValue; + } + + /** + * Returns pressure unitSystem. Constantly equals to 'hPa'. + * + * @return the pressure unitSystem. + */ + public String getUnit() { + return DEFAULT_UNIT; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AtmosphericPressure that = (AtmosphericPressure) o; + return Double.compare(that.seaLevelValue, seaLevelValue) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(seaLevelValue); + } + + @Override + public String toString() { + return "Pressure: " + seaLevelValue + ' ' + getUnit(); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Forecast.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Forecast.java new file mode 100644 index 0000000..e36295a --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Forecast.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + + +import java.util.List; +import java.util.Objects; + +/** + * Represents information about forecast for different timestamps. + */ +public class Forecast { + private Location location; + private List weatherForecasts; + + /** + * Returns location information. + * @return location + */ + public Location getLocation() { + return location; + } + + /** + * Sets forecast location. + * @param location forecast location + */ + public void setLocation(Location location) { + this.location = location; + } + + /** + * Returns list of weather forecasts for different timestamps. + * @return list of forecast-per-timestamp information. + */ + public List getWeatherForecasts() { + return weatherForecasts; + } + + /** + * Sets list of weather forecasts for different timestamps. + * @param weatherForecasts list of forecast information + */ + public void setWeatherForecasts(List weatherForecasts) { + this.weatherForecasts = weatherForecasts; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Forecast forecast = (Forecast) o; + return Objects.equals(location, forecast.location) && Objects.equals(weatherForecasts, forecast.weatherForecasts); + } + + @Override + public int hashCode() { + return Objects.hash(location, weatherForecasts); + } + + @Override + public String toString() { + return "A forecast for " + location.getName() + " with " + weatherForecasts.size() + " timestamps."; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Location.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Location.java new file mode 100644 index 0000000..f541191 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Location.java @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + +import com.github.prominence.openweathermap.api.model.Coordinates; + +import java.time.ZoneOffset; +import java.util.Objects; + +/** + * Represents location information. + */ +public class Location { + private int id; + private String name; + private String countryCode; + + private ZoneOffset zoneOffset; + + private Coordinates coordinates; + + private Long population; + + protected Location(int id, String name) { + this.id = id; + this.name = name; + } + + /** + * Creates {@link Location} object with correctness check. + * @param id location id + * @param name location name + * @return location object + */ + public static Location withValues(int id, String name) { + if (name == null) { + throw new IllegalArgumentException("Name must be set."); + } + return new Location(id, name); + } + + /** + * Returns ID. + * @return location ID + */ + public int getId() { + return id; + } + + /** + * Sets location ID. + * @param id location id + */ + public void setId(int id) { + this.id = id; + } + + /** + * Returns location name. + * @return location name + */ + public String getName() { + return name; + } + + /** + * Sets location name. + * @param name location name + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns country code. + * @return location country code + */ + public String getCountryCode() { + return countryCode; + } + + /** + * Sets location country code. + * @param countryCode location country code + */ + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + /** + * Returns location timezone offset. + * @return timezone offset + */ + public ZoneOffset getZoneOffset() { + return zoneOffset; + } + + /** + * Sets location timezone offset. + * @param zoneOffset timezone offset + */ + public void setZoneOffset(ZoneOffset zoneOffset) { + this.zoneOffset = zoneOffset; + } + + /** + * Returns location coordinates. + * @return location coordinates. + */ + public Coordinates getCoordinate() { + return coordinates; + } + + /** + * Sets location coordinates. + * @param coordinates location coordinates + */ + public void setCoordinate(Coordinates coordinates) { + this.coordinates = coordinates; + } + + /** + * Sets location population. + * @return location population + */ + public Long getPopulation() { + return population; + } + + /** + * Sets location population. + * @param population location population + */ + public void setPopulation(Long population) { + this.population = population; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Location)) return false; + Location location = (Location) o; + return id == location.id && + Objects.equals(name, location.name) && + Objects.equals(countryCode, location.countryCode) && + Objects.equals(zoneOffset, location.zoneOffset) && + Objects.equals(coordinates, location.coordinates) && + Objects.equals(population, location.population); + } + + @Override + public int hashCode() { + return Objects.hash(id, name, countryCode, zoneOffset, coordinates, population); + } + + @Override + public String toString() { + final StringBuilder stringBuilder = new StringBuilder(); + if (coordinates != null) { + stringBuilder.append(coordinates); + stringBuilder.append(". "); + } + stringBuilder.append("ID: "); + stringBuilder.append(id); + stringBuilder.append(", Name: "); + stringBuilder.append(name); + if (countryCode != null) { + stringBuilder.append('('); + stringBuilder.append(countryCode); + stringBuilder.append(')'); + } + if (population != null) { + stringBuilder.append(", Population: "); + stringBuilder.append(population); + } + return stringBuilder.toString(); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Rain.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Rain.java new file mode 100644 index 0000000..055c6a4 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Rain.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + +import java.util.Objects; + +/** + * Represents rain information. + */ +public class Rain { + private static final String DEFAULT_UNIT = "mm"; + + private double level; + + protected Rain(double level) { + this.level = level; + } + + /** + * Creates {@link Rain} object with correctness check. + * @param level rain level value + * @return rain object. + */ + public static Rain withValue(double level) { + if (level < 0) { + throw new IllegalArgumentException("Rain level value cannot be negative."); + } + return new Rain(level); + } + + /** + * Returns rain level value. + * @return rain level value + */ + public double getLevel() { + return level; + } + + /** + * Sets rain level value with correctness check. + * @param level rain level value + */ + public void setLevel(double level) { + if (level < 0) { + throw new IllegalArgumentException("Rain level value cannot be negative."); + } + this.level = level; + } + + /** + * Returns rain level unit of measure. Currently, is constant. + * @return rain level unit of measure + */ + public String getUnit() { + return DEFAULT_UNIT; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Rain rain = (Rain) o; + return Double.compare(rain.level, level) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(level); + } + + @Override + public String toString() { + return "Rain precipitation volume, mm: " + + level + ' ' + + getUnit(); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Snow.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Snow.java new file mode 100644 index 0000000..1e8e01d --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Snow.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + +import java.util.Objects; + +/** + * Represents snow information. + */ +public class Snow { + private static final String DEFAULT_UNIT = "mm"; + + private double level; + + protected Snow(double level) { + this.level = level; + } + + /** + * Creates {@link Snow} object with correctness check. + * @param threeHourLevel snow level value + * @return snow object. + */ + public static Snow withValue(double threeHourLevel) { + if (threeHourLevel < 0) { + throw new IllegalArgumentException("Snow level value cannot be negative."); + } + return new Snow(threeHourLevel); + } + + /** + * Returns snow level value. + * @return snow level value + */ + public double getLevel() { + return level; + } + + /** + * Sets snow level value with correctness check. + * @param level snow level value + */ + public void setLevel(double level) { + if (level < 0) { + throw new IllegalArgumentException("Snow level value cannot be negative."); + } + this.level = level; + } + + /** + * Returns snow level unit of measure. Currently, is constant. + * @return snow level unit of measure + */ + public String getUnit() { + return DEFAULT_UNIT; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Snow snow = (Snow) o; + return Double.compare(snow.level, level) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(level); + } + + @Override + public String toString() { + return "Snow volume, mm: " + + level + ' ' + + getUnit(); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Temperature.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Temperature.java new file mode 100644 index 0000000..18e003d --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Temperature.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2021 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + +import java.util.Objects; + +/** + * The type Daily temperature. + */ +public class Temperature { + private Double morning; + private Double morningFeelsLike; + private Double day; + private Double dayFeelsLike; + private Double eve; + private Double eveFeelsLike; + private Double night; + private Double nightFeelsLike; + private Double min; + private Double max; + private String unit; + + /** + * Gets morning temperature. + * + * @return the morning + */ + public Double getMorning() { + return morning; + } + + /** + * Sets morning temperature. + * + * @param morning the morning + */ + public void setMorning(Double morning) { + this.morning = morning; + } + + /** + * Gets morning feels like temperature. + * + * @return the morning feels like temperature + */ + public Double getMorningFeelsLike() { + return morningFeelsLike; + } + + /** + * Sets morning feels like temperature. + * + * @param morningFeelsLike the morning feels like temperature + */ + public void setMorningFeelsLike(Double morningFeelsLike) { + this.morningFeelsLike = morningFeelsLike; + } + + /** + * Gets day temperature. + * + * @return the day temperature + */ + public Double getDay() { + return day; + } + + /** + * Sets day temperature. + * + * @param day the day temperature + */ + public void setDay(Double day) { + this.day = day; + } + + /** + * Gets day feels like temperature. + * + * @return the day feels like temperature + */ + public Double getDayFeelsLike() { + return dayFeelsLike; + } + + /** + * Sets day feels like temperature. + * + * @param dayFeelsLike the day feels like temperature + */ + public void setDayFeelsLike(Double dayFeelsLike) { + this.dayFeelsLike = dayFeelsLike; + } + + /** + * Gets eve temperature. + * + * @return the eve temperature + */ + public Double getEve() { + return eve; + } + + /** + * Sets eve temperature. + * + * @param eve the eve temperature + */ + public void setEve(Double eve) { + this.eve = eve; + } + + /** + * Gets eve feels like temperature. + * + * @return the eve feels like temperature + */ + public Double getEveFeelsLike() { + return eveFeelsLike; + } + + /** + * Sets eve feels like temperature. + * + * @param eveFeelsLike the eve feels like temperature + */ + public void setEveFeelsLike(Double eveFeelsLike) { + this.eveFeelsLike = eveFeelsLike; + } + + /** + * Gets night temperature. + * + * @return the night temperature + */ + public Double getNight() { + return night; + } + + /** + * Sets night temperature. + * + * @param night the night temperature + */ + public void setNight(Double night) { + this.night = night; + } + + /** + * Gets night feels like temperature. + * + * @return the night feels like temperature + */ + public Double getNightFeelsLike() { + return nightFeelsLike; + } + + /** + * Sets night feels like temperature. + * + * @param nightFeelsLike the night feels like temperature + */ + public void setNightFeelsLike(Double nightFeelsLike) { + this.nightFeelsLike = nightFeelsLike; + } + + /** + * Gets min temperature. + * + * @return the min temperature + */ + public Double getMin() { + return min; + } + + /** + * Sets min temperature. + * + * @param min the min temperature + */ + public void setMin(Double min) { + this.min = min; + } + + /** + * Gets max temperature. + * + * @return the max temperature + */ + public Double getMax() { + return max; + } + + /** + * Sets max temperature. + * + * @param max the max temperature + */ + public void setMax(Double max) { + this.max = max; + } + + /** + * Gets unit temperature. + * + * @return the unit temperature + */ + public String getUnit() { + return unit; + } + + /** + * Sets unit temperature. + * + * @param unit the unit temperature + */ + public void setUnit(String unit) { + this.unit = unit; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Temperature that = (Temperature) o; + return Objects.equals(morning, that.morning) && + Objects.equals(morningFeelsLike, that.morningFeelsLike) && + Objects.equals(day, that.day) && + Objects.equals(dayFeelsLike, that.dayFeelsLike) && + Objects.equals(eve, that.eve) && + Objects.equals(eveFeelsLike, that.eveFeelsLike) && + Objects.equals(night, that.night) && + Objects.equals(nightFeelsLike, that.nightFeelsLike) && + Objects.equals(min, that.min) && + Objects.equals(max, that.max) && + Objects.equals(unit, that.unit); + } + + @Override + public int hashCode() { + return Objects.hash(morning, morningFeelsLike, day, dayFeelsLike, eve, eveFeelsLike, night, nightFeelsLike, min, max, unit); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/WeatherForecast.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/WeatherForecast.java new file mode 100644 index 0000000..6427b21 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/WeatherForecast.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + + +import com.github.prominence.openweathermap.api.model.Clouds; +import com.github.prominence.openweathermap.api.model.Humidity; +import com.github.prominence.openweathermap.api.model.WeatherState; +import com.github.prominence.openweathermap.api.model.Wind; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +/** + * Represents weather forecast information for a particular timestamp. + */ +public class WeatherForecast { + private LocalDateTime forecastTime; + private LocalDateTime sunriseTime; + private LocalDateTime sunsetTime; + + private List weatherStates; + private Temperature temperature; + private AtmosphericPressure atmosphericPressure; + private Humidity humidity; + + private Wind wind; + private Rain rain; + private Snow snow; + private Clouds clouds; + + /** + * Gets forecast time. + * + * @return the forecast time + */ + public LocalDateTime getForecastTime() { + return forecastTime; + } + + /** + * Sets forecast time. + * + * @param forecastTime the forecast time + */ + public void setForecastTime(LocalDateTime forecastTime) { + this.forecastTime = forecastTime; + } + + public LocalDateTime getSunriseTime() { + return sunriseTime; + } + + public void setSunriseTime(LocalDateTime sunriseTime) { + this.sunriseTime = sunriseTime; + } + + public LocalDateTime getSunsetTime() { + return sunsetTime; + } + + public void setSunsetTime(LocalDateTime sunsetTime) { + this.sunsetTime = sunsetTime; + } + + /** + * Gets weather state. + * + * @return the weather state + */ + public List getWeatherStates() { + return weatherStates; + } + + /** + * Sets weather state. + * + * @param weatherStates the weather state + */ + public void setWeatherStates(List weatherStates) { + this.weatherStates = weatherStates; + } + + /** + * Gets temperature. + * + * @return the temperature + */ + public Temperature getTemperature() { + return temperature; + } + + /** + * Sets temperature. + * + * @param temperature the temperature + */ + public void setTemperature(Temperature temperature) { + this.temperature = temperature; + } + + /** + * Gets atmospheric pressure. + * + * @return the atmospheric pressure + */ + public AtmosphericPressure getAtmosphericPressure() { + return atmosphericPressure; + } + + /** + * Sets atmospheric pressure. + * + * @param atmosphericPressure the atmospheric pressure + */ + public void setAtmosphericPressure(AtmosphericPressure atmosphericPressure) { + this.atmosphericPressure = atmosphericPressure; + } + + /** + * Gets humidity. + * + * @return the humidity + */ + public Humidity getHumidity() { + return humidity; + } + + /** + * Sets humidity. + * + * @param humidity the humidity + */ + public void setHumidity(Humidity humidity) { + this.humidity = humidity; + } + + /** + * Gets wind. + * + * @return the wind + */ + public Wind getWind() { + return wind; + } + + /** + * Sets wind. + * + * @param wind the wind + */ + public void setWind(Wind wind) { + this.wind = wind; + } + + /** + * Gets rain. + * + * @return the rain + */ + public Rain getRain() { + return rain; + } + + /** + * Sets rain. + * + * @param rain the rain + */ + public void setRain(Rain rain) { + this.rain = rain; + } + + /** + * Gets snow. + * + * @return the snow + */ + public Snow getSnow() { + return snow; + } + + /** + * Sets snow. + * + * @param snow the snow + */ + public void setSnow(Snow snow) { + this.snow = snow; + } + + /** + * Gets clouds. + * + * @return the clouds + */ + public Clouds getClouds() { + return clouds; + } + + /** + * Sets clouds. + * + * @param clouds the clouds + */ + public void setClouds(Clouds clouds) { + this.clouds = clouds; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WeatherForecast that = (WeatherForecast) o; + return Objects.equals(forecastTime, that.forecastTime) && + Objects.equals(sunriseTime, that.sunriseTime) && + Objects.equals(sunsetTime, that.sunsetTime) && + Objects.equals(weatherStates, that.weatherStates) && + Objects.equals(temperature, that.temperature) && + Objects.equals(atmosphericPressure, that.atmosphericPressure) && + Objects.equals(humidity, that.humidity) && + Objects.equals(wind, that.wind) && + Objects.equals(rain, that.rain) && + Objects.equals(snow, that.snow) && + Objects.equals(clouds, that.clouds); + } + + @Override + public int hashCode() { + return Objects.hash(forecastTime, sunriseTime, sunsetTime, weatherStates, temperature, atmosphericPressure, humidity, wind, rain, snow, clouds); + } + + @Override + public String toString() { + final StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("Timestamp: "); + stringBuilder.append(forecastTime); + if (weatherStates != null && weatherStates.size() > 0) { + stringBuilder.append(", Weather: "); + stringBuilder.append(weatherStates.get(0).getDescription()); + } + if (temperature != null) { + stringBuilder.append(", Min temperature: "); + stringBuilder.append(temperature.getMin()); + stringBuilder.append(temperature.getUnit()); + stringBuilder.append(", Max temperature: "); + stringBuilder.append(temperature.getMax()); + stringBuilder.append(temperature.getUnit()); + } + if (atmosphericPressure != null) { + stringBuilder.append(", "); + stringBuilder.append(atmosphericPressure.getSeaLevelValue()); + stringBuilder.append(' '); + stringBuilder.append(atmosphericPressure.getUnit()); + } + if (clouds != null) { + stringBuilder.append(", "); + stringBuilder.append(clouds); + } + if (rain != null) { + stringBuilder.append(", Rain: "); + stringBuilder.append(rain.getLevel()); + stringBuilder.append(' '); + stringBuilder.append(rain.getUnit()); + } + if (snow != null) { + stringBuilder.append(", Snow: "); + stringBuilder.append(snow.getLevel()); + stringBuilder.append(' '); + stringBuilder.append(snow.getUnit()); + } + return stringBuilder.toString(); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Wind.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Wind.java new file mode 100644 index 0000000..4140c7f --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/climatic/Wind.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2022 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.model.forecast.climatic; + +import java.util.Objects; + +/** + * The type Wind. + */ +public class Wind { + private double speed; + private Double degrees; + private String unit; + + /** + * Instantiates a new Wind. + * + * @param speed the speed + * @param unit the unitSystem + */ + private Wind(double speed, String unit) { + this.speed = speed; + this.unit = unit; + } + + /** + * Creates {@link Wind} object with correctness check. + * + * @param speed the speed + * @param unit the unitSystem + * @return wind object + */ + public static Wind withValue(double speed, String unit) { + if (speed < 0) { + throw new IllegalArgumentException("Wind speed value must be in positive or zero."); + } + if (unit == null) { + throw new IllegalArgumentException("Unit must be set."); + } + return new Wind(speed, unit); + } + + /** + * Gets speed. + * + * @return the speed + */ + public double getSpeed() { + return speed; + } + + /** + * Sets speed. + * + * @param speed the speed + */ + public void setSpeed(double speed) { + if (speed < 0) { + throw new IllegalArgumentException("Wind speed value must be in positive or zero."); + } + this.speed = speed; + } + + /** + * Gets degrees. + * + * @return the degrees + */ + public Double getDegrees() { + return degrees; + } + + /** + * Sets degrees. + * + * @param degrees the degrees + */ + public void setDegrees(double degrees) { + if (degrees < 0 || degrees > 360) { + throw new IllegalArgumentException("Wind direction value must be in [0, 360] range."); + } + this.degrees = degrees; + } + + /** + * Gets unitSystem. + * + * @return the unitSystem + */ + public String getUnit() { + return unit; + } + + /** + * Sets unitSystem. + * + * @param unit the unitSystem + */ + public void setUnit(String unit) { + if (unit == null) { + throw new IllegalArgumentException("Unit must be set."); + } + this.unit = unit; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Wind)) return false; + Wind wind = (Wind) o; + return Double.compare(wind.speed, speed) == 0 && + Objects.equals(degrees, wind.degrees) && + Objects.equals(unit, wind.unit); + } + + @Override + public int hashCode() { + return Objects.hash(speed, degrees, unit); + } + + @Override + public String toString() { + return "Wind speed: " + speed + " " + unit + + ", degrees: " + degrees; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/AtmosphericPressure.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/AtmosphericPressure.java index 7404966..f4e8b4d 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/AtmosphericPressure.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/AtmosphericPressure.java @@ -33,7 +33,7 @@ public class AtmosphericPressure { private double seaLevelValue; - private AtmosphericPressure() { + protected AtmosphericPressure() { } /** diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Forecast.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Forecast.java index 6f2bbff..9841732 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Forecast.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Forecast.java @@ -31,7 +31,7 @@ import java.util.Objects; */ public class Forecast { private Location location; - private List weatherForecasts; + private List weatherForecasts; /** * Returns location information. @@ -53,7 +53,7 @@ public class Forecast { * Returns list of weather forecasts for different timestamps. * @return list of forecast-per-timestamp information. */ - public List getWeatherForecasts() { + public List getWeatherForecasts() { return weatherForecasts; } @@ -61,7 +61,7 @@ public class Forecast { * Sets list of weather forecasts for different timestamps. * @param weatherForecasts list of forecast information */ - public void setWeatherForecasts(List weatherForecasts) { + public void setWeatherForecasts(List weatherForecasts) { this.weatherForecasts = weatherForecasts; } diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Location.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Location.java index d876f45..4cb0beb 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Location.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Location.java @@ -41,7 +41,7 @@ public class Location { private Long population; - private Location(int id, String name) { + protected Location(int id, String name) { this.id = id; this.name = name; } diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Rain.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Rain.java index 10806fd..3358630 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Rain.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Rain.java @@ -32,33 +32,33 @@ public class Rain { private double level; - private Rain(double level) { + protected Rain(double level) { this.level = level; } /** * Creates {@link Rain} object with correctness check. - * @param threeHourLevel 3-hour rain level value + * @param level rain level value * @return rain object. */ - public static Rain withThreeHourLevelValue(double threeHourLevel) { - if (threeHourLevel < 0) { + public static Rain withValue(double level) { + if (level < 0) { throw new IllegalArgumentException("Rain level value cannot be negative."); } - return new Rain(threeHourLevel); + return new Rain(level); } /** - * Returns 3-hour rain level value. - * @return 3-hour rain level value + * Returns rain level value. + * @return rain level value */ public double getLevel() { return level; } /** - * Sets 3-hour rain level value with correctness check. - * @param level 3-hour rain level value + * Sets rain level value with correctness check. + * @param level rain level value */ public void setLevel(double level) { if (level < 0) { @@ -68,7 +68,7 @@ public class Rain { } /** - * Returns rain level unit of measure. Currently is constant. + * Returns rain level unit of measure. Currently, is constant. * @return rain level unit of measure */ public String getUnit() { @@ -90,7 +90,7 @@ public class Rain { @Override public String toString() { - return "3-hour rain level: " + + return "Rain precipitation volume, mm: " + level + ' ' + getUnit(); } diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Snow.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Snow.java index 258163f..bfa6a37 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Snow.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/Snow.java @@ -32,16 +32,16 @@ public class Snow { private double level; - private Snow(double level) { + protected Snow(double level) { this.level = level; } /** * Creates {@link Snow} object with correctness check. - * @param threeHourLevel 3-hour snow level value + * @param threeHourLevel snow level value * @return snow object. */ - public static Snow withThreeHourLevelValue(double threeHourLevel) { + public static Snow withValue(double threeHourLevel) { if (threeHourLevel < 0) { throw new IllegalArgumentException("Snow level value cannot be negative."); } @@ -49,16 +49,16 @@ public class Snow { } /** - * Returns 3-hour snow level value. - * @return 3-hour snow level value + * Returns snow level value. + * @return snow level value */ public double getLevel() { return level; } /** - * Sets 3-hour snow level value with correctness check. - * @param level 3-hour snow level value + * Sets snow level value with correctness check. + * @param level snow level value */ public void setLevel(double level) { if (level < 0) { @@ -68,7 +68,7 @@ public class Snow { } /** - * Returns snow level unit of measure. Currently is constant. + * Returns snow level unit of measure. Currently, is constant. * @return snow level unit of measure */ public String getUnit() { @@ -90,7 +90,7 @@ public class Snow { @Override public String toString() { - return "3-hour snow level: " + + return "Snow volume, mm: " + level + ' ' + getUnit(); } diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/WeatherForecast.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/WeatherForecast.java index 6d9d3b9..a1e0648 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/WeatherForecast.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/daily/WeatherForecast.java @@ -29,6 +29,7 @@ import com.github.prominence.openweathermap.api.model.WeatherState; import com.github.prominence.openweathermap.api.model.Wind; import java.time.LocalDateTime; +import java.util.List; import java.util.Objects; /** @@ -36,12 +37,10 @@ import java.util.Objects; */ public class WeatherForecast { private LocalDateTime forecastTime; - private LocalDateTime sunriseTime; - private LocalDateTime sunsetTime; - private WeatherState weatherState; + private List weatherStates; private Temperature temperature; private AtmosphericPressure atmosphericPressure; private Humidity humidity; @@ -51,6 +50,8 @@ public class WeatherForecast { private Snow snow; private Clouds clouds; + private Double probabilityOfPrecipitation; + /** * Gets forecast time. * @@ -69,22 +70,38 @@ public class WeatherForecast { this.forecastTime = forecastTime; } + public LocalDateTime getSunriseTime() { + return sunriseTime; + } + + public void setSunriseTime(LocalDateTime sunriseTime) { + this.sunriseTime = sunriseTime; + } + + public LocalDateTime getSunsetTime() { + return sunsetTime; + } + + public void setSunsetTime(LocalDateTime sunsetTime) { + this.sunsetTime = sunsetTime; + } + /** * Gets weather state. * * @return the weather state */ - public WeatherState getWeatherState() { - return weatherState; + public List getWeatherStates() { + return weatherStates; } /** * Sets weather state. * - * @param weatherState the weather state + * @param weatherStates the weather state */ - public void setWeatherState(WeatherState weatherState) { - this.weatherState = weatherState; + public void setWeatherStates(List weatherStates) { + this.weatherStates = weatherStates; } /** @@ -213,25 +230,36 @@ public class WeatherForecast { this.clouds = clouds; } + public Double getProbabilityOfPrecipitation() { + return probabilityOfPrecipitation; + } + + public void setProbabilityOfPrecipitation(Double probabilityOfPrecipitation) { + this.probabilityOfPrecipitation = probabilityOfPrecipitation; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; WeatherForecast that = (WeatherForecast) o; return Objects.equals(forecastTime, that.forecastTime) && - Objects.equals(weatherState, that.weatherState) && + Objects.equals(sunriseTime, that.sunriseTime) && + Objects.equals(sunsetTime, that.sunsetTime) && + Objects.equals(weatherStates, that.weatherStates) && Objects.equals(temperature, that.temperature) && Objects.equals(atmosphericPressure, that.atmosphericPressure) && Objects.equals(humidity, that.humidity) && Objects.equals(wind, that.wind) && Objects.equals(rain, that.rain) && Objects.equals(snow, that.snow) && - Objects.equals(clouds, that.clouds); + Objects.equals(clouds, that.clouds) && + Objects.equals(probabilityOfPrecipitation, that.probabilityOfPrecipitation); } @Override public int hashCode() { - return Objects.hash(forecastTime, weatherState, temperature, atmosphericPressure, humidity, wind, rain, snow, clouds); + return Objects.hash(forecastTime, sunriseTime, sunsetTime, weatherStates, temperature, atmosphericPressure, humidity, wind, rain, snow, clouds, probabilityOfPrecipitation); } @Override @@ -239,9 +267,9 @@ public class WeatherForecast { final StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("Timestamp: "); stringBuilder.append(forecastTime); - if (weatherState != null) { + if (weatherStates != null && weatherStates.size() > 0) { stringBuilder.append(", Weather: "); - stringBuilder.append(weatherState.getDescription()); + stringBuilder.append(weatherStates.get(0).getDescription()); } if (temperature != null) { stringBuilder.append(", Min temperature: "); @@ -259,7 +287,7 @@ public class WeatherForecast { } if (clouds != null) { stringBuilder.append(", "); - stringBuilder.append(clouds.toString()); + stringBuilder.append(clouds); } if (rain != null) { stringBuilder.append(", Rain: "); diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java index 0b682bf..0b64cde 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/free/WeatherForecast.java @@ -23,7 +23,6 @@ package com.github.prominence.openweathermap.api.model.forecast.free; import com.github.prominence.openweathermap.api.model.*; -import com.github.prominence.openweathermap.api.model.Wind; import java.time.LocalDateTime; import java.util.Objects; diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/hourly/WeatherForecast.java b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/hourly/WeatherForecast.java index eaa1d63..95d2ea6 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/forecast/hourly/WeatherForecast.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/forecast/hourly/WeatherForecast.java @@ -23,7 +23,6 @@ package com.github.prominence.openweathermap.api.model.forecast.hourly; import com.github.prominence.openweathermap.api.model.*; -import com.github.prominence.openweathermap.api.model.Wind; import java.time.LocalDateTime; import java.util.List; diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Daily.java b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Daily.java index b3232bc..e0de6fc 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Daily.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Daily.java @@ -25,8 +25,8 @@ package com.github.prominence.openweathermap.api.model.onecall.current; import com.github.prominence.openweathermap.api.model.Clouds; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; -import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; import com.github.prominence.openweathermap.api.model.Wind; +import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; import java.time.LocalDateTime; import java.util.List; diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Hourly.java b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Hourly.java index a587071..e9d617b 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Hourly.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/current/Hourly.java @@ -26,7 +26,10 @@ import com.github.prominence.openweathermap.api.model.Clouds; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; import com.github.prominence.openweathermap.api.model.Wind; -import com.github.prominence.openweathermap.api.model.onecall.*; +import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; +import com.github.prominence.openweathermap.api.model.onecall.Rain; +import com.github.prominence.openweathermap.api.model.onecall.Snow; +import com.github.prominence.openweathermap.api.model.onecall.Temperature; import java.time.LocalDateTime; import java.util.List; diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeather.java b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeather.java index 69e64a5..caab8a4 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeather.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeather.java @@ -22,7 +22,7 @@ package com.github.prominence.openweathermap.api.model.onecall.historical; -import com.github.prominence.openweathermap.api.model.onecall.*; +import com.github.prominence.openweathermap.api.model.onecall.Current; /** * The type Historical weather. diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistorical.java b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistorical.java index e59e8d1..d62c7f8 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistorical.java +++ b/src/main/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistorical.java @@ -26,7 +26,10 @@ import com.github.prominence.openweathermap.api.model.Clouds; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; import com.github.prominence.openweathermap.api.model.Wind; -import com.github.prominence.openweathermap.api.model.onecall.*; +import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; +import com.github.prominence.openweathermap.api.model.onecall.Rain; +import com.github.prominence.openweathermap.api.model.onecall.Snow; +import com.github.prominence.openweathermap.api.model.onecall.Temperature; import java.time.LocalDateTime; import java.util.List; diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastAsyncRequestTerminator.java b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastAsyncRequestTerminator.java new file mode 100644 index 0000000..12f4f2c --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastAsyncRequestTerminator.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.request.forecast.climatic; + +import com.github.prominence.openweathermap.api.enums.ResponseType; +import com.github.prominence.openweathermap.api.mapper.ClimaticForecastResponseMapper; +import com.github.prominence.openweathermap.api.model.forecast.climatic.Forecast; +import com.github.prominence.openweathermap.api.request.RequestSettings; +import com.github.prominence.openweathermap.api.utils.RequestUtils; + +import java.util.concurrent.CompletableFuture; + +class ClimaticForecastAsyncRequestTerminator { + private final RequestSettings requestSettings; + + ClimaticForecastAsyncRequestTerminator(RequestSettings requestSettings) { + this.requestSettings = requestSettings; + } + + public CompletableFuture asJava() { + return CompletableFuture.supplyAsync(() -> new ClimaticForecastResponseMapper(requestSettings.getUnitSystem()).mapToForecast(getRawResponse())); + } + + public CompletableFuture asJSON() { + return CompletableFuture.supplyAsync(this::getRawResponse); + } + + public CompletableFuture asXML() { + requestSettings.setResponseType(ResponseType.XML); + return CompletableFuture.supplyAsync(this::getRawResponse); + } + + private String getRawResponse() { + return RequestUtils.getResponse(requestSettings); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestCustomizer.java b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestCustomizer.java new file mode 100644 index 0000000..34e16ac --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestCustomizer.java @@ -0,0 +1,45 @@ +package com.github.prominence.openweathermap.api.request.forecast.climatic; + +import com.github.prominence.openweathermap.api.enums.Language; +import com.github.prominence.openweathermap.api.enums.UnitSystem; +import com.github.prominence.openweathermap.api.request.RequestSettings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClimaticForecastRequestCustomizer { + private static final Logger logger = LoggerFactory.getLogger(ClimaticForecastRequestCustomizer.class); + + private final RequestSettings requestSettings; + + public ClimaticForecastRequestCustomizer(RequestSettings requestSettings) { + this.requestSettings = requestSettings; + } + + public ClimaticForecastRequestCustomizer language(Language language) { + requestSettings.setLanguage(language); + return this; + } + + public ClimaticForecastRequestCustomizer unitSystem(UnitSystem unitSystem) { + requestSettings.setUnitSystem(unitSystem); + return this; + } + + public ClimaticForecastRequestCustomizer numberOfDays(int numberOfDays) { + int days = numberOfDays; + if (days > 30) { + logger.warn("Cannot use more than 30 days for this API request. Please, specify 30 or less days. !!! Requesting information for 30 days..."); + days = 30; + } + requestSettings.putRequestParameter("cnt", Integer.toString(days)); + return this; + } + + public ClimaticForecastRequestTerminator retrieve() { + return new ClimaticForecastRequestTerminator(requestSettings); + } + + public ClimaticForecastAsyncRequestTerminator retrieveAsync() { + return new ClimaticForecastAsyncRequestTerminator(requestSettings); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestTerminator.java b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestTerminator.java new file mode 100644 index 0000000..bfd8197 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequestTerminator.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021 Alexey Zinchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.prominence.openweathermap.api.request.forecast.climatic; + +import com.github.prominence.openweathermap.api.enums.ResponseType; +import com.github.prominence.openweathermap.api.mapper.ClimaticForecastResponseMapper; +import com.github.prominence.openweathermap.api.model.forecast.climatic.Forecast; +import com.github.prominence.openweathermap.api.request.RequestSettings; +import com.github.prominence.openweathermap.api.utils.RequestUtils; + +class ClimaticForecastRequestTerminator { + private final RequestSettings requestSettings; + + ClimaticForecastRequestTerminator(RequestSettings requestSettings) { + this.requestSettings = requestSettings; + } + + public Forecast asJava() { + return new ClimaticForecastResponseMapper(requestSettings.getUnitSystem()).mapToForecast(getRawResponse()); + } + + public String asJSON() { + return getRawResponse(); + } + + public String asXML() { + requestSettings.setResponseType(ResponseType.XML); + return getRawResponse(); + } + + private String getRawResponse() { + return RequestUtils.getResponse(requestSettings); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequester.java b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequester.java new file mode 100644 index 0000000..a937c13 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/climatic/ClimaticForecastRequester.java @@ -0,0 +1,20 @@ +package com.github.prominence.openweathermap.api.request.forecast.climatic; + +import com.github.prominence.openweathermap.api.model.Coordinates; +import com.github.prominence.openweathermap.api.request.RequestSettings; + +public class ClimaticForecastRequester { + private final RequestSettings requestSettings; + + public ClimaticForecastRequester(RequestSettings requestSettings) { + this.requestSettings = requestSettings; + this.requestSettings.setSubdomain("pro"); + this.requestSettings.appendToURL("data/2.5/forecast/climate"); + } + + public ClimaticForecastRequestCustomizer byCoordinates(Coordinates coordinates) { + requestSettings.putRequestParameter("lat", String.valueOf(coordinates.getLatitude())); + requestSettings.putRequestParameter("lon", String.valueOf(coordinates.getLongitude())); + return new ClimaticForecastRequestCustomizer(requestSettings); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequestCustomizer.java b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequestCustomizer.java index 03b7e0a..03b307d 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequestCustomizer.java +++ b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequestCustomizer.java @@ -50,7 +50,7 @@ class DailyForecastRequestCustomizer { public DailyForecastRequestCustomizer numberOfDays(int numberOfDays) { int days = numberOfDays; if (days > 16) { - logger.warn("Cannot use more than 16 days for this api request. Please, specify 16 or less days. !!! Requesting information for 16 days..."); + logger.warn("Cannot use more than 16 days for this API request. Please, specify 16 or less days. !!! Requesting information for 16 days..."); days = 16; } requestSettings.putRequestParameter("cnt", Integer.toString(days)); diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequester.java b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequester.java index 0c6fbba..fe9ab50 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequester.java +++ b/src/main/java/com/github/prominence/openweathermap/api/request/forecast/daily/DailyForecastRequester.java @@ -30,7 +30,7 @@ public class DailyForecastRequester { public DailyForecastRequester(RequestSettings requestSettings) { this.requestSettings = requestSettings; - this.requestSettings.appendToURL("data/2.5/forecast/hourly"); + this.requestSettings.appendToURL("data/2.5/forecast/daily"); } public DailyForecastRequestCustomizer byCoordinates(Coordinates coordinates) { diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/geocoding/direct/DirectGeocodingRequestCustomizer.java b/src/main/java/com/github/prominence/openweathermap/api/request/geocoding/direct/DirectGeocodingRequestCustomizer.java index 3e44225..5ddb9c6 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/request/geocoding/direct/DirectGeocodingRequestCustomizer.java +++ b/src/main/java/com/github/prominence/openweathermap/api/request/geocoding/direct/DirectGeocodingRequestCustomizer.java @@ -23,8 +23,6 @@ package com.github.prominence.openweathermap.api.request.geocoding.direct; import com.github.prominence.openweathermap.api.request.RequestSettings; -import com.github.prominence.openweathermap.api.request.geocoding.reverse.ReverseGeocodingRequestAsyncTerminator; -import com.github.prominence.openweathermap.api.request.geocoding.reverse.ReverseGeocodingRequestTerminator; import java.util.function.Function; diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherAsyncRequestTerminator.java b/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherAsyncRequestTerminator.java index a0f1b77..2c3bfef 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherAsyncRequestTerminator.java +++ b/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherAsyncRequestTerminator.java @@ -23,9 +23,9 @@ package com.github.prominence.openweathermap.api.request.weather; import com.github.prominence.openweathermap.api.enums.ResponseType; -import com.github.prominence.openweathermap.api.request.RequestSettings; import com.github.prominence.openweathermap.api.mapper.CurrentWeatherResponseMapper; import com.github.prominence.openweathermap.api.model.weather.Weather; +import com.github.prominence.openweathermap.api.request.RequestSettings; import com.github.prominence.openweathermap.api.utils.RequestUtils; import java.util.concurrent.CompletableFuture; diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherRequestTerminator.java b/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherRequestTerminator.java index 1601fbc..639779f 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherRequestTerminator.java +++ b/src/main/java/com/github/prominence/openweathermap/api/request/weather/CurrentWeatherRequestTerminator.java @@ -23,9 +23,9 @@ package com.github.prominence.openweathermap.api.request.weather; import com.github.prominence.openweathermap.api.enums.ResponseType; +import com.github.prominence.openweathermap.api.mapper.CurrentWeatherResponseMapper; import com.github.prominence.openweathermap.api.model.weather.Weather; import com.github.prominence.openweathermap.api.request.RequestSettings; -import com.github.prominence.openweathermap.api.mapper.CurrentWeatherResponseMapper; import com.github.prominence.openweathermap.api.utils.RequestUtils; /** diff --git a/src/main/java/com/github/prominence/openweathermap/api/utils/RequestUtils.java b/src/main/java/com/github/prominence/openweathermap/api/utils/RequestUtils.java index ec7e95d..1ef190d 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/utils/RequestUtils.java +++ b/src/main/java/com/github/prominence/openweathermap/api/utils/RequestUtils.java @@ -23,8 +23,8 @@ package com.github.prominence.openweathermap.api.utils; import com.github.prominence.openweathermap.api.conf.TimeoutSettings; -import com.github.prominence.openweathermap.api.exception.NoDataFoundException; import com.github.prominence.openweathermap.api.exception.InvalidAuthTokenException; +import com.github.prominence.openweathermap.api.exception.NoDataFoundException; import com.github.prominence.openweathermap.api.request.RequestSettings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/test/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapperTest.java b/src/test/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapperTest.java new file mode 100644 index 0000000..3f97f38 --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/mapper/ClimaticForecastResponseMapperTest.java @@ -0,0 +1,126 @@ +package com.github.prominence.openweathermap.api.mapper; + +import com.github.prominence.openweathermap.api.enums.UnitSystem; +import com.github.prominence.openweathermap.api.model.Wind; +import com.github.prominence.openweathermap.api.model.*; +import com.github.prominence.openweathermap.api.model.forecast.climatic.AtmosphericPressure; +import com.github.prominence.openweathermap.api.model.forecast.climatic.Temperature; +import com.github.prominence.openweathermap.api.model.forecast.climatic.*; +import com.github.prominence.openweathermap.api.utils.TestMappingUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class ClimaticForecastResponseMapperTest { + + @Test + public void mapToForecast() { + final String jsonResponse = """ + { + "cod": "200", + "city": { + "id": 2643743, + "name": "London", + "coord": { + "lon": -0.1277, + "lat": 51.5073 + }, + "country": "GB" + }, + "message": 0.353472054, + "list": [ + { + "dt": 1594382400, + "sunrise": 1594353335, + "sunset": 1594412149, + "temp": { + "day": 286.98, + "min": 285.22, + "max": 287.97, + "night": 285.22, + "eve": 287.97, + "morn": 287.29 + }, + "feels_like": { + "day": 282.61, + "night": 283.19, + "eve": 284.98, + "morn": 282.68 + }, + "pressure": 1016, + "humidity": 84, + "weather": [ + { + "id": 500, + "main": "Rain", + "description": "light rain", + "icon": "10d" + } + ], + "speed": 6.78, + "deg": 320, + "clouds": 81, + "rain": 1.96, + "snow": 2.21 + } + ] + } + """; + + final Forecast forecast = new ClimaticForecastResponseMapper(UnitSystem.METRIC).mapToForecast(jsonResponse); + assertNotNull(forecast); + + final Location location = forecast.getLocation(); + assertNotNull(location); + assertEquals(Coordinates.of(51.5073, -0.1277), location.getCoordinate()); + assertEquals(2643743, location.getId()); + assertEquals("London", location.getName()); + assertEquals("GB", location.getCountryCode()); + assertNull(location.getPopulation()); + assertNull(location.getZoneOffset()); + + assertEquals(1, forecast.getWeatherForecasts().size()); + final WeatherForecast weatherForecast = forecast.getWeatherForecasts().get(0); + assertEquals(TestMappingUtils.parseDateTime(1594382400), weatherForecast.getForecastTime()); + assertEquals(TestMappingUtils.parseDateTime(1594353335), weatherForecast.getSunriseTime()); + assertEquals(TestMappingUtils.parseDateTime(1594412149), weatherForecast.getSunsetTime()); + + final Temperature temperature = weatherForecast.getTemperature(); + assertEquals(286.98, temperature.getDay()); + assertEquals(285.22, temperature.getMin()); + assertEquals(287.97, temperature.getMax()); + assertEquals(285.22, temperature.getNight()); + assertEquals(287.97, temperature.getEve()); + assertEquals(287.29, temperature.getMorning()); + assertEquals(282.61, temperature.getDayFeelsLike()); + assertEquals(283.19, temperature.getNightFeelsLike()); + assertEquals(284.98, temperature.getEveFeelsLike()); + assertEquals(282.68, temperature.getMorningFeelsLike()); + + final AtmosphericPressure pressure = weatherForecast.getAtmosphericPressure(); + assertEquals(1016, pressure.getSeaLevelValue()); + + final Humidity humidity = weatherForecast.getHumidity(); + assertEquals(84, humidity.getValue()); + + final Wind wind = weatherForecast.getWind(); + assertEquals(6.78, wind.getSpeed()); + assertEquals(320, wind.getDegrees()); + + final Clouds clouds = weatherForecast.getClouds(); + assertEquals(81, clouds.getValue()); + + assertEquals(1, weatherForecast.getWeatherStates().size()); + final WeatherState weatherState = weatherForecast.getWeatherStates().get(0); + assertEquals(500, weatherState.getId()); + assertEquals("Rain", weatherState.getName()); + assertEquals("light rain", weatherState.getDescription()); + assertEquals("10d", weatherState.getIconId()); + + final Rain rain = weatherForecast.getRain(); + assertEquals(1.96, rain.getLevel()); + + final Snow snow = weatherForecast.getSnow(); + assertEquals(2.21, snow.getLevel()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapperTest.java b/src/test/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapperTest.java new file mode 100644 index 0000000..081ee6d --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/mapper/DailyForecastResponseMapperTest.java @@ -0,0 +1,137 @@ +package com.github.prominence.openweathermap.api.mapper; + +import com.github.prominence.openweathermap.api.enums.UnitSystem; +import com.github.prominence.openweathermap.api.model.*; +import com.github.prominence.openweathermap.api.model.forecast.daily.AtmosphericPressure; +import com.github.prominence.openweathermap.api.model.forecast.daily.Temperature; +import com.github.prominence.openweathermap.api.model.forecast.daily.*; +import com.github.prominence.openweathermap.api.utils.TestMappingUtils; +import org.junit.jupiter.api.Test; + +import java.time.ZoneOffset; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class DailyForecastResponseMapperTest { + + @Test + public void mapToForecast() { + final String jsonResponse = """ + { + "city": { + "id": 2643743, + "name": "London", + "coord": { + "lon": -0.1258, + "lat": 51.5085 + }, + "country": "GB", + "population": 0, + "timezone": 3600 + }, + "cod": "200", + "message": 0.7809187, + "cnt": 1, + "list": [ + { + "dt": 1568977200, + "sunrise": 1568958164, + "sunset": 1569002733, + "temp": { + "day": 293.79, + "min": 288.85, + "max": 294.47, + "night": 288.85, + "eve": 290.44, + "morn": 293.79 + }, + "feels_like": { + "day": 278.87, + "night": 282.73, + "eve": 281.92, + "morn": 278.87 + }, + "pressure": 1025.04, + "humidity": 42, + "weather": [ + { + "id": 800, + "main": "Clear", + "description": "sky is clear", + "icon": "01d" + } + ], + "speed": 4.66, + "deg": 102, + "gust": 5.3, + "clouds": 0, + "pop": 0.24, + "rain": 22.2, + "snow": 24.2 + } + ] + } + """; + + final Forecast forecast = new DailyForecastResponseMapper(UnitSystem.METRIC).mapToForecast(jsonResponse); + assertNotNull(forecast); + + final Location location = forecast.getLocation(); + assertNotNull(location); + assertEquals(Coordinates.of(51.5085, -0.1258), location.getCoordinate()); + assertEquals(2643743, location.getId()); + assertEquals("London", location.getName()); + assertEquals("GB", location.getCountryCode()); + assertEquals(0, location.getPopulation()); + assertEquals(ZoneOffset.ofTotalSeconds(3600), location.getZoneOffset()); + + assertEquals(1, forecast.getWeatherForecasts().size()); + final WeatherForecast weatherForecast = forecast.getWeatherForecasts().get(0); + assertEquals(TestMappingUtils.parseDateTime(1568977200), weatherForecast.getForecastTime()); + // TODO: Does the API provide the sunrise and sunset info??? It is not officially described in the API but present in the example. + assertEquals(TestMappingUtils.parseDateTime(1568958164), weatherForecast.getSunriseTime()); + assertEquals(TestMappingUtils.parseDateTime(1569002733), weatherForecast.getSunsetTime()); + + final Temperature temperature = weatherForecast.getTemperature(); + assertEquals(293.79, temperature.getDay()); + assertEquals(288.85, temperature.getMin()); + assertEquals(294.47, temperature.getMax()); + assertEquals(288.85, temperature.getNight()); + assertEquals(290.44, temperature.getEve()); + assertEquals(293.79, temperature.getMorning()); + assertEquals(278.87, temperature.getDayFeelsLike()); + assertEquals(282.73, temperature.getNightFeelsLike()); + assertEquals(281.92, temperature.getEveFeelsLike()); + assertEquals(278.87, temperature.getMorningFeelsLike()); + + final AtmosphericPressure pressure = weatherForecast.getAtmosphericPressure(); + assertEquals(1025.04, pressure.getSeaLevelValue()); + + final Humidity humidity = weatherForecast.getHumidity(); + assertEquals(42, humidity.getValue()); + + final Wind wind = weatherForecast.getWind(); + assertEquals(4.66, wind.getSpeed()); + assertEquals(102, wind.getDegrees()); + assertEquals(5.3, wind.getGust()); + + final Clouds clouds = weatherForecast.getClouds(); + assertEquals(0, clouds.getValue()); + + assertEquals(1, weatherForecast.getWeatherStates().size()); + final WeatherState weatherState = weatherForecast.getWeatherStates().get(0); + assertEquals(800, weatherState.getId()); + assertEquals("Clear", weatherState.getName()); + assertEquals("sky is clear", weatherState.getDescription()); + assertEquals("01d", weatherState.getIconId()); + + final Rain rain = weatherForecast.getRain(); + assertEquals(22.2, rain.getLevel()); + + final Snow snow = weatherForecast.getSnow(); + assertEquals(24.2, snow.getLevel()); + + assertEquals(0.24, weatherForecast.getProbabilityOfPrecipitation()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapperTest.java b/src/test/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapperTest.java index cf4d1b0..04a82c3 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapperTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/mapper/GeocodingResponseMapperTest.java @@ -29,7 +29,8 @@ import org.junit.jupiter.api.Test; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; class GeocodingResponseMapperTest { diff --git a/src/test/java/com/github/prominence/openweathermap/api/mapper/HourlyForecastResponseMapperTest.java b/src/test/java/com/github/prominence/openweathermap/api/mapper/HourlyForecastResponseMapperTest.java index 4aeba61..63f34f9 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/mapper/HourlyForecastResponseMapperTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/mapper/HourlyForecastResponseMapperTest.java @@ -32,7 +32,8 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.TimeZone; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; class HourlyForecastResponseMapperTest { diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetailsUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetailsUnitTest.java index d6ac6fe..d98661e 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetailsUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetailsUnitTest.java @@ -30,7 +30,8 @@ import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class AirPollutionDetailsUnitTest { @Test diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/WindUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/WindUnitTest.java index d357510..59c9588 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/WindUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/WindUnitTest.java @@ -22,8 +22,8 @@ package com.github.prominence.openweathermap.api.model.onecall; -import org.junit.jupiter.api.Test; import com.github.prominence.openweathermap.api.model.Wind; +import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyTemperatureUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyTemperatureUnitTest.java index 93dfb91..f083393 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyTemperatureUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyTemperatureUnitTest.java @@ -24,7 +24,8 @@ package com.github.prominence.openweathermap.api.model.onecall.current; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class DailyTemperatureUnitTest { diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyUnitTest.java index 8a1f676..732f7ec 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/DailyUnitTest.java @@ -25,8 +25,8 @@ package com.github.prominence.openweathermap.api.model.onecall.current; import com.github.prominence.openweathermap.api.model.Clouds; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; -import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; import com.github.prominence.openweathermap.api.model.Wind; +import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/HourlyUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/HourlyUnitTest.java index 938ef9c..cb8d1cd 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/HourlyUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/current/HourlyUnitTest.java @@ -26,7 +26,10 @@ import com.github.prominence.openweathermap.api.model.Clouds; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; import com.github.prominence.openweathermap.api.model.Wind; -import com.github.prominence.openweathermap.api.model.onecall.*; +import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; +import com.github.prominence.openweathermap.api.model.onecall.Rain; +import com.github.prominence.openweathermap.api.model.onecall.Snow; +import com.github.prominence.openweathermap.api.model.onecall.Temperature; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeatherUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeatherUnitTest.java index afbb747..2be1df4 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeatherUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HistoricalWeatherUnitTest.java @@ -26,7 +26,10 @@ import com.github.prominence.openweathermap.api.model.Clouds; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; import com.github.prominence.openweathermap.api.model.Wind; -import com.github.prominence.openweathermap.api.model.onecall.*; +import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; +import com.github.prominence.openweathermap.api.model.onecall.Rain; +import com.github.prominence.openweathermap.api.model.onecall.Snow; +import com.github.prominence.openweathermap.api.model.onecall.Temperature; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistoricalUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistoricalUnitTest.java index bf8832b..57617fb 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistoricalUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/onecall/historical/HourlyHistoricalUnitTest.java @@ -26,7 +26,10 @@ import com.github.prominence.openweathermap.api.model.Clouds; import com.github.prominence.openweathermap.api.model.Humidity; import com.github.prominence.openweathermap.api.model.WeatherState; import com.github.prominence.openweathermap.api.model.Wind; -import com.github.prominence.openweathermap.api.model.onecall.*; +import com.github.prominence.openweathermap.api.model.onecall.AtmosphericPressure; +import com.github.prominence.openweathermap.api.model.onecall.Rain; +import com.github.prominence.openweathermap.api.model.onecall.Snow; +import com.github.prominence.openweathermap.api.model.onecall.Temperature; import org.junit.jupiter.api.Test; import java.time.LocalDateTime; diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/weather/WeatherUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/weather/WeatherUnitTest.java index e2df79f..0c9f9e1 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/model/weather/WeatherUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/model/weather/WeatherUnitTest.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test; import java.time.LocalDateTime; import java.util.Collections; -import java.util.List; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/com/github/prominence/openweathermap/api/utils/TestMappingUtils.java b/src/test/java/com/github/prominence/openweathermap/api/utils/TestMappingUtils.java new file mode 100644 index 0000000..478dbef --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/utils/TestMappingUtils.java @@ -0,0 +1,12 @@ +package com.github.prominence.openweathermap.api.utils; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.TimeZone; + +public class TestMappingUtils { + + public static LocalDateTime parseDateTime(int seconds) { + return LocalDateTime.ofInstant(Instant.ofEpochSecond(seconds), TimeZone.getDefault().toZoneId()); + } +}