diff options
| author | Claude Paroz <claude@2xlibre.net> | 2024-08-18 15:29:30 +0200 |
|---|---|---|
| committer | nessita <124304+nessita@users.noreply.github.com> | 2025-06-12 17:35:14 -0300 |
| commit | f2f6046c0f92ff1faed057da0711ac478eef439c (patch) | |
| tree | 56bcb44d903637bbdf319190a805e69eaf4d9f2a /django | |
| parent | e80b33ae4d6f93375b10b2fe50bd6f588f1246ad (diff) | |
Fixed #25706 -- Refactored geometry widgets to remove inline JavaScript.
Refactored GIS-related JavaScript initialization to eliminate inline
scripts from templates. Added support for specifying a base layer using
the new `base_layer_name` attribute on `BaseGeometryWidget`, allowing
custom map tile providers via user-defined JavaScript.
As a result, the `gis/openlayers-osm.html` template was removed.
Thanks Sarah Boyce for reviews.
Co-authored-by: Natalia <124304+nessita@users.noreply.github.com>
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/gis/forms/widgets.py | 41 | ||||
| -rw-r--r-- | django/contrib/gis/static/gis/js/OLMapWidget.js | 54 | ||||
| -rw-r--r-- | django/contrib/gis/templates/gis/openlayers-osm.html | 12 | ||||
| -rw-r--r-- | django/contrib/gis/templates/gis/openlayers.html | 36 |
4 files changed, 74 insertions, 69 deletions
diff --git a/django/contrib/gis/forms/widgets.py b/django/contrib/gis/forms/widgets.py index 55895ae9f3..c8f9f1208e 100644 --- a/django/contrib/gis/forms/widgets.py +++ b/django/contrib/gis/forms/widgets.py @@ -1,11 +1,9 @@ import logging -from django.conf import settings from django.contrib.gis import gdal from django.contrib.gis.geometry import json_regex from django.contrib.gis.geos import GEOSException, GEOSGeometry from django.forms.widgets import Widget -from django.utils import translation logger = logging.getLogger("django.contrib.gis") @@ -16,6 +14,7 @@ class BaseGeometryWidget(Widget): Render a map using the WKT of the geometry. """ + base_layer = None geom_type = "GEOMETRY" map_srid = 4326 display_raw = False @@ -24,9 +23,10 @@ class BaseGeometryWidget(Widget): template_name = "" # set on subclasses def __init__(self, attrs=None): - self.attrs = {} - for key in ("geom_type", "map_srid", "display_raw"): - self.attrs[key] = getattr(self, key) + self.attrs = { + key: getattr(self, key) + for key in ("base_layer", "geom_type", "map_srid", "display_raw") + } if attrs: self.attrs.update(attrs) @@ -61,26 +61,16 @@ class BaseGeometryWidget(Widget): self.map_srid, err, ) - + context["serialized"] = self.serialize(value) geom_type = gdal.OGRGeomType(self.attrs["geom_type"]).name - context.update( - self.build_attrs( - self.attrs, - { - "name": name, - "module": "geodjango_%s" % name.replace("-", "_"), # JS-safe - "serialized": self.serialize(value), - "geom_type": "Geometry" if geom_type == "Unknown" else geom_type, - "STATIC_URL": settings.STATIC_URL, - "LANGUAGE_BIDI": translation.get_language_bidi(), - **(attrs or {}), - }, - ) + context["widget"]["attrs"]["geom_name"] = ( + "Geometry" if geom_type == "Unknown" else geom_type ) return context class OpenLayersWidget(BaseGeometryWidget): + base_layer = "nasaWorldview" template_name = "gis/openlayers.html" map_srid = 3857 @@ -112,14 +102,15 @@ class OSMWidget(OpenLayersWidget): An OpenLayers/OpenStreetMap-based widget. """ - template_name = "gis/openlayers-osm.html" + base_layer = "osm" default_lon = 5 default_lat = 47 default_zoom = 12 def __init__(self, attrs=None): - super().__init__() - for key in ("default_lon", "default_lat", "default_zoom"): - self.attrs[key] = getattr(self, key) - if attrs: - self.attrs.update(attrs) + if attrs is None: + attrs = {} + attrs.setdefault("default_lon", self.default_lon) + attrs.setdefault("default_lat", self.default_lat) + attrs.setdefault("default_zoom", self.default_zoom) + super().__init__(attrs=attrs) diff --git a/django/contrib/gis/static/gis/js/OLMapWidget.js b/django/contrib/gis/static/gis/js/OLMapWidget.js index a545036c9f..bea4aab863 100644 --- a/django/contrib/gis/static/gis/js/OLMapWidget.js +++ b/django/contrib/gis/static/gis/js/OLMapWidget.js @@ -58,8 +58,16 @@ class MapWidget { this.options[property] = options[property]; } } - if (!options.base_layer) { - this.options.base_layer = new ol.layer.Tile({source: new ol.source.OSM()}); + + // Options' base_layer can be empty, or contain a layerBuilder key, or + // be a layer already constructed. + const base_layer = options.base_layer; + if (typeof base_layer === 'string' && base_layer in MapWidget.layerBuilder) { + this.baseLayer = MapWidget.layerBuilder[base_layer](); + } else if (base_layer && typeof base_layer !== 'string') { + this.baseLayer = base_layer; + } else { + this.baseLayer = MapWidget.layerBuilder.osm(); } this.map = this.createMap(); @@ -120,7 +128,7 @@ class MapWidget { createMap() { return new ol.Map({ target: this.options.map_id, - layers: [this.options.base_layer], + layers: [this.baseLayer], view: new ol.View({ zoom: this.options.default_zoom }) @@ -231,3 +239,43 @@ class MapWidget { document.getElementById(this.options.id).value = jsonFormat.writeGeometry(geometry); } } + +// Static property assignment (ES6-compatible) +MapWidget.layerBuilder = { + nasaWorldview: () => { + return new ol.layer.Tile({ + source: new ol.source.XYZ({ + attributions: "NASA Worldview", + maxZoom: 8, + url: "https://map1{a-c}.vis.earthdata.nasa.gov/wmts-webmerc/" + + "BlueMarble_ShadedRelief_Bathymetry/default/%7BTime%7D/" + + "GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg" + }) + }); + }, + osm: () => { + return new ol.layer.Tile({source: new ol.source.OSM()}); + } +}; + +function initMapWidgetInSection(section) { + const maps = []; + + section.querySelectorAll(".dj_map_wrapper").forEach((wrapper) => { + // Avoid initializing map widget on an empty form. + if (wrapper.id.includes('__prefix__')) { + return; + } + const options = JSON.parse(wrapper.querySelector("#mapwidget-options").textContent); + options.id = wrapper.querySelector("textarea").id; + options.map_id = wrapper.querySelector(".dj_map").id; + maps.push(new MapWidget(options)); + }); + + return maps; +}; + +document.addEventListener("DOMContentLoaded", () => { + initMapWidgetInSection(document); + document.addEventListener('formset:added', (ev) => {initMapWidgetInSection(ev.target);}); +}); diff --git a/django/contrib/gis/templates/gis/openlayers-osm.html b/django/contrib/gis/templates/gis/openlayers-osm.html deleted file mode 100644 index 88b1c8c2b6..0000000000 --- a/django/contrib/gis/templates/gis/openlayers-osm.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "gis/openlayers.html" %} -{% load l10n %} - -{% block options %}{{ block.super }} -options['default_lon'] = {{ default_lon|unlocalize }}; -options['default_lat'] = {{ default_lat|unlocalize }}; -options['default_zoom'] = {{ default_zoom|unlocalize }}; -{% endblock %} - -{% block base_layer %} -var base_layer = new ol.layer.Tile({source: new ol.source.OSM()}); -{% endblock %} diff --git a/django/contrib/gis/templates/gis/openlayers.html b/django/contrib/gis/templates/gis/openlayers.html index f9f7e5fa51..80fa57934b 100644 --- a/django/contrib/gis/templates/gis/openlayers.html +++ b/django/contrib/gis/templates/gis/openlayers.html @@ -1,32 +1,10 @@ -{% load i18n l10n %} +{% load i18n %} -<div id="{{ id }}_div_map" class="dj_map_wrapper"> - <div id="{{ id }}_map" class="dj_map"></div> +<div id="{{ widget.attrs.id }}_div_map" class="dj_map_wrapper"> + <div id="{{ widget.attrs.id }}_map" class="dj_map"></div> {% if not disabled %}<span class="clear_features"><a href="">{% translate "Delete all Features" %}</a></span>{% endif %} - {% if display_raw %}<p>{% translate "Debugging window (serialized value)" %}</p>{% endif %} - <textarea id="{{ id }}" class="vSerializedField required" cols="150" rows="10" name="{{ name }}" - {% if not display_raw %} hidden{% endif %}>{{ serialized }}</textarea> - <script> - {% block base_layer %} - var base_layer = new ol.layer.Tile({ - source: new ol.source.XYZ({ - attributions: "NASA Worldview", - maxZoom: 8, - url: "https://map1{a-c}.vis.earthdata.nasa.gov/wmts-webmerc/" + - "BlueMarble_ShadedRelief_Bathymetry/default/%7BTime%7D/" + - "GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg" - }) - }); - {% endblock %} - {% block options %}var options = { - base_layer: base_layer, - geom_name: '{{ geom_type }}', - id: '{{ id }}', - map_id: '{{ id }}_map', - map_srid: {{ map_srid|unlocalize }}, - name: '{{ name }}' - }; - {% endblock %} - var {{ module }} = new MapWidget(options); - </script> + {% if widget.attrs.display_raw %}<p>{% translate "Debugging window (serialized value)" %}</p>{% endif %} + <textarea id="{{ widget.attrs.id }}" class="vSerializedField required" cols="150" rows="10" name="{{ widget.name }}" + {% if not widget.attrs.display_raw %} hidden{% endif %}>{{ serialized }}</textarea> + {{ widget.attrs|json_script:"mapwidget-options" }} </div> |
