diff --git a/README.md b/README.md index cff7f90..9584326 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ Free: * Current weather data * 5 day / 3-hour forecast * One Call API +* Air pollution ### Will be implemented later: Free: -* Air pollution * Geocoding API * Weather Stations * Weather Triggers @@ -26,14 +26,14 @@ Paid: com.github.prominence openweathermap-api - 2.1.1 + 2.2.0 ``` ### Gradle coordinates: ```groovy -compile('com.github.prominence:openweathermap-api:2.1.1') +compile('com.github.prominence:openweathermap-api:2.2.0') ``` ### Documentation @@ -44,6 +44,7 @@ compile('com.github.prominence:openweathermap-api:2.1.1') * [OpenWeatherMap Java API - 2.0.1](docs/Release_2.0.1.md) * [OpenWeatherMap Java API - 2.1.0](docs/Release_2.1.0.md) * [OpenWeatherMap Java API - 2.1.1](docs/Release_2.1.1.md) +* [OpenWeatherMap Java API - 2.2.0](docs/Release_2.2.0.md) * [OpenWeatherMap Java API - SNAPSHOT](docs/SNAPSHOT.md) ### License diff --git a/docs/Release_2.2.0.md b/docs/Release_2.2.0.md new file mode 100644 index 0000000..5d4495f --- /dev/null +++ b/docs/Release_2.2.0.md @@ -0,0 +1,538 @@ +### Implemented features: +* Current weather data +* 5 day / 3-hour forecast +* One Call API +* Air Pollution + +### Maven coordinates: + +```xml + + com.github.prominence + openweathermap-api + 2.2.0 + +``` + +### Gradle coordinates: + +```groovy +compile('com.github.prominence:openweathermap-api:2.2.0') +``` + +### How to use: + +Firstly, you need to create the instance of `OpenWeatherMapClient` class: +```java +OpenWeatherMapClient openWeatherClient = new OpenWeatherMapClient(API_TOKEN); +``` +where `API_TOKEN` is your token([you can get it here](https://home.openweathermap.org/api_keys)) as `String`. + +Currently, available APIs are: +* `currentWeather()` +* `forecast5Day3HourStep()` +* `oneCall()` +* `airPollution()` + +Default(more or less) customization points: +```java +... +// response language +.language(Language.RUSSIAN) +... +// response units of measure +.unitSystem(UnitSystem.IMPERIAL) +... +``` + +Available output forms: +* `asJava()` +* `asJSON()` + +Additional output forms, available for several APIs: +* `asXML()` +* `asHTML()` + +_All response forms can be in **sync** and **async** variants._ + +#### Current weather data +Examples: +```java +final String weatherJson = openWeatherClient + .currentWeather() + .single() + .byCityName("Minsk") + .language(Language.RUSSIAN) + .unitSystem(UnitSystem.IMPERIAL) + .retrieve() + .asJSON(); +``` + +```java +final Weather weather = openWeatherClient + .currentWeather() + .single() + .byCityName("Minsk") + .language(Language.RUSSIAN) + .unitSystem(UnitSystem.METRIC) + .retrieve() + .asJava(); +``` + +```java +final List weatherList = openWeatherClient + .currentWeather() + .multiple() + .byCitiesInCycle(Coordinate.of(55.5, 37.5)) + .language(Language.GERMAN) + .unitSystem(UnitSystem.IMPERIAL) + .retrieve() + .asJava(); +``` + +```java +final CompletableFuture weatherXmlFuture = openWeatherClient + .currentWeather() + .single() + .byZipCodeAndCountry("220015", "by") + .language(Language.RUSSIAN) + .unitSystem(UnitSystem.METRIC) + .retrieveAsync() + .asXML(); +``` + +You are able to set preferable options(via chain methods) and execute appropriate request. + +`com.github.prominence.openweathermap.api.model.weather.Weather`'s useful public methods(setters are not listed): + +| Method | Description | +|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `getCalculationTime()` | Returns `LocalDateTime` object with data calculation time. | +| `getWeatherState()` | Returns `WeatherState` object with basic weather state information. | +| `getTemperature()` | Returns `Temperature` instance that contains information about temperature. Available fields: `value`, `maxTemperature`, `minTemperature`, `feelsLike` and `unit`. | +| `getAtmosphericPressure()`| Returns `AtmosphericPressure` instance that contains information about atmospheric pressure. Available fields: `value`, `seaLevelValue`, `groundLevelValue` and `unit`. | +| `getHumidity()` | Returns `Humidity` instance that contains humidity percentage information. | +| `getWind()` | Returns `Wind` instance that contains wind information: `speed`, `degrees`, `gust` and `unit`. | +| `getRain()` | Returns `Rain` instance that contains information about rain volume for the last one hour and/or the last 3 hours. Can be absent in case of no data. | +| `getSnow()` | Returns `Snow` instance that contains information about snow volume for the last one hour and/or the last 3 hours. Can be absent in case of no data. | +| `getClouds()` | Returns `Clouds` instance that contains information about cloudiness percentage. | +| `getLocation()` | Returns `Location` object. Available fields: `id`, `name`, `countryCode`, `sunrise` and `sunset` time, `zoneOffset` and `coordinate`. | +| `toString()` | Returns informative string for the whole available weather information. | + +`toString()` output example: +``` +Location: Minsk(BY), Weather: clear sky, -4.22 ℃, 1020.0 hPa, Clouds: 0% +``` + +#### 5 day / 3-hour forecast +Examples: +```java +final Forecast forecast = openWeatherClient + .forecast5Day3HourStep() + .byCityName("Minsk") + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .count(15) + .retrieve() + .asJava(); +``` + +```java +final String forecastJson = getClient() + .forecast5Day3HourStep() + .byCityName("New York", "NY", "US") + .language(Language.SPANISH) + .unitSystem(UnitSystem.IMPERIAL) + .count(15) + .retrieve() + .asJSON(); +``` + +```java +CompletableFuture forecastFuture = getClient() + .forecast5Day3HourStep() + .byCityId(350001514) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .count(15) + .retrieveAsync() + .asXML(); +``` + +```java +final String forecastXml = getClient() + .forecast5Day3HourStep() + .byZipCodeInUSA("10005") + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieve() + .asXML(); +``` + +You are able to set preferable options(via chain methods) and execute appropriate request. + +`com.github.prominence.openweathermap.api.model.forecast.free.Forecast`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------| +| `getLocation()` | Returns `Location` object. Available fields: `id`, `name`, `countryCode`, `sunrise` and `sunset` time, `zoneOffset`, `coordinate` and `population`. | +| `getWeatherForecasts()` | Returns list of `WeatherForecast` objects with forecast information. | +| `toString()` | Returns informative string for the whole available forecast information. | + +`toString()` output example: +``` +A forecast for Minsk with 15 timestamps. +``` + +`com.github.prominence.openweathermap.api.model.forecast.WeatherForecast`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with weather forecast time. | +| `getWeatherState()` | Returns `WeatherState` object with basic weather state information. | +| `getTemperature()` | Returns `Temperature` instance that contains information about temperature. Available fields: `value`, `maxTemperature`, `minTemperature`, `feelsLike` and `unit`. | +| `getAtmosphericPressure()` | Returns `AtmosphericPressure` instance that contains information about atmospheric pressure. Available fields: `value`, `seaLevelValue`, `groundLevelValue` and `unit`. | +| `getHumidity()` | Returns `Humidity` instance that contains humidity percentage information. | +| `getWind()` | Returns `Wind` instance that contains wind information: `speed`, `degrees` and `unit`. | +| `getRain()` | Returns `Rain` instance that contains information about rain volume for the last 3 hours. Can be absent in case of no data. | +| `getSnow()` | Returns `Snow` instance that contains information about snow volume for the last 3 hours. Can be absent in case of no data. | +| `getClouds()` | Returns `Clouds` instance that contains information about cloudiness percentage. | +| `getForecastTimeISO()` | Returns String with time of data forecasted, ISO, UTC. | +| `getDayTime()` | Returns enumerations representing the part of day(day, night). | +| `toString()` | Returns informative string for the forecast of particular timestamp. | + +#### One Call API +Examples: +```java +final CurrentWeatherData currentWeatherData = openWeatherClient + .oneCall() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieve() + .asJava(); +``` + +```java +final CurrentWeatherData currentWeatherData = openWeatherClient + .oneCall() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .exclude(OneCallResultOptions.CURRENT, OneCallResultOptions.MINUTELY) + .retrieve() + .asJava(); +``` + +```java +final CompletableFuture currentWeatherDataFuture = openWeatherClient + .oneCall() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieveAsync() + .asJava(); +``` + +```java +final String responseJson = openWeatherClient + .oneCall() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieve() + .asJSON(); +``` + +```java +final HistoricalWeatherData historicalWeatherData = openWeatherClient + .oneCall() + .historical() + .byCoordinateAndTimestamp(Coordinate.of(60.99, 30.9), LocalDateTime.now().minusDays(5).toEpochSecond(ZoneOffset.UTC)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieve() + .asJava(); +``` + +```java +final String responseJson = openWeatherClient + .oneCall() + .historical() + .byCoordinateAndTimestamp(Coordinate.of(60.99, 30.9), LocalDateTime.now().minusDays(5).toEpochSecond(ZoneOffset.UTC)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieve() + .asJSON(); +``` + +```java +final CompletableFuture historicalWeatherDataFuture = openWeatherClient + .oneCall() + .historical() + .byCoordinateAndTimestamp(Coordinate.of(60.99, 30.9), LocalDateTime.now().minusDays(5).toEpochSecond(ZoneOffset.UTC)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieveAsync() + .asJava(); +``` + +```java +final CompletableFuture responseJsonFuture = openWeatherClient + .oneCall() + .historical() + .byCoordinateAndTimestamp(Coordinate.of(60.99, 30.9), LocalDateTime.now().minusDays(5).toEpochSecond(ZoneOffset.UTC)) + .language(Language.ENGLISH) + .unitSystem(UnitSystem.METRIC) + .retrieveAsync() + .asJSON(); +``` + +You are able to set preferable options(via chain methods) and execute appropriate request. + +`com.github.prominence.openweathermap.api.model.onecall.current.CurrentWeatherData`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|--------------------------------------------------------------------------------| +| `getCoordinate()` | Returns `Coordinate` object. Available fields: `latitude`, `longitude`. | +| `getTimezone()` | Returns location timezone object. | +| `getTimezoneOffset()` | Returns zone offset. | +| `getCurrent()` | Returns `Current` object with current weather state if available. | +| `getMinutelyList()` | Returns list of `Minutely` objects if available. | +| `getHourlyList()` | Returns list of `Houlry` objects if available. | +| `getDailyList()` | Returns list of `Daily` objects if available. | +| `getAlerts()` | Returns list of `Alert` objects if available. | + +`com.github.prominence.openweathermap.api.model.onecall.Current`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|-------------------------------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with weather forecast time. | +| `getSunriseTime()` | Returns `LocalDateTime` object with sunrise time. | +| `getSunsetTime()` | Returns `LocalDateTime` object with sunset time. | +| `getWeatherState()` | Returns `WeatherState` object with basic weather state information. | +| `getTemperature()` | Returns `Temperature` object. Available fields: `value`, `feelsLike`, `dewPoint` and `unit`. | +| `getAtmosphericPressure()` | Returns `AtmosphericPressure` object. Available fields: `seaLevelValue`. | +| `getHumidity()` | Returns `Humidity` object. Available fields: `value` and `unit`. | +| `getClouds()` | Returns `Clouds` object. Available fields: `value` and `unit`. | +| `getUvIndex()` | Returns UV index value. | +| `getVisibilityInMetres()` | Returns visibility in metres. | +| `getWind()` | Returns `Wind` object. Available fields: `speed`, `degrees`, `gust` and `unit`. | +| `getRain()` | Returns `Rain` object. Available fields: `oneHourLevel` and `unit`. | +| `getSnow()` | Returns `Snow` object. Available fields: `oneHourLevel` and `unit`. | + +`com.github.prominence.openweathermap.api.model.onecall.current.Minutely`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|---------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with weather forecast time. | +| `getPrecipitationVolume()` | Returns precipitation volume. | + +`com.github.prominence.openweathermap.api.model.onecall.current.Hourly`'s useful public methods(setters are not listed): + +| Method | Description | +|-----------------------------------------------|---------------------------------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with weather forecast time. | +| `getWeatherState()` | Returns `WeatherState` object with basic weather state information. | +| `getTemperature()` | Returns `Temperature` object. Available fields: `value`, `feelsLike`, `dewPoint` and `unit`. | +| `getAtmosphericPressure()` | Returns `AtmosphericPressure` object. Available fields: `seaLevelValue`. | +| `getHumidity()` | Returns `Humidity` object. Available fields: `value` and `unit`. | +| `getClouds()` | Returns `Clouds` object. Available fields: `value` and `unit`. | +| `getUvIndex()` | Returns UV index value. | +| `getVisibilityInMetres()` | Returns visibility in metres. | +| `getWind()` | Returns `Wind` object. Available fields: `speed`, `degrees`, `gust` and `unit`. | +| `getProbabilityOfPrecipitation()` | Returns probability of precipitation(not percentage). | +| `getProbabilityOfPrecipitationPercentage()` | Returns probability of precipitation percentage. | +| `getRain()` | Returns `Rain` object. Available fields: `oneHourLevel` and `unit`. | +| `getSnow()` | Returns `Snow` object. Available fields: `oneHourLevel` and `unit`. | + +`com.github.prominence.openweathermap.api.model.onecall.current.Daily`'s useful public methods(setters are not listed): + +| Method | Description | +|-----------------------------------------------|---------------------------------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with weather forecast time. | +| `getSunriseTime()` | Returns `LocalDateTime` object with sunrise time. | +| `getSunsetTime()` | Returns `LocalDateTime` object with sunset time. | +| `getWeatherState()` | Returns `WeatherState` object with basic weather state information. | +| `getTemperature()` | Returns `DailyTemperature` object. Available fields: `value`, `feelsLike`, `dewPoint` and `unit`. | +| `getAtmosphericPressure()` | Returns `AtmosphericPressure` object. Available fields: `seaLevelValue`. | +| `getHumidity()` | Returns `Humidity` object. Available fields: `value` and `unit`. | +| `getWind()` | Returns `Wind` object. Available fields: `speed`, `degrees`, `gust` and `unit`. | +| `getClouds()` | Returns `Clouds` object. Available fields: `value` and `unit`. | +| `getUvIndex()` | Returns UV index value. | +| `getProbabilityOfPrecipitation()` | Returns probability of precipitation(not percentage). | +| `getProbabilityOfPrecipitationPercentage()` | Returns probability of precipitation percentage. | +| `getRain()` | Returns `DailyRain` object. Available fields: `value`. | +| `getSnow()` | Returns `DailySnow` object. Available fields: `value`. | + +`com.github.prominence.openweathermap.api.model.onecall.current.Alert`'s useful public methods(setters are not listed): + +| Method | Description | +|------------------------------|--------------------------------------------------------| +| `getSenderName()` | Returns alert sender name. | +| `getEventName()` | Returns alert event name. | +| `getStartTime()` | Returns `LocalDateTime` when event should start. | +| `getEndTime()` | Returns `LocalDateTime` when event should end. | +| `getDescription()` | Returns alert description. | + +`com.github.prominence.openweathermap.api.model.onecall.historical.HistoricalWeatherData`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|-------------------------------------------------------------------------------| +| `getCoordinate()` | Returns `Coordinate` object. Available fields: `latitude`, `longitude`. | +| `getTimezone()` | Returns location timezone object. | +| `getTimezoneOffset()` | Returns zone offset. | +| `getHistoricalWeather()` | Returns `HistoricalWeather` object with historical weather state. | +| `getHourlyList()` | Returns list of `HourlyHistorical` objects. | + +`com.github.prominence.openweathermap.api.model.onecall.historical.HistoricalWeather`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|-------------------------------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with weather forecast time. | +| `getSunriseTime()` | Returns `LocalDateTime` object with sunrise time. | +| `getSunsetTime()` | Returns `LocalDateTime` object with sunset time. | +| `getWeatherState()` | Returns `WeatherState` object with basic weather state information. | +| `getTemperature()` | Returns `Temperature` object. Available fields: `value`, `feelsLike`, `dewPoint` and `unit`. | +| `getAtmosphericPressure()` | Returns `AtmosphericPressure` object. Available fields: `seaLevelValue`. | +| `getHumidity()` | Returns `Humidity` object. Available fields: `value` and `unit`. | +| `getClouds()` | Returns `Clouds` object. Available fields: `value` and `unit`. | +| `getUvIndex()` | Returns UV index value. | +| `getVisibilityInMetres()` | Returns visibility in metres. | +| `getWind()` | Returns `Wind` object. Available fields: `speed`, `degrees`, `gust` and `unit`. | +| `getRain()` | Returns `Rain` object. Available fields: `oneHourLevel` and `unit`. | +| `getSnow()` | Returns `Snow` object. Available fields: `oneHourLevel` and `unit`. | + +`com.github.prominence.openweathermap.api.model.onecall.historical.HourlyHistorical`'s useful public methods(setters are not listed): + +| Method | Description | +|-----------------------------------|---------------------------------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with weather forecast time. | +| `getWeatherState()` | Returns `WeatherState` object with basic weather state information. | +| `getTemperature()` | Returns `Temperature` object. Available fields: `value`, `feelsLike`, `dewPoint` and `unit`. | +| `getAtmosphericPressure()` | Returns `AtmosphericPressure` object. Available fields: `seaLevelValue`. | +| `getHumidity()` | Returns `Humidity` object. Available fields: `value` and `unit`. | +| `getClouds()` | Returns `Clouds` object. Available fields: `value` and `unit`. | +| `getVisibilityInMetres()` | Returns visibility in metres. | +| `getWind()` | Returns `Wind` object. Available fields: `speed`, `degrees`, `gust` and `unit`. | +| `getRain()` | Returns `Rain` object. Available fields: `oneHourLevel` and `unit`. | +| `getSnow()` | Returns `Snow` object. Available fields: `oneHourLevel` and `unit`. | + +#### Air Pollution API +Examples: +```java +final AirPollutionDetails airPollutionDetails = openWeatherClient + .airPollution() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieve() + .asJava(); +``` + +```java +final AirPollutionDetails airPollutionDetails = openWeatherClient + .airPollution() + .historical() + .byCoordinateAndPeriod(Coordinate.of(53.54, 27.34), 1606223802, 1606482999) + .retrieve() + .asJava(); +``` + +`com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|---------------------------------------------------------------------------| +| `getCoordinate()` | Returns `Coordinate` object. Available fields: `latitude`, `longitude`. | +| `getAirPollutionRecords()` | Returns list of `AirPollutionRecord` objects. | + +`com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionRecord`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|---------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with air pollution forecast time. | +| `getAirQualityIndex()` | Returns `AirQualityIndex` object. | +| `getCO()` | Returns carbon monoxide concentration value in μg/m^3.s. | +| `getCarbonMonoxide()` | An alias for `getCO()` method. | +| `getNO()` | Returns nitrogen monoxide concentration value in μg/m^3. | +| `getNitrogenMonoxide()` | An alias for `getNO()` method. | +| `getNO2()` | Returns nitrogen dioxide concentration value in μg/m^3. | +| `getNitrogenDioxide()` | An alias for `getNO2()` method. | +| `getO3()` | Returns ozone concentration value in μg/m^3. | +| `getOzone()` | An alias for `getO3()` method. | +| `getSO2()` | Returns sulphur dioxide concentration value in μg/m^3. | +| `getSulphurDioxide()` | An alias for `getSO2()` method. | +| `getPM2_5()` | Returns fine particles matter concentration value in μg/m^3. | +| `getFineParticlesMatter()` | An alias for `getPM2_5()` method. | +| `getPM10()` | Returns coarse particulate matter concentration value in μg/m^3. | +| `getCoarseParticulateMatter()`| An alias for `getPM10()` method. | +| `getNH3()` | Returns ammonia concentration value in μg/m^3. | +| `getAmmonia()` | An alias for `getNH3()` method. | + +### Constants and options + +#### Language +| Constant | Description | +|-----------------------------------|-------------------------------| +| Language.AFRIKAANS | Afrikaans language. | +| Language.ALBANIAN | ALBANIAN language. | +| Language.ARABIC | Arabic language. | +| Language.AZERBAIJANI | Azerbaijani language. | +| Language.BULGARIAN | Bulgarian language. | +| Language.CATALAN | Catalan language. | +| Language.CZECH | Czech language. | +| Language.DANISH | Danish language. | +| Language.GERMAN | German language. | +| Language.GREEK | Greek language. | +| Language.ENGLISH | English language. | +| Language.BASQUE | Basque language. | +| Language.PERSIAN | Persian (Farsi) language. | +| Language.FINNISH | Finnish language. | +| Language.FRENCH | French language. | +| Language.GALICIAN | Galician language. | +| Language.HEBREW | Hebrew language. | +| Language.HINDI | Hindi language. | +| Language.CROATIAN | Croatian language. | +| Language.HUNGARIAN | Hungarian language. | +| Language.INDONESIAN | Indonesian language. | +| Language.ITALIAN | Italian language. | +| Language.JAPANESE | Japanese language. | +| Language.KOREAN | Korean language. | +| Language.LATVIAN | Latvian language. | +| Language.LITHUANIAN | Lithuanian language. | +| Language.MACEDONIAN | Macedonian language. | +| Language.NORWEGIAN | Norwegian language. | +| Language.DUTCH | Dutch language. | +| Language.POLISH | Polish language. | +| Language.PORTUGUESE | Portuguese language. | +| Language.PORTUGUES_BRAZIL | Português Brasil language. | +| Language.ROMANIAN | Romanian language. | +| Language.RUSSIAN | Russian language. | +| Language.SWEDISH | Swedish language. | +| Language.SLOVAK | Slovak language. | +| Language.SLOVENIAN | Slovenian language. | +| Language.SPANISH | Spanish language. | +| Language.SERBIAN | Serbian language. | +| Language.THAI | Thai language. | +| Language.TURKISH | Turkish language. | +| Language.UKRANIAN | Ukrainian language. | +| Language.VIETNAMESE | Vietnamese language. | +| Language.CHINESE_SIMPLIFIED | Chinese Simplified language. | +| Language.CHINESE_TRADITIONAL | Chinese Traditional language. | +| Language.ZULU | Zulu language. | + +#### Unit +| Constant | Description | +|----------------------|------------------------------------------------| +| Unit.METRIC_SYSTEM | Celsius, meter/sec, hPa, mm(rain, snow). | +| Unit.IMPERIAL_SYSTEM | Fahrenheit, miles/hour, hPa, mm(rain, snow). | +| Unit.STANDARD_SYSTEM | Kelvin, meter/sec, hPa, mm(rain, snow). | + +### Dependencies +* com.fasterxml.jackson.core:jackson-databind:2.12.2 +* org.slf4j:slf4j-api:1.7.30 (*compile*) +* org.junit.jupiter:junit-jupiter-engine:5.7.1 (*test*) +* org.junit.platform:junit-platform-runner:1.7.1 (*test*) \ No newline at end of file diff --git a/docs/SNAPSHOT.md b/docs/SNAPSHOT.md index b67018c..328fb27 100644 --- a/docs/SNAPSHOT.md +++ b/docs/SNAPSHOT.md @@ -2,6 +2,7 @@ * Current weather data * 5 day / 3-hour forecast * One Call API +* Air Pollution ### Maven coordinates: @@ -31,6 +32,7 @@ Currently, available APIs are: * `currentWeather()` * `forecast5Day3HourStep()` * `oneCall()` +* `airPollution()` Default(more or less) customization points: ```java @@ -420,6 +422,56 @@ You are able to set preferable options(via chain methods) and execute appropriat | `getRain()` | Returns `Rain` object. Available fields: `oneHourLevel` and `unit`. | | `getSnow()` | Returns `Snow` object. Available fields: `oneHourLevel` and `unit`. | +#### Air Pollution API +Examples: +```java +final AirPollutionDetails airPollutionDetails = openWeatherClient + .airPollution() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieve() + .asJava(); +``` + +```java +final AirPollutionDetails airPollutionDetails = openWeatherClient + .airPollution() + .historical() + .byCoordinateAndPeriod(Coordinate.of(53.54, 27.34), 1606223802, 1606482999) + .retrieve() + .asJava(); +``` + +`com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|---------------------------------------------------------------------------| +| `getCoordinate()` | Returns `Coordinate` object. Available fields: `latitude`, `longitude`. | +| `getAirPollutionRecords()` | Returns list of `AirPollutionRecord` objects. | + +`com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionRecord`'s useful public methods(setters are not listed): + +| Method | Description | +|-------------------------------|---------------------------------------------------------------------------| +| `getForecastTime()` | Returns `LocalDateTime` object with air pollution forecast time. | +| `getAirQualityIndex()` | Returns `AirQualityIndex` object. | +| `getCO()` | Returns carbon monoxide concentration value in μg/m^3.s. | +| `getCarbonMonoxide()` | An alias for `getCO()` method. | +| `getNO()` | Returns nitrogen monoxide concentration value in μg/m^3. | +| `getNitrogenMonoxide()` | An alias for `getNO()` method. | +| `getNO2()` | Returns nitrogen dioxide concentration value in μg/m^3. | +| `getNitrogenDioxide()` | An alias for `getNO2()` method. | +| `getO3()` | Returns ozone concentration value in μg/m^3. | +| `getOzone()` | An alias for `getO3()` method. | +| `getSO2()` | Returns sulphur dioxide concentration value in μg/m^3. | +| `getSulphurDioxide()` | An alias for `getSO2()` method. | +| `getPM2_5()` | Returns fine particles matter concentration value in μg/m^3. | +| `getFineParticlesMatter()` | An alias for `getPM2_5()` method. | +| `getPM10()` | Returns coarse particulate matter concentration value in μg/m^3. | +| `getCoarseParticulateMatter()`| An alias for `getPM10()` method. | +| `getNH3()` | Returns ammonia concentration value in μg/m^3. | +| `getAmmonia()` | An alias for `getNH3()` method. | + ### Constants and options #### Language diff --git a/pom.xml b/pom.xml index dd4a389..168b4f4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.prominence openweathermap-api - 2.1.1 + 2.2.0 jar Java OpenWeatherMap API 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 5579c4a..145be6d 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/OpenWeatherMapClient.java +++ b/src/main/java/com/github/prominence/openweathermap/api/OpenWeatherMapClient.java @@ -23,6 +23,8 @@ package com.github.prominence.openweathermap.api; import com.github.prominence.openweathermap.api.annotation.SubscriptionAvailability; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequester; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequesterImpl; import com.github.prominence.openweathermap.api.request.forecast.free.FiveDayThreeHourStepForecastRequester; import com.github.prominence.openweathermap.api.request.forecast.free.FiveDayThreeHourStepForecastRequesterImpl; import com.github.prominence.openweathermap.api.request.onecall.OneCallWeatherRequester; @@ -74,4 +76,14 @@ public class OpenWeatherMapClient { public OneCallWeatherRequester oneCall() { return new OneCallWeatherRequesterImpl(apiKey); } + + /** + * Air Pollution API. + * Air Pollution API provides current, forecast and historical air pollution data for any coordinates on the globe. + * @return requester for air pollution information retrieval. + */ + @SubscriptionAvailability(plans = ALL) + public AirPollutionRequester airPollution() { + return new AirPollutionRequesterImpl(apiKey); + } } diff --git a/src/main/java/com/github/prominence/openweathermap/api/enums/AirQualityIndex.java b/src/main/java/com/github/prominence/openweathermap/api/enums/AirQualityIndex.java new file mode 100644 index 0000000..e90d97c --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/enums/AirQualityIndex.java @@ -0,0 +1,80 @@ +/* + * + * * 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.enums; + +import java.util.Arrays; +import java.util.Optional; + +/** + * The enum Air quality index. + */ +public enum AirQualityIndex { + /** + * Good air quality index. + */ + GOOD(1), + /** + * Fair air quality index. + */ + FAIR(2), + /** + * Moderate air quality index. + */ + MODERATE(3), + /** + * Poor air quality index. + */ + POOR(4), + /** + * Very poor air quality index. + */ + VERY_POOR(5); + + private final int value; + + AirQualityIndex(int index) { + this.value = index; + } + + /** + * Gets value. + * + * @return the value + */ + public int getValue() { + return value; + } + + /** + * Gets by index. + * + * @param index the index + * @return the by index + */ + public static AirQualityIndex getByIndex(int index) { + final Optional optionalAirQualityIndex = Arrays.stream(values()).filter(airQualityIndex -> airQualityIndex.getValue() == index).findFirst(); + return optionalAirQualityIndex.orElse(null); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetails.java b/src/main/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetails.java new file mode 100644 index 0000000..c4e7112 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetails.java @@ -0,0 +1,87 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.model.Coordinate; + +import java.util.List; +import java.util.Objects; + +/** + * The type Air pollution. + */ +public class AirPollutionDetails { + private Coordinate coordinate; + private List airPollutionRecords; + + /** + * Gets coordinate. + * + * @return the coordinate + */ + public Coordinate getCoordinate() { + return coordinate; + } + + /** + * Sets coordinate. + * + * @param coordinate the coordinate + */ + public void setCoordinate(Coordinate coordinate) { + this.coordinate = coordinate; + } + + /** + * Gets air pollution details. + * + * @return the air pollution details + */ + public List getAirPollutionRecords() { + return airPollutionRecords; + } + + /** + * Sets air pollution details. + * + * @param airPollutionRecords the air pollution details + */ + public void setAirPollutionRecords(List airPollutionRecords) { + this.airPollutionRecords = airPollutionRecords; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AirPollutionDetails that = (AirPollutionDetails) o; + return Objects.equals(coordinate, that.coordinate) && Objects.equals(airPollutionRecords, that.airPollutionRecords); + } + + @Override + public int hashCode() { + return Objects.hash(coordinate, airPollutionRecords); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionRecord.java b/src/main/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionRecord.java new file mode 100644 index 0000000..7ced2be --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionRecord.java @@ -0,0 +1,376 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.enums.AirQualityIndex; + +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * The type Air pollution record. + */ +public class AirPollutionRecord { + private LocalDateTime forecastTime; + private AirQualityIndex airQualityIndex; + + private Double CO; + private Double NO; + private Double NO2; + private Double O3; + private Double SO2; + private Double PM2_5; + private Double PM10; + private Double NH3; + + /** + * 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; + } + + /** + * Gets air quality index. + * + * @return the air quality index + */ + public AirQualityIndex getAirQualityIndex() { + return airQualityIndex; + } + + /** + * Sets air quality index. + * + * @param airQualityIndex the air quality index + */ + public void setAirQualityIndex(AirQualityIndex airQualityIndex) { + this.airQualityIndex = airQualityIndex; + } + + /** + * Gets carbon monoxide concentration value in μg/m^3. + * + * @return the carbon monoxide value + */ + public Double getCO() { + return CO; + } + + /** + * Gets carbon monoxide concentration value in μg/m^3. + * + * @return the carbon monoxide value + */ + public Double getCarbonMonoxide() { + return getCO(); + } + + /** + * Sets carbon monoxide concentration value in μg/m^3. + * + * @param CO the carbon monoxide value + */ + public void setCO(Double CO) { + this.CO = CO; + } + + /** + * Gets nitrogen monoxide concentration value in μg/m^3. + * + * @return the nitrogen monoxide value + */ + public Double getNO() { + return NO; + } + + /** + * Gets nitrogen monoxide concentration value in μg/m^3. + * + * @return the nitrogen monoxide value + */ + public Double getNitrogenMonoxide() { + return getNO(); + } + + /** + * Sets nitrogen monoxide concentration value in μg/m^3. + * + * @param NO the nitrogen monoxide value + */ + public void setNO(Double NO) { + this.NO = NO; + } + + /** + * Gets nitrogen dioxide concentration value in μg/m^3. + * + * @return the nitrogen dioxide value + */ + public Double getNO2() { + return NO2; + } + + /** + * Gets nitrogen dioxide concentration value in μg/m^3. + * + * @return the nitrogen dioxide value + */ + public Double getNitrogenDioxide() { + return getNO2(); + } + + /** + * Sets nitrogen dioxide concentration value in μg/m^3. + * + * @param NO2 the nitrogen dioxide value + */ + public void setNO2(Double NO2) { + this.NO2 = NO2; + } + + /** + * Gets ozone concentration value in μg/m^3. + * + * @return the ozone value + */ + public Double getO3() { + return O3; + } + + /** + * Gets ozone concentration value in μg/m^3. + * + * @return the ozone value + */ + public Double getOzone() { + return getO3(); + } + + /** + * Sets ozone concentration value in μg/m^3. + * + * @param o3 the ozone value + */ + public void setO3(Double o3) { + O3 = o3; + } + + /** + * Gets sulphur dioxide concentration value in μg/m^3. + * + * @return the sulphur dioxide value + */ + public Double getSO2() { + return SO2; + } + + /** + * Gets sulphur dioxide concentration value in μg/m^3. + * + * @return the sulphur dioxide value + */ + public Double getSulphurDioxide() { + return getSO2(); + } + + /** + * Sets sulphur dioxide concentration value in μg/m^3. + * + * @param SO2 the sulphur dioxide value + */ + public void setSO2(Double SO2) { + this.SO2 = SO2; + } + + /** + * Gets fine particles matter concentration value in μg/m^3. + * + * @return the fine particles matter value + */ + public Double getPM2_5() { + return PM2_5; + } + + /** + * Gets fine particles matter concentration value in μg/m^3. + * + * @return the fine particles matter value + */ + public Double getFineParticlesMatter() { + return getPM2_5(); + } + + /** + * Sets fine particles matter concentration value in μg/m^3. + * + * @param PM2_5 the fine particles matter value + */ + public void setPM2_5(Double PM2_5) { + this.PM2_5 = PM2_5; + } + + /** + * Gets coarse particulate matter concentration value in μg/m^3. + * + * @return the coarse particulate matter value + */ + public Double getPM10() { + return PM10; + } + + /** + * Gets coarse particulate matter concentration value in μg/m^3. + * + * @return the coarse particulate matter value + */ + public Double getCoarseParticulateMatter() { + return getPM10(); + } + + /** + * Sets coarse particulate matter concentration value in μg/m^3. + * + * @param PM10 the coarse particulate matter value + */ + public void setPM10(Double PM10) { + this.PM10 = PM10; + } + + /** + * Gets ammonia concentration value in μg/m^3. + * + * @return the ammonia value + */ + public Double getNH3() { + return NH3; + } + + /** + * Gets ammonia concentration value in μg/m^3. + * + * @return the ammonia value + */ + public Double getAmmonia() { + return getNH3(); + } + + /** + * Sets ammonia concentration value in μg/m^3. + * + * @param NH3 the ammonia value + */ + public void setNH3(Double NH3) { + this.NH3 = NH3; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AirPollutionRecord that = (AirPollutionRecord) o; + return Objects.equals(forecastTime, that.forecastTime) && airQualityIndex == that.airQualityIndex && Objects.equals(CO, that.CO) && Objects.equals(NO, that.NO) && Objects.equals(NO2, that.NO2) && Objects.equals(O3, that.O3) && Objects.equals(SO2, that.SO2) && Objects.equals(PM2_5, that.PM2_5) && Objects.equals(PM10, that.PM10) && Objects.equals(NH3, that.NH3); + } + + @Override + public int hashCode() { + return Objects.hash(forecastTime, airQualityIndex, CO, NO, NO2, O3, SO2, PM2_5, PM10, NH3); + } + + @Override + public String toString() { + final StringBuilder stringBuilder = new StringBuilder() + .append("Air Pollution Record for ") + .append(forecastTime) + .append(", AQI=") + .append(airQualityIndex.name()) + .append("."); + final boolean anyConcentrationAvailable = Stream.of(CO, NO, NO2, O3, SO2, PM2_5, PM10, NH3).anyMatch(Objects::nonNull); + if (anyConcentrationAvailable) { + stringBuilder.append(" Concentrations:"); + if (CO != null) { + stringBuilder + .append(" CO(Carbon monoxide) = ") + .append(CO) + .append(" μg/m^3;"); + } + if (NO != null) { + stringBuilder + .append(" NO(Nitrogen monoxide) = ") + .append(NO) + .append(" μg/m^3;"); + } + if (NO2 != null) { + stringBuilder + .append(" NO2(Nitrogen dioxide) = ") + .append(NO2) + .append(" μg/m^3;"); + } + if (O3 != null) { + stringBuilder + .append(" O3(Ozone) = ") + .append(O3) + .append(" μg/m^3;"); + } + if (SO2 != null) { + stringBuilder + .append(" SO2(Sulphur dioxide) = ") + .append(SO2) + .append(" μg/m^3;"); + } + if (PM2_5 != null) { + stringBuilder + .append(" PM2.5(Fine particles matter) = ") + .append(PM2_5) + .append(" μg/m^3;"); + } + if (PM10 != null) { + stringBuilder + .append(" PM10(Coarse particulate matter) = ") + .append(PM10) + .append(" μg/m^3;"); + } + if (NH3 != null) { + stringBuilder + .append(" NH3(Ammonia) = ") + .append(NH3) + .append(" μg/m^3;"); + } + } + return stringBuilder.toString(); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionAsyncRequestTerminator.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionAsyncRequestTerminator.java new file mode 100644 index 0000000..608e60f --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionAsyncRequestTerminator.java @@ -0,0 +1,34 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails; +import com.github.prominence.openweathermap.api.request.AsyncRequestTerminator; + +/** + * The interface Current air pollution async request terminator. + */ +public interface AirPollutionAsyncRequestTerminator extends AsyncRequestTerminator { +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionAsyncRequestTerminatorImpl.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionAsyncRequestTerminatorImpl.java new file mode 100644 index 0000000..45ffcd2 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionAsyncRequestTerminatorImpl.java @@ -0,0 +1,61 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails; +import com.github.prominence.openweathermap.api.request.RequestUrlBuilder; +import com.github.prominence.openweathermap.api.utils.RequestUtils; + +import java.util.concurrent.CompletableFuture; + +/** + * The type Air pollution async request terminator. + */ +public class AirPollutionAsyncRequestTerminatorImpl implements AirPollutionAsyncRequestTerminator { + private final RequestUrlBuilder urlBuilder; + + /** + * Instantiates a new Air pollution async request terminator. + * + * @param urlBuilder the url builder + */ + public AirPollutionAsyncRequestTerminatorImpl(RequestUrlBuilder urlBuilder) { + this.urlBuilder = urlBuilder; + } + + @Override + public CompletableFuture asJava() { + return CompletableFuture.supplyAsync(() -> new AirPollutionResponseMapper().mapToAirPollution(getRawResponse())); + } + + @Override + public CompletableFuture asJSON() { + return CompletableFuture.supplyAsync(this::getRawResponse); + } + + private String getRawResponse() { + return RequestUtils.getResponse(urlBuilder.buildUrl()); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestCustomizer.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestCustomizer.java new file mode 100644 index 0000000..d4b35ef --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestCustomizer.java @@ -0,0 +1,44 @@ +/* + * + * * 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.air.pollution; + +/** + * The interface Current air pollution request customizer. + */ +public interface AirPollutionRequestCustomizer { + /** + * Retrieve current air pollution request terminator. + * + * @return the current air pollution request terminator + */ + AirPollutionRequestTerminator retrieve(); + + /** + * Retrieve async current air pollution async request terminator. + * + * @return the current air pollution async request terminator + */ + AirPollutionAsyncRequestTerminator retrieveAsync(); +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestCustomizerImpl.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestCustomizerImpl.java new file mode 100644 index 0000000..2c75b81 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestCustomizerImpl.java @@ -0,0 +1,45 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.request.RequestUrlBuilder; + +public class AirPollutionRequestCustomizerImpl implements AirPollutionRequestCustomizer { + private final RequestUrlBuilder urlBuilder; + + public AirPollutionRequestCustomizerImpl(RequestUrlBuilder urlBuilder) { + this.urlBuilder = urlBuilder; + } + + @Override + public AirPollutionRequestTerminator retrieve() { + return new AirPollutionRequestTerminatorImpl(urlBuilder); + } + + @Override + public AirPollutionAsyncRequestTerminator retrieveAsync() { + return new AirPollutionAsyncRequestTerminatorImpl(urlBuilder); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestTerminator.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestTerminator.java new file mode 100644 index 0000000..64f4870 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestTerminator.java @@ -0,0 +1,34 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails; +import com.github.prominence.openweathermap.api.request.RequestTerminator; + +/** + * The interface Current air pollution request terminator. + */ +public interface AirPollutionRequestTerminator extends RequestTerminator { +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestTerminatorImpl.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestTerminatorImpl.java new file mode 100644 index 0000000..aae8ac1 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequestTerminatorImpl.java @@ -0,0 +1,59 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails; +import com.github.prominence.openweathermap.api.request.RequestUrlBuilder; +import com.github.prominence.openweathermap.api.utils.RequestUtils; + +/** + * The type Air pollution request terminator. + */ +public class AirPollutionRequestTerminatorImpl implements AirPollutionRequestTerminator { + private final RequestUrlBuilder urlBuilder; + + /** + * Instantiates a new Air pollution request terminator. + * + * @param urlBuilder the url builder + */ + public AirPollutionRequestTerminatorImpl(RequestUrlBuilder urlBuilder) { + this.urlBuilder = urlBuilder; + } + + @Override + public AirPollutionDetails asJava() { + return new AirPollutionResponseMapper().mapToAirPollution(getRawResponse()); + } + + @Override + public String asJSON() { + return getRawResponse(); + } + + private String getRawResponse() { + return RequestUtils.getResponse(urlBuilder.buildUrl()); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequester.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequester.java new file mode 100644 index 0000000..e0f4ba3 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequester.java @@ -0,0 +1,53 @@ +/* + * 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.air.pollution; + +import com.github.prominence.openweathermap.api.request.air.pollution.current.CurrentAirPollutionRequester; +import com.github.prominence.openweathermap.api.request.air.pollution.forecast.ForecastAirPollutionRequester; +import com.github.prominence.openweathermap.api.request.air.pollution.historical.HistoricalAirPollutionRequester; + +/** + * The interface Air pollution requester. + */ +public interface AirPollutionRequester { + /** + * Current current air pollution requester. + * + * @return the current air pollution requester + */ + CurrentAirPollutionRequester current(); + + /** + * Forecast forecast air pollution requester. + * + * @return the forecast air pollution requester + */ + ForecastAirPollutionRequester forecast(); + + /** + * Historical historical air pollution requester. + * + * @return the historical air pollution requester + */ + HistoricalAirPollutionRequester historical(); +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequesterImpl.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequesterImpl.java new file mode 100644 index 0000000..0faa6b3 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionRequesterImpl.java @@ -0,0 +1,67 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.request.RequestUrlBuilder; +import com.github.prominence.openweathermap.api.request.air.pollution.current.CurrentAirPollutionRequester; +import com.github.prominence.openweathermap.api.request.air.pollution.current.CurrentAirPollutionRequesterImpl; +import com.github.prominence.openweathermap.api.request.air.pollution.forecast.ForecastAirPollutionRequester; +import com.github.prominence.openweathermap.api.request.air.pollution.forecast.ForecastAirPollutionRequesterImpl; +import com.github.prominence.openweathermap.api.request.air.pollution.historical.HistoricalAirPollutionRequester; +import com.github.prominence.openweathermap.api.request.air.pollution.historical.HistoricalAirPollutionRequesterImpl; + +/** + * The type Air pollution requester. + */ +public class AirPollutionRequesterImpl implements AirPollutionRequester { + private final RequestUrlBuilder urlBuilder; + + /** + * Instantiates a new Air pollution requester. + * + * @param apiKey the api key + */ + public AirPollutionRequesterImpl(String apiKey) { + urlBuilder = new RequestUrlBuilder(apiKey); + } + + @Override + public CurrentAirPollutionRequester current() { + urlBuilder.append("air_pollution"); + return new CurrentAirPollutionRequesterImpl(urlBuilder); + } + + @Override + public ForecastAirPollutionRequester forecast() { + urlBuilder.append("air_pollution/forecast"); + return new ForecastAirPollutionRequesterImpl(urlBuilder); + } + + @Override + public HistoricalAirPollutionRequester historical() { + urlBuilder.append("air_pollution/history"); + return new HistoricalAirPollutionRequesterImpl(urlBuilder); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionResponseMapper.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionResponseMapper.java new file mode 100644 index 0000000..fd9b26e --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionResponseMapper.java @@ -0,0 +1,104 @@ +/* + * + * * 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.air.pollution; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.prominence.openweathermap.api.enums.AirQualityIndex; +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails; +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionRecord; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.TimeZone; + +/** + * The type Air pollution response mapper. + */ +public class AirPollutionResponseMapper { + /** + * Map to air pollution air pollution. + * + * @param json the json + * @return the air pollution + */ + public AirPollutionDetails mapToAirPollution(String json) { + final ObjectMapper objectMapper = new ObjectMapper(); + AirPollutionDetails airPollutionDetails; + try { + final JsonNode root = objectMapper.readTree(json); + airPollutionDetails = mapToAirPollution(root); + } catch (JsonProcessingException e) { + throw new RuntimeException("Cannot parse Air Pollution response"); + } + + return airPollutionDetails; + } + + private AirPollutionDetails mapToAirPollution(JsonNode rootNode) { + final AirPollutionDetails airPollutionDetails = new AirPollutionDetails(); + airPollutionDetails.setCoordinate(parseCoordinate(rootNode.get("coord"))); + + final List sampleList = new ArrayList<>(); + final JsonNode sampleListNode = rootNode.get("list"); + sampleListNode.forEach(sampleNode -> { + sampleList.add(parseAirPollutionSample(sampleNode)); + }); + airPollutionDetails.setAirPollutionRecords(sampleList); + + return airPollutionDetails; + } + + private AirPollutionRecord parseAirPollutionSample(JsonNode sampleNode) { + AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + airPollutionRecord.setForecastTime(LocalDateTime.ofInstant(Instant.ofEpochSecond(sampleNode.get("dt").asInt()), TimeZone.getDefault().toZoneId())); + airPollutionRecord.setAirQualityIndex(AirQualityIndex.getByIndex(sampleNode.get("main").get("aqi").asInt())); + + final JsonNode componentsNode = sampleNode.get("components"); + airPollutionRecord.setCO(componentsNode.get("co").asDouble()); + airPollutionRecord.setNO(componentsNode.get("no").asDouble()); + airPollutionRecord.setNO2(componentsNode.get("no2").asDouble()); + airPollutionRecord.setO3(componentsNode.get("o3").asDouble()); + airPollutionRecord.setSO2(componentsNode.get("so2").asDouble()); + airPollutionRecord.setPM2_5(componentsNode.get("pm2_5").asDouble()); + airPollutionRecord.setPM10(componentsNode.get("pm10").asDouble()); + airPollutionRecord.setNH3(componentsNode.get("nh3").asDouble()); + + return airPollutionRecord; + } + + private Coordinate parseCoordinate(JsonNode rootNode) { + final JsonNode latitudeNode = rootNode.get("lat"); + final JsonNode longitudeNode = rootNode.get("lon"); + if (latitudeNode != null && longitudeNode != null) { + return Coordinate.of(latitudeNode.asDouble(), longitudeNode.asDouble()); + } + return null; + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/current/CurrentAirPollutionRequester.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/current/CurrentAirPollutionRequester.java new file mode 100644 index 0000000..f9ef4a4 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/current/CurrentAirPollutionRequester.java @@ -0,0 +1,39 @@ +/* + * 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.air.pollution.current; + +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizer; + +/** + * The interface Current air pollution requester. + */ +public interface CurrentAirPollutionRequester { + /** + * By coordinate current air pollution request customizer. + * + * @param coordinate the coordinate + * @return the current air pollution request customizer + */ + AirPollutionRequestCustomizer byCoordinate(Coordinate coordinate); +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/current/CurrentAirPollutionRequesterImpl.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/current/CurrentAirPollutionRequesterImpl.java new file mode 100644 index 0000000..4b770a0 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/current/CurrentAirPollutionRequesterImpl.java @@ -0,0 +1,53 @@ +/* + * + * * 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.air.pollution.current; + +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.request.RequestUrlBuilder; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizer; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizerImpl; + +/** + * The type Current air pollution requester. + */ +public class CurrentAirPollutionRequesterImpl implements CurrentAirPollutionRequester { + private final RequestUrlBuilder urlBuilder; + + /** + * Instantiates a new Current air pollution requester. + * + * @param urlBuilder the url builder + */ + public CurrentAirPollutionRequesterImpl(RequestUrlBuilder urlBuilder) { + this.urlBuilder = urlBuilder; + } + + @Override + public AirPollutionRequestCustomizer byCoordinate(Coordinate coordinate) { + urlBuilder.addRequestParameter("lat", String.valueOf(coordinate.getLatitude())); + urlBuilder.addRequestParameter("lon", String.valueOf(coordinate.getLongitude())); + return new AirPollutionRequestCustomizerImpl(urlBuilder); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/forecast/ForecastAirPollutionRequester.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/forecast/ForecastAirPollutionRequester.java new file mode 100644 index 0000000..7dec255 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/forecast/ForecastAirPollutionRequester.java @@ -0,0 +1,41 @@ +/* + * + * * 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.air.pollution.forecast; + +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizer; + +/** + * The interface Forecast air pollution requester. + */ +public interface ForecastAirPollutionRequester { + /** + * By coordinate forecast air pollution request customizer. + * + * @param coordinate the coordinate + * @return the forecast air pollution request customizer + */ + AirPollutionRequestCustomizer byCoordinate(Coordinate coordinate); +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/forecast/ForecastAirPollutionRequesterImpl.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/forecast/ForecastAirPollutionRequesterImpl.java new file mode 100644 index 0000000..87d5e37 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/forecast/ForecastAirPollutionRequesterImpl.java @@ -0,0 +1,53 @@ +/* + * + * * 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.air.pollution.forecast; + +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.request.RequestUrlBuilder; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizer; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizerImpl; + +/** + * The type Forecast air pollution requester. + */ +public class ForecastAirPollutionRequesterImpl implements ForecastAirPollutionRequester { + private final RequestUrlBuilder urlBuilder; + + /** + * Instantiates a new Forecast air pollution requester. + * + * @param urlBuilder the url builder + */ + public ForecastAirPollutionRequesterImpl(RequestUrlBuilder urlBuilder) { + this.urlBuilder = urlBuilder; + } + + @Override + public AirPollutionRequestCustomizer byCoordinate(Coordinate coordinate) { + urlBuilder.addRequestParameter("lat", String.valueOf(coordinate.getLatitude())); + urlBuilder.addRequestParameter("lon", String.valueOf(coordinate.getLongitude())); + return new AirPollutionRequestCustomizerImpl(urlBuilder); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/historical/HistoricalAirPollutionRequester.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/historical/HistoricalAirPollutionRequester.java new file mode 100644 index 0000000..f06dc1d --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/historical/HistoricalAirPollutionRequester.java @@ -0,0 +1,43 @@ +/* + * + * * 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.air.pollution.historical; + +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizer; + +/** + * The interface Historical air pollution requester. + */ +public interface HistoricalAirPollutionRequester { + /** + * By coordinate historical air pollution request customizer. + * + * @param coordinate the coordinate + * @param startUnixTime the start unix time + * @param endUnixTime the end unix time + * @return the historical air pollution request customizer + */ + AirPollutionRequestCustomizer byCoordinateAndPeriod(Coordinate coordinate, long startUnixTime, long endUnixTime); +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/historical/HistoricalAirPollutionRequesterImpl.java b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/historical/HistoricalAirPollutionRequesterImpl.java new file mode 100644 index 0000000..8ea1618 --- /dev/null +++ b/src/main/java/com/github/prominence/openweathermap/api/request/air/pollution/historical/HistoricalAirPollutionRequesterImpl.java @@ -0,0 +1,55 @@ +/* + * + * * 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.air.pollution.historical; + +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.request.RequestUrlBuilder; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizer; +import com.github.prominence.openweathermap.api.request.air.pollution.AirPollutionRequestCustomizerImpl; + +/** + * The type Historical air pollution requester. + */ +public class HistoricalAirPollutionRequesterImpl implements HistoricalAirPollutionRequester { + private final RequestUrlBuilder urlBuilder; + + /** + * Instantiates a new Historical air pollution requester. + * + * @param urlBuilder the url builder + */ + public HistoricalAirPollutionRequesterImpl(RequestUrlBuilder urlBuilder) { + this.urlBuilder = urlBuilder; + } + + @Override + public AirPollutionRequestCustomizer byCoordinateAndPeriod(Coordinate coordinate, long startUnixTime, long endUnixTime) { + urlBuilder.addRequestParameter("lat", String.valueOf(coordinate.getLatitude())); + urlBuilder.addRequestParameter("lon", String.valueOf(coordinate.getLongitude())); + urlBuilder.addRequestParameter("start", String.valueOf(startUnixTime)); + urlBuilder.addRequestParameter("end", String.valueOf(endUnixTime)); + return new AirPollutionRequestCustomizerImpl(urlBuilder); + } +} diff --git a/src/main/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapper.java b/src/main/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapper.java index 5b4caf3..7837b94 100644 --- a/src/main/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapper.java +++ b/src/main/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapper.java @@ -72,7 +72,7 @@ public class OneCallWeatherResponseMapper { final JsonNode root = objectMapper.readTree(json); currentData = mapToCurrent(root); } catch (JsonProcessingException e) { - throw new RuntimeException("Cannot parse Forecast response"); + throw new RuntimeException("Cannot parse OneCall response"); } return currentData; @@ -91,7 +91,7 @@ public class OneCallWeatherResponseMapper { final JsonNode root = objectMapper.readTree(json); historicalData = mapToHistorical(root); } catch (JsonProcessingException e) { - throw new RuntimeException("Cannot parse Forecast response"); + throw new RuntimeException("Cannot parse OneCall response"); } return historicalData; 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 new file mode 100644 index 0000000..a274a8e --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionDetailsUnitTest.java @@ -0,0 +1,97 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.model.Coordinate; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class AirPollutionDetailsUnitTest { + @Test + public void getCoordinate() { + final AirPollutionDetails airPollutionDetails = new AirPollutionDetails(); + final Coordinate coordinate = Coordinate.of(22.3, 44.2); + airPollutionDetails.setCoordinate(coordinate); + + assertEquals(coordinate, airPollutionDetails.getCoordinate()); + } + + @Test + public void getAirPollutionSamples() { + final AirPollutionDetails airPollutionDetails = new AirPollutionDetails(); + final List airPollutionRecords = new ArrayList<>(); + airPollutionDetails.setAirPollutionRecords(airPollutionRecords); + + assertEquals(airPollutionRecords, airPollutionDetails.getAirPollutionRecords()); + } + + @Test + public void testEquals() { + final AirPollutionDetails first = new AirPollutionDetails(); + final AirPollutionDetails second = new AirPollutionDetails(); + final Coordinate coordinate = Coordinate.of(22.3, 44.2); + final List airPollutionRecords = new ArrayList<>(); + + assertEquals(first, first); + assertNotEquals(first, null); + assertEquals(first, second); + assertNotEquals(first, new Object()); + + assertEquals(first, second); + + first.setCoordinate(coordinate); + + assertNotEquals(first, second); + + second.setCoordinate(coordinate); + + assertEquals(first, second); + + first.setAirPollutionRecords(airPollutionRecords); + + assertNotEquals(first, second); + + second.setAirPollutionRecords(airPollutionRecords); + + assertEquals(first, second); + } + + @Test + public void testHashCode() { + final AirPollutionDetails first = new AirPollutionDetails(); + final AirPollutionDetails second = new AirPollutionDetails(); + final Coordinate coordinate = Coordinate.of(22.3, 44.2); + + assertEquals(first.hashCode(), second.hashCode()); + + first.setCoordinate(coordinate); + + assertNotEquals(first.hashCode(), second.hashCode()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionRecordUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionRecordUnitTest.java new file mode 100644 index 0000000..2937a17 --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/model/air/pollution/AirPollutionRecordUnitTest.java @@ -0,0 +1,280 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.enums.AirQualityIndex; +import org.junit.jupiter.api.Test; + +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +public class AirPollutionRecordUnitTest { + @Test + public void getForecastTime() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final LocalDateTime now = LocalDateTime.now(); + airPollutionRecord.setForecastTime(now); + + assertEquals(now, airPollutionRecord.getForecastTime()); + } + + @Test + public void getAirQualityIndex() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + airPollutionRecord.setAirQualityIndex(AirQualityIndex.FAIR); + + assertEquals(AirQualityIndex.FAIR, airPollutionRecord.getAirQualityIndex()); + } + + @Test + public void getCarbonMonoxide() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setCO(value); + + assertEquals(value, airPollutionRecord.getCarbonMonoxide(), 0.00001); + } + + @Test + public void getNitrogenMonoxide() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setNO(value); + + assertEquals(value, airPollutionRecord.getNitrogenMonoxide(), 0.00001); + } + + @Test + public void getNitrogenDioxide() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setNO2(value); + + assertEquals(value, airPollutionRecord.getNitrogenDioxide(), 0.00001); + } + + @Test + public void getOzone() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setO3(value); + + assertEquals(value, airPollutionRecord.getOzone(), 0.00001); + } + + @Test + public void getSulphurDioxide() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setSO2(value); + + assertEquals(value, airPollutionRecord.getSulphurDioxide(), 0.00001); + } + + @Test + public void getFineParticlesMatter() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setPM2_5(value); + + assertEquals(value, airPollutionRecord.getFineParticlesMatter(), 0.00001); + } + + @Test + public void getCoarseParticulateMatter() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setPM10(value); + + assertEquals(value, airPollutionRecord.getCoarseParticulateMatter(), 0.00001); + } + + @Test + public void getAmmonia() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final double value = 0.55; + airPollutionRecord.setNH3(value); + + assertEquals(value, airPollutionRecord.getAmmonia()); + } + + @Test + public void testEquals() { + final AirPollutionRecord first = new AirPollutionRecord(); + final AirPollutionRecord second = new AirPollutionRecord(); + final LocalDateTime now = LocalDateTime.now(); + final AirQualityIndex aqi = AirQualityIndex.GOOD; + final double value = 0.55; + + assertEquals(first, first); + assertNotEquals(first, null); + assertEquals(first, second); + assertNotEquals(first, new Object()); + + assertEquals(first, second); + + first.setForecastTime(now); + + assertNotEquals(first, second); + + second.setForecastTime(now); + + assertEquals(first, second); + + first.setAirQualityIndex(aqi); + + assertNotEquals(first, second); + + second.setAirQualityIndex(aqi); + + assertEquals(first, second); + + first.setCO(value); + + assertNotEquals(first, second); + + second.setCO(value); + + assertEquals(first, second); + + first.setNO(value); + + assertNotEquals(first, second); + + second.setNO(value); + + assertEquals(first, second); + + first.setNO2(value); + + assertNotEquals(first, second); + + second.setNO2(value); + + assertEquals(first, second); + + first.setO3(value); + + assertNotEquals(first, second); + + second.setO3(value); + + assertEquals(first, second); + + first.setSO2(value); + + assertNotEquals(first, second); + + second.setSO2(value); + + assertEquals(first, second); + + first.setPM2_5(value); + + assertNotEquals(first, second); + + second.setPM2_5(value); + + assertEquals(first, second); + + first.setPM10(value); + + assertNotEquals(first, second); + + second.setPM10(value); + + assertEquals(first, second); + + first.setNH3(value); + + assertNotEquals(first, second); + + second.setNH3(value); + + assertEquals(first, second); + } + + @Test + public void testHashCode() { + final AirPollutionRecord first = new AirPollutionRecord(); + final AirPollutionRecord second = new AirPollutionRecord(); + + assertEquals(first.hashCode(), second.hashCode()); + + first.setSO2(33.2); + + assertNotEquals(first.hashCode(), second.hashCode()); + } + + @Test + public void testToString() { + final AirPollutionRecord airPollutionRecord = new AirPollutionRecord(); + final LocalDateTime now = LocalDateTime.now(); + final AirQualityIndex aqi = AirQualityIndex.GOOD; + final double value = 0.55; + + airPollutionRecord.setForecastTime(now); + airPollutionRecord.setAirQualityIndex(aqi); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setCO(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setNO(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setNO2(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setO3(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setSO2(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setPM2_5(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setPM10(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setNH3(value); + + assertNotNull(airPollutionRecord.toString()); + + airPollutionRecord.setCO(null); + + assertNotNull(airPollutionRecord.toString()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionIntegrationTest.java b/src/test/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionIntegrationTest.java new file mode 100644 index 0000000..657cd15 --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionIntegrationTest.java @@ -0,0 +1,208 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.ApiTest; +import com.github.prominence.openweathermap.api.model.Coordinate; +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class AirPollutionIntegrationTest extends ApiTest { + @Test + public void whenRetrieveCurrentAirPollutionResponseAsJava_thenOk() { + final AirPollutionDetails airPollutionDetails = getClient() + .airPollution() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieve() + .asJava(); + + assertNotNull(airPollutionDetails); + airPollutionDetails.getAirPollutionRecords().forEach(airPollutionRecord -> { + assertNotNull(airPollutionRecord); + System.out.println(airPollutionRecord); + }); + } + + @Test + public void whenRetrieveCurrentAirPollutionResponseAsJSON_thenOk() { + final String jsonString = getClient() + .airPollution() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieve() + .asJSON(); + + assertNotNull(jsonString); + System.out.println(jsonString); + } + + @Test + public void whenRetrieveCurrentAirPollutionAsyncResponseAsJava_thenOk() throws ExecutionException, InterruptedException { + final CompletableFuture pollutionDetailsFuture = getClient() + .airPollution() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieveAsync() + .asJava(); + + assertNotNull(pollutionDetailsFuture); + assertNotNull(pollutionDetailsFuture.get()); + } + + @Test + public void whenRetrieveCurrentAirPollutionAsyncResponseAsJSON_thenOk() throws ExecutionException, InterruptedException { + final CompletableFuture jsonStringFuture = getClient() + .airPollution() + .current() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieveAsync() + .asJSON(); + + assertNotNull(jsonStringFuture); + final String jsonString = jsonStringFuture.get(); + assertNotNull(jsonString); + System.out.println(jsonString); + } + + @Test + public void whenRetrieveForecastAirPollutionResponseAsJava_thenOk() { + final AirPollutionDetails airPollutionDetails = getClient() + .airPollution() + .forecast() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieve() + .asJava(); + + assertNotNull(airPollutionDetails); + airPollutionDetails.getAirPollutionRecords().forEach(airPollutionRecord -> { + assertNotNull(airPollutionRecord); + System.out.println(airPollutionRecord); + }); + } + + @Test + public void whenRetrieveForecastAirPollutionResponseAsJSON_thenOk() { + final String jsonString = getClient() + .airPollution() + .forecast() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieve() + .asJSON(); + + assertNotNull(jsonString); + System.out.println(jsonString); + } + + @Test + public void whenRetrieveForecastAirPollutionAsyncResponseAsJava_thenOk() throws ExecutionException, InterruptedException { + final CompletableFuture pollutionDetailsFuture = getClient() + .airPollution() + .forecast() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieveAsync() + .asJava(); + + assertNotNull(pollutionDetailsFuture); + assertNotNull(pollutionDetailsFuture.get()); + } + + @Test + public void whenRetrieveForecastAirPollutionAsyncResponseAsJSON_thenOk() throws ExecutionException, InterruptedException { + final CompletableFuture jsonStringFuture = getClient() + .airPollution() + .forecast() + .byCoordinate(Coordinate.of(53.54, 27.34)) + .retrieveAsync() + .asJSON(); + + assertNotNull(jsonStringFuture); + final String jsonString = jsonStringFuture.get(); + assertNotNull(jsonString); + System.out.println(jsonString); + } + + @Test + public void whenRetrieveHistoricalAirPollutionResponseAsJava_thenOk() { + final AirPollutionDetails airPollutionDetails = getClient() + .airPollution() + .historical() + .byCoordinateAndPeriod(Coordinate.of(53.54, 27.34), 1606223802, 1606482999) + .retrieve() + .asJava(); + + assertNotNull(airPollutionDetails); + airPollutionDetails.getAirPollutionRecords().forEach(airPollutionRecord -> { + assertNotNull(airPollutionRecord); + System.out.println(airPollutionRecord); + }); + } + + @Test + public void whenRetrieveHistoricalAirPollutionResponseAsJSON_thenOk() { + final String jsonString = getClient() + .airPollution() + .historical() + .byCoordinateAndPeriod(Coordinate.of(53.54, 27.34), 1606223802, 1606482999) + .retrieve() + .asJSON(); + + assertNotNull(jsonString); + System.out.println(jsonString); + } + + @Test + public void whenRetrieveHistoricalAirPollutionAsyncResponseAsJava_thenOk() throws ExecutionException, InterruptedException { + final CompletableFuture pollutionDetailsFuture = getClient() + .airPollution() + .historical() + .byCoordinateAndPeriod(Coordinate.of(53.54, 27.34), 1606223802, 1606482999) + .retrieveAsync() + .asJava(); + + assertNotNull(pollutionDetailsFuture); + assertNotNull(pollutionDetailsFuture.get()); + } + + @Test + public void whenRetrieveHistoricalAirPollutionAsyncResponseAsJSON_thenOk() throws ExecutionException, InterruptedException { + final CompletableFuture jsonStringFuture = getClient() + .airPollution() + .historical() + .byCoordinateAndPeriod(Coordinate.of(53.54, 27.34), 1606223802, 1606482999) + .retrieveAsync() + .asJSON(); + + assertNotNull(jsonStringFuture); + final String jsonString = jsonStringFuture.get(); + assertNotNull(jsonString); + System.out.println(jsonString); + } +} diff --git a/src/test/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionResponseMapperUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionResponseMapperUnitTest.java new file mode 100644 index 0000000..b8c86b2 --- /dev/null +++ b/src/test/java/com/github/prominence/openweathermap/api/request/air/pollution/AirPollutionResponseMapperUnitTest.java @@ -0,0 +1,57 @@ +/* + * + * * 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.air.pollution; + +import com.github.prominence.openweathermap.api.model.air.pollution.AirPollutionDetails; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class AirPollutionResponseMapperUnitTest { + @Test + public void mapToAirPollution_withInvalidJSON() { + final String jsonString = "{\"coord\":{lon\":27.34,\"lat\":53.54},\"list\":[{\"main\":{\"aqi\":1},\"components\":{\"co\":243.66,\"no\":0,\"no2\":4.07,\"o3\":62.23,\"so2\":1.77,\"pm2_5\":3.87,\"pm10\":4.58,\"nh3\":2.41},\"dt\":1618610400}]}"; + assertThrows(RuntimeException.class, () -> new AirPollutionResponseMapper().mapToAirPollution(jsonString)); + } + + @Test + public void mapToAirPollution_withCoordinatesVariation() { + String jsonResponse = "{\"coord\":{},\"list\":[{\"main\":{\"aqi\":1},\"components\":{\"co\":243.66,\"no\":0,\"no2\":4.07,\"o3\":62.23,\"so2\":1.77,\"pm2_5\":3.87,\"pm10\":4.58,\"nh3\":2.41},\"dt\":1618610400}]}"; + AirPollutionDetails airPollutionDetails = new AirPollutionResponseMapper().mapToAirPollution(jsonResponse); + + assertNotNull(airPollutionDetails); + + jsonResponse = "{\"coord\":{\"lon\":27.34},\"list\":[{\"main\":{\"aqi\":1},\"components\":{\"co\":243.66,\"no\":0,\"no2\":4.07,\"o3\":62.23,\"so2\":1.77,\"pm2_5\":3.87,\"pm10\":4.58,\"nh3\":2.41},\"dt\":1618610400}]}"; + airPollutionDetails = new AirPollutionResponseMapper().mapToAirPollution(jsonResponse); + + assertNotNull(airPollutionDetails); + + jsonResponse = "{\"coord\":{\"lat\":53.54},\"list\":[{\"main\":{\"aqi\":1},\"components\":{\"co\":243.66,\"no\":0,\"no2\":4.07,\"o3\":62.23,\"so2\":1.77,\"pm2_5\":3.87,\"pm10\":4.58,\"nh3\":2.41},\"dt\":1618610400}]}"; + airPollutionDetails = new AirPollutionResponseMapper().mapToAirPollution(jsonResponse); + + assertNotNull(airPollutionDetails); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapperUnitTest.java b/src/test/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapperUnitTest.java index 293a4fe..5dab17b 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapperUnitTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/request/onecall/OneCallWeatherResponseMapperUnitTest.java @@ -31,11 +31,6 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; public class OneCallWeatherResponseMapperUnitTest { - @Test - public void mapToCurrent() { // TODO: remove - final String jsonString = "{\"lat\":53.54,\"lon\":27.34,\"timezone\":\"Europe/Minsk\",\"timezone_offset\":10800,\"current\":{\"dt\":1618050045,\"sunrise\":1618024927,\"sunset\":1618074079,\"temp\":11.84,\"feels_like\":10.12,\"pressure\":1018,\"humidity\":40,\"dew_point\":-1.16,\"uvi\":3.36,\"clouds\":0,\"visibility\":10000,\"wind_speed\":9,\"wind_deg\":200,\"wind_gust\":12,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}]},\"minutely\":[{\"dt\":1618050060,\"precipitation\":0},{\"dt\":1618050120,\"precipitation\":0},{\"dt\":1618050180,\"precipitation\":0},{\"dt\":1618050240,\"precipitation\":0},{\"dt\":1618050300,\"precipitation\":0},{\"dt\":1618050360,\"precipitation\":0},{\"dt\":1618050420,\"precipitation\":0},{\"dt\":1618050480,\"precipitation\":0},{\"dt\":1618050540,\"precipitation\":0},{\"dt\":1618050600,\"precipitation\":0},{\"dt\":1618050660,\"precipitation\":0},{\"dt\":1618050720,\"precipitation\":0},{\"dt\":1618050780,\"precipitation\":0},{\"dt\":1618050840,\"precipitation\":0},{\"dt\":1618050900,\"precipitation\":0},{\"dt\":1618050960,\"precipitation\":0},{\"dt\":1618051020,\"precipitation\":0},{\"dt\":1618051080,\"precipitation\":0},{\"dt\":1618051140,\"precipitation\":0},{\"dt\":1618051200,\"precipitation\":0},{\"dt\":1618051260,\"precipitation\":0},{\"dt\":1618051320,\"precipitation\":0},{\"dt\":1618051380,\"precipitation\":0},{\"dt\":1618051440,\"precipitation\":0},{\"dt\":1618051500,\"precipitation\":0},{\"dt\":1618051560,\"precipitation\":0},{\"dt\":1618051620,\"precipitation\":0},{\"dt\":1618051680,\"precipitation\":0},{\"dt\":1618051740,\"precipitation\":0},{\"dt\":1618051800,\"precipitation\":0},{\"dt\":1618051860,\"precipitation\":0},{\"dt\":1618051920,\"precipitation\":0},{\"dt\":1618051980,\"precipitation\":0},{\"dt\":1618052040,\"precipitation\":0},{\"dt\":1618052100,\"precipitation\":0},{\"dt\":1618052160,\"precipitation\":0},{\"dt\":1618052220,\"precipitation\":0},{\"dt\":1618052280,\"precipitation\":0},{\"dt\":1618052340,\"precipitation\":0},{\"dt\":1618052400,\"precipitation\":0},{\"dt\":1618052460,\"precipitation\":0},{\"dt\":1618052520,\"precipitation\":0},{\"dt\":1618052580,\"precipitation\":0},{\"dt\":1618052640,\"precipitation\":0},{\"dt\":1618052700,\"precipitation\":0},{\"dt\":1618052760,\"precipitation\":0},{\"dt\":1618052820,\"precipitation\":0},{\"dt\":1618052880,\"precipitation\":0},{\"dt\":1618052940,\"precipitation\":0},{\"dt\":1618053000,\"precipitation\":0},{\"dt\":1618053060,\"precipitation\":0},{\"dt\":1618053120,\"precipitation\":0},{\"dt\":1618053180,\"precipitation\":0},{\"dt\":1618053240,\"precipitation\":0},{\"dt\":1618053300,\"precipitation\":0},{\"dt\":1618053360,\"precipitation\":0},{\"dt\":1618053420,\"precipitation\":0},{\"dt\":1618053480,\"precipitation\":0},{\"dt\":1618053540,\"precipitation\":0},{\"dt\":1618053600,\"precipitation\":0},{\"dt\":1618053660,\"precipitation\":0}],\"hourly\":[{\"dt\":1618048800,\"temp\":11.84,\"feels_like\":10.12,\"pressure\":1018,\"humidity\":40,\"dew_point\":-1.16,\"uvi\":3.36,\"clouds\":0,\"visibility\":10000,\"wind_speed\":7.72,\"wind_deg\":200,\"wind_gust\":10.55,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618052400,\"temp\":11.6,\"feels_like\":9.96,\"pressure\":1018,\"humidity\":44,\"dew_point\":-0.2,\"uvi\":3.22,\"clouds\":32,\"visibility\":10000,\"wind_speed\":7.76,\"wind_deg\":202,\"wind_gust\":10.75,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618056000,\"temp\":12.26,\"feels_like\":10.72,\"pressure\":1018,\"humidity\":45,\"dew_point\":0.68,\"uvi\":2.67,\"clouds\":49,\"visibility\":10000,\"wind_speed\":7.66,\"wind_deg\":205,\"wind_gust\":10.76,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618059600,\"temp\":12.86,\"feels_like\":11.4,\"pressure\":1018,\"humidity\":46,\"dew_point\":1.53,\"uvi\":1.22,\"clouds\":19,\"visibility\":10000,\"wind_speed\":7.15,\"wind_deg\":210,\"wind_gust\":9.99,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02d\"}],\"pop\":0},{\"dt\":1618063200,\"temp\":13.04,\"feels_like\":11.68,\"pressure\":1018,\"humidity\":49,\"dew_point\":2.58,\"uvi\":0.7,\"clouds\":50,\"visibility\":10000,\"wind_speed\":5.95,\"wind_deg\":210,\"wind_gust\":9.4,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618066800,\"temp\":12.53,\"feels_like\":11.2,\"pressure\":1018,\"humidity\":52,\"dew_point\":3.07,\"uvi\":0.32,\"clouds\":48,\"visibility\":10000,\"wind_speed\":5.43,\"wind_deg\":209,\"wind_gust\":9.32,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618070400,\"temp\":11.02,\"feels_like\":9.72,\"pressure\":1018,\"humidity\":59,\"dew_point\":3.37,\"uvi\":0.12,\"clouds\":51,\"visibility\":10000,\"wind_speed\":4.22,\"wind_deg\":200,\"wind_gust\":8.94,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618074000,\"temp\":8.81,\"feels_like\":6.7,\"pressure\":1019,\"humidity\":67,\"dew_point\":3.23,\"uvi\":0,\"clouds\":55,\"visibility\":10000,\"wind_speed\":3.67,\"wind_deg\":184,\"wind_gust\":8.41,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618077600,\"temp\":7.94,\"feels_like\":5.47,\"pressure\":1020,\"humidity\":71,\"dew_point\":3.06,\"uvi\":0,\"clouds\":59,\"visibility\":10000,\"wind_speed\":4,\"wind_deg\":177,\"wind_gust\":10.47,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618081200,\"temp\":7.68,\"feels_like\":4.81,\"pressure\":1020,\"humidity\":71,\"dew_point\":2.92,\"uvi\":0,\"clouds\":79,\"visibility\":10000,\"wind_speed\":4.71,\"wind_deg\":179,\"wind_gust\":12.11,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618084800,\"temp\":7.78,\"feels_like\":4.76,\"pressure\":1020,\"humidity\":71,\"dew_point\":2.89,\"uvi\":0,\"clouds\":81,\"visibility\":10000,\"wind_speed\":5.12,\"wind_deg\":185,\"wind_gust\":13.11,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618088400,\"temp\":7.45,\"feels_like\":4.33,\"pressure\":1020,\"humidity\":71,\"dew_point\":2.77,\"uvi\":0,\"clouds\":86,\"visibility\":10000,\"wind_speed\":5.17,\"wind_deg\":190,\"wind_gust\":13.37,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618092000,\"temp\":6.97,\"feels_like\":3.85,\"pressure\":1021,\"humidity\":74,\"dew_point\":2.67,\"uvi\":0,\"clouds\":88,\"visibility\":10000,\"wind_speed\":4.89,\"wind_deg\":192,\"wind_gust\":12.68,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618095600,\"temp\":6.46,\"feels_like\":3.4,\"pressure\":1021,\"humidity\":76,\"dew_point\":2.7,\"uvi\":0,\"clouds\":89,\"visibility\":10000,\"wind_speed\":4.5,\"wind_deg\":191,\"wind_gust\":11.89,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618099200,\"temp\":5.96,\"feels_like\":2.9,\"pressure\":1021,\"humidity\":79,\"dew_point\":2.65,\"uvi\":0,\"clouds\":91,\"visibility\":10000,\"wind_speed\":4.26,\"wind_deg\":188,\"wind_gust\":11.23,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618102800,\"temp\":5.31,\"feels_like\":2.22,\"pressure\":1022,\"humidity\":81,\"dew_point\":2.47,\"uvi\":0,\"clouds\":89,\"visibility\":10000,\"wind_speed\":4.04,\"wind_deg\":186,\"wind_gust\":10.72,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618106400,\"temp\":5.2,\"feels_like\":2.18,\"pressure\":1022,\"humidity\":82,\"dew_point\":2.41,\"uvi\":0,\"clouds\":95,\"visibility\":10000,\"wind_speed\":3.87,\"wind_deg\":190,\"wind_gust\":10.92,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618110000,\"temp\":4.56,\"feels_like\":1.51,\"pressure\":1023,\"humidity\":85,\"dew_point\":2.29,\"uvi\":0,\"clouds\":88,\"visibility\":10000,\"wind_speed\":3.69,\"wind_deg\":186,\"wind_gust\":10.33,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618113600,\"temp\":4.51,\"feels_like\":1.64,\"pressure\":1023,\"humidity\":85,\"dew_point\":2.33,\"uvi\":0,\"clouds\":84,\"visibility\":10000,\"wind_speed\":3.39,\"wind_deg\":191,\"wind_gust\":10.18,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618117200,\"temp\":6.05,\"feels_like\":3.41,\"pressure\":1024,\"humidity\":80,\"dew_point\":2.89,\"uvi\":0.15,\"clouds\":80,\"visibility\":10000,\"wind_speed\":3.55,\"wind_deg\":193,\"wind_gust\":10.09,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618120800,\"temp\":7.96,\"feels_like\":5.31,\"pressure\":1024,\"humidity\":72,\"dew_point\":3.29,\"uvi\":0.38,\"clouds\":80,\"visibility\":10000,\"wind_speed\":4.39,\"wind_deg\":197,\"wind_gust\":8.53,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618124400,\"temp\":9.61,\"feels_like\":7.3,\"pressure\":1024,\"humidity\":67,\"dew_point\":3.88,\"uvi\":1.01,\"clouds\":100,\"visibility\":10000,\"wind_speed\":4.5,\"wind_deg\":197,\"wind_gust\":7.77,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618128000,\"temp\":11.14,\"feels_like\":9.93,\"pressure\":1025,\"humidity\":62,\"dew_point\":4.27,\"uvi\":1.54,\"clouds\":100,\"visibility\":10000,\"wind_speed\":4.71,\"wind_deg\":198,\"wind_gust\":7.19,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618131600,\"temp\":12.8,\"feels_like\":11.62,\"pressure\":1025,\"humidity\":57,\"dew_point\":4.57,\"uvi\":1.97,\"clouds\":100,\"visibility\":10000,\"wind_speed\":4.87,\"wind_deg\":196,\"wind_gust\":6.82,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618135200,\"temp\":14.03,\"feels_like\":12.87,\"pressure\":1025,\"humidity\":53,\"dew_point\":4.65,\"uvi\":3.09,\"clouds\":100,\"visibility\":10000,\"wind_speed\":5.11,\"wind_deg\":194,\"wind_gust\":6.7,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618138800,\"temp\":14.82,\"feels_like\":13.66,\"pressure\":1025,\"humidity\":50,\"dew_point\":4.62,\"uvi\":2.96,\"clouds\":100,\"visibility\":10000,\"wind_speed\":5.32,\"wind_deg\":191,\"wind_gust\":6.82,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618142400,\"temp\":15.64,\"feels_like\":14.49,\"pressure\":1025,\"humidity\":47,\"dew_point\":4.48,\"uvi\":2.45,\"clouds\":94,\"visibility\":10000,\"wind_speed\":5.42,\"wind_deg\":192,\"wind_gust\":6.7,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618146000,\"temp\":15.81,\"feels_like\":14.62,\"pressure\":1024,\"humidity\":45,\"dew_point\":4.15,\"uvi\":2.02,\"clouds\":24,\"visibility\":10000,\"wind_speed\":5.15,\"wind_deg\":193,\"wind_gust\":6.3,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02d\"}],\"pop\":0},{\"dt\":1618149600,\"temp\":15.71,\"feels_like\":14.51,\"pressure\":1025,\"humidity\":45,\"dew_point\":3.87,\"uvi\":1.17,\"clouds\":48,\"visibility\":10000,\"wind_speed\":4.79,\"wind_deg\":188,\"wind_gust\":6.01,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618153200,\"temp\":14.79,\"feels_like\":13.63,\"pressure\":1024,\"humidity\":50,\"dew_point\":4.71,\"uvi\":0.53,\"clouds\":63,\"visibility\":10000,\"wind_speed\":3.73,\"wind_deg\":177,\"wind_gust\":6.35,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618156800,\"temp\":12.85,\"feels_like\":11.73,\"pressure\":1024,\"humidity\":59,\"dew_point\":5.18,\"uvi\":0.17,\"clouds\":72,\"visibility\":10000,\"wind_speed\":3.05,\"wind_deg\":159,\"wind_gust\":6.38,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618160400,\"temp\":10.92,\"feels_like\":9.71,\"pressure\":1025,\"humidity\":63,\"dew_point\":4.26,\"uvi\":0,\"clouds\":78,\"visibility\":10000,\"wind_speed\":3.39,\"wind_deg\":152,\"wind_gust\":6.64,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618164000,\"temp\":9.65,\"feels_like\":7.72,\"pressure\":1026,\"humidity\":66,\"dew_point\":3.66,\"uvi\":0,\"clouds\":81,\"visibility\":10000,\"wind_speed\":3.68,\"wind_deg\":148,\"wind_gust\":9.04,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618167600,\"temp\":8.82,\"feels_like\":6.59,\"pressure\":1026,\"humidity\":66,\"dew_point\":3.05,\"uvi\":0,\"clouds\":100,\"visibility\":10000,\"wind_speed\":3.92,\"wind_deg\":147,\"wind_gust\":11.02,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618171200,\"temp\":8.07,\"feels_like\":5.87,\"pressure\":1026,\"humidity\":67,\"dew_point\":2.43,\"uvi\":0,\"clouds\":100,\"visibility\":10000,\"wind_speed\":3.54,\"wind_deg\":154,\"wind_gust\":11.12,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618174800,\"temp\":7.24,\"feels_like\":4.99,\"pressure\":1027,\"humidity\":68,\"dew_point\":1.96,\"uvi\":0,\"clouds\":98,\"visibility\":10000,\"wind_speed\":3.32,\"wind_deg\":154,\"wind_gust\":10.55,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618178400,\"temp\":6.58,\"feels_like\":4.31,\"pressure\":1027,\"humidity\":70,\"dew_point\":1.65,\"uvi\":0,\"clouds\":98,\"visibility\":10000,\"wind_speed\":3.14,\"wind_deg\":154,\"wind_gust\":9.19,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618182000,\"temp\":6.03,\"feels_like\":3.57,\"pressure\":1027,\"humidity\":71,\"dew_point\":1.26,\"uvi\":0,\"clouds\":82,\"visibility\":10000,\"wind_speed\":3.25,\"wind_deg\":150,\"wind_gust\":8.44,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618185600,\"temp\":5.51,\"feels_like\":2.94,\"pressure\":1027,\"humidity\":72,\"dew_point\":0.95,\"uvi\":0,\"clouds\":71,\"visibility\":10000,\"wind_speed\":3.26,\"wind_deg\":150,\"wind_gust\":8.58,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618189200,\"temp\":5,\"feels_like\":2.29,\"pressure\":1027,\"humidity\":74,\"dew_point\":0.72,\"uvi\":0,\"clouds\":9,\"visibility\":10000,\"wind_speed\":3.3,\"wind_deg\":147,\"wind_gust\":8.63,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01n\"}],\"pop\":0},{\"dt\":1618192800,\"temp\":4.47,\"feels_like\":1.69,\"pressure\":1027,\"humidity\":75,\"dew_point\":0.52,\"uvi\":0,\"clouds\":14,\"visibility\":10000,\"wind_speed\":3.25,\"wind_deg\":145,\"wind_gust\":8.14,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}],\"pop\":0},{\"dt\":1618196400,\"temp\":4.03,\"feels_like\":1.08,\"pressure\":1026,\"humidity\":76,\"dew_point\":0.22,\"uvi\":0,\"clouds\":12,\"visibility\":10000,\"wind_speed\":3.36,\"wind_deg\":138,\"wind_gust\":8.13,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}],\"pop\":0},{\"dt\":1618200000,\"temp\":4,\"feels_like\":0.9,\"pressure\":1026,\"humidity\":75,\"dew_point\":0.13,\"uvi\":0.08,\"clouds\":10,\"visibility\":10000,\"wind_speed\":3.57,\"wind_deg\":131,\"wind_gust\":9.24,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618203600,\"temp\":5.73,\"feels_like\":2.73,\"pressure\":1026,\"humidity\":68,\"dew_point\":0.52,\"uvi\":0.33,\"clouds\":8,\"visibility\":10000,\"wind_speed\":4.05,\"wind_deg\":138,\"wind_gust\":10.36,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618207200,\"temp\":7.87,\"feels_like\":4.9,\"pressure\":1025,\"humidity\":59,\"dew_point\":0.53,\"uvi\":0.84,\"clouds\":7,\"visibility\":10000,\"wind_speed\":5.05,\"wind_deg\":145,\"wind_gust\":10.48,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618210800,\"temp\":9.9,\"feels_like\":7.29,\"pressure\":1025,\"humidity\":53,\"dew_point\":0.8,\"uvi\":1.56,\"clouds\":0,\"visibility\":10000,\"wind_speed\":5.48,\"wind_deg\":147,\"wind_gust\":9.78,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618214400,\"temp\":11.5,\"feels_like\":9.93,\"pressure\":1025,\"humidity\":47,\"dew_point\":0.86,\"uvi\":2.37,\"clouds\":0,\"visibility\":10000,\"wind_speed\":5.94,\"wind_deg\":152,\"wind_gust\":8.78,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618218000,\"temp\":12.64,\"feels_like\":11.08,\"pressure\":1024,\"humidity\":43,\"dew_point\":0.68,\"uvi\":3.04,\"clouds\":0,\"visibility\":10000,\"wind_speed\":6.25,\"wind_deg\":151,\"wind_gust\":8.5,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0}],\"daily\":[{\"dt\":1618048800,\"sunrise\":1618024927,\"sunset\":1618074079,\"temp\":{\"day\":11.84,\"min\":1.69,\"max\":13.04,\"night\":7.78,\"eve\":11.02,\"morn\":1.69},\"feels_like\":{\"day\":10.12,\"night\":-2.86,\"eve\":9.72,\"morn\":-2.86},\"pressure\":1018,\"humidity\":40,\"dew_point\":-1.16,\"wind_speed\":7.72,\"wind_deg\":200,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"clouds\":0,\"pop\":0,\"uvi\":3.36},{\"dt\":1618135200,\"sunrise\":1618111186,\"sunset\":1618160588,\"temp\":{\"day\":14.03,\"min\":4.51,\"max\":15.81,\"night\":8.07,\"eve\":12.85,\"morn\":4.51},\"feels_like\":{\"day\":12.87,\"night\":1.64,\"eve\":11.73,\"morn\":1.64},\"pressure\":1025,\"humidity\":53,\"dew_point\":4.65,\"wind_speed\":5.11,\"wind_deg\":194,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"clouds\":100,\"pop\":0,\"uvi\":3.09},{\"dt\":1618221600,\"sunrise\":1618197445,\"sunset\":1618247098,\"temp\":{\"day\":13.62,\"min\":4,\"max\":14.94,\"night\":7.62,\"eve\":12.21,\"morn\":4},\"feels_like\":{\"day\":12.08,\"night\":0.9,\"eve\":10.87,\"morn\":0.9},\"pressure\":1024,\"humidity\":40,\"dew_point\":0.58,\"wind_speed\":6.09,\"wind_deg\":152,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"clouds\":0,\"pop\":0,\"uvi\":3.11},{\"dt\":1618308000,\"sunrise\":1618283705,\"sunset\":1618333607,\"temp\":{\"day\":14.42,\"min\":4.45,\"max\":16.91,\"night\":11.72,\"eve\":16.64,\"morn\":4.45},\"feels_like\":{\"day\":13.17,\"night\":2.28,\"eve\":15.43,\"morn\":2.28},\"pressure\":1019,\"humidity\":48,\"dew_point\":3.67,\"wind_speed\":3.07,\"wind_deg\":153,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"clouds\":8,\"pop\":0,\"uvi\":3.53},{\"dt\":1618394400,\"sunrise\":1618369965,\"sunset\":1618420117,\"temp\":{\"day\":10.12,\"min\":7.84,\"max\":10.7,\"night\":8.87,\"eve\":9.78,\"morn\":7.84},\"feels_like\":{\"day\":9.54,\"night\":6.26,\"eve\":8.02,\"morn\":6.26},\"pressure\":1012,\"humidity\":90,\"dew_point\":8.65,\"wind_speed\":3.6,\"wind_deg\":129,\"weather\":[{\"id\":500,\"main\":\"Rain\",\"description\":\"light rain\",\"icon\":\"10d\"}],\"clouds\":100,\"pop\":0.98,\"rain\":2.37,\"uvi\":2.7},{\"dt\":1618480800,\"sunrise\":1618456226,\"sunset\":1618506627,\"temp\":{\"day\":8.88,\"min\":6.86,\"max\":10.63,\"night\":8.88,\"eve\":10.63,\"morn\":6.86},\"feels_like\":{\"day\":7.04,\"night\":5.16,\"eve\":9.81,\"morn\":5.16},\"pressure\":1011,\"humidity\":86,\"dew_point\":6.85,\"wind_speed\":3.22,\"wind_deg\":98,\"weather\":[{\"id\":500,\"main\":\"Rain\",\"description\":\"light rain\",\"icon\":\"10d\"}],\"clouds\":100,\"pop\":0.91,\"rain\":4.23,\"uvi\":3},{\"dt\":1618567200,\"sunrise\":1618542488,\"sunset\":1618593137,\"temp\":{\"day\":5.06,\"min\":4.81,\"max\":8.16,\"night\":4.84,\"eve\":7.24,\"morn\":5.39},\"feels_like\":{\"day\":2.81,\"night\":1.18,\"eve\":6.67,\"morn\":1.18},\"pressure\":1010,\"humidity\":91,\"dew_point\":3.81,\"wind_speed\":2.69,\"wind_deg\":101,\"weather\":[{\"id\":501,\"main\":\"Rain\",\"description\":\"moderate rain\",\"icon\":\"10d\"}],\"clouds\":100,\"pop\":1,\"rain\":15.37,\"uvi\":3},{\"dt\":1618653600,\"sunrise\":1618628751,\"sunset\":1618679646,\"temp\":{\"day\":3.07,\"min\":1.88,\"max\":5.32,\"night\":1.88,\"eve\":2.04,\"morn\":4.62},\"feels_like\":{\"day\":-0.42,\"night\":1.75,\"eve\":-1.74,\"morn\":1.75},\"pressure\":1010,\"humidity\":97,\"dew_point\":2.74,\"wind_speed\":3.86,\"wind_deg\":287,\"weather\":[{\"id\":616,\"main\":\"Snow\",\"description\":\"rain and snow\",\"icon\":\"13d\"}],\"clouds\":100,\"pop\":1,\"rain\":6.67,\"snow\":8.83,\"uvi\":3}]}"; - } - @Test public void mapToCurrent_withInvalidJSON() { final String jsonString = "{\"lat\":53.54,\"lon\":27.34,timezone\":\"Europe/Minsk\",\"timezone_offset\":10800,\"current\":{\"dt\":1618050045,\"sunrise\":1618024927,\"sunset\":1618074079,\"temp\":11.84,\"feels_like\":10.12,\"pressure\":1018,\"humidity\":40,\"dew_point\":-1.16,\"uvi\":3.36,\"clouds\":0,\"visibility\":10000,\"wind_speed\":9,\"wind_deg\":200,\"wind_gust\":12,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}]},\"minutely\":[{\"dt\":1618050060,\"precipitation\":0},{\"dt\":1618050120,\"precipitation\":0},{\"dt\":1618050180,\"precipitation\":0},{\"dt\":1618050240,\"precipitation\":0},{\"dt\":1618050300,\"precipitation\":0},{\"dt\":1618050360,\"precipitation\":0},{\"dt\":1618050420,\"precipitation\":0},{\"dt\":1618050480,\"precipitation\":0},{\"dt\":1618050540,\"precipitation\":0},{\"dt\":1618050600,\"precipitation\":0},{\"dt\":1618050660,\"precipitation\":0},{\"dt\":1618050720,\"precipitation\":0},{\"dt\":1618050780,\"precipitation\":0},{\"dt\":1618050840,\"precipitation\":0},{\"dt\":1618050900,\"precipitation\":0},{\"dt\":1618050960,\"precipitation\":0},{\"dt\":1618051020,\"precipitation\":0},{\"dt\":1618051080,\"precipitation\":0},{\"dt\":1618051140,\"precipitation\":0},{\"dt\":1618051200,\"precipitation\":0},{\"dt\":1618051260,\"precipitation\":0},{\"dt\":1618051320,\"precipitation\":0},{\"dt\":1618051380,\"precipitation\":0},{\"dt\":1618051440,\"precipitation\":0},{\"dt\":1618051500,\"precipitation\":0},{\"dt\":1618051560,\"precipitation\":0},{\"dt\":1618051620,\"precipitation\":0},{\"dt\":1618051680,\"precipitation\":0},{\"dt\":1618051740,\"precipitation\":0},{\"dt\":1618051800,\"precipitation\":0},{\"dt\":1618051860,\"precipitation\":0},{\"dt\":1618051920,\"precipitation\":0},{\"dt\":1618051980,\"precipitation\":0},{\"dt\":1618052040,\"precipitation\":0},{\"dt\":1618052100,\"precipitation\":0},{\"dt\":1618052160,\"precipitation\":0},{\"dt\":1618052220,\"precipitation\":0},{\"dt\":1618052280,\"precipitation\":0},{\"dt\":1618052340,\"precipitation\":0},{\"dt\":1618052400,\"precipitation\":0},{\"dt\":1618052460,\"precipitation\":0},{\"dt\":1618052520,\"precipitation\":0},{\"dt\":1618052580,\"precipitation\":0},{\"dt\":1618052640,\"precipitation\":0},{\"dt\":1618052700,\"precipitation\":0},{\"dt\":1618052760,\"precipitation\":0},{\"dt\":1618052820,\"precipitation\":0},{\"dt\":1618052880,\"precipitation\":0},{\"dt\":1618052940,\"precipitation\":0},{\"dt\":1618053000,\"precipitation\":0},{\"dt\":1618053060,\"precipitation\":0},{\"dt\":1618053120,\"precipitation\":0},{\"dt\":1618053180,\"precipitation\":0},{\"dt\":1618053240,\"precipitation\":0},{\"dt\":1618053300,\"precipitation\":0},{\"dt\":1618053360,\"precipitation\":0},{\"dt\":1618053420,\"precipitation\":0},{\"dt\":1618053480,\"precipitation\":0},{\"dt\":1618053540,\"precipitation\":0},{\"dt\":1618053600,\"precipitation\":0},{\"dt\":1618053660,\"precipitation\":0}],\"hourly\":[{\"dt\":1618048800,\"temp\":11.84,\"feels_like\":10.12,\"pressure\":1018,\"humidity\":40,\"dew_point\":-1.16,\"uvi\":3.36,\"clouds\":0,\"visibility\":10000,\"wind_speed\":7.72,\"wind_deg\":200,\"wind_gust\":10.55,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618052400,\"temp\":11.6,\"feels_like\":9.96,\"pressure\":1018,\"humidity\":44,\"dew_point\":-0.2,\"uvi\":3.22,\"clouds\":32,\"visibility\":10000,\"wind_speed\":7.76,\"wind_deg\":202,\"wind_gust\":10.75,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618056000,\"temp\":12.26,\"feels_like\":10.72,\"pressure\":1018,\"humidity\":45,\"dew_point\":0.68,\"uvi\":2.67,\"clouds\":49,\"visibility\":10000,\"wind_speed\":7.66,\"wind_deg\":205,\"wind_gust\":10.76,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618059600,\"temp\":12.86,\"feels_like\":11.4,\"pressure\":1018,\"humidity\":46,\"dew_point\":1.53,\"uvi\":1.22,\"clouds\":19,\"visibility\":10000,\"wind_speed\":7.15,\"wind_deg\":210,\"wind_gust\":9.99,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02d\"}],\"pop\":0},{\"dt\":1618063200,\"temp\":13.04,\"feels_like\":11.68,\"pressure\":1018,\"humidity\":49,\"dew_point\":2.58,\"uvi\":0.7,\"clouds\":50,\"visibility\":10000,\"wind_speed\":5.95,\"wind_deg\":210,\"wind_gust\":9.4,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618066800,\"temp\":12.53,\"feels_like\":11.2,\"pressure\":1018,\"humidity\":52,\"dew_point\":3.07,\"uvi\":0.32,\"clouds\":48,\"visibility\":10000,\"wind_speed\":5.43,\"wind_deg\":209,\"wind_gust\":9.32,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618070400,\"temp\":11.02,\"feels_like\":9.72,\"pressure\":1018,\"humidity\":59,\"dew_point\":3.37,\"uvi\":0.12,\"clouds\":51,\"visibility\":10000,\"wind_speed\":4.22,\"wind_deg\":200,\"wind_gust\":8.94,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618074000,\"temp\":8.81,\"feels_like\":6.7,\"pressure\":1019,\"humidity\":67,\"dew_point\":3.23,\"uvi\":0,\"clouds\":55,\"visibility\":10000,\"wind_speed\":3.67,\"wind_deg\":184,\"wind_gust\":8.41,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618077600,\"temp\":7.94,\"feels_like\":5.47,\"pressure\":1020,\"humidity\":71,\"dew_point\":3.06,\"uvi\":0,\"clouds\":59,\"visibility\":10000,\"wind_speed\":4,\"wind_deg\":177,\"wind_gust\":10.47,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618081200,\"temp\":7.68,\"feels_like\":4.81,\"pressure\":1020,\"humidity\":71,\"dew_point\":2.92,\"uvi\":0,\"clouds\":79,\"visibility\":10000,\"wind_speed\":4.71,\"wind_deg\":179,\"wind_gust\":12.11,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618084800,\"temp\":7.78,\"feels_like\":4.76,\"pressure\":1020,\"humidity\":71,\"dew_point\":2.89,\"uvi\":0,\"clouds\":81,\"visibility\":10000,\"wind_speed\":5.12,\"wind_deg\":185,\"wind_gust\":13.11,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618088400,\"temp\":7.45,\"feels_like\":4.33,\"pressure\":1020,\"humidity\":71,\"dew_point\":2.77,\"uvi\":0,\"clouds\":86,\"visibility\":10000,\"wind_speed\":5.17,\"wind_deg\":190,\"wind_gust\":13.37,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618092000,\"temp\":6.97,\"feels_like\":3.85,\"pressure\":1021,\"humidity\":74,\"dew_point\":2.67,\"uvi\":0,\"clouds\":88,\"visibility\":10000,\"wind_speed\":4.89,\"wind_deg\":192,\"wind_gust\":12.68,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618095600,\"temp\":6.46,\"feels_like\":3.4,\"pressure\":1021,\"humidity\":76,\"dew_point\":2.7,\"uvi\":0,\"clouds\":89,\"visibility\":10000,\"wind_speed\":4.5,\"wind_deg\":191,\"wind_gust\":11.89,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618099200,\"temp\":5.96,\"feels_like\":2.9,\"pressure\":1021,\"humidity\":79,\"dew_point\":2.65,\"uvi\":0,\"clouds\":91,\"visibility\":10000,\"wind_speed\":4.26,\"wind_deg\":188,\"wind_gust\":11.23,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618102800,\"temp\":5.31,\"feels_like\":2.22,\"pressure\":1022,\"humidity\":81,\"dew_point\":2.47,\"uvi\":0,\"clouds\":89,\"visibility\":10000,\"wind_speed\":4.04,\"wind_deg\":186,\"wind_gust\":10.72,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618106400,\"temp\":5.2,\"feels_like\":2.18,\"pressure\":1022,\"humidity\":82,\"dew_point\":2.41,\"uvi\":0,\"clouds\":95,\"visibility\":10000,\"wind_speed\":3.87,\"wind_deg\":190,\"wind_gust\":10.92,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618110000,\"temp\":4.56,\"feels_like\":1.51,\"pressure\":1023,\"humidity\":85,\"dew_point\":2.29,\"uvi\":0,\"clouds\":88,\"visibility\":10000,\"wind_speed\":3.69,\"wind_deg\":186,\"wind_gust\":10.33,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618113600,\"temp\":4.51,\"feels_like\":1.64,\"pressure\":1023,\"humidity\":85,\"dew_point\":2.33,\"uvi\":0,\"clouds\":84,\"visibility\":10000,\"wind_speed\":3.39,\"wind_deg\":191,\"wind_gust\":10.18,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618117200,\"temp\":6.05,\"feels_like\":3.41,\"pressure\":1024,\"humidity\":80,\"dew_point\":2.89,\"uvi\":0.15,\"clouds\":80,\"visibility\":10000,\"wind_speed\":3.55,\"wind_deg\":193,\"wind_gust\":10.09,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618120800,\"temp\":7.96,\"feels_like\":5.31,\"pressure\":1024,\"humidity\":72,\"dew_point\":3.29,\"uvi\":0.38,\"clouds\":80,\"visibility\":10000,\"wind_speed\":4.39,\"wind_deg\":197,\"wind_gust\":8.53,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618124400,\"temp\":9.61,\"feels_like\":7.3,\"pressure\":1024,\"humidity\":67,\"dew_point\":3.88,\"uvi\":1.01,\"clouds\":100,\"visibility\":10000,\"wind_speed\":4.5,\"wind_deg\":197,\"wind_gust\":7.77,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618128000,\"temp\":11.14,\"feels_like\":9.93,\"pressure\":1025,\"humidity\":62,\"dew_point\":4.27,\"uvi\":1.54,\"clouds\":100,\"visibility\":10000,\"wind_speed\":4.71,\"wind_deg\":198,\"wind_gust\":7.19,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618131600,\"temp\":12.8,\"feels_like\":11.62,\"pressure\":1025,\"humidity\":57,\"dew_point\":4.57,\"uvi\":1.97,\"clouds\":100,\"visibility\":10000,\"wind_speed\":4.87,\"wind_deg\":196,\"wind_gust\":6.82,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618135200,\"temp\":14.03,\"feels_like\":12.87,\"pressure\":1025,\"humidity\":53,\"dew_point\":4.65,\"uvi\":3.09,\"clouds\":100,\"visibility\":10000,\"wind_speed\":5.11,\"wind_deg\":194,\"wind_gust\":6.7,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618138800,\"temp\":14.82,\"feels_like\":13.66,\"pressure\":1025,\"humidity\":50,\"dew_point\":4.62,\"uvi\":2.96,\"clouds\":100,\"visibility\":10000,\"wind_speed\":5.32,\"wind_deg\":191,\"wind_gust\":6.82,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618142400,\"temp\":15.64,\"feels_like\":14.49,\"pressure\":1025,\"humidity\":47,\"dew_point\":4.48,\"uvi\":2.45,\"clouds\":94,\"visibility\":10000,\"wind_speed\":5.42,\"wind_deg\":192,\"wind_gust\":6.7,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618146000,\"temp\":15.81,\"feels_like\":14.62,\"pressure\":1024,\"humidity\":45,\"dew_point\":4.15,\"uvi\":2.02,\"clouds\":24,\"visibility\":10000,\"wind_speed\":5.15,\"wind_deg\":193,\"wind_gust\":6.3,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02d\"}],\"pop\":0},{\"dt\":1618149600,\"temp\":15.71,\"feels_like\":14.51,\"pressure\":1025,\"humidity\":45,\"dew_point\":3.87,\"uvi\":1.17,\"clouds\":48,\"visibility\":10000,\"wind_speed\":4.79,\"wind_deg\":188,\"wind_gust\":6.01,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}],\"pop\":0},{\"dt\":1618153200,\"temp\":14.79,\"feels_like\":13.63,\"pressure\":1024,\"humidity\":50,\"dew_point\":4.71,\"uvi\":0.53,\"clouds\":63,\"visibility\":10000,\"wind_speed\":3.73,\"wind_deg\":177,\"wind_gust\":6.35,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618156800,\"temp\":12.85,\"feels_like\":11.73,\"pressure\":1024,\"humidity\":59,\"dew_point\":5.18,\"uvi\":0.17,\"clouds\":72,\"visibility\":10000,\"wind_speed\":3.05,\"wind_deg\":159,\"wind_gust\":6.38,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618160400,\"temp\":10.92,\"feels_like\":9.71,\"pressure\":1025,\"humidity\":63,\"dew_point\":4.26,\"uvi\":0,\"clouds\":78,\"visibility\":10000,\"wind_speed\":3.39,\"wind_deg\":152,\"wind_gust\":6.64,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}],\"pop\":0},{\"dt\":1618164000,\"temp\":9.65,\"feels_like\":7.72,\"pressure\":1026,\"humidity\":66,\"dew_point\":3.66,\"uvi\":0,\"clouds\":81,\"visibility\":10000,\"wind_speed\":3.68,\"wind_deg\":148,\"wind_gust\":9.04,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618167600,\"temp\":8.82,\"feels_like\":6.59,\"pressure\":1026,\"humidity\":66,\"dew_point\":3.05,\"uvi\":0,\"clouds\":100,\"visibility\":10000,\"wind_speed\":3.92,\"wind_deg\":147,\"wind_gust\":11.02,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618171200,\"temp\":8.07,\"feels_like\":5.87,\"pressure\":1026,\"humidity\":67,\"dew_point\":2.43,\"uvi\":0,\"clouds\":100,\"visibility\":10000,\"wind_speed\":3.54,\"wind_deg\":154,\"wind_gust\":11.12,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618174800,\"temp\":7.24,\"feels_like\":4.99,\"pressure\":1027,\"humidity\":68,\"dew_point\":1.96,\"uvi\":0,\"clouds\":98,\"visibility\":10000,\"wind_speed\":3.32,\"wind_deg\":154,\"wind_gust\":10.55,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618178400,\"temp\":6.58,\"feels_like\":4.31,\"pressure\":1027,\"humidity\":70,\"dew_point\":1.65,\"uvi\":0,\"clouds\":98,\"visibility\":10000,\"wind_speed\":3.14,\"wind_deg\":154,\"wind_gust\":9.19,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618182000,\"temp\":6.03,\"feels_like\":3.57,\"pressure\":1027,\"humidity\":71,\"dew_point\":1.26,\"uvi\":0,\"clouds\":82,\"visibility\":10000,\"wind_speed\":3.25,\"wind_deg\":150,\"wind_gust\":8.44,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618185600,\"temp\":5.51,\"feels_like\":2.94,\"pressure\":1027,\"humidity\":72,\"dew_point\":0.95,\"uvi\":0,\"clouds\":71,\"visibility\":10000,\"wind_speed\":3.26,\"wind_deg\":150,\"wind_gust\":8.58,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}],\"pop\":0},{\"dt\":1618189200,\"temp\":5,\"feels_like\":2.29,\"pressure\":1027,\"humidity\":74,\"dew_point\":0.72,\"uvi\":0,\"clouds\":9,\"visibility\":10000,\"wind_speed\":3.3,\"wind_deg\":147,\"wind_gust\":8.63,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01n\"}],\"pop\":0},{\"dt\":1618192800,\"temp\":4.47,\"feels_like\":1.69,\"pressure\":1027,\"humidity\":75,\"dew_point\":0.52,\"uvi\":0,\"clouds\":14,\"visibility\":10000,\"wind_speed\":3.25,\"wind_deg\":145,\"wind_gust\":8.14,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}],\"pop\":0},{\"dt\":1618196400,\"temp\":4.03,\"feels_like\":1.08,\"pressure\":1026,\"humidity\":76,\"dew_point\":0.22,\"uvi\":0,\"clouds\":12,\"visibility\":10000,\"wind_speed\":3.36,\"wind_deg\":138,\"wind_gust\":8.13,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}],\"pop\":0},{\"dt\":1618200000,\"temp\":4,\"feels_like\":0.9,\"pressure\":1026,\"humidity\":75,\"dew_point\":0.13,\"uvi\":0.08,\"clouds\":10,\"visibility\":10000,\"wind_speed\":3.57,\"wind_deg\":131,\"wind_gust\":9.24,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618203600,\"temp\":5.73,\"feels_like\":2.73,\"pressure\":1026,\"humidity\":68,\"dew_point\":0.52,\"uvi\":0.33,\"clouds\":8,\"visibility\":10000,\"wind_speed\":4.05,\"wind_deg\":138,\"wind_gust\":10.36,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618207200,\"temp\":7.87,\"feels_like\":4.9,\"pressure\":1025,\"humidity\":59,\"dew_point\":0.53,\"uvi\":0.84,\"clouds\":7,\"visibility\":10000,\"wind_speed\":5.05,\"wind_deg\":145,\"wind_gust\":10.48,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618210800,\"temp\":9.9,\"feels_like\":7.29,\"pressure\":1025,\"humidity\":53,\"dew_point\":0.8,\"uvi\":1.56,\"clouds\":0,\"visibility\":10000,\"wind_speed\":5.48,\"wind_deg\":147,\"wind_gust\":9.78,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618214400,\"temp\":11.5,\"feels_like\":9.93,\"pressure\":1025,\"humidity\":47,\"dew_point\":0.86,\"uvi\":2.37,\"clouds\":0,\"visibility\":10000,\"wind_speed\":5.94,\"wind_deg\":152,\"wind_gust\":8.78,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0},{\"dt\":1618218000,\"temp\":12.64,\"feels_like\":11.08,\"pressure\":1024,\"humidity\":43,\"dew_point\":0.68,\"uvi\":3.04,\"clouds\":0,\"visibility\":10000,\"wind_speed\":6.25,\"wind_deg\":151,\"wind_gust\":8.5,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"pop\":0}],\"daily\":[{\"dt\":1618048800,\"sunrise\":1618024927,\"sunset\":1618074079,\"temp\":{\"day\":11.84,\"min\":1.69,\"max\":13.04,\"night\":7.78,\"eve\":11.02,\"morn\":1.69},\"feels_like\":{\"day\":10.12,\"night\":-2.86,\"eve\":9.72,\"morn\":-2.86},\"pressure\":1018,\"humidity\":40,\"dew_point\":-1.16,\"wind_speed\":7.72,\"wind_deg\":200,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"clouds\":0,\"pop\":0,\"uvi\":3.36},{\"dt\":1618135200,\"sunrise\":1618111186,\"sunset\":1618160588,\"temp\":{\"day\":14.03,\"min\":4.51,\"max\":15.81,\"night\":8.07,\"eve\":12.85,\"morn\":4.51},\"feels_like\":{\"day\":12.87,\"night\":1.64,\"eve\":11.73,\"morn\":1.64},\"pressure\":1025,\"humidity\":53,\"dew_point\":4.65,\"wind_speed\":5.11,\"wind_deg\":194,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}],\"clouds\":100,\"pop\":0,\"uvi\":3.09},{\"dt\":1618221600,\"sunrise\":1618197445,\"sunset\":1618247098,\"temp\":{\"day\":13.62,\"min\":4,\"max\":14.94,\"night\":7.62,\"eve\":12.21,\"morn\":4},\"feels_like\":{\"day\":12.08,\"night\":0.9,\"eve\":10.87,\"morn\":0.9},\"pressure\":1024,\"humidity\":40,\"dew_point\":0.58,\"wind_speed\":6.09,\"wind_deg\":152,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"clouds\":0,\"pop\":0,\"uvi\":3.11},{\"dt\":1618308000,\"sunrise\":1618283705,\"sunset\":1618333607,\"temp\":{\"day\":14.42,\"min\":4.45,\"max\":16.91,\"night\":11.72,\"eve\":16.64,\"morn\":4.45},\"feels_like\":{\"day\":13.17,\"night\":2.28,\"eve\":15.43,\"morn\":2.28},\"pressure\":1019,\"humidity\":48,\"dew_point\":3.67,\"wind_speed\":3.07,\"wind_deg\":153,\"weather\":[{\"id\":800,\"main\":\"Clear\",\"description\":\"clear sky\",\"icon\":\"01d\"}],\"clouds\":8,\"pop\":0,\"uvi\":3.53},{\"dt\":1618394400,\"sunrise\":1618369965,\"sunset\":1618420117,\"temp\":{\"day\":10.12,\"min\":7.84,\"max\":10.7,\"night\":8.87,\"eve\":9.78,\"morn\":7.84},\"feels_like\":{\"day\":9.54,\"night\":6.26,\"eve\":8.02,\"morn\":6.26},\"pressure\":1012,\"humidity\":90,\"dew_point\":8.65,\"wind_speed\":3.6,\"wind_deg\":129,\"weather\":[{\"id\":500,\"main\":\"Rain\",\"description\":\"light rain\",\"icon\":\"10d\"}],\"clouds\":100,\"pop\":0.98,\"rain\":2.37,\"uvi\":2.7},{\"dt\":1618480800,\"sunrise\":1618456226,\"sunset\":1618506627,\"temp\":{\"day\":8.88,\"min\":6.86,\"max\":10.63,\"night\":8.88,\"eve\":10.63,\"morn\":6.86},\"feels_like\":{\"day\":7.04,\"night\":5.16,\"eve\":9.81,\"morn\":5.16},\"pressure\":1011,\"humidity\":86,\"dew_point\":6.85,\"wind_speed\":3.22,\"wind_deg\":98,\"weather\":[{\"id\":500,\"main\":\"Rain\",\"description\":\"light rain\",\"icon\":\"10d\"}],\"clouds\":100,\"pop\":0.91,\"rain\":4.23,\"uvi\":3},{\"dt\":1618567200,\"sunrise\":1618542488,\"sunset\":1618593137,\"temp\":{\"day\":5.06,\"min\":4.81,\"max\":8.16,\"night\":4.84,\"eve\":7.24,\"morn\":5.39},\"feels_like\":{\"day\":2.81,\"night\":1.18,\"eve\":6.67,\"morn\":1.18},\"pressure\":1010,\"humidity\":91,\"dew_point\":3.81,\"wind_speed\":2.69,\"wind_deg\":101,\"weather\":[{\"id\":501,\"main\":\"Rain\",\"description\":\"moderate rain\",\"icon\":\"10d\"}],\"clouds\":100,\"pop\":1,\"rain\":15.37,\"uvi\":3},{\"dt\":1618653600,\"sunrise\":1618628751,\"sunset\":1618679646,\"temp\":{\"day\":3.07,\"min\":1.88,\"max\":5.32,\"night\":1.88,\"eve\":2.04,\"morn\":4.62},\"feels_like\":{\"day\":-0.42,\"night\":1.75,\"eve\":-1.74,\"morn\":1.75},\"pressure\":1010,\"humidity\":97,\"dew_point\":2.74,\"wind_speed\":3.86,\"wind_deg\":287,\"weather\":[{\"id\":616,\"main\":\"Snow\",\"description\":\"rain and snow\",\"icon\":\"13d\"}],\"clouds\":100,\"pop\":1,\"rain\":6.67,\"snow\":8.83,\"uvi\":3}]}"; @@ -90,12 +85,6 @@ public class OneCallWeatherResponseMapperUnitTest { assertNotNull(currentWeatherData.getAlerts()); } - @Test - public void mapToHistorical() { - final String jsonString = "{\"lat\":60.99,\"lon\":30.9,\"timezone\":\"Europe/Moscow\",\"timezone_offset\":10800,\"current\":{\"dt\":1617739371,\"sunrise\":1617678248,\"sunset\":1617727984,\"snow\":{\"1h\":20},\"temp\":0,\"feels_like\":-10.26,\"pressure\":986,\"humidity\":73,\"dew_point\":-3.77,\"uvi\":1.69,\"clouds\":33,\"wind_deg\":196,\"wind_gust\":15.97,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},\"hourly\":[{\"dt\":1617667200,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617670800,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617674400,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617678000,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617681600,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617685200,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617688800,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617692400,\"temp\":0.49,\"feels_like\":-7.81,\"pressure\":988,\"humidity\":76,\"dew_point\":-2.87,\"clouds\":100,\"wind_speed\":8.41,\"wind_deg\":196,\"wind_gust\":10.8,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617696000,\"temp\":0.74,\"feels_like\":-7.87,\"pressure\":988,\"humidity\":74,\"dew_point\":-2.97,\"clouds\":100,\"wind_speed\":8.83,\"wind_deg\":196,\"wind_gust\":11.38,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617699600,\"temp\":0.98,\"feels_like\":-7.65,\"pressure\":989,\"humidity\":69,\"dew_point\":-3.59,\"clouds\":99,\"wind_speed\":8.75,\"wind_deg\":203,\"wind_gust\":11.54,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617703200,\"temp\":1.18,\"feels_like\":-7.24,\"pressure\":989,\"humidity\":67,\"dew_point\":-3.77,\"clouds\":86,\"wind_speed\":8.41,\"wind_deg\":205,\"wind_gust\":11.41,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617706800,\"temp\":1.39,\"feels_like\":-6.84,\"pressure\":990,\"humidity\":66,\"dew_point\":-3.77,\"clouds\":77,\"wind_speed\":8.15,\"wind_deg\":199,\"wind_gust\":10.92,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}]},{\"dt\":1617710400,\"temp\":1.53,\"feels_like\":-6.93,\"pressure\":990,\"humidity\":66,\"dew_point\":-3.65,\"clouds\":42,\"wind_speed\":8.49,\"wind_deg\":191,\"wind_gust\":11.93,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}]},{\"dt\":1617714000,\"temp\":1.76,\"feels_like\":-7.16,\"pressure\":991,\"humidity\":66,\"dew_point\":-3.46,\"clouds\":47,\"wind_speed\":9.19,\"wind_deg\":199,\"wind_gust\":13.07,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}]},{\"dt\":1617717600,\"temp\":1.64,\"feels_like\":-7.85,\"pressure\":991,\"humidity\":72,\"dew_point\":-2.53,\"clouds\":67,\"wind_speed\":10.18,\"wind_deg\":205,\"wind_gust\":13.98,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}]},{\"dt\":1617721200,\"temp\":1.49,\"feels_like\":-7.59,\"pressure\":991,\"humidity\":76,\"dew_point\":-2.01,\"clouds\":52,\"wind_speed\":9.7,\"wind_deg\":197,\"wind_gust\":13.49,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}]},{\"dt\":1617724800,\"temp\":1.68,\"feels_like\":-7.57,\"pressure\":992,\"humidity\":76,\"dew_point\":-1.85,\"clouds\":42,\"wind_speed\":9.97,\"wind_deg\":193,\"wind_gust\":14.21,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}]},{\"dt\":1617728400,\"temp\":1.67,\"feels_like\":-7.65,\"pressure\":984,\"humidity\":75,\"dew_point\":-2.01,\"clouds\":38,\"wind_speed\":10.04,\"wind_deg\":190,\"wind_gust\":14.59,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617732000,\"temp\":1.67,\"feels_like\":-8.41,\"pressure\":984,\"humidity\":73,\"dew_point\":-2.34,\"clouds\":37,\"wind_speed\":11.06,\"wind_deg\":191,\"wind_gust\":16.02,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617735600,\"temp\":1.11,\"feels_like\":-9.22,\"pressure\":985,\"humidity\":73,\"dew_point\":-2.82,\"clouds\":56,\"wind_speed\":11.32,\"wind_deg\":195,\"wind_gust\":16.33,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}]},{\"dt\":1617739200,\"temp\":0,\"feels_like\":-10.26,\"pressure\":986,\"humidity\":73,\"dew_point\":-3.77,\"clouds\":33,\"wind_speed\":11.04,\"wind_deg\":196,\"wind_gust\":15.97,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617742800,\"temp\":0.56,\"feels_like\":-9.73,\"pressure\":986,\"humidity\":72,\"dew_point\":-3.45,\"clouds\":26,\"wind_speed\":11.14,\"wind_deg\":202,\"wind_gust\":16.22,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617746400,\"temp\":0.56,\"feels_like\":-9.57,\"pressure\":986,\"humidity\":72,\"dew_point\":-3.45,\"clouds\":21,\"wind_speed\":10.92,\"wind_deg\":206,\"wind_gust\":16.09,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}]},{\"dt\":1617750000,\"temp\":0,\"feels_like\":-9.73,\"pressure\":987,\"humidity\":74,\"dew_point\":-3.61,\"clouds\":19,\"wind_speed\":10.31,\"wind_deg\":207,\"wind_gust\":15.05,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}]}]}"; - - } - @Test public void mapToHistorical_withInvalidJSON() { final String jsonString = "{\"lat\":60.99lon\":30.9,\"timezone\":\"Europe/Moscow\",\"timezone_offset\":10800,\"current\":{\"dt\":1617739371,\"sunrise\":1617678248,\"sunset\":1617727984,\"temp\":0,\"feels_like\":-10.26,\"pressure\":986,\"humidity\":73,\"dew_point\":-3.77,\"uvi\":1.69,\"clouds\":33,\"wind_speed\":11.04,\"wind_gust\":15.97,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},\"hourly\":[{\"dt\":1617667200,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617670800,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617674400,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617678000,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04n\"}]},{\"dt\":1617681600,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617685200,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617688800,\"temp\":0.22,\"feels_like\":-7.76,\"pressure\":988,\"humidity\":80,\"dew_point\":-2.49,\"clouds\":100,\"wind_speed\":8.02,\"wind_deg\":199,\"wind_gust\":10.5,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617692400,\"temp\":0.49,\"feels_like\":-7.81,\"pressure\":988,\"humidity\":76,\"dew_point\":-2.87,\"clouds\":100,\"wind_speed\":8.41,\"wind_deg\":196,\"wind_gust\":10.8,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617696000,\"temp\":0.74,\"feels_like\":-7.87,\"pressure\":988,\"humidity\":74,\"dew_point\":-2.97,\"clouds\":100,\"wind_speed\":8.83,\"wind_deg\":196,\"wind_gust\":11.38,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617699600,\"temp\":0.98,\"feels_like\":-7.65,\"pressure\":989,\"humidity\":69,\"dew_point\":-3.59,\"clouds\":99,\"wind_speed\":8.75,\"wind_deg\":203,\"wind_gust\":11.54,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617703200,\"temp\":1.18,\"feels_like\":-7.24,\"pressure\":989,\"humidity\":67,\"dew_point\":-3.77,\"clouds\":86,\"wind_speed\":8.41,\"wind_deg\":205,\"wind_gust\":11.41,\"weather\":[{\"id\":804,\"main\":\"Clouds\",\"description\":\"overcast clouds\",\"icon\":\"04d\"}]},{\"dt\":1617706800,\"temp\":1.39,\"feels_like\":-6.84,\"pressure\":990,\"humidity\":66,\"dew_point\":-3.77,\"clouds\":77,\"wind_speed\":8.15,\"wind_deg\":199,\"wind_gust\":10.92,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}]},{\"dt\":1617710400,\"temp\":1.53,\"feels_like\":-6.93,\"pressure\":990,\"humidity\":66,\"dew_point\":-3.65,\"clouds\":42,\"wind_speed\":8.49,\"wind_deg\":191,\"wind_gust\":11.93,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}]},{\"dt\":1617714000,\"temp\":1.76,\"feels_like\":-7.16,\"pressure\":991,\"humidity\":66,\"dew_point\":-3.46,\"clouds\":47,\"wind_speed\":9.19,\"wind_deg\":199,\"wind_gust\":13.07,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}]},{\"dt\":1617717600,\"temp\":1.64,\"feels_like\":-7.85,\"pressure\":991,\"humidity\":72,\"dew_point\":-2.53,\"clouds\":67,\"wind_speed\":10.18,\"wind_deg\":205,\"wind_gust\":13.98,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}]},{\"dt\":1617721200,\"temp\":1.49,\"feels_like\":-7.59,\"pressure\":991,\"humidity\":76,\"dew_point\":-2.01,\"clouds\":52,\"wind_speed\":9.7,\"wind_deg\":197,\"wind_gust\":13.49,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04d\"}]},{\"dt\":1617724800,\"temp\":1.68,\"feels_like\":-7.57,\"pressure\":992,\"humidity\":76,\"dew_point\":-1.85,\"clouds\":42,\"wind_speed\":9.97,\"wind_deg\":193,\"wind_gust\":14.21,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03d\"}]},{\"dt\":1617728400,\"temp\":1.67,\"feels_like\":-7.65,\"pressure\":984,\"humidity\":75,\"dew_point\":-2.01,\"clouds\":38,\"wind_speed\":10.04,\"wind_deg\":190,\"wind_gust\":14.59,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617732000,\"temp\":1.67,\"feels_like\":-8.41,\"pressure\":984,\"humidity\":73,\"dew_point\":-2.34,\"clouds\":37,\"wind_speed\":11.06,\"wind_deg\":191,\"wind_gust\":16.02,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617735600,\"temp\":1.11,\"feels_like\":-9.22,\"pressure\":985,\"humidity\":73,\"dew_point\":-2.82,\"clouds\":56,\"wind_speed\":11.32,\"wind_deg\":195,\"wind_gust\":16.33,\"weather\":[{\"id\":803,\"main\":\"Clouds\",\"description\":\"broken clouds\",\"icon\":\"04n\"}]},{\"dt\":1617739200,\"temp\":0,\"feels_like\":-10.26,\"pressure\":986,\"humidity\":73,\"dew_point\":-3.77,\"clouds\":33,\"wind_speed\":11.04,\"wind_deg\":196,\"wind_gust\":15.97,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617742800,\"temp\":0.56,\"feels_like\":-9.73,\"pressure\":986,\"humidity\":72,\"dew_point\":-3.45,\"clouds\":26,\"wind_speed\":11.14,\"wind_deg\":202,\"wind_gust\":16.22,\"weather\":[{\"id\":802,\"main\":\"Clouds\",\"description\":\"scattered clouds\",\"icon\":\"03n\"}]},{\"dt\":1617746400,\"temp\":0.56,\"feels_like\":-9.57,\"pressure\":986,\"humidity\":72,\"dew_point\":-3.45,\"clouds\":21,\"wind_speed\":10.92,\"wind_deg\":206,\"wind_gust\":16.09,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}]},{\"dt\":1617750000,\"temp\":0,\"feels_like\":-9.73,\"pressure\":987,\"humidity\":74,\"dew_point\":-3.61,\"clouds\":19,\"wind_speed\":10.31,\"wind_deg\":207,\"wind_gust\":15.05,\"weather\":[{\"id\":801,\"main\":\"Clouds\",\"description\":\"few clouds\",\"icon\":\"02n\"}]}]}"; diff --git a/src/test/java/com/github/prominence/openweathermap/api/request/weather/multiple/MultipleResultCurrentWeatherIntegrationTest.java b/src/test/java/com/github/prominence/openweathermap/api/request/weather/multiple/MultipleResultCurrentWeatherIntegrationTest.java index cb4ea85..2996967 100644 --- a/src/test/java/com/github/prominence/openweathermap/api/request/weather/multiple/MultipleResultCurrentWeatherIntegrationTest.java +++ b/src/test/java/com/github/prominence/openweathermap/api/request/weather/multiple/MultipleResultCurrentWeatherIntegrationTest.java @@ -86,6 +86,34 @@ public class MultipleResultCurrentWeatherIntegrationTest extends ApiTest { assertTrue(weatherJson.startsWith("{")); } + @Test + public void whenGetMultipleCurrentWeatherByCoordinateRectangleAsyncRequestAsJava_thenReturnNotNull() throws ExecutionException, InterruptedException { + final CompletableFuture> weatherListFuture = getClient() + .currentWeather() + .multiple() + .byRectangle(CoordinateRectangle.withValues(12, 32, 15, 37), 10) + .language(Language.ROMANIAN) + .unitSystem(UnitSystem.METRIC) + .retrieveAsync() + .asJava(); + + assertNotNull(weatherListFuture.get()); + } + + @Test + public void whenGetMultipleCurrentWeatherByCoordinateRectangleAsyncRequestAsJSON_thenReturnNotNull() throws ExecutionException, InterruptedException { + final CompletableFuture weatherJsonFuture = getClient() + .currentWeather() + .multiple() + .byRectangle(CoordinateRectangle.withValues(12, 32, 15, 37), 10) + .language(Language.ROMANIAN) + .unitSystem(UnitSystem.METRIC) + .retrieveAsync() + .asJSON(); + + assertTrue(weatherJsonFuture.get().startsWith("{")); + } + @Test public void whenGetMultipleCurrentWeatherByCitiesInCycleRequestAsJava_thenReturnNotNull() { final List weatherList = getClient() @@ -225,6 +253,21 @@ public class MultipleResultCurrentWeatherIntegrationTest extends ApiTest { System.out.println(weatherFuture.get()); } + @Test + public void whenGetMultipleCurrentWeatherByCoordinateAsyncRequestAsXML_thenReturnNotNull() throws ExecutionException, InterruptedException { + final CompletableFuture weatherXMLFuture = getClient() + .currentWeather() + .multiple() + .byCitiesInCycle(Coordinate.of(55.5, 37.5), 10) + .language(Language.GERMAN) + .unitSystem(UnitSystem.IMPERIAL) + .retrieveAsync() + .asXML(); + + assertNotNull(weatherXMLFuture); + System.out.println(weatherXMLFuture.get()); + } + @Test public void whenRequestCurrentWeatherWithInvalidApiKey_thenThrowAnException() { OpenWeatherMapClient client = new OpenWeatherMapClient("invalidKey");