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.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@10634 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
8010e0c32e
commit
66db0b43ad
|
@ -1 +1,2 @@
|
|||
from django.forms import *
|
||||
from django.contrib.gis.forms.fields import GeometryField
|
||||
|
|
|
@ -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,15 +36,15 @@ class GeometryField(forms.Field):
|
|||
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'])
|
||||
|
||||
|
@ -46,4 +53,15 @@ class GeometryField(forms.Field):
|
|||
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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue