JavaFX 1.0 - JSON Weather Service Redux.

Last June, I demonstrated a JavaFX class to access the JSON Weather Service from the GeoNames geographical database at http://www.geonames.org/export/JSON-webservices.html#weatherJSON.  Please see my earlier blog, http://blogs.sun.com/clarkeman/entry/javafx_script_and_json_weather  for more information about the Weather Service and how I implemented it in a pre-release version of JavaFX.

With the release of JavaFX 1.0, much has changed so I decided to update this example. The main differences are that  XML and JSON parsing were totally refactored and the original json.async.AsyncJsonCall  class is no longer with us. However, you can still leverage the javafx.async.RemoteTextDocument  and the new javafx.data.pull.PullParser classes to obtain the same effect.

I enhanced the example a little, so now you can enter an airport code. While the data is being retrieved, I use the IndeterminateProgressBar class that is described in my recent post, JavaFX - Inverting Text Color.

The Weather class is basically the same as before. The new javafx.data.pull.PullParser class, is setup as a JSON parser and the onEvent action gets the data from the JSON stream and assigns it into the Weather class. PullParser can also be used for XML streams. 


package jsonweather;

import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.text.TextOrigin;
import javafx.scene.control.TextBox;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.data.pull.PullParser;
import javafx.data.pull.Event;
import javafx.async.RemoteTextDocument;
import java.net.URL;
import java.lang.System;
import java.io.InputStream;
import java.io.ByteArrayInputStream;

// ICAO code in US is typically 'K' + IATA airport code
// MCO is Orlando FL US IATA code, so ICAO is KMCO.
// New York - JFK is KJFK.
// See http://en.wikipedia.org/wiki/List_of_airports_by_ICAO_code
var airportCode = "MCO";
var url = bind "http://ws.geonames.org/weatherIcaoJSON?ICAO=K{airportCode.toUpperCase()}";

class Weather {
    public var station:String;
    public var clouds:String;
    public var windDirection:Integer;
    public var windSpeed:Number;
    public var temperature:Number;
    public var dewPoint:Number;
    public var humidity:Integer;
    public var seaLevelPressure:Number;
    public var observation:String;
}
var weather:Weather = Weather{};

var jsonInput: InputStream; // holds the input stream for the JSON data

// The JSON parser
var parser = PullParser {
    documentType: PullParser.JSON;
    input: bind jsonInput
    onEvent: function(event: Event) { // parse the JSON Weather data and populate the Weather object

        if(event.type == PullParser.END_VALUE) {
            //println("{event}");
            if(event.name == "clouds") {
                weather.clouds = event.text;
            }else if (event.name == "stationName") {
                weather.station = event.text;
            }else if (event.name == "windDirection") {
                weather.windDirection = event.integerValue;
            }else if (event.name == "windSpeed") {
                weather.windSpeed = java.lang.Double.valueOf(event.text);
            }else if (event.name == "temperature") {
                weather.temperature = java.lang.Double.valueOf(event.text);
            }else if (event.name == "dewPoint") {
                weather.dewPoint = java.lang.Double.valueOf(event.text);
            }else if (event.name == "humidity") {
                weather.humidity = event.integerValue;
            }else if (event.name == "seaLevelPressure") {
                weather.seaLevelPressure = event.numberValue;
            } else if (event.name == "observation") {
                weather.observation = event.text;
            }
        }
    }
}


var app:VBox;
var bar:IndeterminateProgressBar;
var input:TextBox;
var rtd:RemoteTextDocument;


var stage:Stage = Stage {
    title: "JSON Weather"
    width: 700
    height: 400
    scene: Scene {
        content: [ app=VBox {
            translateX: 50
            translateY: 50
            spacing: 10
            content: [
                HBox {
                    content: [
                        Text {
                            translateY: bind input.boundsInLocal.height/2;
                            content: "Airport Code:"
                        },
                        input = TextBox {
                            columns: 4
                            value: bind airportCode with inverse
                            selectOnFocus: true
                            action: function(): Void {
                                app.opacity = .5;
                                bar.visible = true;
                                println("airport code = {airportCode} url = {url}");
                                weather = Weather{};
                                if(rtd != null) rtd.cancel();

                                // The remote access from the Geo Names site.

                                rtd = RemoteTextDocument {
                                    url: bind url
                                    onDone: function(success:Boolean):Void {
                                        if(success) {
                                            var json = rtd.document;
                                            jsonInput = new ByteArrayInputStream(json.getBytes());
                                            parser.parse();
                                            jsonInput.close();
                                        }else {
                                            System.out.println("failure = {rtd.failureText}");
                                        }
                                        // reset back to original state
                                        app.opacity = 1.0;
                                        bar.visible = false;
                                        rtd = null;
                                    }
                                };

                            }
                        }
                    ]
                },
                HBox {
                    content: [
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: "Station:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind weather.station
                            fill: Color.BLUE
                        }
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "Clouds:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind weather.clouds
                            fill: Color.BLUE
                        }
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "Wind Direction:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind "{weather.windDirection} degrees"
                            fill: Color.BLUE}
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "Wind Speed:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind "{weather.windSpeed} knots"
                            fill: Color.BLUE}
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "Temperature:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind "{weather.temperature}C degrees"
                            fill: Color.BLUE}
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "Dew Point:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind "{weather.dewPoint}C degrees"
                            fill: Color.BLUE}
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "Humidity:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind "{weather.humidity}%"
                            fill: Color.BLUE}
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "Sea Level Pressure:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind "{weather.seaLevelPressure}mb"
                            fill: Color.BLUE}
                    ]
                },
                HBox {
                    content: [
                        Text {textOrigin: TextOrigin.TOP content: "METAR Observation:"},
                        Text {
                            textOrigin: TextOrigin.TOP
                            content: bind "{weather.observation}"
                            fill: Color.RED}
                    ]
                },

            ]
        },
        bar = IndeterminateProgressBar {
            visible: false
            width: bind stage.width - 50
            height: 35
            text: "Loading..."
        }
        ]
    }
}

input.requestFocus();


I am using the new class, javafx.scene.control.TextBox, to enter an arbiritary airport code. After the code is entered, and the user hits the enter key, the action for the TextBox creates a RemoteTextDocument object with the latest airport code. This action also makes the IndeterminateProgressBar visible which in turn starts its animation. Once the RemoteTextDocument is done fetching the JSON stream, it invokes the PullParser to set the Weather attributes. When all is done, the progress bar is set to invisible, stoping its animation, and the new weather information is displayed. 

The following image is what the screen looks like while RemoteTextDocument is doing its work in the background. This shows a request to get the weather for Los Angeles (LAX).

LAX Weather Loading

And this is Orlando, after the weather data has been retrieved and parsed:

Orlando Weather

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

jimclarke

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today