Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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
Justin Bronn authored April 25, 2009
1  django/contrib/gis/forms/__init__.py
... ...
@@ -1 +1,2 @@
  1
+from django.forms import *
1 2
 from django.contrib.gis.forms.fields import GeometryField
44  django/contrib/gis/forms/fields.py
... ...
@@ -1,12 +1,15 @@
1 1
 from django import forms
2  
-from django.contrib.gis.db.backend import SpatialBackend
3 2
 from django.utils.translation import ugettext_lazy as _
4 3
 
  4
+# While this couples the geographic forms to the GEOS library,
  5
+# it decouples from database (by not importing SpatialBackend).
  6
+from django.contrib.gis.geos import GEOSGeometry
  7
+
5 8
 class GeometryField(forms.Field):
6 9
     """
7 10
     This is the basic form field for a Geometry.  Any textual input that is
8  
-    accepted by SpatialBackend.Geometry is accepted by this form.  By default, 
9  
-    this is GEOSGeometry, which accepts WKT, HEXEWKB, WKB, and GeoJSON.
  11
+    accepted by GEOSGeometry is accepted by this form.  By default,
  12
+    this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
10 13
     """
11 14
     widget = forms.Textarea
12 15
 
@@ -14,12 +17,16 @@ class GeometryField(forms.Field):
14 17
         'no_geom' : _(u'No geometry value provided.'),
15 18
         'invalid_geom' : _(u'Invalid geometry value.'),
16 19
         'invalid_geom_type' : _(u'Invalid geometry type.'),
17  
-    }
  20
+        'transform_error' : _(u'An error occurred when transforming the geometry'
  21
+                              'to the SRID of the geometry form field.'),
  22
+        }
18 23
 
19 24
     def __init__(self, **kwargs):
20  
-        self.null = kwargs.pop('null')
21  
-        self.geom_type = kwargs.pop('geom_type')
22  
-        self.srid = kwargs.pop('srid')
  25
+        # Pop out attributes from the database field, or use sensible
  26
+        # defaults (e.g., allow None).
  27
+        self.srid = kwargs.pop('srid', None)
  28
+        self.geom_type = kwargs.pop('geom_type', 'GEOMETRY')
  29
+        self.null = kwargs.pop('null', True)
23 30
         super(GeometryField, self).__init__(**kwargs)
24 31
 
25 32
     def clean(self, value):
@@ -29,21 +36,32 @@ def clean(self, value):
29 36
         the value cannot be instantiated as a Geometry.
