For a client/server application I’m currently developing, I chose grails (www.grails.org) as the framework to develop the web application and webservice part. Grails is a framework very similar in concepts to Ruby on Rails, which I personally like a lot for it’s convention over configuration attitude.

The main downside of grails, as far as I’m concerned is the huge amount of *.jar dependencies that it comes along with. This is, as long as applications do not stray from the standard data types of a web framework, very well hidden from the developer.

In this blog post I will outline a few points, on how to save geo-spatial points (no pun intended) using grails.

For said application, I have to save GIS spatial data in the database and I want to access them from the web application. I also want to call the special GIS-related stored procedures on the server. This seemed to be very hard at first, since almost no documentation on the topic can be found, especially in the documentation of the grails framework (not only for GIS data, but also for other custom data types, that is). On the mailing lists of Hibernate and Spring (libraries both used by grails), no working solutions could be found, but only the problem statements.

Digging deeper however revealed the solution to the problem, which I’m going to present now.

Let’s say we want to create a webapp, that can do the following things:

Store upcoming and past events, whereas each event has:
  • A Date,
  • a title,
  • a description,
  • and a location (This will be the GIS data)
and store members with:
  • Events they are attending.
  • A name.
  • A birthday.

I suppose creating a web-app using grails is already clear to readers at this point.

The domain classes

Let’s have a look at the domain classes, shall we?
# Event.groovy
class Event {
    String name
    String description
    Date date

    static hasMany = [participants : Participant]
    static belongsTo = [creator : Participant]

    public void findWithinLimit(double distance){
    }
}
# Participant.groovy
class Participant {
    String name
    Date birthDate

    static hasMany = [events : Event]
}

I intentionally left out the GIS attribute of the event class. You can easily start off with this as your domain classes and add GIS functionality later.

Adding GIS functions to PostgreSQL

Now, lets add the GIS functionality to the database. Assuming you use PostgreSQL this can be done in the following way:

  1. Add the plpgsql language to the database (createlang command on shell)
  2. Execute the update script on the database (my database is called geoEvent) to add the PostGIS functions and data types. On my computer that script is called lwpostgis.sql and sits in /usr/share. I execute the script using pgadmin3, which is my method of choice to work with postgresql databases…

This will make your database ready to store GIS data.

Now, first things first. Let’s add all the libraries we need to make grails aware of the data types we’re going to be using.

You’ll need:

  • hibernate-spatial.jar (find this on the hibernatespatial site)
  • hibernate-spatial-postgis.jar (same site)
  • jts-1.8.jar (JTS site)
  • postgis_1.4.0.jar (PostGIS site)
  • postgresql-8.2-VERSION.jdbc3.jar (it may be possible to run this with jdbc4, but I haven’t tested that one yet. Version works fine, however)

Enough libraries. ;)

Adding GIS to the domain classes

Now let’s get those GIS data types into the domain classes.
# Event.groovy
import com.vividsolutions.jts.geom.Point
class Event {
    String name
    String description
    Date date
    Point location

    static hasMany = [participants : Participant]

    public void findWithinLimit(double distance){

    }
}

Hooooold your horses, we’re not yet ready to fire up the grails app again. :D

Bind hibernate

You will first have to tell hibernate, that there is new data types that are mapped to the database in a certain way.

In grails-app/conf/hibernate create a file called hibernate.cfg.xml
<hibernate-configuration>
  <session-factory>
    <!-- SPATIAL SQL dialect -->
    <property name="dialect">
      org.hibernatespatial.postgis.PostgisDialect
    </property>

    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">false</property>

    <mapping resource="Event.hbm.xml" />
  </session-factory>
</hibernate-configuration>

This tells hibernate, that there is a special SQL dialect to learn and that there is a mapping for the type Event (This is by convention, I’d say).

Event.hbm.xml looks like this:
<hibernate-mapping>
  <class name="Event" table="event">
    <id name="id" column="id">
      <generator class="native" />
    </id>
    <property name="location" 
      type="org.hibernatespatial.GeometryUserType">
      <column name="location" sql-type="GEOMETRY" />
    </property>
    <property name="name" />
    <property name="description" />
    <property name="date" />
  </class>
</hibernate-mapping>

That’s it! Now you can fire up your application again and shine with GIS data. At least it seems that way. The tables all get created in the database, the attributes are all there and so are the foreign keys and link tables. Read on the next page how to add data to your database using your grails scaffolds.

Ticket to forms and back

To have grails render your Point in a way, that a user can add new data to the database using the scaffolded code you will have to add a PropertyEditor (which is a concept from the Spring framework…).

A property editor tells your application, how to translate a certain data type into String format and back. This doesn’t look so bad after all:

package com.vividsolutions.jts.geom;

import java.beans.PropertyEditorSupport;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

