Permalink
Browse files

Cleanup, bug fixes, and new tests for the GEOS I/O objects and `GEOSB…

…ase` object.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10177 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent faf1609 commit f0b7cc4a2331884c29fd431c8bc76c13d9d82d46 @jbronn jbronn committed Mar 28, 2009
@@ -41,9 +41,7 @@ def _get_ptr(self):
def _set_ptr(self, ptr):
# Only allow the pointer to be set with pointers of the
# compatible type or None (NULL).
- if isinstance(ptr, int):
- self._ptr = self.ptr_type(ptr)
- elif isinstance(ptr, (self.ptr_type, NoneType)):
+ if isinstance(ptr, (self.ptr_type, NoneType)):
self._ptr = ptr
else:
raise TypeError('Incompatible pointer type')
@@ -20,7 +20,6 @@
# prototypes module -- which handles all interaction with
# the underlying GEOS library.
from django.contrib.gis.geos import prototypes as capi
-from django.contrib.gis.geos import io
# Regular expression for recognizing HEXEWKB and WKT. A prophylactic measure
# to prevent potentially malicious input from reaching the underlying C
@@ -62,21 +61,21 @@ def __init__(self, geo_input, srid=None):
if wkt_m:
# Handling WKT input.
if wkt_m.group('srid'): srid = int(wkt_m.group('srid'))
- g = io.wkt_r.read(wkt_m.group('wkt'))
+ g = wkt_r.read(wkt_m.group('wkt'))
elif hex_regex.match(geo_input):
# Handling HEXEWKB input.
- g = io.wkb_r.read(geo_input)
+ g = wkb_r.read(geo_input)
elif gdal.GEOJSON and gdal.geometries.json_regex.match(geo_input):
# Handling GeoJSON input.
- g = io.wkb_r.read(gdal.OGRGeometry(geo_input).wkb)
+ g = wkb_r.read(gdal.OGRGeometry(geo_input).wkb)
else:
raise ValueError('String or unicode input unrecognized as WKT EWKT, and HEXEWKB.')
elif isinstance(geo_input, GEOM_PTR):
# When the input is a pointer to a geomtry (GEOM_PTR).
g = geo_input
elif isinstance(geo_input, buffer):
# When the input is a buffer (WKB).
- g = io.wkb_r.read(geo_input)
+ g = wkb_r.read(geo_input)
elif isinstance(geo_input, GEOSGeometry):
g = capi.geom_clone(geo_input.ptr)
else:
@@ -366,7 +365,7 @@ def ewkt(self):
@property
def wkt(self):
"Returns the WKT (Well-Known Text) of the Geometry."
- return io.wkt_w.write(self.ptr)
+ return wkt_w.write(self)
@property
def hex(self):
@@ -377,7 +376,7 @@ def hex(self):
"""
# A possible faster, all-python, implementation:
# str(self.wkb).encode('hex')
- return io.wkb_w.write_hex(self.ptr)
+ return wkb_w.write_hex(self)
@property
def json(self):
@@ -394,7 +393,7 @@ def json(self):
@property
def wkb(self):
"Returns the WKB of the Geometry as a buffer."
- return io.wkb_w.write(self.ptr)
+ return wkb_w.write(self)
@property
def kml(self):
@@ -456,7 +455,7 @@ def transform(self, ct, clone=False):
g = gdal.OGRGeometry(self.wkb, srid)
g.transform(ct)
# Getting a new GEOS pointer
- ptr = io.wkb_r.read(g.wkb)
+ ptr = wkb_r.read(g.wkb)
if clone:
# User wants a cloned transformed geometry returned.
return GEOSGeometry(ptr, srid=g.srid)
@@ -618,6 +617,9 @@ def clone(self):
7 : GeometryCollection,
}
+# Similarly, import the GEOS I/O instances here to avoid conflicts.
+from django.contrib.gis.geos.io import wkt_r, wkt_w, wkb_r, wkb_w
+
# If supported, import the PreparedGeometry class.
if GEOS_PREPARE:
from django.contrib.gis.geos.prepared import PreparedGeometry
@@ -6,11 +6,12 @@
from ctypes import byref, c_size_t
from django.contrib.gis.geos.base import GEOSBase
from django.contrib.gis.geos.error import GEOSException
+from django.contrib.gis.geos.geometry import GEOSGeometry
from django.contrib.gis.geos.libgeos import GEOM_PTR
from django.contrib.gis.geos.prototypes import io as capi
class IOBase(GEOSBase):
- "Base class for IO objects that that have `destroy` method."
+ "Base class for GEOS I/O objects."
def __init__(self):
# Getting the pointer with the constructor.
self.ptr = self.constructor()
@@ -19,36 +20,43 @@ def __del__(self):
# Cleaning up with the appropriate destructor.
if self._ptr: self.destructor(self._ptr)
- def _get_geom_ptr(self, geom):
- if hasattr(geom, 'ptr'): geom = geom.ptr
- if not isinstance(geom, GEOM_PTR): raise TypeError
- return geom
-
### WKT Reading and Writing objects ###
-class WKTReader(IOBase):
+
+# Non-public class for internal use because its `read` method returns
+# _pointers_ instead of a GEOSGeometry object.
+class _WKTReader(IOBase):
constructor = capi.wkt_reader_create
destructor = capi.wkt_reader_destroy
ptr_type = capi.WKT_READ_PTR
- def read(self, wkt, ptr=False):
+ def read(self, wkt):
if not isinstance(wkt, basestring): raise TypeError
return capi.wkt_reader_read(self.ptr, wkt)
+class WKTReader(_WKTReader):
+ def read(self, wkt):
+ "Returns a GEOSGeometry for the given WKT string."
+ return GEOSGeometry(super(WKTReader, self).read(wkt))
+
class WKTWriter(IOBase):
constructor = capi.wkt_writer_create
destructor = capi.wkt_writer_destroy
ptr_type = capi.WKT_WRITE_PTR
def write(self, geom):
- return capi.wkt_writer_write(self.ptr, self._get_geom_ptr(geom))
+ "Returns the WKT representation of the given geometry."
+ return capi.wkt_writer_write(self.ptr, geom.ptr)
### WKB Reading and Writing objects ###
-class WKBReader(IOBase):
+
+# Non-public class for the same reason as _WKTReader above.
+class _WKBReader(IOBase):
constructor = capi.wkb_reader_create
destructor = capi.wkb_reader_destroy
ptr_type = capi.WKB_READ_PTR
def read(self, wkb):
+ "Returns a _pointer_ to C GEOS Geometry object from the given WKB."
if isinstance(wkb, buffer):
wkb_s = str(wkb)
return capi.wkb_reader_read(self.ptr, wkb_s, len(wkb_s))
@@ -57,16 +65,23 @@ def read(self, wkb):
else:
raise TypeError
+class WKBReader(_WKBReader):
+ def read(self, wkb):
+ "Returns a GEOSGeometry for the given WKB buffer."
+ return GEOSGeometry(super(WKBReader, self).read(wkb))
+
class WKBWriter(IOBase):
constructor = capi.wkb_writer_create
destructor = capi.wkb_writer_destroy
- ptr_type = capi.WKB_READ_PTR
+ ptr_type = capi.WKB_WRITE_PTR
def write(self, geom):
- return buffer(capi.wkb_writer_write(self.ptr, self._get_geom_ptr(geom), byref(c_size_t())))
+ "Returns the WKB representation of the given geometry."
+ return buffer(capi.wkb_writer_write(self.ptr, geom.ptr, byref(c_size_t())))
def write_hex(self, geom):
- return capi.wkb_writer_write_hex(self.ptr, self._get_geom_ptr(geom), byref(c_size_t()))
+ "Returns the HEXEWKB representation of the given geometry."
+ return capi.wkb_writer_write_hex(self.ptr, geom.ptr, byref(c_size_t()))
### WKBWriter Properties ###
@@ -102,8 +117,7 @@ def _set_include_srid(self, include):
srid = property(_get_include_srid, _set_include_srid)
# Instances of the WKT and WKB reader/writer objects.
-wkt_r = WKTReader()
+wkt_r = _WKTReader()
wkt_w = WKTWriter()
-wkb_r = WKBReader()
+wkb_r = _WKBReader()
wkb_w = WKBWriter()
-
@@ -2,10 +2,11 @@
GEOS Testing module.
"""
from unittest import TestSuite, TextTestRunner
-import test_geos, test_geos_mutation, test_mutable_list
+import test_geos, test_io, test_geos_mutation, test_mutable_list
test_suites = [
test_geos.suite(),
+ test_io.suite(),
test_geos_mutation.suite(),
test_mutable_list.suite(),
]
@@ -1,7 +1,6 @@
-import random, unittest, sys
-from ctypes import ArgumentError
+import ctypes, random, unittest, sys
from django.contrib.gis.geos import *
-from django.contrib.gis.geos.base import gdal, numpy
+from django.contrib.gis.geos.base import gdal, numpy, GEOSBase
from django.contrib.gis.tests.geometries import *
class GEOSTest(unittest.TestCase):
@@ -18,6 +17,48 @@ def null_srid(self):
else:
return None
+ def test00_base(self):
+ "Tests out the GEOSBase class."
+ # Testing out GEOSBase class, which provides a `ptr` property
+ # that abstracts out access to underlying C pointers.
+ class FakeGeom1(GEOSBase):
+ pass
+
+ # This one only accepts pointers to floats
+ c_float_p = ctypes.POINTER(ctypes.c_float)
+ class FakeGeom2(GEOSBase):
+ ptr_type = c_float_p
+
+ # Default ptr_type is `c_void_p`.
+ fg1 = FakeGeom1()
+ # Default ptr_type is C float pointer
+ fg2 = FakeGeom2()
+
+ # These assignments are OK -- None is allowed because
+ # it's equivalent to the NULL pointer.
+ fg1.ptr = ctypes.c_void_p()
+ fg1.ptr = None
+ fg2.ptr = c_float_p(ctypes.c_float(5.23))
+ fg2.ptr = None
+
+ # Because pointers have been set to NULL, an exception should be
+ # raised when we try to access it. Raising an exception is
+ # preferrable to a segmentation fault that commonly occurs when
+ # a C method is given a NULL memory reference.
+ for fg in (fg1, fg2):
+ # Equivalent to `fg.ptr`
+ self.assertRaises(GEOSException, fg._get_ptr)
+
+ # Anything that is either not None or the acceptable pointer type will
+ # result in a TypeError when trying to assign it to the `ptr` property.
+ # Thus, memmory addresses (integers) and pointers of the incorrect type
+ # (in `bad_ptrs`) will not be allowed.
+ bad_ptrs = (5, ctypes.c_char_p('foobar'))
+ for bad_ptr in bad_ptrs:
+ # Equivalent to `fg.ptr = bad_ptr`
+ self.assertRaises(TypeError, fg1._set_ptr, bad_ptr)
+ self.assertRaises(TypeError, fg2._set_ptr, bad_ptr)
+
def test01a_wkt(self):
"Testing WKT output."
for g in wkt_out:
@@ -494,7 +535,7 @@ def test14_buffer(self):
exp_buf = fromstr(g_tup[1].wkt)
# Can't use a floating-point for the number of quadsegs.
- self.assertRaises(ArgumentError, g.buffer, g_tup[2], float(g_tup[3]))
+ self.assertRaises(ctypes.ArgumentError, g.buffer, g_tup[2], float(g_tup[3]))
# Constructing our buffer
buf = g.buffer(g_tup[2], g_tup[3])
@@ -518,7 +559,7 @@ def test15_srid(self):
self.assertEqual(4326, pnt.srid)
pnt.srid = 3084
self.assertEqual(3084, pnt.srid)
- self.assertRaises(ArgumentError, pnt.set_srid, '4326')
+ self.assertRaises(ctypes.ArgumentError, pnt.set_srid, '4326')
# Testing SRID keyword on fromstr(), and on Polygon rings.
poly = fromstr(polygons[1].wkt, srid=4269)
@@ -824,7 +865,7 @@ def test26_prepared(self):
self.assertEqual(mpoly.intersects(pnt), prep.intersects(pnt))
self.assertEqual(c, prep.covers(pnt))
- def test26_line_merge(self):
+ def test26_line_merge(self):
"Testing line merge support"
ref_geoms = (fromstr('LINESTRING(1 1, 1 1, 3 3)'),
fromstr('MULTILINESTRING((1 1, 3 3), (3 3, 4 2))'),
Oops, something went wrong. Retry.

0 comments on commit f0b7cc4

Please sign in to comment.