30 37
         """
31 38
         if not value:
32  
-            if self.null:
33  
-                # The geometry column allows NULL, return None.
  39
+            if self.null and not self.required:
  40
+                # The geometry column allows NULL and is not required.
34 41
                 return None
35 42
             else:
36 43
                 raise forms.ValidationError(self.error_messages['no_geom'])
37  
-     
  44
+
  45
+        # Trying to create a Geometry object from the form value.
38 46
         try:
39  
-            # Trying to create a Geometry object from the form value.
40  
-            geom = SpatialBackend.Geometry(value)
  47
+            geom = GEOSGeometry(value)
41 48
         except:
42 49
             raise forms.ValidationError(self.error_messages['invalid_geom'])
43  
-  
  50
+
44 51
         # Ensuring that the geometry is of the correct type (indicated
45 52
         # using the OGC string label).
46 53
         if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
47 54
             raise forms.ValidationError(self.error_messages['invalid_geom_type'])
48 55
 
  56
+        # Transforming the geometry if the SRID was set.
  57
+        if self.srid:
  58
+            if not geom.srid:
  59
+                # Should match that of the field if not given.
  60
+                geom.srid = self.srid
  61
+            elif self.srid != -1 and self.srid != geom.srid:
  62
+                try:
  63
+                    geom.transform(self.srid)
  64
+                except:
  65
+                    raise forms.ValidationError(self.error_messages['transform_error'])
  66
+
49 67
         return geom
2  django/contrib/gis/tests/__init__.py
@@ -35,7 +35,7 @@ def geo_suite():
35 35
 
36 36
     if HAS_GDAL:
37 37
         # These tests require GDAL.
38  
-        test_suite_names.append('test_spatialrefsys')
  38
+        test_suite_names.extend(['test_spatialrefsys', 'test_geoforms'])
39 39
         test_apps.append('layermap')
40 40
 
41 41
         # Adding the GDAL tests.
65  django/contrib/gis/tests/test_geoforms.py
... ...
@@ -0,0 +1,65 @@
  1
+import unittest
  2
+
  3
+from django.forms import ValidationError
  4
+from django.contrib.gis import forms
  5
+from django.contrib.gis.geos import GEOSGeometry
  6
+
  7
+class GeometryFieldTest(unittest.TestCase):
  8
+
  9
+    def test00_init(self):
  10
+        "Testing GeometryField initialization with defaults."
  11
+        fld = forms.GeometryField()
  12
+        for bad_default in ('blah', 3, 'FoO', None, 0):
  13
+            self.assertRaises(ValidationError, fld.clean, bad_default)
  14
+
  15
+    def test01_srid(self):
  16
+        "Testing GeometryField with a SRID set."
  17
+        # Input that doesn't specify the SRID is assumed to be in the SRID
  18
+        # of the input field.
  19
+        fld = forms.GeometryField(srid=4326)
  20
+        geom = fld.clean('POINT(5 23)')
  21
+        self.assertEqual(4326, geom.srid)
  22
+        # Making the field in a different SRID from that of the geometry, and
  23
+        # asserting it transforms.
  24
+        fld = forms.GeometryField(srid=32140)
  25
+        tol = 0.0000001
  26
+        xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140)
  27
+        # The cleaned geometry should be transformed to 32140.
  28
+        cleaned_geom = fld.clean('SRID=4326;POINT (-95.363151 29.763374)')
  29
+        self.failUnless(xform_geom.equals_exact(cleaned_geom, tol))
  30
+
  31
+    def test02_null(self):
  32
+        "Testing GeometryField's handling of null (None) geometries."
  33
+        # Form fields, by default, are required (`required=True`)
  34
+        fld = forms.GeometryField()
  35
+        self.assertRaises(forms.ValidationError, fld.clean, None)
  36
+
  37
+        # Still not allowed if `null=False`.
  38
+        fld = forms.GeometryField(required=False, null=False)
  39
+        self.assertRaises(forms.ValidationError, fld.clean, None)
  40
+
  41
+        # This will clean None as a geometry (See #10660).
  42
+        fld = forms.GeometryField(required=False)
  43
+        self.assertEqual(None, fld.clean(None))
  44
+
  45
+    def test03_geom_type(self):
  46
+        "Testing GeometryField's handling of different geometry types."
  47
+        # By default, all geometry types are allowed.
  48
+        fld = forms.GeometryField()
  49
+        for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'):
  50
+            self.assertEqual(GEOSGeometry(wkt), fld.clean(wkt))
  51
+
  52
+        pnt_fld = forms.GeometryField(geom_type='POINT')
  53
+        self.assertEqual(GEOSGeometry('POINT(5 23)'), pnt_fld.clean('POINT(5 23)'))
  54
+        self.assertRaises(forms.ValidationError, pnt_fld.clean, 'LINESTRING(0 0, 1 1)')
  55
+
  56
+def suite():
  57
+    s = unittest.TestSuite()
  58
+    s.addTest(unittest.makeSuite(GeometryFieldTest))
  59
+    return s
  60
+
  61
+def run(verbosity=2):
  62
+    unittest.TextTestRunner(verbosity=verbosity).run(suite())
  63
+
  64
+if __name__=="__main__":
  65
+    run()

0 notes on commit 66db0b4

Please sign in to comment.
Something went wrong with that request. Please try again.