public class PointEditor
        extends PropertyEditorSupport{

       private Point point;

    @Override
    public String getAsText() {
        point = (Point) super.getValue();
        return String.format("POINT(%f %f)",
                    point.getX(), point.getY());
    }

    @Override
    public void setAsText(String text)
                throws IllegalArgumentException {
        WKTReader reader = new WKTReader();
        try {
            point = (Point)reader.read(text);

            super.setValue(point);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
Now, grails will pick up this PropertyEditor automatically, iff you put it in the correct place. You will have to put it in same directory as the class you are trying to render and parse from a String. So this goes into the src/java/com.vividsolutions.jts.geom package. This will enable you to render and enter Points as Strings of the form:
POINT(48.8 8.8)
POINT(20 30 40) // 3d-Point

That’s quite enough for today. I will follow this post up with a tutorial on how to use the stored procedures to find nearby Points efficiently, as is already hinted in the code by the function findWithinLimit(distance).

18 Responses to “Grails + Spatial Data (PostGIS)”

  1. Tobias says:
    thanks a lot for this post, But your "Event.hbm.xml" seems to be encrypted would love to see the file. thanks.
  2. Silvio says:
    Hmm, there seems to be a problem with the brackets that got replaced with html equivalents there. Will look into it right away!
  3. Silvio says:
    I figured out what was wrong with the XML-code highlighter. Now you can actually read the XML. Hope it helps. I will soon post on another issue that _will_ arise for people working with custom types like that in grails...
  4. Joe Roberts says:

    Thanks for this post, just what I needed but I’m struggling to get it working. Did you have to make any changes to your DataSource.groovy file?

  5. Joe Roberts says:

    Problem solved. The changes I made to my DataSource.groovy are:

    driverClassName = “org.postgis.DriverWrapper” dialect = org.hibernatespatial.postgis.PostgisDialect

    Everything else as you described, big thanks!

  6. Jeffrey Johnson says:

    This worked like a charm. Thanks a bunch of writting this tutorial.

    One quick note. You should explicitly include the DOCTYPE at the top of your hibernate XML samples here. Took me quite a while to figure out this was causing problems.

    <!DOCTYPE hibernate-configuration SYSTEM “http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd”>

    <?xml version=”1.0”?> <!DOCTYPE hibernate-mapping PUBLIC ”-//Hibernate/Hibernate Mapping DTD 3.0//EN” “http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd”>

  7. eric says:

    I have a problem,like this:

    Caused by: java.lang.RuntimeException: Uncompilable source code – could not find symbol symbol: class Point location: package com.vividsolutions.jts.geom at com.vividsolutions.jts.geom.PointEditor.<clinit>(PointEditor.java:4) ... 2 more 2009-03-17 15:41:37,998 [9844362@qtp0-4] ERROR StackTrace – Sanitizing stacktrace: java.lang.RuntimeException: Uncompilable source code – could not find symbol symbol: class Point location: package com.vividsolutions.jts.geom at com.vividsolutions.jts.geom.PointEditor.<clinit>(PointEditor.java:4) at EventController$_closure8.doCall(EventController.groovy:91) at EventController$_closure8.doCall(EventController.groovy)

  8. Pete says:

    Great post, really enjoyed it. I was able to avoid using the hibernate mapping files (i.e. Event.hbm.xml) by using the following in my domain classes

    static mapping = {
      columns {
        location type: org.hibernatespatial.GeometryUserType
      }
    }

    This way, the other non-geometry fields are still mapped automatically by GORM.

  9. Silvio says:

    @Pete: Thanks for that comment. I am currently getting back into PostGIS and Grails and will document further info we find here…

  10. John Cartwright says:

    Thanks for the great tutorial!

    I’m getting an exception trying to add a point – could someone help me understand what the problem might be?

    groovy.lang.MissingMethodException: No signature of method: static com.vividsolutions.jts.geom.Point.list() is applicable for argument types: () values: []

    —john

  11. Silvio says:

    @John: This probably means that you have scaffolded views that want to display foreign key relationshsips between domain data and the associated geodata by using this method. There is two ways of fixing this:

    1. See the part about the PointEditor. The importance of the package where you should put this class in cannot be overstressed. (This way you’ll have to enter WKT-type data)
    2. You can also add lat / lon input fields to any form and then parse the entered data, once the form is submitted.

    I think this error only occurred in my apps, when I forgot to tell grails how to render geodata, since it doesn’t know that just like that.

  12. John Cartwright says:

    Thanks for your reply Silvio. I’ve confirmed that the PointEditor class is defined in

    src/java/com/vividsolutions/jts/geom/PointEditor.java

    Is there a way I can be more explicit about associating this class w/ the Point type?

    Would it be possible for you to send me your working project as defined in the tutorial?

    Thanks again for your help!

    —john

  13. AndrewBoldman says:

    Hi, cool post. I have been wondering about this topic,so thanks for writing.

  14. Kelly Brown says:

    I really like your post. Does it copyright protected?

  15. KattyBlackyard says:

    Original post by Dmitri Gromov

  16. GarykPatton says:

    Hello, can you please post some more information on this topic? I would like to read more.

  17. KonstantinMiller says:

    How soon will you update your blog? I’m interested in reading some more information on this issue.

  18. Silvio says:

    @John: I don’t currently know of a way to be more explicit about the PropertyEditor that is chosen for a specific type.

    I cannot send you the code for this project though, because I extracted the code and it’s simplified and stripped down from what it was before. I might however set up a new project that does exactly this on github and then update the blog.

    @Kelly: I’d say it’s creative commons attribution-share alike. So feel free to use it.

    @Gary: I will soon post more on this topic probably including a sample project.

    @Konstantin: I will try to update the blog more often on topics like this.

    @everyone: Sorry I took so long to aprove these comments. I’m missing an email notification system from the blogging software right now. I also have to dig the real comments out of a lot of spam…

Leave a Reply

You can use Textile markup in your comments!