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.

widgets.py 4.8 KiB

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