Hidden Talent of HtmlUnit -Reverse-geocoding using Javascript API

September 20th, 2011 | Posted by admin in front end | java | Javascript | Software Development

Before Google provided HTTP reverse-geocoding service, the world was a dark place if you needed reverse-geocoding in your application.
The only way to perform reverse-geocoding was using javascript API that too using third party library.
Since the application I was working on a year or so back required reverse-geocoding- worse it was central to our geo-location based reporting – I had to find a
way of reverse geocoding high volume longitude/latitude pairs on the server side.
After contemplating various solutions/non-solutions I decided to give myself some time to come up with a solution that didn’t
require us to pay for premium service or ditch googlemaps altogether.
That’s when I came across HtmlUnit. As soon as I read about its capabilities, I suddenly had a brilliant idea!
The idea was to use HtmlUnit to access a page with a small googlemap loaded ( reverse geocoding using GDirection required an instance of googlemap to be present)
on it, that has a form for entering longitude and latitude, a button to submit reverse-geocoding request and a DIV element for populating reverse-geocoded address response
from Google.

Javascript code:

  // GMap2 object
            var map;
            // GReverseGeocoder object
            var rg;
            // text input fields
            var lat;
            var lng;
            // result div
            var addressText;

            function load() {
                if (GBrowserIsCompatible()) {
                    lat = document.getElementById("lat");
                    lng = document.getElementById("lng");
                    addressText = document.getElementById("address");

                    map = new GMap2(document.getElementById("map"));
                    map.setCenter(new GLatLng(-33.83955, 151.2084), 15);
                    map.addControl(new GLargeMapControl());
                    rg = new GReverseGeocoder(map);

                    // add listner for the result
                    GEvent.addListener(rg, "load", function(response){
                        addressText.innerHTML=response.address
                    });
                    //add listener for error response
                    GEvent.addListener(rg, "error", function(lastpoint){
                        addressText.innerHTML = "ERROR:point " + lastpoint;
                    });

                    // Listener to detect clicks on map to get coordinates to fill in the lat and lng fields-for testing purpose.
                    GEvent.addListener(map, "click", function(marker, point){
                        lat.value=point.lat();
                        lng.value=point.lng();
                    });

                }  

            }

            // get the input form lat and lng fields and issue a reverse geocode
            // request
            function reverse(){
                var point = new GLatLng(lat.value,lng.value);
                rg.reverseGeocode(point);
                return false;
            }

HTML code:

 <form name="rev" id="rev"  action="" method="post"  onSubmit=" reverse();return false;">
            <table>
                <tr><td>Latitude (WGS84)</td><td><input id="lat" name="lat" type="text" size="20" value='' /></td></tr>
                <tr><td>Longitude (WGS84)</td><td><input id="lng" name="lng" type="text" size="20" value='' /></td></tr>
                <tr><td><input name="submit" id="submit" type="submit" onClick="reverse()" value="Get Address"></td><td> address:<div id="address"></div></td></tr>

                <tr><td colspan="2"><div id="map" style="width:400px; height:400px;"></div></td></tr>
            </table>

        </form>

I wrote a Java method which accessed the webpage, populated longitude/latitude textbox in the webpage, clicked the submit button, and wait for the response
to be populated in the div element -all done using HtmlUnit’s headless browser. After sleeping for 2 seconds the method then read the content of the address div element.
And it worked flawlessly.

Java Code:

public static String latLngToAddress(Float lat, Float lon) {

        try {
            WebClient wc = new WebClient();
            wc.setThrowExceptionOnScriptError(false);

            HtmlPage page = (HtmlPage) wc.getPage("http://localhost:8084/ReverseGeocoder/");

            HtmlForm form = page.getFormByName("rev");
            HtmlInput latInput = (HtmlTextInput) form.getInputByName("lat");
            latInput .setValueAttribute(lat.toString());

            HtmlTextInput lngInput = (HtmlTextInput) form.getInputByName("lng");
            lngInput.setValueAttribute(lon.toString());

            HtmlSubmitInput button = (HtmlSubmitInput) form.getInputByName("submit");
            button.click();

            Thread.sleep(2000);

            return page.getElementById("address").getTextContent();

        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }

I spent quite a bit of time then to finally come up with this solution. But before I finished the application, Google finally provided HTTP reverse-geocoding service which meant that there was
no need to use my solution. But at least I’m glad I tried it and came up with a solution when none existed :-)

You can follow any responses to this entry through the RSS 2.0 You can leave a response, or trackback.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>