widgets.py 4.98 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
# RemovedInDjango50Warning.
import logging
import warnings

from django.contrib.gis.gdal import GDALException
from django.contrib.gis.geos import GEOSException, GEOSGeometry
from django.forms.widgets import Textarea
from django.utils import translation
from django.utils.deprecation import RemovedInDjango50Warning

# Creating a template context that contains Django settings
# values needed by admin map templates.
geo_context = {"LANGUAGE_BIDI": translation.get_language_bidi()}
logger = logging.getLogger("django.contrib.gis")


class OpenLayersWidget(Textarea):
    """
    Render an OpenLayers map using the WKT of the geometry.
    """

    def __init__(self, *args, **kwargs):
        warnings.warn(
            "django.contrib.gis.admin.OpenLayersWidget is deprecated.",
            RemovedInDjango50Warning,
            stacklevel=2,
        )
        super().__init__(*args, **kwargs)

    def get_context(self, name, value, attrs):
        # Update the template parameters with any attributes passed in.
        if attrs:
            self.params.update(attrs)
            self.params["editable"] = self.params["modifiable"]
        else:
            self.params["editable"] = True

        # Defaulting the WKT value to a blank string -- this
        # will be tested in the JavaScript and the appropriate
        # interface will be constructed.
        self.params["wkt"] = ""

        # If a string reaches here (via a validation error on another
        # field) then just reconstruct the Geometry.
        if value and isinstance(value, str):
            try:
                value = GEOSGeometry(value)
            except (GEOSException, ValueError) as err:
                logger.error("Error creating geometry from value '%s' (%s)", value, err)
                value = None

        if (
            value
            and value.geom_type.upper() != self.geom_type
            and self.geom_type != "GEOMETRY"
        ):
            value = None

        # Constructing the dictionary of the map options.
        self.params["map_options"] = self.map_options()

        # Constructing the JavaScript module name using the name of
        # the GeometryField (passed in via the `attrs` keyword).
        # Use the 'name' attr for the field name (rather than 'field')
        self.params["name"] = name
        # note: we must switch out dashes for underscores since js
        # functions are created using the module variable
        js_safe_name = self.params["name"].replace("-", "_")
        self.params["module"] = "geodjango_%s" % js_safe_name

        if value:
            # Transforming the geometry to the projection used on the
            # OpenLayers map.
            srid = self.params["srid"]
            if value.srid != srid:
                try:
                    ogr = value.ogr
                    ogr.transform(srid)
                    wkt = ogr.wkt
                except GDALException as err:
                    logger.error(
                        "Error transforming geometry from srid '%s' to srid '%s' (%s)",
                        value.srid,
                        srid,
                        err,
                    )
                    wkt = ""
            else:
                wkt = value.wkt

            # Setting the parameter WKT with that of the transformed
            # geometry.
            self.params["wkt"] = wkt

        self.params.update(geo_context)
        return self.params

    def map_options(self):
        """Build the map options hash for the OpenLayers template."""
        # JavaScript construction utilities for the Bounds and Projection.
        def ol_bounds(extent):
            return "new OpenLayers.Bounds(%s)" % extent

        def ol_projection(srid):
            return 'new OpenLayers.Projection("EPSG:%s")' % srid

        # An array of the parameter name, the name of their OpenLayers
        # counterpart, and the type of variable they are.
        map_types = [
            ("srid", "projection", "srid"),
            ("display_srid", "displayProjection", "srid"),
            ("units", "units", str),
            ("max_resolution", "maxResolution", float),
            ("max_extent", "maxExtent", "bounds"),
            ("num_zoom", "numZoomLevels", int),
            ("max_zoom", "maxZoomLevels", int),
            ("min_zoom", "minZoomLevel", int),
        ]

        # Building the map options hash.
        map_options = {}
        for param_name, js_name, option_type in map_types:
            if self.params.get(param_name, False):
                if option_type == "srid":
                    value = ol_projection(self.params[param_name])
                elif option_type == "bounds":
                    value = ol_bounds(self.params[param_name])
                elif option_type in (float, int):
                    value = self.params[param_name]
                elif option_type in (str,):
                    value = '"%s"' % self.params[param_name]
                else:
                    raise TypeError
                map_options[js_name] = value
        return map_options