Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #10660 -- `GeometryField` no longer requires `srid`/`null` keyw…

…ords, and now respects `required`; coordinate transformations now done inside `gis.forms.GeometryField` -- benefit being that `OSMGeoAdmin` no longer requires 900913 entry in `spatial_ref_sys` thus enabling it to work with MySQL/Oracle spatial backends; added tests for geographic forms.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10634 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 66db0b43ad8759c1d95669847ac8153c36f2ac48 1 parent 8010e0c
@jbronn jbronn authored
View
1  django/contrib/gis/forms/__init__.py
@@ -1 +1,2 @@
+from django.forms import *
from django.contrib.gis.forms.fields import GeometryField
View
44 django/contrib/gis/forms/fields.py
@@ -1,12 +1,15 @@
from django import forms
-from django.contrib.gis.db.backend import SpatialBackend
from django.utils.translation import ugettext_lazy as _
+# While this couples the geographic forms to the GEOS library,
+# it decouples from database (by not importing SpatialBackend).
+from django.contrib.gis.geos import GEOSGeometry
+
class GeometryField(forms.Field):
"""
This is the basic form field for a Geometry. Any textual input that is
- accepted by SpatialBackend.Geometry is accepted by this form. By default,
- this is GEOSGeometry, which accepts WKT, HEXEWKB, WKB, and GeoJSON.
+ accepted by GEOSGeometry is accepted by this form. By default,
+ this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
"""
widget = forms.Textarea
@@ -14,12 +17,16 @@ class GeometryField(forms.Field):
'no_geom' : _(u'No geometry value provided.'),
'invalid_geom' : _(u'Invalid geometry value.'),
'invalid_geom_type' : _(u'Invalid geometry type.'),
- }
+ 'transform_error' : _(u'An error occurred when transforming the geometry'
+ 'to the SRID of the geometry form field.'),
+ }
def __init__(self, **kwargs):
- self.null = kwargs.pop('null')
- self.geom_type = kwargs.pop('geom_type')
- self.srid = kwargs.pop('srid')
+ # Pop out attributes from the database field, or use sensible
+ # defaults (e.g., allow None).
+ self.srid = kwargs.pop('srid', None)
+ self.geom_type = kwargs.pop('geom_type', 'GEOMETRY')
+ self.null = kwargs.pop('null', True)
super(GeometryField, self).__init__(**kwargs)
def clean(self, value):
@@ -29,21 +36,32 @@ def clean(self, value):
the value cannot be instantiated as a Geometry.
"""
if not value:
- if self.null:
- # The geometry column allows NULL, return None.
+ if self.null and not self.required:
+ # The geometry column allows NULL and is not required.
return None
else:
raise forms.ValidationError(self.error_messages['no_geom'])
-
+
+ # Trying to create a Geometry object from the form value.
try:
- # Trying to create a Geometry object from the form value.
- geom = SpatialBackend.Geometry(value)
+ geom = GEOSGeometry(value)
except:
raise forms.ValidationError(self.error_messages['invalid_geom'])
-
+
# Ensuring that the geometry is of the correct type (indicated
# using the OGC string label).
if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
raise forms.ValidationError(self.error_messages['invalid_geom_type'])
+ # Transforming the geometry if the SRID was set.
+ if self.srid:
+ if not geom.srid:
+ # Should match that of the field if not given.
+ geom.srid = self.srid
+ elif self.srid != -1 and self.srid != geom.srid:
+ try:
+ geom.transform(self.srid)
+ except:
+ raise forms.ValidationError(self.error_messages['transform_error'])
+
return geom
View
2  django/contrib/gis/tests/__init__.py
@@ -35,7 +35,7 @@ def geo_suite():
if HAS_GDAL:
# These tests require GDAL.
- test_suite_names.append('test_spatialrefsys')
+ test_suite_names.extend(['test_spatialrefsys', 'test_geoforms'])
test_apps.append('layermap')
# Adding the GDAL tests.
View
65 django/contrib/gis/tests/test_geoforms.py
@@ -0,0 +1,65 @@
+import unittest
+
+from django.forms import ValidationError
+from django.contrib.gis import forms
+from django.contrib.gis.geos import GEOSGeometry
+
+class GeometryFieldTest(unittest.TestCase):
+
+ def test00_init(self):
+ "Testing GeometryField initialization with defaults."
+ fld = forms.GeometryField()
+ for bad_default in ('blah', 3, 'FoO', None, 0):
+ self.assertRaises(ValidationError, fld.clean, bad_default)
+
+ def test01_srid(self):
+ "Testing GeometryField with a SRID set."
+ # Input that doesn't specify the SRID is assumed to be in the SRID
+ # of the input field.
+ fld = forms.GeometryField(srid=4326)
+ geom = fld.clean('POINT(5 23)')
+ self.assertEqual(4326, geom.srid)
+ # Making the field in a different SRID from that of the geometry, and
+ # asserting it transforms.
+ fld = forms.GeometryField(srid=32140)
+ tol = 0.0000001
+ xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140)
+ # The cleaned geometry should be transformed to 32140.
+ cleaned_geom = fld.clean('SRID=4326;POINT (-95.363151 29.763374)')
+ self.failUnless(xform_geom.equals_exact(cleaned_geom, tol))
+
+ def test02_null(self):
+ "Testing GeometryField's handling of null (None) geometries."
+ # Form fields, by default, are required (`required=True`)
+ fld = forms.GeometryField()
+ self.assertRaises(forms.ValidationError, fld.clean, None)
+
+ # Still not allowed if `null=False`.
+ fld = forms.GeometryField(required=False, null=False)
+ self.assertRaises(forms.ValidationError, fld.clean, None)
+
+ # This will clean None as a geometry (See #10660).
+ fld = forms.GeometryField(required=False)
+ self.assertEqual(None, fld.clean(None))
+
+ def test03_geom_type(self):
+ "Testing GeometryField's handling of different geometry types."
+ # By default, all geometry types are allowed.
+ fld = forms.GeometryField()
+ for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'):
+ self.assertEqual(GEOSGeometry(wkt), fld.clean(wkt))
+
+ pnt_fld = forms.GeometryField(geom_type='POINT')
+ self.assertEqual(GEOSGeometry('POINT(5 23)'), pnt_fld.clean('POINT(5 23)'))
+ self.assertRaises(forms.ValidationError, pnt_fld.clean, 'LINESTRING(0 0, 1 1)')
+
+def suite():
+ s = unittest.TestSuite()
+ s.addTest(unittest.makeSuite(GeometryFieldTest))
+ return s
+
+def run(verbosity=2):
+ unittest.TextTestRunner(verbosity=verbosity).run(suite())
+
+if __name__=="__main__":
+ run()
Please sign in to comment.
Something went wrong with that request. Please try again.