[geos-devel] Updated SWIG bindings and Patch

Charlie Savage cfis at interserv.com
Tue Jan 10 03:43:32 EST 2006


I've finally had a chance over the last couple of days to dig into the 
Geos SWIG bindings and made a bunch of changes - see the attached 
patch.  The good news is that everything now works, I can run through 
the example test program without causing any segmentation faults.  So at 
this point I think the SWIG bindings are in pretty good shape for Ruby, 
and hopefully not to far behind for Python.

Changes include:

1.  Went through public GEOS api and figured out which methods return 
new objects and which methods take ownership of objects.  I then updated 
the SWIG bindings appropriately.

2.  Added better exception handling

Last, I just want to publicly write down the tricky bit of the SWIG 
bindings for anyone who struggles with them in the future.  The key 
issue is that GEOS makes heavy use of vectors of pointers (geometry, 
linestrings and polygons).  This can be tricky in a scripting language - 
imagine this scenario (in Ruby):

// create a geometry vector
geoms = Geos::GeometryVector.new()
// create a point
point = Geos::GeomFactory.createPoint(someCoord)
// put the point in the vector
geoms.push(point)
// release the reference to the point
point = nil
// run the garbage collector
GC.start()

Although the Ruby point object has gone out of scope, we do not want to 
free the underlying C++ object because its still in the vector and may 
be accessed again.  I've setup the Ruby SWIG bindings to handle this 
appropriately via the use of Ruby mark functions.

I haven't tested this with Python, but Python works differently than 
Ruby since it uses reference counting to keep track of objects.  So the 
Python binding benefits from the changes described above, but at some 
point needs to be checked that it is handling vectors of geometries 
correctly.

Thanks,

Charlie


Index: swig/geos.i
===================================================================
RCS file: /home/cvs/postgis/geos/swig/geos.i,v
retrieving revision 1.6
diff -u -b -r1.6 geos.i
--- swig/geos.i    22 Dec 2005 12:46:13 -0000    1.6
+++ swig/geos.i    10 Jan 2006 08:08:19 -0000
@@ -90,7 +90,7 @@
   /* Convert from scripting language string to char* */
   if (SWIG_AsCharPtrAndSize($input, &buf, &size, &alloc) != SWIG_OK)
   {
-    %argument_fail(SWIG_TypeError, "(TYPEMAP, SIZE)", $argnum);
+    %argument_fail(SWIG_TypeError, "(TYPEMAP, SIZE)", $symname, $argnum);
   }
  
   /* Write data to the stream */
@@ -133,20 +133,80 @@
 %apply SWIGTYPE *DISOWN { geos::CoordinateSequence * };
 %apply SWIGTYPE *DISOWN { geos::LinearRing * };
 %apply SWIGTYPE *DISOWN { std::vector<geos::Geometry * > * };
+%apply SWIGTYPE *DISOWN { std::vector<geos::Coordinate> * };
 
 
+// These methods create new objects
+%newobject *::clone;
+%newobject *::getCoordinates;
+%newobject geos::WKBReader::read;
+%newobject geos::WKBReader::read_hex;
+%newobject geos::WKTReader::read;
+%newobject geos::DefaultCoordinateSequenceFactory::create;
+%newobject geos::GeometryFactory::createPointFromInternalCoord;
+%newobject geos::GeometryFactory::toGeometry;
+%newobject geos::GeometryFactory::createPoint;
+%newobject geos::GeometryFactory::createGeometryCollection;
+%newobject geos::GeometryFactory::createMultiLineString;
+%newobject geos::GeometryFactory::createMultiPolygon;
+%newobject geos::GeometryFactory::createLinearRing;
+%newobject geos::GeometryFactory::createMultiPoint;
+%newobject geos::GeometryFactory::createPolygon;
+%newobject geos::GeometryFactory::createLineString;
+%newobject geos::GeometryFactory::buildGeometry;
+%newobject geos::GeometryFactory::createGeometry;
+%newobject geos::GeometricShapeFactory::getEnvelope;
+
 /* ================= Exception Handling  ============== */
 
