Commit 11fdab00 authored by Christopher Bohn's avatar Christopher Bohn 🤔
Browse files

Modified existing methods to handle hourly and daily data from "onecall"

Still need some new methods:
- Hourly & Daily dew point
- Hourly & Daily UV index
- Daily morn, day, eve, night, min, max temps, ambient & feels_like
- Dail rain & snow totals
parent 22d0d1b2
......@@ -393,6 +393,13 @@ public class OpenWeatherConnector {
return currentTimestamp;
}
/**
* Extracts epoch-based timestamps from the specified JSON list and places {@link Date} timestamps into the provided
* {@link List}.
* @param jsonListName the name of the JSON list containing data with timestamps
* @param timestamps the possibly-nonempty {@link List} to place the {@link Date} timestamps in
* @return the provided {@link List}, populated with the additional timestamps
*/
private List<Date> moveTimestampsFromJsonArrayToJavaList(String jsonListName, List<Date> timestamps) {
if (data.containsKey(jsonListName)) {
JSONArray entries = (JSONArray)data.get(jsonListName);
......@@ -490,15 +497,20 @@ public class OpenWeatherConnector {
* <p>Provides the broad categories for the forecasted weather. If there are more than one category provided, the
* "primary" category will at the head of the list. The weather categories are guaranteed to be in the same order
* as the corresponding descriptions provided by {@link #getWeatherDescriptions()}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return list of forecasted weather categories
* @see #getWeatherDescriptions()
*/
public List<WeatherCategory> getWeatherCategories(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return getWeatherCategories(getListEntry("list", timestamp));
checkForDataReadiness(Set.of("forecast", "onecall"));
String listName = "list";
if (dataSet.equals("onecall")) {
List<Date> hours = getTimestamps("hourly");
listName = hours.contains(timestamp) ? "hourly" : "daily";
}
return getWeatherCategories(getListEntry(listName, timestamp));
}
/**
......@@ -544,15 +556,20 @@ public class OpenWeatherConnector {
* <p>Provides descriptions of the forecasted weather. If there are more than one weather description provided,
* the "primary" description will be at the head of the list. The weather descriptions are guaranteed to be in
* the same order as the corresponding categories provided by {@link #getWeatherCategories()}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return list of forecasted weather descriptions
* @see #getWeatherCategories()
*/
public List<String> getWeatherDescriptions(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return getWeatherDescriptions(getListEntry("list", timestamp));
checkForDataReadiness(Set.of("forecast", "onecall"));
String listName = "list";
if (dataSet.equals("onecall")) {
List<Date> hours = getTimestamps("hourly");
listName = hours.contains(timestamp) ? "hourly" : "daily";
}
return getWeatherDescriptions(getListEntry(listName, timestamp));
}
/**
......@@ -571,14 +588,19 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted visibility in the default <a href="https://openweathermap.org/current#data">units</a>
* or those specified in the call to {@link #retrieveData(String)}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" (hourly only) datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted visibility, or {@link Long#MIN_VALUE} if visibility is not in the forecast
*/
public long getVisibility(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractLongFromJSON(getListEntry("list", timestamp), "visibility", Long.MIN_VALUE);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("onecall") && !getTimestamps("hourly").contains(timestamp)) {
return Long.MIN_VALUE;
} else {
String listName = dataSet.equals("onecall") ? "hourly" : "list";
return extractLongFromJSON(getListEntry(listName, timestamp), "visibility", Long.MIN_VALUE);
}
}
/**
......@@ -597,14 +619,20 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted temperature in the default <a href="https://openweathermap.org/current#data">units</a>
* or those specified in the call to {@link #retrieveData(String)}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" (hourly only) datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted temperature, or {@link Double#NaN} if temperature is not in the forecast
*/
public double getTemperature(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractDoubleFromJSON(getListEntry("list", timestamp), "main", "temp", Double.NaN);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("forecast")) {
return extractDoubleFromJSON(getListEntry("list", timestamp), "main", "temp", Double.NaN);
} else if (dataSet.equals("onecall") && getTimestamps("hourly").contains(timestamp)) {
return extractDoubleFromJSON(getListEntry("hourly", timestamp), "temp", Double.NaN);
} else {
return Double.NaN;
}
}
/**
......@@ -621,14 +649,21 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted relative humidity (percent of saturation).</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted humidity, or {@link Long#MIN_VALUE} if visibility is not in the forecast
*/
public long getHumidity(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractLongFromJSON(getListEntry("list", timestamp), "main", "humidity", Long.MIN_VALUE);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("forecast")) {
return extractLongFromJSON(getListEntry("list", timestamp), "main", "humidity", Long.MIN_VALUE);
} else if (dataSet.equals("onecall")) {
return extractLongFromJSON(getListEntry(getTimestamps("hourly").contains(timestamp) ? "hourly" : "daily",
timestamp), "humidity", Long.MIN_VALUE);
} else {
return Long.MIN_VALUE;
}
}
/**
......@@ -662,15 +697,21 @@ public class OpenWeatherConnector {
* <p>Provides the forecasted "feels like" temperature in the default
* <a href="https://openweathermap.org/current#data">units</a>
* or those specified in the call to {@link #retrieveData(String)}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" (hourly only) datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted "feels like" temperature, or {@link Double#NaN} if "feels like" temperature is not in
* the forecast
*/
public double getFeelsLike(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractDoubleFromJSON(getListEntry("list", timestamp), "main", "feels_like", Double.NaN);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("forecast")) {
return extractDoubleFromJSON(getListEntry("list", timestamp), "main", "feels_like", Double.NaN);
} else if (dataSet.equals("onecall") && getTimestamps("hourly").contains(timestamp)) {
return extractDoubleFromJSON(getListEntry("hourly", timestamp), "feels_like", Double.NaN);
} else {
return Double.NaN;
}
}
/**
......@@ -689,14 +730,21 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted pressure in the default <a href="https://openweathermap.org/current#data">units</a>
* or those specified in the call to {@link #retrieveData(String)}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted pressure, or {@link Long#MIN_VALUE} if pressure is not in the forecast
*/
public long getPressure(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractLongFromJSON(getListEntry("list", timestamp), "main", "pressure", Long.MIN_VALUE);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("forecast")) {
return extractLongFromJSON(getListEntry("list", timestamp), "main", "pressure", Long.MIN_VALUE);
} else if (dataSet.equals("onecall")) {
return extractLongFromJSON(getListEntry(getTimestamps("hourly").contains(timestamp) ? "hourly" : "daily",
timestamp), "pressure", Long.MIN_VALUE);
} else {
return Long.MIN_VALUE;
}
}
/**
......@@ -716,14 +764,21 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted wind direction, in degrees.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted wind direction, or {@link Long#MIN_VALUE} if winds are not in the forecast
*/
public long getWindDirection(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractLongFromJSON(getListEntry("list", timestamp), "wind", "deg", Long.MIN_VALUE);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("forecast")) {
return extractLongFromJSON(getListEntry("list", timestamp), "wind", "deg", Long.MIN_VALUE);
} else if (dataSet.equals("onecall")) {
return extractLongFromJSON(getListEntry(getTimestamps("hourly").contains(timestamp) ? "hourly" : "daily",
timestamp), "wind_deg", Long.MIN_VALUE);
} else {
return Long.MIN_VALUE;
}
}
/**
......@@ -745,14 +800,21 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted wind speed in the default <a href="https://openweathermap.org/current#data">units</a>
* or those specified in the call to {@link #retrieveData(String)}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted wind speed, or 0.0 if winds are not in the forecast
*/
public double getWindSpeed(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractDoubleFromJSON(getListEntry("list", timestamp), "wind", "speed", 0.0);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("forecast")) {
return extractDoubleFromJSON(getListEntry("list", timestamp), "wind", "speed", 0.0);
} else if (dataSet.equals("onecall")) {
return extractDoubleFromJSON(getListEntry(getTimestamps("hourly").contains(timestamp) ? "hourly" : "daily",
timestamp), "wind_speed", 0.0);
} else {
return 0.0;
}
}
/**
......@@ -774,14 +836,21 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted wind gust in the default <a href="https://openweathermap.org/current#data">units</a>
* or those specified in the call to {@link #retrieveData(String)}.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted wind gust, or current wind speed if wind gust is not in the forecast
*/
public double getWindGust(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractDoubleFromJSON(getListEntry("list", timestamp), "wind", "gust", getWindSpeed(timestamp));
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.equals("forecast")) {
return extractDoubleFromJSON(getListEntry("list", timestamp), "wind", "gust", getWindSpeed(timestamp));
} else if (dataSet.equals("onecall")) {
return extractDoubleFromJSON(getListEntry(getTimestamps("hourly").contains(timestamp) ? "hourly" : "daily",
timestamp), "wind_gust", getWindSpeed(timestamp));
} else {
return getWindSpeed(timestamp);
}
}
/**
......@@ -812,26 +881,36 @@ public class OpenWeatherConnector {
/**
* <p>Provides the forecasted cloud cover percentage (cloudiness).</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted cloudiness, or 0 if cloudiness is not in current observation
*/
public long getCloudCover(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractLongFromJSON(getListEntry("list", timestamp), "clouds", "all", 0);
checkForDataReadiness(Set.of("forecast", "onecall"));
if (dataSet.startsWith("onecall")) {
return extractLongFromJSON(getListEntry(getTimestamps("hourly").contains(timestamp) ? "hourly" : "daily",
timestamp), "clouds", 0);
} else {
return extractLongFromJSON(getListEntry("list", timestamp), "clouds", "all", 0);
}
}
/**
* <p>Provides the forecasted probability of rain, on a 0.0-1.0 scale.</p>
* <p>Available for the "forecast" dataset.</p>
* <p>Available for the "forecast" and "onecall" datasets.</p>
*
* @param timestamp the date/time corresponding to the desired data
* @return the forecasted probability of precipitation, or NaN if cloudiness is not in current observation
*/
public double getProbabilityOfPrecipitation(Date timestamp) {
checkForDataReadiness(Set.of("forecast"));
return extractDoubleFromJSON(getListEntry("list", timestamp), "pop", Double.NaN);
checkForDataReadiness(Set.of("forecast", "onecall"));
String listName = "list";
if (dataSet.equals("onecall")) {
List<Date> hours = getTimestamps("hourly");
listName = hours.contains(timestamp) ? "hourly" : "daily";
}
return extractDoubleFromJSON(getListEntry(listName, timestamp), "pop", Double.NaN);
}
/**
......
......@@ -83,6 +83,8 @@ public class ExceptionsTest {
public void testAllowedOneCallMethods() {
OpenWeatherConnector connector = oneCall;
Date minutely = connector.getTimestamps("minutely").get(0);
Date hourly = connector.getTimestamps("hourly").get(0);
Date daily = connector.getTimestamps("daily").get(0);
connector.getLatitude();
connector.getLongitude();
connector.getTimestamp();
......@@ -102,7 +104,20 @@ public class ExceptionsTest {
connector.getOneHourSnowfall();
connector.getDewPoint();
connector.getUltravioletIndex();
connector.getMinutelyPrecipitation(minutely);
connector.getMinutelyPrecipitation(minutely); // only
connector.getWeatherCategories(hourly);
connector.getWeatherDescriptions(daily);
connector.getVisibility(hourly); // only
connector.getTemperature(hourly); // only
connector.getHumidity(hourly);
connector.getFeelsLike(hourly); // only
connector.getPressure(daily);
connector.getWindDirection(hourly);
connector.getWindSpeed(daily);
connector.getWindGust(daily);
connector.getProbabilityOfPrecipitation(hourly);
connector.getCloudCover(daily);
// TODO: Introduce new methods
// pass
}
......@@ -768,61 +783,6 @@ public class ExceptionsTest {
OpenWeatherConnector connector = oneCall;
Date timestamp = OpenWeatherConnector.IMPOSSIBLE_DATE;
String failMessage = "Expected UnsupportedOperationException";
try {
connector.getWeatherCategories(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getWeatherDescriptions(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getVisibility(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getTemperature(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getHumidity(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getFeelsLike(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getPressure(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getWindDirection(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getWindGust(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getCloudCover(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getProbabilityOfPrecipitation(timestamp);
fail(failMessage);
} catch (UnsupportedOperationException ignored) {
}
try {
connector.getThreeHourRainfall(timestamp);
fail(failMessage);
......
......@@ -13,11 +13,15 @@ import static org.junit.Assert.assertEquals;
public class OneCall_WebsiteExampleTest {
private OpenWeatherConnector connector;
private Date minutely, hourly, daily;
@Before
public void setup() throws IOException {
connector = new OpenWeatherConnector("onecall");
connector.retrieveData("website-example.json");
minutely = connector.getTimestamps("minutely").get(0);
hourly = connector.getTimestamps("hourly").get(0);
daily = connector.getTimestamps("daily").get(0);
}
@Test
......@@ -130,10 +134,124 @@ public class OneCall_WebsiteExampleTest {
}
@Test
public void testMinutelyPrecipitation() {
Date timestamp = connector.getTimestamps("minutely").get(0);
public void testWeatherForecast() {
List<OpenWeatherConnector.WeatherCategory> expectedCategories = List.of(
OpenWeatherConnector.WeatherCategory.CLOUDS
);
List<String> expectedDescriptions = List.of("few clouds");
int expectedListSize = 1;
List<OpenWeatherConnector.WeatherCategory> actualCategories = connector.getWeatherCategories(hourly);
List<String> actualDescriptions = connector.getWeatherDescriptions(hourly);
assertEquals(expectedListSize, actualCategories.size());
assertEquals(expectedListSize, actualDescriptions.size());
assertEquals(expectedCategories, actualCategories);
assertEquals(expectedDescriptions, actualDescriptions);
expectedCategories = List.of(OpenWeatherConnector.WeatherCategory.RAIN);
expectedDescriptions = List.of("light rain");
actualCategories = connector.getWeatherCategories(daily);
actualDescriptions = connector.getWeatherDescriptions(daily);
assertEquals(expectedListSize, actualCategories.size());
assertEquals(expectedListSize, actualDescriptions.size());
assertEquals(expectedCategories, actualCategories);
assertEquals(expectedDescriptions, actualDescriptions);
}
@Test
public void testMainObservationForecast() {
long expectedVisibility = 306;
double expectedAmbientTemperature = 282.58;
long expectedHumidity = 68;
double expectedFeelsLikeTemperature = 280.4;
long expectedPressure = 1019;
double expectedDewPoint = 276.98;
double expectedUVI = 1.4;
long actualVisibility = connector.getVisibility(hourly);
double actualAmbientTemperature = connector.getTemperature(hourly);
long actualHumidity = connector.getHumidity(hourly);
double actualFeelsLikeTemperature = connector.getFeelsLike(hourly);
long actualPressure = connector.getPressure(hourly);
assertEquals(expectedVisibility, actualVisibility);
assertEquals(expectedAmbientTemperature, actualAmbientTemperature, 0.0001);
assertEquals(expectedHumidity, actualHumidity);
assertEquals(expectedFeelsLikeTemperature, actualFeelsLikeTemperature, 0.0001);
assertEquals(expectedPressure, actualPressure);
expectedVisibility = Long.MIN_VALUE;
expectedAmbientTemperature = Double.NaN;
expectedHumidity = 81;
expectedFeelsLikeTemperature = Double.NaN;
expectedPressure = 1020;
actualVisibility = connector.getVisibility(daily);
expectedDewPoint = 276.77;
expectedUVI = 1.93;
actualAmbientTemperature = connector.getTemperature(daily);
actualHumidity = connector.getHumidity(daily);
actualFeelsLikeTemperature = connector.getFeelsLike(daily);
actualPressure = connector.getPressure(daily);
assertEquals(expectedVisibility, actualVisibility);
assertEquals(expectedAmbientTemperature, actualAmbientTemperature, 0.0001);
assertEquals(expectedHumidity, actualHumidity);
assertEquals(expectedFeelsLikeTemperature, actualFeelsLikeTemperature, 0.0001);
assertEquals(expectedPressure, actualPressure);
// TODO: Need to do hourly and daily dew point and UV index
// TODO: Need to do daily morn, day, eve, night, min, max temperatures, ambient and "feels like"
}
@Test
public void testWindForecast() {
long expectedDirection = 296;
double expectedSpeed = 4.12;
double expectedGust = 7.33;
long actualDirection = connector.getWindDirection(hourly);
double actualSpeed = connector.getWindSpeed(hourly);
double actualGust = connector.getWindGust(hourly);
assertEquals(expectedDirection, actualDirection);
assertEquals(expectedSpeed, actualSpeed, 0.0001);
assertEquals(expectedGust, actualGust, 0.0001);
expectedDirection = 294;
expectedSpeed = 3.06;
expectedGust = 3.06;
actualDirection = connector.getWindDirection(daily);
actualSpeed = connector.getWindSpeed(daily);
actualGust = connector.getWindGust(daily);
assertEquals(expectedDirection, actualDirection);
assertEquals(expectedSpeed, actualSpeed, 0.0001);
assertEquals(expectedGust, actualGust, 0.0001);
}
@Test
public void testCloudinessForecast() {
long expectedClouds = 19;
long actualClouds = connector.getCloudCover(hourly);
assertEquals(expectedClouds, actualClouds);
expectedClouds = 56;
actualClouds = connector.getCloudCover(daily);
assertEquals(expectedClouds, actualClouds);
}
@Test
public void testPrecipitationForecast() {
double expectedPrecipitation = 0.205;
double actualPrecipitation = connector.getMinutelyPrecipitation(timestamp);
double actualPrecipitation = connector.getMinutelyPrecipitation(minutely);
assertEquals(expectedPrecipitation, actualPrecipitation, 0.0001);
double expectedProbability = 0.0;
double expectedRain = 0.0;
double expectedSnow = 0.0;
double actualProbability = connector.getProbabilityOfPrecipitation(hourly);
// double actual1HourRain = connector.getOneHourRainfall(hourly);
// double actual1HourSnow = connector.getOneHourSnowfall(hourly);
assertEquals(expectedProbability, actualProbability, 0.0001);
expectedProbability = 0.2;
expectedRain = 0.62;
expectedSnow = 0.0;
actualProbability = connector.getProbabilityOfPrecipitation(daily);
// TODO: Need to do daily rain and snow totals
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment