Saturday, January 5, 2008

Using GroovyWS with the TerraService (Part 1)

I got recently a post on the difficulty of retrieving complex elements by some webservices, I will try to clarify some issues on this in this post using the Microsoft Terra webservice.
The TerraService is an WebService API which allows you to query the Microsoft TerraServer database of high resolution aerial imagery. Among other things, you can:
  • Find Geographic and Image Coordinates by Place Name
  • Convert coordinates from one projection system to another
  • Fetch tile meta-data and image-data
Today, I will just concentrate on using the search API to show how to deal with arrays with GroovyWS.

If you look at the TerraService WSDL you will notice that some functions return arrays. This is clear from the following part of the WSDL:


<s:element name="GetPlaceList">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="placeName" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="MaxItems" type="s:int"/>
<s:element minOccurs="1" maxOccurs="1" name="imagePresence" type="s:boolean"/>
</s:sequence>
</s:complexType>
</s:element>

<s:element name="GetPlaceListResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="GetPlaceListResult" type="tns:ArrayOfPlaceFacts"/>
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="ArrayOfPlaceFacts">
<s:sequence>
<s:element minOccurs="0" maxOccurs="unbounded" name="PlaceFacts" type="tns:PlaceFacts"/>
</s:sequence>
</s:complexType>

<wsdl:message name="GetPlaceListSoapIn">
<wsdl:part name="parameters" element="tns:GetPlaceList"/>
</wsdl:message>
<wsdl:message name="GetPlaceListSoapOut">
<wsdl:part name="parameters" element="tns:GetPlaceListResponse"/>
</wsdl:message>

<wsdl:operation name="GetPlaceList">
<wsdl:input message="tns:GetPlaceListSoapIn"/>
<wsdl:output message="tns:GetPlaceListSoapOut">
</wsdl:operation>

Therefore, the following call to GetPlaceList will return an object embedding an array of PlaceFacts.

def proxy = new WSClient("http://terraservice.net/TerraService.asmx?WSDL", this.class.classLoader)
def plist = proxy.GetPlaceList("mountain view", 5, true)

From this object, one can call getPlaceFacts to get the array. Here we asked for its size:

println plist.placeFacts.size()

Then one call go through all the places returned by the call to the TerraService:

for (pfact in plist.placeFacts) {
println pfact.place.city
println pfact.place.state
}

All this might look a bit cryptic, and it is probably. The fact is that there is no magic. When you want to call a remote service, you have to know its signature. If you don't have any manual then you have to read and understand the WSDL which represents the contract you are offered.

If you have any idea on how thing could be improved by GroovyWS for helping you in writing your scripts then do not hesitate to give your feedback.

Tuesday, January 1, 2008

Happy new year - Bonne année 2008

Quelques petites nouvelles de début d'année: 2007 s'est finie pour nous par un séjour de quelques jours à Malacca (Malaisie). Nul doute que soph postera prochainement une description plus complète de nos visites. J'en profite pour vous souhaitez une très bonne année 2008; quelle vous apporte surtout la santé (le reste suivra, j'en suis sûr).

Dans le chapître des bonnes résolutions (car point de nouvelle année sans de nouveaux objectifs), je vous dirais bien que j'essayerai d'être plus présent sur ces pages, que je prendrai plus de vacances, que je ferai le marathon de Singapour, .... M'enfin n'y croyez pas trop !

A toutes fins utiles, et pour ceux qui voudraient se joindre à moi, le marathon de Singapour aura lieu cette année le 7 décembre.

Sur ces bonnes résolutions, je vous laisse et n'hésitez pas à me communiquer vos bonnes résolutions en commentaires.

Sunday, December 16, 2007

GroowyWS 0.2.0

It has been a long time since I talked about GroovyWS ... Groovy has been out for a week now and I have been working a bit on GroovyWS as well so I decided to push a new version. The changes brought by this new version are minor in term of workload but they should (hopefully) made GroovyWS more usuable in a production environment.
The first change deals with https support. Now you cab use the WSClient with a serving using secure http. This has been tested using Amazon S3 web services:

def proxy = new WSClient("https://s3.amazonaws.com/doc/2006-03-01/AmazonS3.wsdl",..)

You can now create buckets :-)

The other major change is the support for servers using basic authentication. So if your server requests to have an "Authorization: Basic bG9naW46cGFzc3dk" header in the request, you are able to set the correct properties using:

System.props["http.user"] = "login"
System.props["http.password"] = "passwd"

Do not forget that the token transmitted is just a base64 encoding of "login:passwd", so basically your are transmitting your credentials in clear text :-). You don't trust me ? Try this:

echo -n 'login:passwd' | openssl enc -a -e

or

echo 'bG9naW46cGFzc3dk' | openssl enc -a -d

Only use this over https !

The last minor change is to publish the SOAPAction property in the WSDL generated by the server. This makes odd commercial products work with WSServer !

What are the next steps ? I would like to add WS-Security support. This is something I already have partially, I am missing a public server to test it and would like to know how you would envisage its usage using groovy. So if you have any idea, do not hesitate to contact me ...

Sunday, August 26, 2007

GroovySOAP is now GroovyWS

It has been a long time without news. The fact is that I have been moving from France to Singapore and that keeps me busy with and away from the keyboard. Anyway I am back. You probably now that CXF has taken over XFire. Thanks to the work done by Dan & Al, the move has been pretty fast and we now have the best from XFire & Celtix.
I thought it was time to move also from GroovySOAP (relying on XFire) to GroovyWS (build on top of CXF). What are the difference so far ? If you choose GroovyWS you will have to use Java5 but you will gain an easiest way to build your clients (no worries about the complex types, they are automatically generated) and a proxy support.
In the future, I will probably start to work on basic authentication as this has been asked several times.
The build can be found here.

Friday, August 24, 2007

Songs of the sea

Hier, soir nous avons passé la soirée à Sentosa accompagné de Jean-Yves et David. On peut accèder à cette île (destinée uniquement aux loisirs) par différents moyens. Cette fois-ci, David avait choisi les oeufs (oui vous avez bien lu) afin de nous faire profiter de la vue sur la ville. Au programme: "Songs of the Sea" un spectacle réellement féérique fait de feux (follets) et de jeux de lumières et de lasers projetés sur des jets d'eaux. Le spectacle raconte l'histoire de Li - jeune homme doté d'une voix charmeuse. Li et ses amis découvrent l'existence de la princesse Ami sous l'emprise d'un village enchanté. Réussiront-ils à la délivrer avec l'aide des créatures de la mer ?

Bon allez, si vous voulez la réponse il vous faudra venir découvrir le spectacle ... Bon, au pire nous vous avons mis quelques photos ici.

Sunday, August 19, 2007

Visite au zoo

Première semaine à Singapour ! Après avoir tenté d'évaluer les différents moyens pour aller de notre nouveau domicile au boulot (1h15 ;-) ), le premier week-end nous a permis de souffler un peu. Samedi, ce fut direction le centre ville afin de rendre visite au Merlion (emblème de Singapour) situé en bord de mer, puis de faire du shopping chez Carrefour: nostalgie oblige. Dimanche, direction le zoo à une trentaine de minutes de chez nous (bus 138 !), on en a pris plein les yeux avec des animaux inconnus chez nous (par exemple les tragulidés - oui je sais ils ne sont pas particulièrement beaux !). Aux programmes: perroquets colorés, caméléon, singes en tout genre, lémuriens, éléphants et de nombreux autres que je vous invite à découvrir ici . Nous avons même pu découvrir quelques serpents fort peu recommendables qui trainent par ici.

A bientôt, pour de nouvelles aventures.
Posted by Picasa

Wednesday, May 9, 2007

Embedding Google Maps in your Groovy application

