ADF11gR2: Plotting custom locations on a Thematic Map using a GeoCoder Service

After my initial adventures (see https://technology.amis.nl/blog/12198/first-impressions-of-the-thematic-map-component-in-jdeveloper-11gr2) I continue to explore the wonderful world of the Thematic Map component. The previous article demonstrated how data in database tables could be turned into plotted cities on a Thematic Map. This made use of the preshipped maps and the preshipped list of cities – which is finite. Today I will create a user interface in which users can enter any location they fancy. Provided that location is recognized by the Google GeoCoder Service – that also powers Google Maps – the coordinates for that location are retrieved and used to plot the location as custom point on the Thematic Map.

I will make use of the shipped base map of Europe – which defines about 140 cities – not nearly enough to cover all locations our users might fancy, so we need to resort to coordinates (longitude/lattitude).

The final result of this article’s actions will look like this:

Image

Model

The locations that will be plotted on the Thematic Map are not retrieved from a back end data source. Instead, they are entered at run time by the end user and captured in an in memory collection (ArrayList) held in a Managed Bean (note: I tried using the POJO Data Control but ran into freezing issues with JDeveloper when creating the treetable data binding for it).

Locations are captured in a Java Class Location that has properties for Label, Description, Location, Country and (derived) coordinates Longitude and Lattitude. The class contains the logic to call out to the Google GeoCoder Service for any location that does not already have its coordinates derived.

The code to turn a textual location description into coordinates that the Thematic Map will be able to interpret looks like this:

        private static float[] getCoordinatesForLocation(String location) {
            URL geoCodeUrl;
            String url= "http://maps.googleapis.com/maps/api/geocode/json?address=" + location +
                                "&oe=utf8&sensor=false";
            try {
                geoCodeUrl = new URL(url);
            } catch (MalformedURLException e) {
                System.out.println(e.getMessage()+" url="+url);
                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) {
              float[] coordinates ;
              try{
                  // find first occurrence of lat
                  int posLAT = coord.indexOf("\"lat\"");
                  String latString = coord.substring(posLAT,posLAT+21);
                  String lat = latString.split(":")[1].replaceAll(" ", "").replaceAll(",", "");
                  // find first occurrence of lng
                  int posLNG = coord.indexOf("\"lng\"");
                  String lngString = coord.substring(posLNG,posLNG+21);
                String lng = lngString.split(":")[1].replaceAll(" ", "").replaceAll(",", "");
                coordinates =
                    new float[] { Float.parseFloat(lat), Float.parseFloat(lng) };
                return coordinates;
              }catch (Exception e) {System.out.println("Coordinates stank "+coord);}
            }
            System.out.println("Failed to create proper coordinates; sorry!");
            return null;
        }

The LocationsCollector class is the point of entry for the Thematic Map’s data layer: this class (or rather the managed been based on it) provides methods to retrieve the currently defined Location and to add a new Location. The code of this class is not very remarkable, perhaps apart from the use of the JSF 2.0 Managed Bean and Managed Property annotations:

Image

The method addCurrentLocation(ActionEvent ae) has some relevance as it will be instrumental in allowing the user to add custom locations to the collection and indirectly to the map:

Image

Map definition

The Thematic Map component was added to a JSF page and configured to show Europe and add a PointDataLayer, showing locations defined by coordinate in the locations collection returned by the locationsCollector bean. The marker is configured for this data layer, to ensure that each location is indeed put on the map, with a label and a tooltip (shortDesc):

Image

The map will be refreshed when the component with id cb1 (that will turn out to be the command button labeled Add Location) is partially submitted (to show the newly added location on the map).

Form to add locations

The page uses a PanelStretchLayout for its main structure. The Center facet contains a PanelSplitter with the map in the right facet and a simple Location Form in the left facet. This form is used to enter Location details and submit those by pressing the aforementioned command button cb1:

Image

note that the command button has partialSubmit is true and refers to the addCurrentLocation method on the managed bean locationsCollector as the action listener. All form items refer to the currentLocation managed bean, based on the Location class.

Table with list of all locations

The page finally contains a table with the details for all locations added so far. This table is based on the same #{locationsCollector.locations} List and is also refreshed when the command button is submitted:

Image

Running the Map application

When we run the application, we will get the inital map – on which the initial Location element is displayed: home sweet home:

Image

I can enter a new location in the entry form:

Image

and press the Add Location button. This will submit the location details to the LocationsCollector bean. The bean adds the Location to the List, the Map is refreshed and during the refresh of the map, it will ask for the lattitude and longitude coordinates of the new Location. These will then have to be fetched from the Geocoder service. Finally, the map can be redrawn, including the new location:

Image

of course the table is refreshed as well.

Resources

Download JDeveloper 11gR2 application: ThematicMapWithCustomLocations.