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:
parent
6538a26a47
commit
b0b7bbced7
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user