Grails + Spatial Data (PostGIS)
October 4th, 2008
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)
- 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:
- Add the plpgsql language to the database (createlang command on shell)
- 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)”
Leave a Reply
You can use Textile markup in your comments!
October 20th, 2008 at 10:09 PM thanks a lot for this post, But your "Event.hbm.xml" seems to be encrypted would love to see the file. thanks.
October 22nd, 2008 at 11:34 AM Hmm, there seems to be a problem with the brackets that got replaced with html equivalents there. Will look into it right away!
October 22nd, 2008 at 11:49 AM 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...
November 21st, 2008 at 10:16 PM
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?
November 22nd, 2008 at 07:05 PM
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!
February 10th, 2009 at 02:44 AM
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”>
March 17th, 2009 at 08:52 AM
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)
March 19th, 2009 at 04:59 AM
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
This way, the other non-geometry fields are still mapped automatically by GORM.
March 30th, 2009 at 11:59 AM
@Pete: Thanks for that comment. I am currently getting back into PostGIS and Grails and will document further info we find here…
April 26th, 2009 at 10:54 PM
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
May 11th, 2009 at 09:25 AM
@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:
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.
May 22nd, 2009 at 06:07 AM
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
June 4th, 2009 at 10:26 PM
Hi, cool post. I have been wondering about this topic,so thanks for writing.
June 12th, 2009 at 08:09 PM
I really like your post. Does it copyright protected?
June 15th, 2009 at 08:41 AM
Original post by Dmitri Gromov
June 16th, 2009 at 07:07 AM
Hello, can you please post some more information on this topic? I would like to read more.
July 6th, 2009 at 09:30 PM
How soon will you update your blog? I’m interested in reading some more information on this issue.
July 25th, 2009 at 09:12 PM
@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…