Leveraging HTML 5 Navigator API to show the browser's current location on an ADF Faces 11gR2 Thematic Map component

This article demonstrates how, through a bit of JavaScript interacting with the HTML 5 Navigator API, some more JavaScript using the ADF Faces Rich Components API and the ADF Faces 11gR2 Thematic Map component, it becomes quite easy to not only learn about the end user’s physical location but to also show that location on a map – and along with it typically the nearest branches of your company.

The page developed in this article will essentially look as is shown in the next figure:

Image

The HTML 5 Navigator API is available in most modern browsers – including Firefox 3.5+, Chrome 5.0+, Safari 5.0+, IE 9, iPhone 3.0+ and Android 2.0+. Note that the user should explicitly allow an application to learn about his or her location – and not get a handle on it just like that.

See Dive into HTML 5 for more details: http://diveintohtml5.org/geolocation.html

The essential piece of JavaScript to interact with the Navigator API is shown below:

Image

When the navigator object is available and supports the geolocation option, then it is invoked to return the current position of the browser. The callback handler function object – showLocation – is passed to the function getCurrentPosition, along with a function to handle errors.

The showLocation function is shown here. It is passed a position parameter that is an object (see http://www.w3.org/TR/geolocation-API/#position) that contains the longitude and latitude coordinates found for the browser.

Image

The function then leverages the ADF Faces Rich Component JavaScript framework to push an event from the browser to the server. This event is called uploadLocation, is contains the longitude and latitude and is handled through a server listener:

<af:serverListener method=”#{locationBean.locationAbsorber}” type=”uploadLocation”/>

that is associated with a component with absolute id it1 (which happens to be an invisible inputText component).

The locationBean is a managed bean defined based on the class LocationBean. This class receives the location information from the client, uses the Google GeoCoder Service ‘reverse option’ of coordinates to human readable location and creates a Location object. It finally instructs ADF Faces to refresh the Thematic Map shown in the browser – knowing that there is now a location to be shown on the map.

    List<Location> locations = new ArrayList<Location>();
    UIComponent map ;

    public void locationAbsorber(ClientEvent event) {
        Double latitude = (Double)event.getParameters().get("latitude");
        Double longitude = (Double)event.getParameters().get("longitude");
        addCurrentLocation(longitude, latitude);
    }

    public void addLocation(Location loc) {
        locations.add(loc);
    }
    public void addCurrentLocation(Double longitude, Double latitude) {
        Location loc = new Location();
        loc.setLabel( "Your Current Location");
        loc.setCountry("NLD");
        loc.setDescription("location made available through browser and HTML5 Navigator API");
        loc.setDescription(getHumanReadableAddressForGeoLocation(longitude.floatValue(), latitude.floatValue()));
        loc.setCoordinates(new float[] {latitude.floatValue(), longitude.floatValue()});
        locations.add(loc);
        AdfFacesContext adfFacesCtx = AdfFacesContext.getCurrentInstance();
        // PPR
        adfFacesCtx.addPartialTarget(map);
    }

    private String getHumanReadableAddressForGeoLocation(float longitude, float latitude) {
        URL geoCodeUrl;
        String url= "http://maps.googleapis.com/maps/api/geocode/json?latlng=" + Float.toString(latitude)+","+Float.toString(longitude)
+"&oe=utf8&sensor=false";
        try {
            geoCodeUrl = new URL(url);
        } catch (MalformedURLException e) {
            return null;
        }
        BufferedReader in;
        String coord = null;
        try {

            in = new BufferedReader(new InputStreamReader(geoCodeUrl.openStream()));
            char[] buf = new char[8000];

            in.read(buf);
            coord = new StringBuilder().append(buf).toString();

            in.close();
        } catch (IOException e) {
            System.out.println(e.getMessage()+" IO Exception ");
            return null;
        }
        if (coord != null) {
         try{
            // find first occurrence of formatted_address
            int posFA = coord.indexOf("\"formatted_address\"");
            //TODO really parse the JSON result
            String latString = coord.substring(posFA+41,posFA+68);
            return latString;
          }catch (Exception e) {System.out.println("Coordinates stank "+coord);}
        }
        System.out.println("Failed to create proper coordinates; sorry!");
        return null;
    }
...

The page itself contains the thematic map component – configured to show Europe, lazily assuming that the user (me) will be on that continent. It also contains an invisible inputText component with which the serverListener has been associated.

Image

When we run the page, the user will be asked by the browser to share his or her location with the web application:

Image

When the user consents, the location is made available to the JavaScript function that passes it on to the server who makes good use of it to update the map and send the refresh instruction to the client:

Image

Resources

Download the JDeveloper 11gR2 application: Html5TrialsCurrentLocationOnMap .