-/* Setup up generalized exception handling */
+/* Mark these classes as exception classes */
+%exceptionclass geos::GEOSException;
+
+/* These are all subclasses of GEOSException */
+%exceptionclass geos::AssertionFailedException;
+%exceptionclass geos::IllegalArgumentException;
+%exceptionclass geos::ParseException;
+%exceptionclass geos::TopologyException;
+%exceptionclass geos::UnsupportedOperationException;
+
+/* This exception class is not surfaced to SWIG
+%exceptionclass geos::NotRepresentableException;*/
+
+/* Setup up generalized exception handling.  Note that GEOS throws
+   classes that are allocated on the heap so we need to catch them
+   by pointer and are responsible for freeing them.  To do this
+   we'll just pass the object along the scripting language (wrapped
+   of course) and make it responsbile for freeing the object via the
+   use of the SWIG_POINTER_OWN flag.*/
+
 %exception {
     try {
         $action
     }
+  catch (geos::AssertionFailedException *e) {
+            %raise(SWIG_NewPointerObj(e, 
SWIGTYPE_p_geos__AssertionFailedException, SWIG_POINTER_OWN), 
"geos::AssertionFailedException", 
SWIGTYPE_p_geos__AssertionFailedException);
+  }
+  catch (geos::IllegalArgumentException *e) {
+            %raise(SWIG_NewPointerObj(e, 
SWIGTYPE_p_geos__IllegalArgumentException, SWIG_POINTER_OWN), 
"geos::IllegalArgumentException", 
SWIGTYPE_p_geos__IllegalArgumentException);
+  }
+  catch (geos::ParseException *e) {
+            %raise(SWIG_NewPointerObj(e, 
SWIGTYPE_p_geos__ParseException, SWIG_POINTER_OWN), 
"geos::ParseException", SWIGTYPE_p_geos__ParseException);
+  }
+  catch (geos::TopologyException *e) {
+            %raise(SWIG_NewPointerObj(e, 
SWIGTYPE_p_geos__TopologyException, SWIG_POINTER_OWN), 
"geos::TopologyException", SWIGTYPE_p_geos__TopologyException);
+  }
+  catch (geos::UnsupportedOperationException *e) {
+            %raise(SWIG_NewPointerObj(e, 
SWIGTYPE_p_geos__UnsupportedOperationException, SWIG_POINTER_OWN), 
"geos::UnsupportedOperationException", 
SWIGTYPE_p_geos__UnsupportedOperationException);
+  }
     catch (geos::GEOSException *e) {
-        SWIG_exception(SWIG_RuntimeError, e->toString().data());
+            %raise(SWIG_NewPointerObj(e, 
SWIGTYPE_p_geos__GEOSException, SWIG_POINTER_OWN), 
"geos::GEOSException", SWIGTYPE_p_geos__GEOSException);
+  }
+  catch (...) {
+      SWIG_exception(SWIG_RuntimeError, "Unknown exception took place 
in the method: $symname.");
     }
 }
 
+
 /* =============  Classes to ignore (why are these ignored? ======= */
 %ignore geos::LineMergeDirectedEdge;
 %ignore geos::PolygonizeEdge;
Index: swig/ruby/renames.i
===================================================================
RCS file: /home/cvs/postgis/geos/swig/ruby/renames.i,v
retrieving revision 1.1
diff -u -b -r1.1 renames.i
--- swig/ruby/renames.i    22 Dec 2005 12:46:13 -0000    1.1
+++ swig/ruby/renames.i    8 Jan 2006 21:04:34 -0000
@@ -301,6 +301,8 @@
 %rename(get_cut_edges) geos::Polygonizer::getCutEdges;
 %rename(get_invalid_ring_lines) geos::Polygonizer::getInvalidRingLines;
 %rename(print_hex) geos::WKBReader::printHEX;
+%rename(read_hex) geos::WKBReader::readHEX;
+%rename(write_hex) geos::WKBWriter::writeHEX;
 
 // -------  SWIG Aliases ------------
 %alias geos::CoordinateSequenceFactory::create "create";
Index: swig/ruby/ruby.i
===================================================================
RCS file: /home/cvs/postgis/geos/swig/ruby/ruby.i,v
retrieving revision 1.1
diff -u -b -r1.1 ruby.i
--- swig/ruby/ruby.i    22 Dec 2005 12:46:13 -0000    1.1
+++ swig/ruby/ruby.i    10 Jan 2006 08:23:48 -0000
@@ -48,7 +48,200 @@
 #undef select
 %}
 
