feat: add experimental "map preview"
This is the same as "map new", but it is not in edit mode. This allow to click on the elements and see the popups instead of editing it when using a `dataUrl` query string. This way of using uMap is not documented, but it's used by some partners (Deveco recently, data.gouv.fr historicaly). In the same time, this PR adds two things: - possibility to pass data direclty in querystring (instead of an URL): in the case of Deveco, they have pages where only point is shown (for each company) - possibility to pass style options directly from query string: may allow for example to control the `popupTemplate`, eg. to use a table one that will display all properties of the clicked feature Note: dataUrl and such also works in normal "map new" view. There are two use cases around those parameters, from external sites: - see this data on uMap (should point on map preview) - create a map with those data (should point on map new)
This commit is contained in:
parent
2b08d499ac
commit
c334f7554e
4 changed files with 128 additions and 19 deletions
|
@ -233,18 +233,35 @@ L.U.Map.include({
|
|||
|
||||
// Creation mode
|
||||
if (!this.options.umap_id) {
|
||||
if (!this.options.preview) {
|
||||
this.isDirty = true
|
||||
this.enableEdit()
|
||||
}
|
||||
this._default_extent = true
|
||||
this.options.name = L._('Untitled map')
|
||||
this.options.editMode = 'advanced'
|
||||
this.enableEdit()
|
||||
let style = L.Util.queryString('style', null)
|
||||
if (style) {
|
||||
style = decodeURIComponent(style)
|
||||
try {
|
||||
style = JSON.parse(style)
|
||||
L.Util.setOptions(this, style)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
let data = L.Util.queryString('data', null)
|
||||
let dataUrl = L.Util.queryString('dataUrl', null)
|
||||
const dataFormat = L.Util.queryString('dataFormat', 'geojson')
|
||||
if (dataUrl) {
|
||||
dataUrl = decodeURIComponent(dataUrl)
|
||||
dataUrl = this.localizeUrl(dataUrl)
|
||||
dataUrl = this.proxyUrl(dataUrl)
|
||||
const datalayer = this.createDataLayer()
|
||||
datalayer.importFromUrl(dataUrl, dataFormat)
|
||||
} else if (data) {
|
||||
data = decodeURIComponent(data)
|
||||
const datalayer = this.createDataLayer()
|
||||
datalayer.importRaw(data, dataFormat)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,7 +307,7 @@ L.U.Map.include({
|
|||
}
|
||||
})
|
||||
|
||||
window.onbeforeunload = () => this.isDirty || null
|
||||
window.onbeforeunload = () => this.editEnabled && this.isDirty || null
|
||||
this.backup()
|
||||
this.initContextMenu()
|
||||
this.on('click contextmenu.show', this.closeInplaceToolbar)
|
||||
|
|
78
umap/tests/integration/test_map_preview.py
Normal file
78
umap/tests/integration/test_map_preview.py
Normal file
|
@ -0,0 +1,78 @@
|
|||
import json
|
||||
from urllib.parse import quote
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import expect
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
GEOJSON = {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"properties": {
|
||||
"name": "Niagara Falls",
|
||||
},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [-79.04, 43.08],
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
CSV = "name,latitude,longitude\nNiagara Falls,43.08,-79.04"
|
||||
|
||||
|
||||
def test_map_preview(page, live_server, tilelayer):
|
||||
page.goto(f"{live_server.url}/map/")
|
||||
# Edit mode is not enabled
|
||||
edit_button = page.get_by_role("button", name="Edit")
|
||||
expect(edit_button).to_be_visible()
|
||||
|
||||
|
||||
def test_map_preview_can_load_remote_geojson(page, live_server, tilelayer):
|
||||
def handle(route):
|
||||
route.fulfill(json=GEOJSON)
|
||||
|
||||
# Intercept the route to the proxy
|
||||
page.route("*/**/ajax-proxy/**", handle)
|
||||
|
||||
page.goto(f"{live_server.url}/map/?dataUrl=http://some.org/geo.json")
|
||||
markers = page.locator(".leaflet-marker-icon")
|
||||
expect(markers).to_have_count(1)
|
||||
|
||||
|
||||
def test_map_preview_can_load_remote_csv(page, live_server, tilelayer):
|
||||
def handle(route):
|
||||
csv = """name,latitude,longitude\nNiagara Falls,43.08,-79.04"""
|
||||
route.fulfill(body=csv)
|
||||
|
||||
# Intercept the route to the proxy
|
||||
page.route("*/**/ajax-proxy/**", handle)
|
||||
|
||||
page.goto(f"{live_server.url}/map/?dataUrl=http://some.org/geo.csv&dataFormat=csv")
|
||||
markers = page.locator(".leaflet-marker-icon")
|
||||
expect(markers).to_have_count(1)
|
||||
|
||||
|
||||
def test_map_preview_can_load_geojson_in_querystring(page, live_server, tilelayer):
|
||||
page.goto(f"{live_server.url}/map/?data={quote(json.dumps(GEOJSON))}")
|
||||
markers = page.locator(".leaflet-marker-icon")
|
||||
expect(markers).to_have_count(1)
|
||||
|
||||
|
||||
def test_map_preview_can_load_csv_in_querystring(page, live_server, tilelayer):
|
||||
page.goto(f"{live_server.url}/map/?data={quote(CSV)}&dataFormat=csv")
|
||||
markers = page.locator(".leaflet-marker-icon")
|
||||
expect(markers).to_have_count(1)
|
||||
|
||||
|
||||
def test_map_preview_can_change_styling_from_querystring(page, live_server, tilelayer):
|
||||
style = {"color": "DarkRed"}
|
||||
page.goto(
|
||||
f"{live_server.url}/map/?data={quote(json.dumps(GEOJSON))}&style={quote(json.dumps(style))}"
|
||||
)
|
||||
markers = page.locator(".leaflet-marker-icon .icon_container")
|
||||
expect(markers).to_have_count(1)
|
||||
expect(markers).to_have_css("background-color", "rgb(139, 0, 0)")
|
|
@ -96,6 +96,7 @@ i18n_urls += decorated_patterns(
|
|||
)
|
||||
i18n_urls += decorated_patterns(
|
||||
[ensure_csrf_cookie],
|
||||
re_path(r"^map/$", views.MapPreview.as_view(), name="map_preview"),
|
||||
re_path(r"^map/new/$", views.MapNew.as_view(), name="map_new"),
|
||||
)
|
||||
i18n_urls += decorated_patterns(
|
||||
|
|
|
@ -455,8 +455,7 @@ class MapDetailMixin:
|
|||
if domain and "{" not in domain:
|
||||
context["preconnect_domains"] = [f"//{domain}"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
def get_map_properties(self):
|
||||
user = self.request.user
|
||||
properties = {
|
||||
"urls": _urls_for_js(),
|
||||
|
@ -486,6 +485,17 @@ class MapDetailMixin:
|
|||
if self.get_short_url():
|
||||
properties["shortUrl"] = self.get_short_url()
|
||||
|
||||
if not user.is_anonymous:
|
||||
properties["user"] = {
|
||||
"id": user.pk,
|
||||
"name": str(user),
|
||||
"url": reverse("user_dashboard"),
|
||||
}
|
||||
return properties
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
properties = self.get_map_properties()
|
||||
if settings.USE_I18N:
|
||||
lang = settings.LANGUAGE_CODE
|
||||
# Check attr in case the middleware is not active
|
||||
|
@ -495,19 +505,13 @@ class MapDetailMixin:
|
|||
locale = to_locale(lang)
|
||||
properties["locale"] = locale
|
||||
context["locale"] = locale
|
||||
if not user.is_anonymous:
|
||||
properties["user"] = {
|
||||
"id": user.pk,
|
||||
"name": str(user),
|
||||
"url": reverse("user_dashboard"),
|
||||
}
|
||||
map_settings = self.get_geojson()
|
||||
if "properties" not in map_settings:
|
||||
map_settings["properties"] = {}
|
||||
map_settings["properties"].update(properties)
|
||||
map_settings["properties"]["datalayers"] = self.get_datalayers()
|
||||
context["map_settings"] = json.dumps(map_settings, indent=settings.DEBUG)
|
||||
self.set_preconnect(map_settings["properties"], context)
|
||||
geojson = self.get_geojson()
|
||||
if "properties" not in geojson:
|
||||
geojson["properties"] = {}
|
||||
geojson["properties"].update(properties)
|
||||
geojson["properties"]["datalayers"] = self.get_datalayers()
|
||||
context["map_settings"] = json.dumps(geojson, indent=settings.DEBUG)
|
||||
self.set_preconnect(geojson["properties"], context)
|
||||
return context
|
||||
|
||||
def get_datalayers(self):
|
||||
|
@ -709,6 +713,15 @@ class MapNew(MapDetailMixin, TemplateView):
|
|||
template_name = "umap/map_detail.html"
|
||||
|
||||
|
||||
class MapPreview(MapDetailMixin, TemplateView):
|
||||
template_name = "umap/map_detail.html"
|
||||
|
||||
def get_map_properties(self):
|
||||
properties = super().get_map_properties()
|
||||
properties["preview"] = True
|
||||
return properties
|
||||
|
||||
|
||||
class MapCreate(FormLessEditMixin, PermissionsMixin, CreateView):
|
||||
model = Map
|
||||
form_class = MapSettingsForm
|
||||
|
|
Loading…
Reference in a new issue