summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2024-08-18 15:29:30 +0200
committernessita <124304+nessita@users.noreply.github.com>2025-06-12 17:35:14 -0300
commitf2f6046c0f92ff1faed057da0711ac478eef439c (patch)
tree56bcb44d903637bbdf319190a805e69eaf4d9f2a /django
parente80b33ae4d6f93375b10b2fe50bd6f588f1246ad (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.py41
-rw-r--r--django/contrib/gis/static/gis/js/OLMapWidget.js54
-rw-r--r--django/contrib/gis/templates/gis/openlayers-osm.html12
-rw-r--r--django/contrib/gis/templates/gis/openlayers.html36
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>