You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fields.py 4.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from django import forms
  2. from django.contrib.gis.gdal import GDALException
  3. from django.contrib.gis.geos import GEOSException, GEOSGeometry
  4. from django.utils.translation import gettext_lazy as _
  5. from .widgets import OpenLayersWidget
  6. class GeometryField(forms.Field):
  7. """
  8. This is the basic form field for a Geometry. Any textual input that is
  9. accepted by GEOSGeometry is accepted by this form. By default,
  10. this includes WKT, HEXEWKB, WKB (in a buffer), and GeoJSON.
  11. """
  12. widget = OpenLayersWidget
  13. geom_type = 'GEOMETRY'
  14. default_error_messages = {
  15. 'required': _('No geometry value provided.'),
  16. 'invalid_geom': _('Invalid geometry value.'),
  17. 'invalid_geom_type': _('Invalid geometry type.'),
  18. 'transform_error': _('An error occurred when transforming the geometry '
  19. 'to the SRID of the geometry form field.'),
  20. }
  21. def __init__(self, *, srid=None, geom_type=None, **kwargs):
  22. self.srid = srid
  23. if geom_type is not None:
  24. self.geom_type = geom_type
  25. super().__init__(**kwargs)
  26. self.widget.attrs['geom_type'] = self.geom_type
  27. def to_python(self, value):
  28. """Transform the value to a Geometry object."""
  29. if value in self.empty_values:
  30. return None
  31. if not isinstance(value, GEOSGeometry):
  32. if hasattr(self.widget, 'deserialize'):
  33. try:
  34. value = self.widget.deserialize(value)
  35. except GDALException:
  36. value = None
  37. else:
  38. try:
  39. value = GEOSGeometry(value)
  40. except (GEOSException, ValueError, TypeError):
  41. value = None
  42. if value is None:
  43. raise forms.ValidationError(self.error_messages['invalid_geom'], code='invalid_geom')
  44. # Try to set the srid
  45. if not value.srid:
  46. try:
  47. value.srid = self.widget.map_srid
  48. except AttributeError:
  49. if self.srid:
  50. value.srid = self.srid
  51. return value
  52. def clean(self, value):
  53. """
  54. Validate that the input value can be converted to a Geometry object
  55. and return it. Raise a ValidationError if the value cannot be
  56. instantiated as a Geometry.
  57. """
  58. geom = super().clean(value)
  59. if geom is None:
  60. return geom
  61. # Ensuring that the geometry is of the correct type (indicated
  62. # using the OGC string label).
  63. if str(geom.geom_type).upper() != self.geom_type and not self.geom_type == 'GEOMETRY':
  64. raise forms.ValidationError(self.error_messages['invalid_geom_type'], code='invalid_geom_type')
  65. # Transforming the geometry if the SRID was set.
  66. if self.srid and self.srid != -1 and self.srid != geom.srid:
  67. try:
  68. geom.transform(self.srid)
  69. except GEOSException:
  70. raise forms.ValidationError(
  71. self.error_messages['transform_error'], code='transform_error')
  72. return geom
  73. def has_changed(self, initial, data):
  74. """ Compare geographic value of data with its initial value. """
  75. try:
  76. data = self.to_python(data)
  77. initial = self.to_python(initial)
  78. except forms.ValidationError:
  79. return True
  80. # Only do a geographic comparison if both values are available
  81. if initial and data:
  82. data.transform(initial.srid)
  83. # If the initial value was not added by the browser, the geometry
  84. # provided may be slightly different, the first time it is saved.
  85. # The comparison is done with a very low tolerance.
  86. return not initial.equals_exact(data, tolerance=0.000001)
  87. else:
  88. # Check for change of state of existence
  89. return bool(initial) != bool(data)
  90. class GeometryCollectionField(GeometryField):
  91. geom_type = 'GEOMETRYCOLLECTION'
  92. class PointField(GeometryField):
  93. geom_type = 'POINT'
  94. class MultiPointField(GeometryField):
  95. geom_type = 'MULTIPOINT'
  96. class LineStringField(GeometryField):
  97. geom_type = 'LINESTRING'
  98. class MultiLineStringField(GeometryField):
  99. geom_type = 'MULTILINESTRING'
  100. class PolygonField(GeometryField):
  101. geom_type = 'POLYGON'
  102. class MultiPolygonField(GeometryField):
  103. geom_type = 'MULTIPOLYGON'