Montag, 1. Juli 2013

Eclipse E4, EMF and XWT in Action

Introduction

This article will show the Eclipse technologies stack consisting of Eclipse E4, EMF and XWT in action. First, we will have a look at the functionality the application has to provide, then see how this can be done using those Eclipse frameworks.

The application should be as in the following screenshot:



Requirements: User must be able to...

  • ... create a new layered geographic map or load a list of existing ones
  • ... create new layers (Markers/Points-of-interests, Google, OpenStreetMap, OGC WMS, GML, KML, ...)
  • ... re-order the layers of a selected map
  • ... edit layers, add new Points-of-interests (POIs) to a Markers layer and/or geocode them
  • ... show a preview of the map
All business cases can already be handled by the Geoff project. The interesting part is what frameworks we want to use to implement the requirements. Obviously, as the title says, we have chosen Eclipse E4 as the platform, EMF for modeling the business domain and XWT for declaratively defining the UI via XML.

Modeling our business domain

We are not going to describe the full domain model, but present a simplified one which looks like the following:


There is a base object GeoMap which has a list of Layers which can be any of Markers, OSM, Google, etc.
The full ecore meta model is available in the Geoff git repository. To simplify the UI development, we will use  the generated edit code of the model as well.

Defining the E4 application model

There is a left pane with some parts providing the editing capabilities and there is a right pane which hosts a preview of the configured map:


Providing the contents of the parts in XWT

The entry point of the application is the map selection/creation part:


This UI consists of a TreeViewer with the maps configured so far and a Toolbar (style=VERTICAL) with the actions.


A name is given to the TreeViewer via name="mapsViewer". This allows other objects to reference it. The input is configured using XWT's powerful binding expression: {Binding path=input}. This tells XWT to bind the 'input' property of the data object (provided to the root of the UI) to the 'input' property of the JFace TreeViewer. As we are using the generated EMF Edit code of our business model, we want to use an AdapterFactoryContentProvider as a content provider for out TreeViewer. This is done using the Make object which will use E4's DI engine to create and inject a new instance of the provided class. Remember that the regular AdapterFactoryContentProvider of EMF Edit does not allow DI, so, we have to redefine the class as folows:


As you can see, we are using the @Inject annotation to get missing input, in this case, the DI will provide an AdapterFactory instance to the constructor. Furthermore, we make the content provider be able to hide all children in our root object (GeoMap), that is, we can make the content provider to only return a flat list of the elements. Background: JFace's ListViewer does not allow to show an object's image out of the box.
The label provider of our TreeViewer is configured in the same way.

Next comes a highlight of the XWT E4 integration efforts...
How would you listen for the TreeViewer's selection and put it to your IEclipseContext?

The E4 way would be:


So, lots of boilerplate code! In XWT this can be reduced to just an 'Export' instruction:


This tells XWT to listen to the viewer's singleSelection property and export it into the current IEclipseContext. Remember that Eclipse Contexts are hierarchical: if you are in a Perspective, then each part inside that Perspective has its own context. So, we have to walk one level up from the current context. In the Java code we have called context.getParent(). In the XWT Export instruction we do this by providing the level argument. In this case level="1" just means one level up, so, the export will be done in the Perspective's context and the object is available to all other parts in that Perspective.

Next, the ToolBar on the right side of the TreeViewer:


The UI definition should be self-explanatory, but there is one simplification here. We use XWT's selectionEvent attribute on each ToolItem to define the handling code. For instance, selectionEvent="newMap" tells XWT to look for a method in the handler class that is called 'newMap' and execute it once the ToolItem is pressed. The handling method looks like this:



The first thing to note is that the key point to using the E4 integration of XWT is to subclass your part's POJO from E4XWTUI.
Next, we annotate all fields we are interested in to be injected and define the event handler method newMap() which will be called once the ToolItem is pressed.

The next big step is to make different XWT UIs communicate with each other. In the prior step we listen to the TreeViewer's single selection property and export it into the current Perspective's IEclipseContext to make it available to other parts.
One other part is the Map Details Part:


Once the selection of the maps viewer changes, it will be exported for use with other UI parts.
The Map Details Part's UI looks like this:



To listen to IEclipseContext variables/objects that are set/reset, we have to use the Var instruction:


x:name="map" tells XWT to name this object "map", so other objects can reference it inside the same UI definition. varName="geoMap" tells it to listen for a variable called "geoMap" inside the active IEclipseContext chain.

Next, we can define all other UI controls, for example:



The interesting part is again the powerful expression: text="{Binding elementName=map, path=value.(g:GeoMap.name)}

This means to bind the GeoMap object's name property which can be obtained from the previous Var instruction to the text property of the SWT Text control.

The remaining UI is done following these principles.

Summary

The most important part of this article was to show XWT's E4 integration to create very complex and custom SWT UIs. This was achieved by providing the building blocks to make parts communicate with each other by publishing variables and listening to them.
The benefits we get when using these technologies stack:
  • E4's powerful DI engine and application model
  • EMF for rapid business domain modelling and code generation
  • strict separation of UI and business logic using XWT (no single SWT widget was instantiated in this application in Java code, all done in XML)
  • loosely coupled UIs that can communicate with each other via the IEclipseContext hierarchy


