123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- from django import forms
- from django.contrib.gis.gdal import GDALException
- from django.contrib.gis.geos import GEOSException, GEOSGeometry
- from django.core.exceptions import ValidationError
- from django.utils.translation import gettext_lazy as _
- from .widgets import OpenLayersWidget
- class GeometryField(forms.Field):
- """
- This is the basic form field for a Geometry. Any textual input that is
- accepted by GEOSGeometry is accepted by this form. By default,
- this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
- """
- widget = OpenLayersWidget
- geom_type = 'GEOMETRY'
- default_error_messages = {
- 'required': _('No geometry value provided.'),
- 'invalid_geom': _('Invalid geometry value.'),
- 'invalid_geom_type': _('Invalid geometry type.'),
- 'transform_error': _('An error occurred when transforming the geometry '
- 'to the SRID of the geometry form field.'),
- }
- def __init__(self, *, srid=None, geom_type=None, **kwargs):
- self.srid = srid
- if geom_type is not None:
- self.geom_type = geom_type
- super().__init__(**kwargs)
- self.widget.attrs['geom_type'] = self.geom_type
- def to_python(self, value):
- """Transform the value to a Geometry object."""
- if value in self.empty_values:
- return None
- if not isinstance(value, GEOSGeometry):
- if hasattr(self.widget, 'deserialize'):
- try:
- value = self.widget.deserialize(value)
- except GDALException:
- value = None
- else:
- try:
- value = GEOSGeometry(value)
- except (GEOSException, ValueError, TypeError):
- value = None
- if value is None:
- raise ValidationError(self.error_messages['invalid_geom'], code='invalid_geom')
- # Try to set the srid
- if not value.srid:
- try:
- value.srid = self.widget.map_srid
- except AttributeError:
- if self.srid:
- value.srid = self.srid
- return value
- def clean(self, value):
- """
- Validate that the input value can be converted to a Geometry object
- and return it. Raise a ValidationError if the value cannot be
- instantiated as a Geometry.
- """
- geom = super().clean(value)
- if geom is None:
- return 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 ValidationError(self.error_messages['invalid_geom_type'], code='invalid_geom_type')
- # Transforming the geometry if the SRID was set.
- if self.srid and self.srid != -1 and self.srid != geom.srid:
- try:
- geom.transform(self.srid)
- except GEOSException:
- raise ValidationError(
- self.error_messages['transform_error'], code='transform_error')
- return geom
- def has_changed(self, initial, data):
- """ Compare geographic value of data with its initial value. """
- try:
- data = self.to_python(data)
- initial = self.to_python(initial)
- except ValidationError:
- return True
- # Only do a geographic comparison if both values are available
- if initial and 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)
- class GeometryCollectionField(GeometryField):
- geom_type = 'GEOMETRYCOLLECTION'
- class PointField(GeometryField):
- geom_type = 'POINT'
- class MultiPointField(GeometryField):
- geom_type = 'MULTIPOINT'
- class LineStringField(GeometryField):
- geom_type = 'LINESTRING'
- class MultiLineStringField(GeometryField):
- geom_type = 'MULTILINESTRING'
- class PolygonField(GeometryField):
- geom_type = 'POLYGON'
- class MultiPolygonField(GeometryField):
- geom_type = 'MULTIPOLYGON'
|