How to use timeline weather API to retrieve historical weather data and weather forecast data in Java

Our recently released Timeline Weather API allows you to easily retrieve hourly and daily historical weather data and weather forecast data in a single RESTful API call. In this article we demonstrate how to use the API in the Java programming language.

For the full source of this example, download it from our GitHub repository: https://github.com/visualcrossing/WeatherApi/tree/master/Java/com/visualcrossing/weather/samples

The Timeline Weather API is a simple-to-use API that retrieves weather data for a location. The location can be specified using an address, partial address, longitude and latitude or weather station ID. You can then optionally provide a start and end date to request information for a particular date range. With no date information, the result will be the weather forecast for the next 15 days. With a single date, it will be weather data for the requested date and with a date range, the result will include the data for the requested date range.

Here is an example request:

https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/New%20York%20City%2CNY/2020-1-1?unitGroup=us&key=YOUR_API_KEY

Note that the request above includes an API Key. If you don’t have an API key yet, head over to Visual Crossing Weather Data Services to sign up for you free account – you will be able to make up to 1000 requests for free every day. You can also create Timeline API requests interactively directly within the tool and explore the JSON output.

Sample one – retrieve data using native HttpURLConnection

In our first code snippet we will construct the query and submit it using native Java HTTP libraries.

First, here are some parameters such as the location and date range we are requesting:

String apiEndPoint="https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/";
String location="Washington, DC, United States";
String startDate="2020-12-01"; //optional
String endDate="2020-12-31"; //optional (requires a startDate)
String unitGroup="us"; //us,metric,uk 
String apiKey="YOUR_API_KEY";

Don’t forget to replace ‘YOUR_API_KEY’ with your actual API key.

Now we build the request URL from the parameters:

//Build the URL pieces
StringBuilder requestBuilder=new StringBuilder(apiEndPoint);
requestBuilder.append(location);
		
if (startDate!=null && !startDate.isEmpty()) {
	requestBuilder.append("/").append(startDate);
	if (endDate!=null && !endDate.isEmpty()) {
		requestBuilder.append("/").append(endDate);
	}
}
		
//Build the parameters to send via GET or POST
StringBuilder paramBuilder=new StringBuilder();
paramBuilder.append("&").append("unitGroup=").append(unitGroup);
paramBuilder.append("&").append("key=").append(apiKey);

//for GET requests, add the parameters to the request
if ("GET".equals(method)) {
	requestBuilder.append("?").append(paramBuilder);
}

We are now ready to submit the request. Let’s submit the request and, if we are using a POST method request, also send the parameters in the body.

//set up the connection
URL url = new URL(requestBuilder.toString());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
	
//If post method, send post request
if ("POST".equals(method)) {
	conn.setDoOutput( true );
	conn.setInstanceFollowRedirects( false );
	conn.setRequestMethod( "POST" );
	conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded"); 
	conn.setRequestProperty( "charset", "utf-8");
	conn.setRequestProperty( "Content-Length", Integer.toString( paramBuilder.length() ));
	conn.setUseCaches( false );
	DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
	wr.writeBytes(paramBuilder.toString());
	 wr.flush();
	wr.close();
}

We can now read the response from the server. In this case we do a simple check for an error case by testing if the result HTTP 200 or not. We then read the response. If the response is not successful, we exit. If it is successful, we pass the raw text response off to another method to parse the response as JSON and show some data on the console.


//check the response code and set up the reader for the appropriate stream
int responseCode = conn.getResponseCode();
boolean isSuccess=responseCode==200;
StringBuffer response = new StringBuffer();
try ( 
	 BufferedReader in = new BufferedReader(new InputStreamReader(isSuccess?conn.getInputStream():conn.getErrorStream()))
	 ) {

	//read the response
	String inputLine;
	while ((inputLine = in.readLine()) != null) {
	    response.append(inputLine);
	 }
	in.close();
}  

if (!isSuccess) {
   	System.out.printf("Bad response status code:%d, %s%n", responseCode,response.toString());		
	 return;
}
	    
