widgets.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import logging
  2. from django.forms.widgets import Textarea
  3. from django.template import loader
  4. from django.utils import six
  5. from django.utils import translation
  6. from django.contrib.gis.gdal import GDALException
  7. from django.contrib.gis.geos import GEOSGeometry, GEOSException
  8. # Creating a template context that contains Django settings
  9. # values needed by admin map templates.
  10. geo_context = {'LANGUAGE_BIDI': translation.get_language_bidi()}
  11. logger = logging.getLogger('django.contrib.gis')
  12. class OpenLayersWidget(Textarea):
  13. """
  14. Renders an OpenLayers map using the WKT of the geometry.
  15. """
  16. def render(self, name, value, attrs=None):
  17. # Update the template parameters with any attributes passed in.
  18. if attrs:
  19. self.params.update(attrs)
  20. self.params['editable'] = self.params['modifiable']
  21. else:
  22. self.params['editable'] = True
  23. # Defaulting the WKT value to a blank string -- this
  24. # will be tested in the JavaScript and the appropriate
  25. # interface will be constructed.
  26. self.params['wkt'] = ''
  27. # If a string reaches here (via a validation error on another
  28. # field) then just reconstruct the Geometry.
  29. if isinstance(value, six.string_types):
  30. try:
  31. value = GEOSGeometry(value)
  32. except (GEOSException, ValueError) as err:
  33. logger.error(
  34. "Error creating geometry from value '%s' (%s)" % (
  35. value, err)
  36. )
  37. value = None
  38. if (value and value.geom_type.upper() != self.geom_type and
  39. self.geom_type != 'GEOMETRY'):
  40. value = None
  41. # Constructing the dictionary of the map options.
  42. self.params['map_options'] = self.map_options()
  43. # Constructing the JavaScript module name using the name of
  44. # the GeometryField (passed in via the `attrs` keyword).
  45. # Use the 'name' attr for the field name (rather than 'field')
  46. self.params['name'] = name
  47. # note: we must switch out dashes for underscores since js
  48. # functions are created using the module variable
  49. js_safe_name = self.params['name'].replace('-', '_')
  50. self.params['module'] = 'geodjango_%s' % js_safe_name
  51. if value:
  52. # Transforming the geometry to the projection used on the
  53. # OpenLayers map.
  54. srid = self.params['srid']
  55. if value.srid != srid:
  56. try:
  57. ogr = value.ogr
  58. ogr.transform(srid)
  59. wkt = ogr.wkt
  60. except GDALException as err:
  61. logger.error(
  62. "Error transforming geometry from srid '%s' to srid '%s' (%s)" % (
  63. value.srid, srid, err)
  64. )
  65. wkt = ''
  66. else:
  67. wkt = value.wkt
  68. # Setting the parameter WKT with that of the transformed
  69. # geometry.
  70. self.params['wkt'] = wkt
  71. self.params.update(geo_context)
  72. return loader.render_to_string(self.template, self.params)
  73. def map_options(self):
  74. "Builds the map options hash for the OpenLayers template."
  75. # JavaScript construction utilities for the Bounds and Projection.
  76. def ol_bounds(extent):
  77. return 'new OpenLayers.Bounds(%s)' % str(extent)
  78. def ol_projection(srid):
  79. return 'new OpenLayers.Projection("EPSG:%s")' % srid
  80. # An array of the parameter name, the name of their OpenLayers
  81. # counterpart, and the type of variable they are.
  82. map_types = [('srid', 'projection', 'srid'),
  83. ('display_srid', 'displayProjection', 'srid'),
  84. ('units', 'units', str),
  85. ('max_resolution', 'maxResolution', float),
  86. ('max_extent', 'maxExtent', 'bounds'),
  87. ('num_zoom', 'numZoomLevels', int),
  88. ('max_zoom', 'maxZoomLevels', int),
  89. ('min_zoom', 'minZoomLevel', int),
  90. ]
  91. # Building the map options hash.
  92. map_options = {}
  93. for param_name, js_name, option_type in map_types:
  94. if self.params.get(param_name, False):
  95. if option_type == 'srid':
  96. value = ol_projection(self.params[param_name])
  97. elif option_type == 'bounds':
  98. value = ol_bounds(self.params[param_name])
  99. elif option_type in (float, int):
  100. value = self.params[param_name]
  101. elif option_type in (str,):
  102. value = '"%s"' % self.params[param_name]
  103. else:
  104. raise TypeError
  105. map_options[js_name] = value
  106. return map_options