1
1
from __future__ import unicode_literals
3
5
from django import forms
6
from django.utils import six
4
7
from django.utils.translation import ugettext_lazy as _
6
9
# While this couples the geographic forms to the GEOS library,
7
10
# it decouples from database (by not importing SpatialBackend).
8
from django.contrib.gis.geos import GEOSException, GEOSGeometry
11
from django.contrib.gis.geos import GEOSException, GEOSGeometry, fromstr
12
from .widgets import OpenLayersWidget
10
15
class GeometryField(forms.Field):
13
18
accepted by GEOSGeometry is accepted by this form. By default,
14
19
this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
16
widget = forms.Textarea
21
widget = OpenLayersWidget
22
geom_type = 'GEOMETRY'
18
24
default_error_messages = {
19
'no_geom' : _('No geometry value provided.'),
25
'required' : _('No geometry value provided.'),
20
26
'invalid_geom' : _('Invalid geometry value.'),
21
27
'invalid_geom_type' : _('Invalid geometry type.'),
22
28
'transform_error' : _('An error occurred when transforming the geometry '
27
33
# Pop out attributes from the database field, or use sensible
28
34
# defaults (e.g., allow None).
29
35
self.srid = kwargs.pop('srid', None)
30
self.geom_type = kwargs.pop('geom_type', 'GEOMETRY')
31
self.null = kwargs.pop('null', True)
36
self.geom_type = kwargs.pop('geom_type', self.geom_type)
38
kwargs.pop('null', True)
39
warnings.warn("Passing 'null' keyword argument to GeometryField is deprecated.",
40
DeprecationWarning, stacklevel=2)
32
41
super(GeometryField, self).__init__(**kwargs)
42
self.widget.attrs['geom_type'] = self.geom_type
34
44
def to_python(self, value):
36
46
Transforms the value to a Geometry object.
39
return GEOSGeometry(value)
40
except (GEOSException, ValueError, TypeError):
41
raise forms.ValidationError(self.error_messages['invalid_geom'])
48
if value in self.empty_values:
51
if not isinstance(value, GEOSGeometry):
53
value = GEOSGeometry(value)
55
value.srid = self.widget.map_srid
56
except (GEOSException, ValueError, TypeError):
57
raise forms.ValidationError(self.error_messages['invalid_geom'], code='invalid_geom')
43
60
def clean(self, value):
46
63
object (which is returned). A ValidationError is raised if
47
64
the value cannot be instantiated as a Geometry.
50
if self.null and not self.required:
51
# The geometry column allows NULL and is not required.
54
raise forms.ValidationError(self.error_messages['no_geom'])
56
# Transform the value to a python object first
57
geom = self.to_python(value)
66
geom = super(GeometryField, self).clean(value)
59
70
# Ensuring that the geometry is of the correct type (indicated
60
71
# using the OGC string label).
61
72
if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
62
raise forms.ValidationError(self.error_messages['invalid_geom_type'])
73
raise forms.ValidationError(self.error_messages['invalid_geom_type'], code='invalid_geom_type')
64
75
# Transforming the geometry if the SRID was set.
71
82
geom.transform(self.srid)
73
raise forms.ValidationError(self.error_messages['transform_error'])
84
raise forms.ValidationError(self.error_messages['transform_error'], code='transform_error')
88
def _has_changed(self, initial, data):
89
""" Compare geographic value of data with its initial value. """
92
data = self.to_python(data)
93
initial = self.to_python(initial)
94
except ValidationError:
97
# Only do a geographic comparison if both values are available
99
data.transform(initial.srid)
100
# If the initial value was not added by the browser, the geometry
101
# provided may be slightly different, the first time it is saved.
102
# The comparison is done with a very low tolerance.
103
return not initial.equals_exact(data, tolerance=0.000001)
105
# Check for change of state of existence
106
return bool(initial) != bool(data)
109
class GeometryCollectionField(GeometryField):
110
geom_type = 'GEOMETRYCOLLECTION'
113
class PointField(GeometryField):
117
class MultiPointField(GeometryField):
118
geom_type = 'MULTIPOINT'
121
class LineStringField(GeometryField):
122
geom_type = 'LINESTRING'
125
class MultiLineStringField(GeometryField):
126
geom_type = 'MULTILINESTRING'
129
class PolygonField(GeometryField):
130
geom_type = 'POLYGON'
133
class MultiPolygonField(GeometryField):
134
geom_type = 'MULTIPOLYGON'