2012-06-08 00:08:47 +08:00
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
2008-08-06 02:13:06 +08:00
|
|
|
from django import forms
|
2013-01-26 03:50:46 +08:00
|
|
|
from django.utils import six
|
2008-08-06 02:13:06 +08:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
|
2009-04-26 02:24:32 +08:00
|
|
|
# While this couples the geographic forms to the GEOS library,
|
|
|
|
# it decouples from database (by not importing SpatialBackend).
|
2013-01-26 03:50:46 +08:00
|
|
|
from django.contrib.gis.geos import GEOSException, GEOSGeometry, fromstr
|
|
|
|
|
2009-04-26 02:24:32 +08:00
|
|
|
|
2008-08-06 02:13:06 +08:00
|
|
|
class GeometryField(forms.Field):
|
2008-08-26 08:34:34 +08:00
|
|
|
"""
|
|
|
|
This is the basic form field for a Geometry. Any textual input that is
|
2009-04-26 02:24:32 +08:00
|
|
|
accepted by GEOSGeometry is accepted by this form. By default,
|
|
|
|
this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
|
2008-08-26 08:34:34 +08:00
|
|
|
"""
|
2008-08-06 02:13:06 +08:00
|
|
|
widget = forms.Textarea
|
|
|
|
|
|
|
|
default_error_messages = {
|
2012-06-08 00:08:47 +08:00
|
|
|
'no_geom' : _('No geometry value provided.'),
|
|
|
|
'invalid_geom' : _('Invalid geometry value.'),
|
|
|
|
'invalid_geom_type' : _('Invalid geometry type.'),
|
|
|
|
'transform_error' : _('An error occurred when transforming the geometry '
|
2009-04-26 02:24:32 +08:00
|
|
|
'to the SRID of the geometry form field.'),
|
|
|
|
}
|
2008-08-26 08:34:34 +08:00
|
|
|
|
2008-08-06 02:13:06 +08:00
|
|
|
def __init__(self, **kwargs):
|
2009-04-26 02:24:32 +08:00
|
|
|
# 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)
|
2008-08-06 02:13:06 +08:00
|
|
|
super(GeometryField, self).__init__(**kwargs)
|
|
|
|
|
2012-06-19 20:55:40 +08:00
|
|
|
def to_python(self, value):
|
|
|
|
"""
|
|
|
|
Transforms the value to a Geometry object.
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
return GEOSGeometry(value)
|
|
|
|
except (GEOSException, ValueError, TypeError):
|
|
|
|
raise forms.ValidationError(self.error_messages['invalid_geom'])
|
|
|
|
|
2008-08-06 02:13:06 +08:00
|
|
|
def clean(self, value):
|
|
|
|
"""
|
|
|
|
Validates that the input value can be converted to a Geometry
|
|
|
|
object (which is returned). A ValidationError is raised if
|
|
|
|
the value cannot be instantiated as a Geometry.
|
|
|
|
"""
|
|
|
|
if not value:
|
2009-04-26 02:24:32 +08:00
|
|
|
if self.null and not self.required:
|
|
|
|
# The geometry column allows NULL and is not required.
|
2008-08-06 02:13:06 +08:00
|
|
|
return None
|
|
|
|
else:
|
|
|
|
raise forms.ValidationError(self.error_messages['no_geom'])
|
2009-04-26 02:24:32 +08:00
|
|
|
|
2012-06-19 20:55:40 +08:00
|
|
|
# Transform the value to a python object first
|
|
|
|
geom = self.to_python(value)
|
2009-04-26 02:24:32 +08:00
|
|
|
|
2008-08-26 08:34:34 +08:00
|
|
|
# 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'])
|
|
|
|
|
2009-04-26 02:24:32 +08:00
|
|
|
# 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'])
|
|
|
|
|
2008-08-26 08:34:34 +08:00
|
|
|
return geom
|
2013-01-26 03:50:46 +08:00
|
|
|
|
|
|
|
def _has_changed(self, initial, data):
|
|
|
|
""" Compare geographic value of data with its initial value. """
|
|
|
|
|
|
|
|
# Ensure we are dealing with a geographic object
|
|
|
|
if isinstance(initial, six.string_types):
|
|
|
|
try:
|
|
|
|
initial = GEOSGeometry(initial)
|
|
|
|
except (GEOSException, ValueError):
|
|
|
|
initial = None
|
|
|
|
|
|
|
|
# Only do a geographic comparison if both values are available
|
|
|
|
if initial and data:
|
|
|
|
data = fromstr(data)
|
|
|
|
data.transform(initial.srid)
|
|
|
|
# If the initial value was not added by the browser, the geometry
|
|
|
|
# provided may be slightly different, the first time it is saved.
|
|
|
|
# The comparison is done with a very low tolerance.
|
|
|
|
return not initial.equals_exact(data, tolerance=0.000001)
|
|
|
|
else:
|
|
|
|
# Check for change of state of existence
|
|
|
|
return bool(initial) != bool(data)
|