Author: jbronn
Date: 2009-04-25 13:24:32 -0500 (Sat, 25 Apr 2009)
New Revision: 10634

Added:
   django/trunk/django/contrib/gis/tests/test_geoforms.py
Modified:
   django/trunk/django/contrib/gis/forms/__init__.py
   django/trunk/django/contrib/gis/forms/fields.py
   django/trunk/django/contrib/gis/tests/__init__.py
Log:
Fixed #10660 -- `GeometryField` no longer requires `srid`/`null` keywords, 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.


Modified: django/trunk/django/contrib/gis/forms/__init__.py
===================================================================
--- django/trunk/django/contrib/gis/forms/__init__.py   2009-04-24 20:34:48 UTC 
(rev 10633)
+++ django/trunk/django/contrib/gis/forms/__init__.py   2009-04-25 18:24:32 UTC 
(rev 10634)
@@ -1 +1,2 @@
+from django.forms import *
 from django.contrib.gis.forms.fields import GeometryField

Modified: django/trunk/django/contrib/gis/forms/fields.py
===================================================================
--- django/trunk/django/contrib/gis/forms/fields.py     2009-04-24 20:34:48 UTC 
(rev 10633)
+++ django/trunk/django/contrib/gis/forms/fields.py     2009-04-25 18:24:32 UTC 
(rev 10634)
@@ -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 @@
         '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 @@
         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

Modified: django/trunk/django/contrib/gis/tests/__init__.py
===================================================================
--- django/trunk/django/contrib/gis/tests/__init__.py   2009-04-24 20:34:48 UTC 
(rev 10633)
+++ django/trunk/django/contrib/gis/tests/__init__.py   2009-04-25 18:24:32 UTC 
(rev 10634)
@@ -35,7 +35,7 @@
 
     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.

Added: django/trunk/django/contrib/gis/tests/test_geoforms.py
===================================================================
--- django/trunk/django/contrib/gis/tests/test_geoforms.py                      
        (rev 0)
+++ django/trunk/django/contrib/gis/tests/test_geoforms.py      2009-04-25 
18:24:32 UTC (rev 10634)
@@ -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()


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to