Freitag, 21. Juni 2013

Geocoding location names with Geoff

What is geocoding?

Imagine you have some data in a database that (logically) could be visualized on a geographical map, but you are missing the geographic coordinates (typically latitude/longitude pairs), i.e. you only have the object's logical name:
  • city names
  • postal addresses, street names
  • points of interest: restaurants and bars, libraries, shops, etc.
In order to get the coordinates, you could look up those names in a search engine and show it on a map like Goolge Maps does, then, grab its coordinates and assign them to the objects.
This process is called geocoding: assigning geographical coordinates to a location name.

Querying geocoding services

As you guess, manually geocoding location names can take a lot of time if you have a bunch of objects.
Luckily, this procedure can be automated. There are (public) geocoding services available that can be used to automatically lookup location names and get a geocoded equivalent:
  • Commercial geocoding APIs/webservices providers (Google, Microsoft, Nokia, etc.)- may be 'free' but constrained depending on how many queries you execute
    • Obtain a proper key from those providers
  • OpenStreetMap's geocoding (public) API/webservice - not to be queried in productional environments
    • Host the OpenStreetMap planet files yourself or 
    • consume from a (commercial) provider that has done this already

Using Geoff's API to query a geocoding service

The Geoff project (which is a proposed project at the Eclipse Locationtech IWG)  has a simple interface (IGeocodingService) that acts as a facade/frontend to the public/commercial geocoding services:

public List executeQuery(String query);

It takes a query string as parameter (a location name, for example) and returns a list of potential matches of type POI (point of interest) which has a LongLat (geographic coordinates) and service specific properties.
The result is a List of best matches of geocoded POIs as in general it is not always possible to uniquely identify the location name when automatic geocoding is done. In such cases, you will have to check and resolve the best match.

Currently, there is a default implementation that queries OpenStreetMaps's Nominatim to geocode location names. Other implementations may follow.

Visualizing geocoded location names with Geoff

Once you have finished the geocoding process, you can use Geoff to visualize the POIs on a map (in your Eclipse RCP application). The following steps will walk you through a simple use case...

Setup the map

Geoff has an EMF ecore meta model to define a map object.

1. create the root map container
GeoMap map = MapFactory.eINSTANCE.createGeoMap();
MapOptions opts = MapFactory.eINSTANCE.createMapOptions();
opts.setNumZoomLevels(16);
map.setOptions(opts);

2. create an OpenStreetMap base layer
OSM layer = LayersFactory.eINSTANCE.createOSM();
LayerOptions opts = LayersFactory.eINSTANCE.createLayerOptions();
opts.setIsBaseLayer(true);
layer.setOptions(opts);
map.getLayers().add(layer);

3. create a second layer that will contain some POIs (Markers)
Markers markersLayer = LayersFactory.eINSTANCE.createMarkers();markersLayer.setName("Markers");map.getLayers().add(markersLayer);

4. a) obtain a list of location names from some backend data source
String[] cities = new String[] { "Berlin", "Paris", "London", "Madrid", "Rom" };

4. b) geocode the location names and add to second layer
IGeocodingService geo = IGeocodingService.Util.getFirstFound();
for (String city : cities) { List results = geo.executeQuery(city); if (!results.isEmpty()) { POI poi = results.get(0); Marker marker = MarkersFactory.eINSTANCE.createMarker(); LonLat lonLat = TypesFactory.eINSTANCE.createLonLat(); lonLat.setLon((float) poi.getLatLon().getLon()); lonLat.setLat((float) poi.getLatLon().getLat()); marker.setLonLat(lonLat); markersLayer.getMarkers().add(marker); }}

Setup the UI container (this part is SWT/E4 specific)

@PostConstruct
public void createUI(Composite parent) {
GeoMap map = ... //map object populated in first step
GeoffMapComposite mapComposite = new GeoffMapComposite(parent, SWT.NONE);
mapComposite.setMap(map, "map");
}

Preview

The output should be similar to the following screenshot...


How was that done?

GeoffMapComposite is a wrapper for SWT's Browser widget. It runs the JavaScript framework OpenLayers. Once you call GeoffMapComposite.setMap(), it will use Geoff's code generation functionality to generate OpenLayers specific JavaScript code at run-time and execute it in the wrapped Browser widget.


Mittwoch, 27. März 2013

Geoff: Visualizing geographical maps in Eclipse RCP/RAP applications

The Eclipse Foundation has set up a new industry working group, namely LocationTech.
Its main focus is on

- Storage and processing of massive data volume
- Model driven design
- Desktop, Web, and mobile mapping
- Real time analysis of business critical data

With geoff (geo fast forward), I have proposed a new project that aims at providing a solution to be used for existing (or maybe new ones, too) Eclipse RCP applications to enable visualization of geographical (geo spatial) maps. It uses model driven design (EMF) to provide a declarative configuration of a layered map that can be rendered via the JavaScript framework called OpenLayers 2.12.


EclipseSource's Ralf Sternberg was very cooperative to host an RAP version of the current state on their demo server - Thanks a lot!

Geoff RAP Demo Application

You can follow the progress on GitHub: Geoff on GitHub