1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

gis: GEOSGeometry updates, improved initialization & tests, simplified some routines.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@5474 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2007-06-15 03:49:25 +00:00
parent 6538a26a47
commit b0b7bbced7
3 changed files with 55 additions and 51 deletions

View File

@ -32,6 +32,7 @@ from ctypes import \
CDLL, CFUNCTYPE, byref, string_at, create_string_buffer, \ CDLL, CFUNCTYPE, byref, string_at, create_string_buffer, \
c_char_p, c_double, c_float, c_int, c_uint, c_size_t c_char_p, c_double, c_float, c_int, c_uint, c_size_t
import os, sys import os, sys
from types import StringType, IntType, FloatType
""" """
The goal of this module is to be a ctypes wrapper around the GEOS library The goal of this module is to be a ctypes wrapper around the GEOS library
@ -120,32 +121,35 @@ class GEOSGeometry(object):
_g = 0 # Initially NULL _g = 0 # Initially NULL
#### Python 'magic' routines #### #### Python 'magic' routines ####
def __init__(self, input, geom_type='wkt'): def __init__(self, input, input_type='wkt'):
"Takes an input and the type of the input for initialization." "Takes an input and the type of the input for initialization."
if geom_type == 'wkt': if isinstance(input, StringType):
# If the geometry is in WKT form if input_type == 'wkt':
g = lgeos.GEOSGeomFromWKT(c_char_p(input)) # If the geometry is in WKT form
elif geom_type == 'hex': g = lgeos.GEOSGeomFromWKT(c_char_p(input))
# If the geometry is in HEX form. elif input_type == 'hex':
sz = c_size_t(len(input)) # If the geometry is in HEX form.
buf = create_string_buffer(input) sz = c_size_t(len(input))
g = lgeos.GEOSGeomFromHEX_buf(buf, sz) buf = create_string_buffer(input)
elif geom_type == 'geos': g = lgeos.GEOSGeomFromHEX_buf(buf, sz)
else:
raise GEOSException, 'GEOS input geometry type "%s" not supported!' % input_type
elif isinstance(input, IntType):
# When the input is a C pointer (Python integer) # When the input is a C pointer (Python integer)
g = input g = input
else: else:
# Invalid geometry type. # Invalid geometry type.
raise GEOSException, 'Improper geometry input type!' raise GEOSException, 'Improper geometry input type: %s' % str(type(input))
# If the geometry pointer is NULL (0), then raise an exception. # If the geometry pointer is NULL (0), raise an exception.
if not g: if not g:
raise GEOSException, 'Invalid %s given!' % geom_type.upper() raise GEOSException, 'Invalid %s given!' % input_type.upper()
else: else:
self._g = g self._g = g
# Setting the class type (e.g. 'Point', 'Polygon', etc.) # Setting the class type (e.g. 'Point', 'Polygon', etc.)
self.__class__ = GEO_CLASSES[self.geom_type] self.__class__ = GEOS_CLASSES[self.geom_type]
def __del__(self): def __del__(self):
"This cleans up the memory allocated for the geometry." "This cleans up the memory allocated for the geometry."
@ -184,6 +188,11 @@ class GEOSGeometry(object):
if n == -1: raise GEOSException, 'Error getting number of coordinates!' if n == -1: raise GEOSException, 'Error getting number of coordinates!'
else: return n else: return n
@property
def num_points(self):
"Returns the number points, or coordinates, in the geometry."
return self.num_coords
@property @property
def dims(self): def dims(self):
"Returns the dimension of this Geometry (0=point, 1=line, 2=surface)." "Returns the dimension of this Geometry (0=point, 1=line, 2=surface)."
@ -327,44 +336,39 @@ class GEOSGeometry(object):
the number of segment used to approximate a quarter circle (defaults to 8). the number of segment used to approximate a quarter circle (defaults to 8).
(Text from PostGIS documentation at ch. 6.1.3) (Text from PostGIS documentation at ch. 6.1.3)
""" """
if type(width) != type(0.0): if not isinstance(width, FloatType):
raise TypeError, 'width parameter must be a float' raise TypeError, 'width parameter must be a float'
if type(quadsegs) != type(0): if not isinstance(quadsegs, IntType):
raise TypeError, 'quadsegs parameter must be an integer' raise TypeError, 'quadsegs parameter must be an integer'
b = lgeos.GEOSBuffer(self._g, c_double(width), c_int(quadsegs)) b = lgeos.GEOSBuffer(self._g, c_double(width), c_int(quadsegs))
return GEOSGeometry(b, 'geos') return GEOSGeometry(b)
@property @property
def envelope(self): def envelope(self):
"Return the geometries bounding box." "Return the geometries bounding box."
e = lgeos.GEOSEnvelope(self._g) return GEOSGeometry(lgeos.GEOSEnvelope(self._g))
return GEOSGeometry(e, 'geos')
@property @property
def centroid(self): def centroid(self):
"""The centroid is equal to the centroid of the set of component Geometrys """The centroid is equal to the centroid of the set of component Geometrys
of highest dimension (since the lower-dimension geometries contribute zero of highest dimension (since the lower-dimension geometries contribute zero
"weight" to the centroid).""" "weight" to the centroid)."""
g = lgeos.GEOSGetCentroid(self._g) return GEOSGeometry(lgeos.GEOSGetCentroid(self._g))
return GEOSGeometry(g, 'geos')
@property @property
def boundary(self): def boundary(self):
"Returns the boundary as a newly allocated Geometry object." "Returns the boundary as a newly allocated Geometry object."
g = lgeos.GEOSBoundary(self._g) return GEOSGeometry(lgeos.GEOSBoundary(self._g))
return GEOSGeometry(g, 'geos')
@property @property
def convex_hull(self): def convex_hull(self):
"Returns the smallest convex Polygon that contains all the points in the Geometry." "Returns the smallest convex Polygon that contains all the points in the Geometry."
g = lgeos.GEOSConvexHull(self._g) return GEOSGeometry(lgeos.GEOSConvexHull(self._g))
return GEOSGeometry(g, 'geos')
@property @property
def point_on_surface(self): def point_on_surface(self):
"Computes an interior point of this Geometry." "Computes an interior point of this Geometry."
g = lgeos.GEOSPointOnSurface(self._g) return GEOSGeometry(lgeos.GEOSPointOnSurface(self._g))
return GEOSGeometry(g, 'geos')
def relate(self, other): def relate(self, other):
"Returns the DE-9IM intersection matrix for this geometry and the other." "Returns the DE-9IM intersection matrix for this geometry and the other."
@ -373,24 +377,20 @@ class GEOSGeometry(object):
def difference(self, other): def difference(self, other):
"""Returns a Geometry representing the points making up this Geometry """Returns a Geometry representing the points making up this Geometry
that do not make up other.""" that do not make up other."""
d = lgeos.GEOSDifference(self._g, other._g) return GEOSGeometry(lgeos.GEOSDifference(self._g, other._g))
return GEOSGeometry(d, 'geos')
def sym_difference(self, other): def sym_difference(self, other):
"""Returns a set combining the points in this Geometry not in other, """Returns a set combining the points in this Geometry not in other,
and the points in other not in this Geometry.""" and the points in other not in this Geometry."""
d = lgeos.GEOSSymDifference(self._g, other._g) return GEOSGeometry(lgeos.GEOSSymDifference(self._g, other._g))
return GEOSGeometry(d, 'geos')
def intersection(self, other): def intersection(self, other):
"Returns a Geometry representing the points shared by this Geometry and other." "Returns a Geometry representing the points shared by this Geometry and other."
i = lgeos.GEOSIntersection(self._g, other._g) return GEOSGeometry(lgeos.GEOSIntersection(self._g, other._g))
return GEOSGeometry(i, 'geos')
def union(self, other): def union(self, other):
"Returns a Geometry representing all the points in this Geometry and other." "Returns a Geometry representing all the points in this Geometry and other."
u = lgeos.GEOSUnion(self._g, other._g) return GEOSGeometry(lgeos.GEOSUnion(self._g, other._g))
return GEOSGeometry(u, 'geos')
#### Other Routines #### #### Other Routines ####
@property @property
@ -640,7 +640,6 @@ class Polygon(GEOSGeometry):
def __len__(self): def __len__(self):
"Returns the number of rings in this Polygon." "Returns the number of rings in this Polygon."
return self.num_interior_rings + 1 return self.num_interior_rings + 1
def get_interior_ring(self, ring_i): def get_interior_ring(self, ring_i):
"Gets the interior ring at the specified index." "Gets the interior ring at the specified index."
@ -698,8 +697,8 @@ class GeometryCollection(GEOSGeometry):
def __getitem__(self, index): def __getitem__(self, index):
"For indexing on the multiple geometries." "For indexing on the multiple geometries."
self._checkindex(index) self._checkindex(index)
item = lgeos.GEOSGeom_clone(lgeos.GEOSGetGeometryN(self._g, c_int(index))) # Cloning the GEOS Geometry first, before returning it.
return GEOSGeometry(item, 'geos') return GEOSGeometry(lgeos.GEOSGeom_clone(lgeos.GEOSGetGeometryN(self._g, c_int(index))))
def __len__(self): def __len__(self):
"Returns the number of geometries in this collection." "Returns the number of geometries in this collection."
@ -711,12 +710,12 @@ class MultiLineString(GeometryCollection): pass
class MultiPolygon(GeometryCollection): pass class MultiPolygon(GeometryCollection): pass
# Class mapping dictionary # Class mapping dictionary
GEO_CLASSES = {'Point' : Point, GEOS_CLASSES = {'Point' : Point,
'Polygon' : Polygon, 'Polygon' : Polygon,
'LineString' : LineString, 'LineString' : LineString,
'LinearRing' : LinearRing, 'LinearRing' : LinearRing,
'GeometryCollection' : GeometryCollection, 'GeometryCollection' : GeometryCollection,
'MultiPoint' : MultiPoint, 'MultiPoint' : MultiPoint,
'MultiLineString' : MultiLineString, 'MultiLineString' : MultiLineString,
'MultiPolygon' : MultiPolygon, 'MultiPolygon' : MultiPolygon,
} }

View File

@ -83,6 +83,12 @@ class GeosTest2(unittest.TestCase):
self.assertEqual(poly.ring, False) self.assertEqual(poly.ring, False)
self.assertEqual(p.n_i, poly.num_interior_rings) self.assertEqual(p.n_i, poly.num_interior_rings)
self.assertEqual(p.n_i + 1, len(poly)) # Testing __len__ self.assertEqual(p.n_i + 1, len(poly)) # Testing __len__
self.assertEqual(p.n_p, poly.num_points)
# Area & Centroid
self.assertAlmostEqual(p.area, poly.area, 9)
self.assertAlmostEqual(p.centroid[0], poly.centroid.tuple[0], 9)
self.assertAlmostEqual(p.centroid[1], poly.centroid.tuple[1], 9)
# Testing the geometry equivalence # Testing the geometry equivalence
self.assertEqual(True, poly == GEOSGeometry(p.wkt)) self.assertEqual(True, poly == GEOSGeometry(p.wkt))
@ -145,7 +151,6 @@ class GeosTest2(unittest.TestCase):
self.assertAlmostEqual(l.centroid[0], ml.centroid.x, 9) self.assertAlmostEqual(l.centroid[0], ml.centroid.x, 9)
self.assertAlmostEqual(l.centroid[1], ml.centroid.y, 9) self.assertAlmostEqual(l.centroid[1], ml.centroid.y, 9)
self.assertEqual(True, ml == GEOSGeometry(l.wkt)) self.assertEqual(True, ml == GEOSGeometry(l.wkt))
self.assertEqual(False, ml == prev) self.assertEqual(False, ml == prev)
prev = ml prev = ml

View File

@ -3,7 +3,7 @@ from django.contrib.gis.geos import GEOSGeometry, hex_to_wkt, wkt_to_hex, centro
from geos import geomToWKT, geomToHEX, geomFromWKT, geomFromHEX from geos import geomToWKT, geomToHEX, geomFromWKT, geomFromHEX
from geometries import swig_geoms as geos_geoms from geometries import swig_geoms as geos_geoms
class GeosTest(unittest.TestCase): class GeosTestWithSwig(unittest.TestCase):
def test001_hex_to_wkt(self): def test001_hex_to_wkt(self):
"Testing HEX to WKT conversion." "Testing HEX to WKT conversion."
@ -20,7 +20,7 @@ class GeosTest(unittest.TestCase):
def test003_centroid(self): def test003_centroid(self):
"Testing the centroid property." "Testing the centroid property."
for g in geos_geoms: for g in geos_geoms:
wkt1 = (centroid(g.wkt, geom_type='wkt')).wkt wkt1 = centroid(g.wkt, geom_type='wkt')
wkt2 = geomToWKT((geomFromWKT(g.wkt)).getCentroid()) wkt2 = geomToWKT((geomFromWKT(g.wkt)).getCentroid())
self.assertEqual(wkt1, wkt2) self.assertEqual(wkt1, wkt2)
@ -60,7 +60,7 @@ class GeosTest(unittest.TestCase):
def suite(): def suite():
s = unittest.TestSuite() s = unittest.TestSuite()
s.addTest(unittest.makeSuite(GeosTest)) s.addTest(unittest.makeSuite(GeosTestWithSwig))
return s return s
def run(verbosity=2): def run(verbosity=2):