Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #14318 -- Added `GEOSGeometry.valid_reason` property. Thanks, R…

…ob Coup.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14447 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit cabc21ca60758626ef8a5a8ff8c7f551a7e53db7 1 parent 877033b
@jbronn jbronn authored
View
17 django/contrib/gis/geos/geometry.py
@@ -275,6 +275,15 @@ def valid(self):
"This property tests the validity of this Geometry."
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. ####
def contains(self, other):
"Returns true if other.within(this) returns true."
@@ -376,7 +385,7 @@ def hex(self):
"""
Returns the WKB of this Geometry in hexadecimal form. Please note
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).
"""
# A possible faster, all-python, implementation:
@@ -386,14 +395,14 @@ def hex(self):
@property
def hexewkb(self):
"""
- Returns the EWKB of this Geometry in hexadecimal form. This is an
- extension of the WKB specification that includes SRID and Z values
+ Returns the EWKB of this Geometry in hexadecimal form. This is an
+ extension of the WKB specification that includes SRID and Z values
that are a part of this geometry.
"""
if self.hasz:
if not GEOS_PREPARE:
# 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)
else:
return ewkb_w().write_hex(self)
View
2  django/contrib/gis/geos/prototypes/__init__.py
@@ -18,7 +18,7 @@
to_hex, to_wkb, to_wkt
# 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
from django.contrib.gis.geos.prototypes.predicates import geos_hasz, geos_isempty, \
View
15 django/contrib/gis/geos/prototypes/misc.py
@@ -3,10 +3,13 @@
ones that return the area, distance, and length.
"""
from ctypes import c_int, c_double, POINTER
-from django.contrib.gis.geos.libgeos import GEOM_PTR
-from django.contrib.gis.geos.prototypes.errcheck import check_dbl
+from django.contrib.gis.geos.libgeos import GEOM_PTR, GEOS_PREPARE
+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
+__all__ = ['geos_area', 'geos_distance', 'geos_length']
+
### ctypes generator function ###
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_distance = dbl_from_geom(GEOSFunc('GEOSDistance'), num_geom=2)
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
21 django/contrib/gis/geos/tests/test_geos.py
@@ -1,6 +1,7 @@
import ctypes, random, unittest, sys
from django.contrib.gis.geos import *
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
class GEOSTest(unittest.TestCase, TestDataMixin):
@@ -917,6 +918,26 @@ def test26_line_merge(self):
for geom, merged in zip(ref_geoms, ref_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():
s = unittest.TestSuite()
s.addTest(unittest.makeSuite(GEOSTest))
View
6 docs/ref/contrib/gis/geos.txt
@@ -219,6 +219,12 @@ definition.
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
Property that may be used to retrieve or set the SRID associated with the
Please sign in to comment.
Something went wrong with that request. Please try again.