Google provides a wide variety of API for embedding their applications into your web pages. They are very convenient if your are developing web applications but I find it very painful to embed them into any Java application. Hopefully as far as GMaps is considered, the swingx-ws project is providing a JXMapViewer class in which you can configure a tile provider for using the Google map servers. Let's use Groovy to build a small apps using Google geocoding and putting some information on a map.

Central to our small application is the JXMapViewer. Each mapviewer is associated to a tile factory which delivers the image pieces. Once configured, the mapviewer will take care of the rest querying the map server when required depending on your location. Configuring the tile factory is the more tricky part of the snipplet, here are all the arguments of the constructor:

def tfi = new TileFactoryInfo(0, 20, 17, 256, true, true,"can't disclose", "x", "y", "zoom")

The first four numbers to the TileFactoryInfo constructor represent the minimum zoom, the maximum zoom, the total zoom levels, and the tile size (very often a tile is a 256 pixels square) . The two booleans are used to indicate if the x coordinates go left to right or right to left and if the y coordinate goes from top to bottom or out from the equator. The rest of the parameters are for the base url and the name of the http parameters in the get request to fetch tiles.

Let's continue by defining our Swing interface including the JXMapViewer. Here the code defining our panel which includes a textfield (in which you could type an address), the widget containing the map viewer and two buttons for zooming in and out.


import java.awt.*
import java.awt.BorderLayout as BL

import javax.swing.*
import javax.swing.WindowConstants as WC
import javax.swing.JOptionPane as JOP
import javax.swing.BorderFactory as BF
import javax.swing.SwingUtilities as SU

import org.jdesktop.swingx.mapviewer.*
import org.jdesktop.swingx.JXMapViewer

import groovy.swing.SwingBuilder

def googleKey = "your google api key"

def mapViewer = new JXMapViewer()
def tfi = new TileFactoryInfo(...)

mapViewer.tileFactory = new DefaultTileFactory(tfi)
mapViewer.zoom = 5
mapViewer.centerPosition = [48.856558, 2.350966] // Paris

def swing = new SwingBuilder()
def frame = swing.frame(title: 'Groovy Maps',defaultCloseOperation:WC.DISPOSE_ON_CLOSE) {
panel(id: "mainPanel", layout: new BL()) {
panel(constraints: BL.NORTH, layout: new BL()) {
textField(id: "address", constraints: BL.CENTER, columns: 50,
border: BF.createTitledBorder("Address"), actionPerformed: {
def address = swing.address.text
// geocoding code here
}
widget(mapViewer)
panel(constraints: BL.SOUTH) {
button(text: "+", actionPerformed: { mapViewer.zoom -= 1 })
button(text: "-", actionPerformed: { mapViewer.zoom += 1 })
}
}
}
frame.pack()
frame.size = [800, 600]
frame.locationRelativeTo = null

frame.visible = true



Using the Google geocoder is pretty simple when using an http request to http://maps.google.com/maps/geo? with the following parameters in the URI:
# q, the address that you want to geocode,
# key, you API key,
# output , the format in which the output should be generated. In our example, we will use xml, the other options are kml, csv, or json.

In our case, the address will come from a texfield so the code looks like this:

textField(id: "address", constraints: BL.CENTER, columns: 50,
border: BF.createTitledBorder("Address"), actionPerformed: {
def address = swing.address.text

// geocoding
SU.invokeLater {
Thread.start {
def geocodingUrl = "http://maps.google.com/maps/geo?q=
{URLEncoder.encode(address)}&output=xml&key=${googleKey}".toURL()
def node = new XmlSlurper().parseText(geocodingUrl.text)
if (node.Response.Status.code == "200") {
def coord = node.Response.Placemark.Point.coordinates.text().
tokenize(',').collect{Float.parseFloat(it) }
mapViewer.centerPosition = coord[1..0]
}
}
}
})


Assemby all the pieces give you the global application ...

In conclusion, JXMapViewer is a powerful component that make it easy to embed maps in your groovy application. In a next stage, I will show you how to add more information in a different layer.