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 transform the value to a geometry object if value in self emptyvalues return none if not isinstancevalue geosgeometry if hasattrself widget deserialize try value self widget deserializevalue except gdalexception value none else try value geosgeometryvalue except geosexception valueerror typeerror value none if value is none raise validationerror self errormessagesinvalidgeom codeinvalidgeom try to set the srid if not value srid try value srid self widget mapsrid except attributeerror if self srid value srid self srid return value def cleanself value geom super cleanvalue if geom is none return geom ensuring that the geometry is of the correct type indicated using the ogc string label if strgeom geomtype upper self geomtype and self geomtype geometry raise validationerror self errormessagesinvalidgeomtype codeinvalidgeomtype transforming the geometry if the srid was set if self srid and self srid 1 and self srid geom srid try geom transformself srid except geosexception raise validationerror self errormessagestransformerror codetransformerror return geom def haschangedself initial data only do a geographic comparison if both values are available 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 check for change of state of existence 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 transform the value to a geometry object try to set the srid 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 ensuring that the geometry is of the correct type indicated using the ogc string label transforming the geometry if the srid was set compare geographic value of data with its initial value only do a geographic comparison if both values are available 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 check for change of state of existence
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):
    

    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):
        
        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"
                )

        
        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):
        
        geom = super().clean(value)
        if geom is None:
            return geom

        
        
        if (
            str(geom.geom_type).upper() != self.geom_type
            and self.geom_type != "GEOMETRY"
        ):
            raise ValidationError(
                self.error_messages["invalid_geom_type"], code="invalid_geom_type"
            )

        
        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):
        

        try:
            data = self.to_python(data)
            initial = self.to_python(initial)
        except ValidationError:
            return True

        
        if initial and data:
            data.transform(initial.srid)
            
            
            
            return not initial.equals_exact(data, tolerance=0.000001)
        else:
            
            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"