This page last changed on Feb 13, 2005 by dblasby.
Compiling JTS into a .so
This is summary of how to compile JTS using GJC. You might want to see the Postgresql Bindings page for more info.
I've been thinking about a new idea ("Spatial DB in a Box") that will (hopefully) extend any object-relational database into a spatial database with only writing a few lines of
DB specific code. The basic idea is to auto-generate "glue" code between the DB and JTS. This is working quite well on Java databases like Cloudscape (derby), but I also wanted to support non-java DBs like PostgreSQL and MySQL. For this I need a compiled - native - version of JTS so the non-java DBs can actually use JTS. Here are the very positive results of my experiments.
(For more info on the "Spatial DB in a Box", see SpatialDBBox)
I did briefly look at a few commercial compilers, but they tend to only support windows. The Gnu compiler (gcc) supports compiling java. Is actually quite easy to get GCJ working, once you figure it out!!
All these instructions are what I did on my linux laptop. You might have to do things slightly differently. I dont think you will have any problems if you can do this and get a response:
$ gcj --version
Version 3.4.2 came auto-installed in Fedora - but you can download it from the gcc homepage: http://gcc.gnu.org/ or http://gcc.gnu.org/java
All the files I mention are available at the wiki page (see attachment at the bottom of this page).
1. simple JTS Java program
This will build a simple java program that will run a big buffer test.
a) in a directory, put "jts-1.5.jar", helloworld.java, and "data1"
b) compile helloworld.java. You can either use java or gjc:
gcj -C -classpath jts-1.5.jar helloworld.java
c) you can run the program using java (takes about 30 seconds):
~/j2sdk1.4.2_07/bin/java -classpath jts-1.5.jar:. helloworld
d) compile JTS and helloworld into native code (this takes about 20 seconds):
gcj -O2 -classpath jts-1.5.jar jts-1.5.jar helloworld.java --main=helloworld -o helloworld
e) run the compiled program (takes about 45 seconds):
2. building/running JTS test suite
The next step is to run the JTS test suites. There were two problems:
a) the JTS test suite has missing classes. Java dynamically links in classes, so it doesnt notice them missing. GCJ statically links in the classes so it does notice them missing. I've provided a .jar with the missing classes - "xml-apis.jar" (note - this isnt the real "xml-apis.jar" - its a very heavily hacked one that just contains the missing classes).
b) the JTS test suite has a swing GUI. GCJ doesnt support swing. I've modified the test suite so it doesnt have any GUI. Thats in JTS_Test2.jar.
Compiling is easy (csh):
a) set your classpath (your shell might do it differently)
b) compile everything (takes about 1 minute):
gcj -O2 -classpath $CLASSPATH xerces.jar xml-apis.jar jdom.jar Acme.jar jts-1.5.jar
NOTE: if you get errors, its almost certainly that you didnt set your CLASSPATH properly.
Try "echo $CLASSPATH" and see if its set! Also, make sure everything in (b) is on
one line! Yes - its does take a few minutes to compile all those .jars!
c) run the program on the test suite (takes 3 seconds for 1500 tests):
./a.out -Files validate_testfiles/
./a.out -Files vivid_testfiles/
NOTE: if you get problems, check to make sure CLASSPATH is set correctly!
3. calling the compiled JTS from C/C++ (and jts.so)
This is a bit more complex, because you generate .h files from the java .class files.
a) StaticGeometry.class – this is a (mostly) auto-generated java class thats created by walking around all the Geometry, Point, LineString, etc.. classes and produces a giant static class so its easier to call JTS functions from procedural (non-object oriented) languages like SQL.
b) Geometry.class – you need this so your c++ program needs to be able to deal with Geometry objects.
0. compile StaticGeometry.java (ignore the warning it gives you)
gcj -C -classpath jts-1.5.jar test/StaticGeometry.java
mv test/StaticGeometry.class .
1. create the Geometry.h file:
2. create the StaticGeomtry.h file:
3. convert JTS into an library (libjts.so):
gcj -O2 -shared -o libjts.so jts-1.5.jar
4. convert StaticGeometry into a library (libstaticgeom.so):
gcj -classpath jts-1.5.jar -shared -o libstatic_geom.so StaticGeometry.class
5. tell the operative system about these two libraries (you must be root):
/sbin/ldconfig ~dblasby/test (or wherever you put your files)
NOTE: on some systems you might just need to "set LD_LIBRARY_PATH=."
5. compile the c++ program (test.cc):
g++ test.cc -lgcj -ljts -lstatic_geom -L.
6. run it:
7. the output should be:
len1 = 42.4264
len2 = 40
LINESTRING (0 0, 10 10)
length of intersection: 14.1421
Performance (large buffer that the GEOS folks have been talking about recently).
Java (1.5.0_01) 27 seconds --
Java (1.4.2_07) 30 seconds 11%
Gcj (3.4.2) 45 seconds 67%
GEOS (CVS) 99 seconds 367%
I'm pretty amazed at how well Java does!
Memory management is pretty good in the compiled GJC program. I actively tried to leak memory, but was unable! The garbage collector must have been quite busy behind the scenes.
I'm not 100% what platforms are supported by GCJ (http://gcc.gnu.org/java/status.html might be old). I'd like people to try it out on a few platforms so we know if it will
work or not.
In summary, GCJ can be used to easily compile JTS for applications that cannot run inside a JVM. Performance is good, there are many supported platforms, and applications
that use it can instantly benefit from JTS changes made by Martin!
Thanks to Martin Davis, Jody Garnett, and Jon Aquino for their assistance.
NOTE: The only problem I've found is that the WKT writer seems to not like zero ordinates (ie. 'POINT(0 0)'). GEOS also had this problem. Its being caused by the NumberFormat given a print pattern like "#.############" (# means dont print anything if there's leading or trailing "0"s). GJC literally interprets this and will not spit anything out! The patch is to change the pattern to "0.############". The jts-1.5.jar I've included has this fix, Martin Davis has patched the CVS version of jts.
I've been testing the memory management (garbage collection) used by GCJ, and it seems to be quite wise. If you look at the attached file "test3.c", its a simple program that creates Geometry objects that are fairly large (a big polygon with about 3600 points in it).
If you look at the main loop, you'll see:
int t =0;
Geometry *geoms; while (t<20000)
ret = JTSCPP_geomFromWKT(wkt1);
geoms[t] = ret.data.g;
Basically, this will create 20,000 JTS Geometry objects and store a pointer to them in "geoms". This leaks memory very quickly - if you run this program you'll see the process size grow very rapidly. This is supposed to happen.
Now, if we comment out the "geomst = ret.data.g;" line - or change it to "geoms0 = ret.data.g;", and re-run the program, we see that it will use a constant amount of memory! The reason for this is because the GCJ garbage collector will automagically free up the non-longer-required Geometries you're creating!
NOTE: you cannot test a GCJ program with valgrind! See the GCJ mailing list for more information.