Fixed #14318 -- Added `GEOSGeometry.valid_reason` property. Thanks, Rob Coup.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14447 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2010-11-04 01:29:37 +00:00
parent 877033b479
commit cabc21ca60
5 changed files with 54 additions and 7 deletions

View File

@ -275,6 +275,15 @@ class GEOSGeometry(GEOSBase, ListMixin):
"This property tests the validity of this Geometry." "This property tests the validity of this Geometry."
return capi.geos_isvalid(self.ptr) return capi.geos_isvalid(self.ptr)
@property
def valid_reason(self):
"""
Returns a string containing the reason for any invalidity.
"""
if not GEOS_PREPARE:
raise GEOSException('Upgrade GEOS to 3.1 to get validity reason.')
return capi.geos_isvalidreason(self.ptr)
#### Binary predicates. #### #### Binary predicates. ####
def contains(self, other): def contains(self, other):
"Returns true if other.within(this) returns true." "Returns true if other.within(this) returns true."
@ -376,7 +385,7 @@ class GEOSGeometry(GEOSBase, ListMixin):
""" """
Returns the WKB of this Geometry in hexadecimal form. Please note Returns the WKB of this Geometry in hexadecimal form. Please note
that the SRID and Z values are not included in this representation that the SRID and Z values are not included in this representation
because it is not a part of the OGC specification (use the `hexewkb` because it is not a part of the OGC specification (use the `hexewkb`
property instead). property instead).
""" """
# A possible faster, all-python, implementation: # A possible faster, all-python, implementation:
@ -386,14 +395,14 @@ class GEOSGeometry(GEOSBase, ListMixin):
@property @property
def hexewkb(self): def hexewkb(self):
""" """
Returns the EWKB of this Geometry in hexadecimal form. This is an Returns the EWKB of this Geometry in hexadecimal form. This is an
extension of the WKB specification that includes SRID and Z values extension of the WKB specification that includes SRID and Z values
that are a part of this geometry. that are a part of this geometry.
""" """
if self.hasz: if self.hasz:
if not GEOS_PREPARE: if not GEOS_PREPARE:
# See: http://trac.osgeo.org/geos/ticket/216 # See: http://trac.osgeo.org/geos/ticket/216
raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D HEXEWKB.') raise GEOSException('Upgrade GEOS to 3.1 to get valid 3D HEXEWKB.')
return ewkb_w3d().write_hex(self) return ewkb_w3d().write_hex(self)
else: else:
return ewkb_w().write_hex(self) return ewkb_w().write_hex(self)

View File

@ -18,7 +18,7 @@ from django.contrib.gis.geos.prototypes.geom import from_hex, from_wkb, from_wkt
to_hex, to_wkb, to_wkt to_hex, to_wkb, to_wkt
# Miscellaneous routines. # Miscellaneous routines.
from django.contrib.gis.geos.prototypes.misc import geos_area, geos_distance, geos_length from django.contrib.gis.geos.prototypes.misc import *
# Predicates # Predicates
from django.contrib.gis.geos.prototypes.predicates import geos_hasz, geos_isempty, \ from django.contrib.gis.geos.prototypes.predicates import geos_hasz, geos_isempty, \

View File

@ -3,10 +3,13 @@
ones that return the area, distance, and length. ones that return the area, distance, and length.
""" """
from ctypes import c_int, c_double, POINTER from ctypes import c_int, c_double, POINTER
from django.contrib.gis.geos.libgeos import GEOM_PTR from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOS_PREPARE
from django.contrib.gis.geos.prototypes.errcheck import check_dbl from django.contrib.gis.geos.prototypes.errcheck import check_dbl, check_string
from django.contrib.gis.geos.prototypes.geom import geos_char_p
from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc from django.contrib.gis.geos.prototypes.threadsafe import GEOSFunc
__all__ = ['geos_area', 'geos_distance', 'geos_length']
### ctypes generator function ### ### ctypes generator function ###
def dbl_from_geom(func, num_geom=1): def dbl_from_geom(func, num_geom=1):
""" """
@ -26,3 +29,11 @@ def dbl_from_geom(func, num_geom=1):
geos_area = dbl_from_geom(GEOSFunc('GEOSArea')) geos_area = dbl_from_geom(GEOSFunc('GEOSArea'))
geos_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2) geos_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2)
geos_length = dbl_from_geom(GEOSFunc('GEOSLength')) geos_length = dbl_from_geom(GEOSFunc('GEOSLength'))
# Validity reason; only in GEOS 3.1+
if GEOS_PREPARE:
geos_isvalidreason = GEOSFunc('GEOSisValidReason')
geos_isvalidreason.argtypes = [GEOM_PTR]
geos_isvalidreason.restype = geos_char_p
geos_isvalidreason.errcheck = check_string
__all__.append('geos_isvalidreason')

View File

@ -1,6 +1,7 @@
import ctypes, random, unittest, sys import ctypes, random, unittest, sys
from django.contrib.gis.geos import * from django.contrib.gis.geos import *
from django.contrib.gis.geos.base import gdal, numpy, GEOSBase from django.contrib.gis.geos.base import gdal, numpy, GEOSBase
from django.contrib.gis.geos.libgeos import GEOS_PREPARE
from django.contrib.gis.geometry.test_data import TestDataMixin from django.contrib.gis.geometry.test_data import TestDataMixin
class GEOSTest(unittest.TestCase, TestDataMixin): class GEOSTest(unittest.TestCase, TestDataMixin):
@ -917,6 +918,26 @@ class GEOSTest(unittest.TestCase, TestDataMixin):
for geom, merged in zip(ref_geoms, ref_merged): for geom, merged in zip(ref_geoms, ref_merged):
self.assertEqual(merged, geom.merged) self.assertEqual(merged, geom.merged)
def test27_valid_reason(self):
"Testing IsValidReason support"
# Skipping tests if GEOS < v3.1.
if not GEOS_PREPARE: return
g = GEOSGeometry("POINT(0 0)")
self.assert_(g.valid)
self.assert_(isinstance(g.valid_reason, basestring))
self.assertEqual(g.valid_reason, "Valid Geometry")
print "\nBEGIN - expecting GEOS_NOTICE; safe to ignore.\n"
g = GEOSGeometry("LINESTRING(0 0, 0 0)")
self.assert_(not g.valid)
self.assert_(isinstance(g.valid_reason, basestring))
self.assertEqual(g.valid_reason, "Too few points in geometry component[0 0]")
print "\nEND - expecting GEOS_NOTICE; safe to ignore.\n"
def suite(): def suite():
s = unittest.TestSuite() s = unittest.TestSuite()
s.addTest(unittest.makeSuite(GEOSTest)) s.addTest(unittest.makeSuite(GEOSTest))

View File

@ -219,6 +219,12 @@ definition.
Returns a boolean indicating whether the geometry is valid. Returns a boolean indicating whether the geometry is valid.
.. attribute:: GEOSGeometry.valid_reason
.. versionadded:: 1.3
Returns a string describing the reason why a geometry is invalid.
.. attribute:: GEOSGeometry.srid .. attribute:: GEOSGeometry.srid
Property that may be used to retrieve or set the SRID associated with the Property that may be used to retrieve or set the SRID associated with the