-/* Define various template classes */
+
+/* Extend a few classes to add nice string representations */
+%extend geos::PrecisionModel {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::PrecisionModel ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+%extend geos::Coordinate {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::Coordinate ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+%extend geos::CoordinateSequence {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::CoordinateSequence ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+%extend geos::DefaultCoordinateSequence {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::DefaultCoordinateSequence ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+%extend geos::Envelope {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::Envelope ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+%extend geos::Geometry {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::Geometry ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+%extend geos::LineSegment {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::LineSegment ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+%extend geos::IntersectionMatrix {
+    string to_s()
+    {
+        ostringstream os;
+        os << "<Geos::IntersectionMatrix ";
+        os << self->toString();
+        os << ">";
+        return os.str();
+    }
+};
+
+/* Geos uses vectors of pointers to pass around geometries.  These will be
+     wrapped by SWIG - but we have to be careful.  The problem is
+   if we put a C++ object into the vector that a Ruby object owns, when
+   that Ruby object goes out of scope the C++ object will be freed.  Thus
+   the pointer inside the vector becomes corrupt.  To stop this from 
happening
+   we have to implement a Ruby mark function, which in turn requires 
turning
+   on SWIG Ruby Object tracking for these containers.*/
+
+// First declare mark functions for the containers
+%markfunc std::vector<geos::Geometry *> "mark_GeometryVector";
+%markfunc std::vector<geos::LineString *> "mark_LineStringVector";
+%markfunc std::vector<geos::Polygon *> "mark_PolygonVector";
+
+// Now define the containers
 %template("GeometryVector") std::vector<geos::Geometry *>;
 %template("LineStringVector") std::vector<geos::LineString *>;
 %template("PolygonVector") std::vector<geos::Polygon *>;
+
+// Now track the objects that go go into the containers, which
+// is Geometry or any class inherited from Geometry
+%trackobjects geos::Geometry;
+%trackobjects geos::Point;
+%trackobjects geos::LineString;
+%trackobjects geos::LinearRing;
+%trackobjects geos::Polygon;
+%trackobjects geos::GeometryCollection;
+%trackobjects geos::MultiPoint;
+%trackobjects geos::MultiLineString;
+%trackobjects geos::MultiPolygon;
+
+// Last define the mark functions
+%header %{
+    static void mark_GeometryVector(void* ptr)
+    {
+      typedef std::vector<geos::Geometry *> GeometryVector;
+      typedef GeometryVector::iterator GeometryVectorIter;
+     
+      GeometryVector *vec = reinterpret_cast<GeometryVector*>(ptr);
+      GeometryVectorIter iter = vec->begin();
+      GeometryVectorIter last = vec->end();
+     
+        for(; iter != last; ++iter)
+        {
+            geos::Geometry *geometry = *iter;
+            VALUE object = SWIG_RubyInstanceFor(geometry);
+            if (object != Qnil)
+            {
+                rb_gc_mark(object);
+            }
+            else
+            {
+                // This should not happen
+                rb_raise(rb_eRuntimeError, "Unknown object stored in 
vector");
+            }
+        }
+    }
+
+    static void mark_LineStringVector(void* ptr)
+    {
+      typedef std::vector<geos::LineString *> LineStringVector;
+      typedef LineStringVector::iterator LineStringVectorIter;
+     
+      LineStringVector *vec = reinterpret_cast<LineStringVector*>(ptr);
+      LineStringVectorIter iter = vec->begin();
+      LineStringVectorIter last = vec->end();
+     
+        for(; iter != last; ++iter)
+        {
+            geos::LineString *geometry = *iter;
+            VALUE object = SWIG_RubyInstanceFor(geometry);
+            if (object != Qnil)
+            {
+                rb_gc_mark(object);
+            }
+            else
+            {
+                // This should not happen
+                rb_raise(rb_eRuntimeError, "Unknown object stored in 
vector");
+            }
+        }
+    }
+
+    static void mark_PolygonVector(void* ptr)
+    {
+      typedef std::vector<geos::Polygon *> PolygonVector;
+      typedef PolygonVector::iterator PolygonVectorIter;
+     
+      PolygonVector *vec = reinterpret_cast<PolygonVector*>(ptr);
+      PolygonVectorIter iter = vec->begin();
+      PolygonVectorIter last = vec->end();
+     
+        for(; iter != last; ++iter)
+        {
+            geos::Polygon *geometry = *iter;
+            VALUE object = SWIG_RubyInstanceFor(geometry);
+            if (object != Qnil)
+            {
+                rb_gc_mark(object);
+            }
+            else
+            {
+                // This should not happen
+                rb_raise(rb_eRuntimeError, "Unknown object stored in 
vector");
+            }
+        }
+    }
+%}



-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 2781 bytes
Desc: S/MIME Cryptographic Signature
Url : http://lists.osgeo.org/pipermail/geos-devel/attachments/20060110/7f227c57/smime.bin


More information about the geos-devel mailing list