diff --git a/src/main/java/eirb/pg203/Main.java b/src/main/java/eirb/pg203/Main.java index 2b3ff42..d6bab3c 100644 --- a/src/main/java/eirb/pg203/Main.java +++ b/src/main/java/eirb/pg203/Main.java @@ -15,6 +15,7 @@ public class Main { display.addAPI(openMeteo); display.addAPI(openWeatherMap); - display.display(5, "Bordeaux"); + // weatherAPI can't fetch for more than 3 days with free plan + display.display(3, "Bordeaux"); } } diff --git a/src/main/java/eirb/pg203/OpenMeteo.java b/src/main/java/eirb/pg203/OpenMeteo.java index 975bc4c..c9a070f 100644 --- a/src/main/java/eirb/pg203/OpenMeteo.java +++ b/src/main/java/eirb/pg203/OpenMeteo.java @@ -9,15 +9,18 @@ import java.net.URI; import java.net.URL; import java.time.Instant; import java.util.ArrayList; +import eirb.pg203.WeatherData.Condition; // https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&hourly=temperature_2m public class OpenMeteo implements WeatherDataAPI { private static final String forecastBaseURL = "https://api.open-meteo.com/v1/forecast"; + private static final String dailyQuery = "weather_code,temperature_2m_max,temperature_2m_min,wind_speed_10m_max,wind_direction_10m_dominant"; + // https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM private JSONObject fetchWeather(int days, City city) throws IOException { URL url = URI.create( - String.format(forecastBaseURL + "?latitude=%.2f&longitude=%.2f&forecast_days=%d&daily=apparent_temperature_min,apparent_temperature_max", + String.format(forecastBaseURL + "?latitude=%.2f&longitude=%.2f&forecast_days=%d&daily=" + dailyQuery, city.getCityCoords().getLat(), city.getCityCoords().getLon(), days @@ -27,34 +30,66 @@ public class OpenMeteo implements WeatherDataAPI { return JSONFetcher.fetch(url); } + private static Condition getConditionFromCode(int WMOCode) { + if (WMOCode < 20) + return Condition.SUNNY; + else if (WMOCode < 30) + return Condition.RAINY; + else if (WMOCode < 50) + return Condition.CLOUDY; + else + return Condition.RAINY; + } + + + private static WeatherData getWeatherDataFromForecast(JSONObject response, int day, String city) { + JSONObject daily = response.getJSONObject("daily"); + float max_temp = daily.getJSONArray("temperature_2m_max").getFloat(day); + float min_temp = daily.getJSONArray("temperature_2m_min").getFloat(day); + float temp_c = (min_temp + max_temp) / 2; + + float windSpeed = daily.getJSONArray("wind_speed_10m_max").getFloat(day); + float windDirection = daily.getJSONArray("wind_direction_10m_dominant").getFloat(day); + int conditionCode = daily.getJSONArray("weather_code").getInt(day); + + return new WeatherData( + new City(city), + Instant.now(), + temp_c, + windSpeed, + windDirection, + getConditionFromCode(conditionCode) + ); + } + /** * @param day Day, 0 ≤ day ≤ 14 */ @Override - public Temperature getTemperature(int day, String city) throws IOException { - JSONObject result = fetchWeather(day+1, new City(city)); - JSONObject daily = result.getJSONObject("daily"); - float temp_c = (daily.getJSONArray("apparent_temperature_max").getFloat(0) + daily.getJSONArray("apparent_temperature_min").getFloat(0)) / 2; + public WeatherData getTemperature(int day, String city) throws IOException { + JSONObject result = fetchWeather(day + 1, new City(city)); - return new Temperature(temp_c, new City(city), Instant.now()); + return getWeatherDataFromForecast(result, day, city); } @Override - public Temperature getTemperature(int day, int hour, String city) throws IOException{ + public WeatherData getTemperature(int day, int hour, String city) throws IOException{ return getTemperature(day, city); } - @Override - public ArrayList getTemperatures(int days, String city) throws IOException { - JSONObject result = fetchWeather(days, new City(city)); - ArrayList temperatures = new ArrayList<>(); - JSONObject daily = result.getJSONObject("daily"); - for (int day = 0; day < days; ++day) { - float temp_c = (daily.getJSONArray("apparent_temperature_max").getFloat(day) + daily.getJSONArray("apparent_temperature_min").getFloat(day)) / 2; - temperatures.add(new Temperature(temp_c, new City(city), Instant.now())); - } - return temperatures; - } + @Override + public ArrayList getTemperatures(int days, String city) throws IOException { + JSONObject result = fetchWeather(days, new City(city)); + ArrayList weatherDatas = new ArrayList<>(); + + for (int day = 0; day < days; ++day) { + weatherDatas.add( + getWeatherDataFromForecast(result, day, city) + ); + } + + return weatherDatas; + } @Override public String getAPIName() { diff --git a/src/main/java/eirb/pg203/OpenWeatherMap.java b/src/main/java/eirb/pg203/OpenWeatherMap.java index ed5b8bb..6ea2d91 100644 --- a/src/main/java/eirb/pg203/OpenWeatherMap.java +++ b/src/main/java/eirb/pg203/OpenWeatherMap.java @@ -12,6 +12,7 @@ import java.time.DayOfWeek; import java.time.Instant; import java.time.ZoneId; import java.util.ArrayList; +import eirb.pg203.WeatherData.Condition; // https://api.open-meteo.com/v1/forecast?latitude=52.52&longitude=13.41&hourly=temperature_2m @@ -36,43 +37,91 @@ public class OpenWeatherMap implements WeatherDataAPI { return JSONFetcher.fetch(url); } + private static WeatherData getWeatherDataFromForecast(JSONObject response, int day, String city) { + JSONArray list = response.getJSONArray("list"); + + DayOfWeek targetedDay = Instant.now().plusSeconds(day * 24 * 3600).atZone(ZoneId.systemDefault()).getDayOfWeek(); + DayOfWeek dayOfWeek; + int dataCount = 0; + float temp_c = 0; + float windSpeed = 0; + float windDirection = 0; + float pop = 0; // probability of precipitation + float cloudiness = 0; // percent + + for (int i = 0 ; i < list.length() ; ++i) + { + JSONObject data = list.getJSONObject(i); + int timestamp = data.getInt("dt"); + dayOfWeek = Instant.ofEpochSecond(timestamp).atZone(ZoneId.systemDefault()).getDayOfWeek(); + + if (dayOfWeek == targetedDay) + { + temp_c += data.getJSONObject("main").getFloat("temp"); + windSpeed += data.getJSONObject("wind").getFloat("speed"); + windDirection += data.getJSONObject("wind").getFloat("deg"); + cloudiness += data.getJSONObject("clouds").getFloat("all"); + pop += data.getFloat("pop"); + dataCount++; + } + } + + if (dataCount != 0) + { + temp_c /= dataCount; + windSpeed /= dataCount; + windDirection /= dataCount; + pop /= dataCount; + cloudiness /= dataCount; + } + + Condition condition = Condition.SUNNY; + if (pop > 50) + condition = Condition.RAINY; + else if (cloudiness > 75) + condition = Condition.CLOUDY; + else if (cloudiness > 25) + condition = Condition.PARTIAL; + + + return new WeatherData( + new City(city), + Instant.now().plusSeconds(day * 24 * 3600), + temp_c, + windSpeed, + windDirection, + condition + ); + } + /** * @param day Day, 0 ≤ day ≤ 14 */ @Override - public Temperature getTemperature(int day, String city) throws IOException { + public WeatherData getTemperature(int day, String city) throws IOException { JSONObject result = fetchWeather(day+1, new City(city)); - JSONArray list = result.getJSONArray("list"); - float temp_c = list.getJSONObject(0).getJSONObject("main").getFloat("temp"); - return new Temperature(temp_c, new City(city), Instant.now()); + return getWeatherDataFromForecast(result, day, city); } @Override - public Temperature getTemperature(int day, int hour, String city) throws IOException{ + public WeatherData getTemperature(int day, int hour, String city) throws IOException{ return getTemperature(day, city); } @Override - public ArrayList getTemperatures(int days, String city) throws IOException { + public ArrayList getTemperatures(int days, String city) throws IOException { JSONObject result = fetchWeather(days, new City(city)); - JSONArray list = result.getJSONArray("list"); - ArrayList temperatures = new ArrayList<>(); - int cnt = result.getInt("cnt"); - DayOfWeek day = Instant.now().atZone(ZoneId.systemDefault()).getDayOfWeek(); + ArrayList weatherDatas = new ArrayList<>(); - for (int i = 0; i < cnt; ++i) { - JSONObject data = list.getJSONObject(i); - int timestamp = data.getInt("dt"); - - if (day == Instant.ofEpochSecond(timestamp).atZone(ZoneId.systemDefault()).getDayOfWeek()) - { - float temp_c = data.getJSONObject("main").getFloat("temp"); - temperatures.add(new Temperature(temp_c, new City(city), Instant.now())); - } + for (int day = 0; day < days ; ++day) { + weatherDatas.add( + getWeatherDataFromForecast(result, day, city) + ); } - return temperatures; + + return weatherDatas; } @Override diff --git a/src/main/java/eirb/pg203/WeatherAPI.java b/src/main/java/eirb/pg203/WeatherAPI.java index 8bffa02..0424acf 100644 --- a/src/main/java/eirb/pg203/WeatherAPI.java +++ b/src/main/java/eirb/pg203/WeatherAPI.java @@ -3,6 +3,7 @@ package eirb.pg203; import org.json.JSONArray; import org.json.JSONObject; +import eirb.pg203.WeatherData.Condition; import eirb.pg203.utils.JSONFetcher; import java.io.IOException; @@ -31,33 +32,73 @@ public class WeatherAPI implements WeatherDataAPI{ return JSONFetcher.fetch(url); } + private static WeatherData.Condition getConditionFromString(String str) { + if (str.toLowerCase().contains("rain")) + return Condition.RAINY; + + else if (str.toLowerCase().contains("partly")) + return Condition.PARTIAL; + + else if (str.toLowerCase().contains("cloud")) + return Condition.CLOUDY; + + return Condition.SUNNY; + } + + private static WeatherData getWeatherDataFromForecast(JSONObject response, int day, String city) { + JSONObject forecastDay = response.getJSONObject("forecast").getJSONArray("forecastday").getJSONObject(day); + JSONArray forecastHour = forecastDay.getJSONArray("hour"); + float temp_c = forecastDay.getJSONObject("day").getFloat("avgtemp_c"); + + // Calculates the mean for the day + float windSpeed = 0; + float windDirection = 0; + for (int i = 0 ; i < forecastHour.length() ; ++i) + { + windSpeed += forecastHour.getJSONObject(i).getFloat("wind_kph"); + windDirection += forecastHour.getJSONObject(i).getFloat("wind_degree"); + } + windSpeed /= forecastHour.length(); + windDirection /= forecastHour.length(); + + String conditionStr = forecastDay.getJSONObject("day").getJSONObject("condition").getString("text"); + + return new WeatherData( + new City(city), + Instant.now(), + temp_c, + windSpeed, + windDirection, + getConditionFromString(conditionStr) + ); + } + /** * @param day Day, 0 ≤ day ≤ 14 */ @Override - public Temperature getTemperature(int day, String city) throws IOException { + public WeatherData getTemperature(int day, String city) throws IOException { JSONObject result = fetchWeather(day+1, city); - JSONArray forecast = result.getJSONObject("forecast").getJSONArray("forecastday"); - float temp_c = forecast.getJSONObject(day).getJSONObject("day").getFloat("avgtemp_c"); - - return new Temperature(temp_c, new City(city), Instant.now()); + return getWeatherDataFromForecast(result, day, city); } @Override - public Temperature getTemperature(int day, int hour, String city) throws IOException{ + public WeatherData getTemperature(int day, int hour, String city) throws IOException{ return getTemperature(day, city); } @Override - public ArrayList getTemperatures(int days, String city) throws IOException { + public ArrayList getTemperatures(int days, String city) throws IOException { JSONObject result = fetchWeather(days, city); - ArrayList temperatures = new ArrayList<>(); + ArrayList weatherDatas = new ArrayList<>(); + for (int day = 0; day < days; ++day) { - JSONArray forecast = result.getJSONObject("forecast").getJSONArray("forecastday"); - float temp_c = forecast.getJSONObject(day).getJSONObject("day").getFloat("avgtemp_c"); - temperatures.add(new Temperature(temp_c, new City(city), Instant.now())); + weatherDatas.add( + getWeatherDataFromForecast(result, day, city) + ); } - return temperatures; + + return weatherDatas; } @Override diff --git a/src/main/java/eirb/pg203/WeatherData.java b/src/main/java/eirb/pg203/WeatherData.java new file mode 100644 index 0000000..c8b972c --- /dev/null +++ b/src/main/java/eirb/pg203/WeatherData.java @@ -0,0 +1,96 @@ +package eirb.pg203; + +import java.time.Instant; +import java.util.concurrent.locks.Condition; + +class WeatherData { + enum Condition { + SUNNY("☀️"), + PARTIAL("🌤"), + CLOUDY("☁️"), + RAINY("🌧"), + ERROR("E"); + + private String desc; + Condition(String desc) { this.desc = desc; } + + @Override + public String toString() { + return this.desc; + } + } + + private City city; + private Instant date; + private float temp; + private Condition condition; // cloudly, sunny ... + private float windSpeed; + private float windDirection; + + WeatherData(City city, Instant date, float temp, float windSpeed, float windDirection, Condition condition) { + this.city = city; + this.date = date; + this.temp = temp; + this.condition = condition; + this.windSpeed = windSpeed; + this.windDirection = windDirection; + } + + public City getCity() { + return city; + } + + public Instant getDate() { + return date; + } + + public Condition getCondition() { + return condition; + } + + public float getTemp() { + return temp; + } + + public float getWindSpeed() { + return windSpeed; + } + + public float getWindDirection() { + return windDirection; + } + + public void setCity(City city) { + this.city = city; + } + + public void setDate(Instant date) { + this.date = date; + } + + public void setCondition(Condition condition) { + this.condition = condition; + } + + public void setTemp(float temp) { + this.temp = temp; + } + + public void setWindSpeed(float windSpeed) { + this.windSpeed = windSpeed; + } + + public void setWindDirection(float windDirection) { + this.windDirection = windDirection; + } + + @Override + public String toString() { + return String.format("%05.2f° %s %05.2fkm/h %06.2f°", + this.getTemp(), + this.getCondition().toString(), + this.getWindSpeed(), + this.getWindDirection() + ); + } +} diff --git a/src/main/java/eirb/pg203/WeatherDataAPI.java b/src/main/java/eirb/pg203/WeatherDataAPI.java index d76802c..ccea000 100644 --- a/src/main/java/eirb/pg203/WeatherDataAPI.java +++ b/src/main/java/eirb/pg203/WeatherDataAPI.java @@ -12,7 +12,7 @@ public interface WeatherDataAPI { * @return Temperature of the day from the city * @throws IOException when request failed */ - Temperature getTemperature(int day, String city) throws IOException; + WeatherData getTemperature(int day, String city) throws IOException; /** * @@ -22,9 +22,9 @@ public interface WeatherDataAPI { * @return Temperature of the day for a hour from the city * @throws IOException when request failed */ - Temperature getTemperature(int day, int hour, String city) throws IOException; + WeatherData getTemperature(int day, int hour, String city) throws IOException; - ArrayList getTemperatures(int days, String city) throws IOException; + ArrayList getTemperatures(int days, String city) throws IOException; /*** * Name of the API diff --git a/src/main/java/eirb/pg203/WeatherDisplayBasic.java b/src/main/java/eirb/pg203/WeatherDisplayBasic.java index cf2eb9c..f614ee7 100644 --- a/src/main/java/eirb/pg203/WeatherDisplayBasic.java +++ b/src/main/java/eirb/pg203/WeatherDisplayBasic.java @@ -21,29 +21,28 @@ class WeatherDisplayBasic implements WeatherDisplay { System.err.println(line); } - private void displayTemperatures(String ApiName, ArrayList temperatures) { + private void displayWeatherDatas(String ApiName, ArrayList weatherDatas) { StringBuilder line = new StringBuilder(); line.append(ApiName); - for (Temperature temp: temperatures) { + for (WeatherData weatherData: weatherDatas) { line.append('\t') - .append(temp.getTemp()); + .append(weatherData.toString()); } System.err.println(line); } public void display(int days, String city) { - ArrayList temperatures; + ArrayList weatherDatas; displayHeader(days); for (WeatherDataAPI w: apis) { try { - temperatures = w.getTemperatures(days, city); - displayTemperatures(w.getAPIName(), temperatures); + weatherDatas = w.getTemperatures(days, city); + displayWeatherDatas(w.getAPIName(), weatherDatas); } catch (Exception e) { System.err.println(e); - System.err.println("ntm++ martin"); } } }