//pass the string response to be parsed and used
parseTimelineJson(response.toString());

Sample two- retrieve data using Apache HTTP Components

In the second example, we are going to repeat what we did above, but use the Apache HTTP Components library. This is a very popular library for simplifying HTTP requests with Java. Other HTTP components include OkHttp  and, if you are working in Java 9, the new HttpClient API that is built into the Java JVM core.

URIBuilder builder = new URIBuilder(requestBuilder.toString());
builder.setParameter("unitGroup", unitGroup)
	.setParameter("key", apiKey);

We set up the request again,. The syntax is a little simpler but the process is very similar. We can then submit the request, check for a bad status response and then send the raw text data to our JSON parsing routine:

HttpGet get = new HttpGet(builder.build());
		
CloseableHttpClient httpclient = HttpClients.createDefault();
		
CloseableHttpResponse response = httpclient.execute(get);    
		
String rawResult=null;
try {
	if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
		System.out.printf("Bad response status code:%d%n",		
					response.getStatusLine().getStatusCode());
		return;
	}			
	HttpEntity entity = response.getEntity();
	if (entity != null) {
		rawResult=EntityUtils.toString(entity, Charset.forName("utf-8"));
	}
		    
} finally {
	response.close();
}
		
parseTimelineJson(rawResult);

Sample three – parse the text response as JSON

Both of the above methods resulted in a text response from the Weather API that is JSON format. For more information on the exact format, see the documentation. We can now parse the JSON and write a simple bit of code to display the daily weather data values that were returned.

We are going to use the JSON.org parser for simplicity. Any JSON parser should work however so if you are already using one in your project, feel free to use that.

First we parse the text:

JSONObject timelineResponse = new JSONObject(rawResult);
		
ZoneId zoneId=ZoneId.of(timelineResponse.getString("timezone"));
		
System.out.printf("Weather data for: %s%n", timelineResponse.getString("resolvedAddress"));

After parsing the text, we extract the timezone for the requested location. This is returned in the JSON as the ‘timezone’ property. We can use the result to create a Java ZoneId instance, which we will use to construct ZoneDateTime instances for the weather data.

To use the data, we create a simple tab limited result on the console using a loop around the ‘days’ property. The days property is an array of dates within the result.

JSONArray values=timelineResponse.getJSONArray("days");
		
System.out.printf("Date\tMaxTemp\tMinTemp\tPrecip\tSource%n");
for (int i = 0; i < values.length(); i++) {
    JSONObject dayValue = values.getJSONObject(i);

    ZonedDateTime datetime=
        ZonedDateTime.ofInstant(Instant.ofEpochSecond(
            dayValue.getLong("datetimeEpoch")), zoneId);
            
    double maxtemp=dayValue.getDouble("tempmax");
    double mintemp=dayValue.getDouble("tempmin");
    double pop=dayValue.getDouble("precip");
    String source=dayValue.getString("source");
    System.out.printf("%s\t%.1f\t%.1f\t%.1f\t%s%n",         
        datetime.format(DateTimeFormatter.ISO_LOCAL_DATE),
            maxtemp, mintemp, pop,source );
}

Each day object holds the weather data about that day such as maximum and minimum precipitation, wind data etc. In addition, you can retrieve hourly weather data and those hours are found using the 'hours' array within each day.

The date for each date can be reconstructed from the datetimeEpoch property which represents the number of seconds since the UNIX epoch (Midnight on the 1st January 1970 UTC time). We use the ZoneId that we created in the previous step to create a ZonedDateTime instance.

The final result is a simple, tab limited weather data output to the console:

Weather data for: Washington, DC, United States
 Date    MaxTemp MinTemp Precip  Source
 2020-12-01    48.8    40.2    0.0 obs
 2020-12-02    50.2    37.8    0.0 obs
 2020-12-03    53.6    33.2    0.0 obs
 2020-12-04    52.7    47.4    0.3 obs
 2020-12-05    48.6    43.9    1.0 obs
 2020-12-06    45.6    35.8    0.0 obs
 2020-12-07    41.1    33.2    0.0 obs