widgets.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. from __future__ import unicode_literals
  2. import logging
  3. from django.conf import settings
  4. from django.contrib.gis import gdal
  5. from django.contrib.gis.geos import GEOSGeometry, GEOSException
  6. from django.forms.widgets import Widget
  7. from django.template import loader
  8. from django.utils import six
  9. from django.utils import translation
  10. logger = logging.getLogger('django.contrib.gis')
  11. class BaseGeometryWidget(Widget):
  12. """
  13. The base class for rich geometry widgets.
  14. Renders a map using the WKT of the geometry.
  15. """
  16. geom_type = 'GEOMETRY'
  17. map_srid = 4326
  18. map_width = 600
  19. map_height = 400
  20. display_raw = False
  21. supports_3d = False
  22. template_name = '' # set on subclasses
  23. def __init__(self, attrs=None):
  24. self.attrs = {}
  25. for key in ('geom_type', 'map_srid', 'map_width', 'map_height', 'display_raw'):
  26. self.attrs[key] = getattr(self, key)
  27. if attrs:
  28. self.attrs.update(attrs)
  29. def serialize(self, value):
  30. return value.wkt if value else ''
  31. def deserialize(self, value):
  32. try:
  33. return GEOSGeometry(value, self.map_srid)
  34. except (GEOSException, ValueError) as err:
  35. logger.error(
  36. "Error creating geometry from value '%s' (%s)" % (
  37. value, err)
  38. )
  39. return None
  40. def render(self, name, value, attrs=None):
  41. # If a string reaches here (via a validation error on another
  42. # field) then just reconstruct the Geometry.
  43. if isinstance(value, six.string_types):
  44. value = self.deserialize(value)
  45. if value:
  46. # Check that srid of value and map match
  47. if value.srid != self.map_srid:
  48. try:
  49. ogr = value.ogr
  50. ogr.transform(self.map_srid)
  51. value = ogr
  52. except gdal.OGRException as err:
  53. logger.error(
  54. "Error transforming geometry from srid '%s' to srid '%s' (%s)" % (
  55. value.srid, self.map_srid, err)
  56. )
  57. context = self.build_attrs(
  58. attrs,
  59. name=name,
  60. module='geodjango_%s' % name.replace('-', '_'), # JS-safe
  61. serialized=self.serialize(value),
  62. geom_type=gdal.OGRGeomType(self.attrs['geom_type']),
  63. STATIC_URL=settings.STATIC_URL,
  64. LANGUAGE_BIDI=translation.get_language_bidi(),
  65. )
  66. return loader.render_to_string(self.template_name, context)
  67. class OpenLayersWidget(BaseGeometryWidget):
  68. template_name = 'gis/openlayers.html'
  69. class Media:
  70. js = (
  71. 'http://openlayers.org/api/2.13/OpenLayers.js',
  72. 'gis/js/OLMapWidget.js',
  73. )
  74. class OSMWidget(BaseGeometryWidget):
  75. """
  76. An OpenLayers/OpenStreetMap-based widget.
  77. """
  78. template_name = 'gis/openlayers-osm.html'
  79. default_lon = 5
  80. default_lat = 47
  81. class Media:
  82. js = (
  83. 'http://openlayers.org/api/2.13/OpenLayers.js',
  84. 'http://www.openstreetmap.org/openlayers/OpenStreetMap.js',
  85. 'gis/js/OLMapWidget.js',
  86. )
  87. def __init__(self, attrs=None):
  88. super(OSMWidget, self).__init__()
  89. for key in ('default_lon', 'default_lat'):
  90. self.attrs[key] = getattr(self, key)
  91. if attrs:
  92. self.attrs.update(attrs)
  93. @property
  94. def map_srid(self):
  95. # Use the official spherical mercator projection SRID on versions
  96. # of GDAL that support it; otherwise, fallback to 900913.
  97. if gdal.HAS_GDAL and gdal.GDAL_VERSION >= (1, 7):
  98. return 3857
  99. else:
  100. return 900913