From 177ef3a901088b25e4470423ea5ab5de17fe36a5 Mon Sep 17 00:00:00 2001
From: Julian <14220769+Qup42@users.noreply.github.com>
Date: Mon, 23 Sep 2024 21:19:20 +0200
Subject: [PATCH] Make "Map view" URL configurable in backend settings (#106)
When a query results contains WKT literals in its last column, a "Map view" button appears. A click on that button leads to an instance of https://github.com/ad-freiburg/qlever-petrimaps, which visualizes the result.
So far, this only worked for selected backends, and it was hard-coded in `backend/static/js/qleverUI.js` which ones. Now this can be configured invididually for each backend, via a field `Map view base URL`. The URL should be the base URL of the `qlever-petrimaps` instance. When the field is empty (which is the default), no "Map View" button is shown for that backend. Fixes #102 . Since a new field was added to the backend configuration, this requires a migration.
---
backend/admin.py | 2 +-
.../migrations/0073_backend_mapviewbaseurl.py | 24 ++++++++++++++++++
backend/models.py | 8 ++++++
backend/static/js/helper.js | 4 +--
backend/static/js/qleverUI.js | 5 ++--
backend/templates/partials/head.html | 1 +
db/qleverui.sqlite3 | Bin 296960 -> 394240 bytes
docs/configure_qleverui.md | 12 +++++++++
8 files changed, 50 insertions(+), 6 deletions(-)
create mode 100644 backend/migrations/0073_backend_mapviewbaseurl.py
diff --git a/backend/admin.py b/backend/admin.py
index 0705331a..3c7caaad 100644
--- a/backend/admin.py
+++ b/backend/admin.py
@@ -24,7 +24,7 @@ class BackendAdmin(ImportExportModelAdmin):
}
fieldsets = (
("General", {
- 'fields': ('name', 'slug', 'sortKey', 'baseUrl', 'isDefault', 'isNoSlugMode', 'apiToken')
+ 'fields': ('name', 'slug', 'sortKey', 'baseUrl', 'mapViewBaseURL', 'isDefault', 'isNoSlugMode', 'apiToken')
}),
('UI Suggestions', {
'fields': ('maxDefault', 'fillPrefixes', 'filterEntities', 'filteredLanguage', 'supportedKeywords', 'supportedFunctions', 'suggestPrefixnamesForPredicates', 'supportedPredicateSuggestions', 'suggestedPrefixes'),
diff --git a/backend/migrations/0073_backend_mapviewbaseurl.py b/backend/migrations/0073_backend_mapviewbaseurl.py
new file mode 100644
index 00000000..d3a09b97
--- /dev/null
+++ b/backend/migrations/0073_backend_mapviewbaseurl.py
@@ -0,0 +1,24 @@
+# Generated by Django 5.0.3 on 2024-09-22 19:29
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("backend", "0072_backend_isnoslugmode"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="backend",
+ name="mapViewBaseURL",
+ field=models.CharField(
+ blank=True,
+ default="",
+ help_text="The base URL of the https://github.com/ad-freiburg/qlever-petrimaps instance; if empty, no Map View button will appear",
+ max_length=2048,
+ verbose_name="Map view base URL",
+ ),
+ ),
+ ]
diff --git a/backend/models.py b/backend/models.py
index 30b687c9..4ccb3523 100644
--- a/backend/models.py
+++ b/backend/models.py
@@ -329,6 +329,14 @@ class Backend(models.Model):
help_text="The query for context-insensitive object autocompletion",
verbose_name="Context-insensitive object autocompletion query")
+ mapViewBaseURL = models.CharField(
+ default="",
+ blank=True,
+ max_length=2048, # URLs don't have a length limit, but this should be plenty long
+ verbose_name="Map view base URL",
+ help_text="The base URL of the https://github.com/ad-freiburg/qlever-petrimaps instance; if empty, no Map View button will appear",
+ )
+
def save(self, *args, **kwargs):
# We need to replace \r because QLever can't handle them very well
for field in (
diff --git a/backend/static/js/helper.js b/backend/static/js/helper.js
index 1b89d396..5cbec054 100644
--- a/backend/static/js/helper.js
+++ b/backend/static/js/helper.js
@@ -852,8 +852,8 @@ function getFormattedResultEntry(str, maxLength, column = undefined) {
icon_class = "glyphicon glyphicon-search";
str = "Query view";
} else {
- mapview_url = `https://qlever.cs.uni-freiburg.de/mapui-petri/` +
- `?query=${encodeURIComponent(str)}` +
+ mapview_url = MAP_VIEW_BASE_URL +
+ `/?query=${encodeURIComponent(str)}` +
`&mode=objects&backend=${BASEURL}`;
icon_class = "glyphicon glyphicon-globe";
str = "Map view";
diff --git a/backend/static/js/qleverUI.js b/backend/static/js/qleverUI.js
index 41016f23..2fd9be71 100755
--- a/backend/static/js/qleverUI.js
+++ b/backend/static/js/qleverUI.js
@@ -582,10 +582,9 @@ async function processQuery(sendLimit=0, element=$("#exebtn")) {
let mapViewButtonPetri = '';
if (result.res.length > 0 && /wktLiteral/.test(result.res[0][columns.length - 1])) {
let mapViewUrlVanilla = 'http://qlever.cs.uni-freiburg.de/mapui/index.html?';
- let mapViewUrlPetri = 'http://qlever.cs.uni-freiburg.de/mapui-petri/?';
let params = new URLSearchParams({ query: normalizeQuery(query), backend: BASEURL });
mapViewButtonVanilla = ` Map view`;
- mapViewButtonPetri = ` Map view`;
+ mapViewButtonPetri = ` Map view`;
}
// Show the buttons (if there are any).
@@ -595,7 +594,7 @@ async function processQuery(sendLimit=0, element=$("#exebtn")) {
// the Django configuration of the respective backend).
var res = "
";
if (showAllButton || (mapViewButtonVanilla && mapViewButtonPetri)) {
- if (BASEURL.match("wikidata|osm|ohm|dblp")) {
+ if (MAP_VIEW_BASE_URL.length > 0) {
res += `
${showAllButton} ${mapViewButtonPetri}
`;
} else {
res += `
${showAllButton}
`;
diff --git a/backend/templates/partials/head.html b/backend/templates/partials/head.html
index f4b7374b..4f22f0bb 100644
--- a/backend/templates/partials/head.html
+++ b/backend/templates/partials/head.html
@@ -66,6 +66,7 @@
{% for example in examples %}
examples.push(`{{ example.query|safe }}`);
{% endfor %}
+ var MAP_VIEW_BASE_URL = "{{ backend.mapViewBaseURL }}";
diff --git a/db/qleverui.sqlite3 b/db/qleverui.sqlite3
index 579bf1e0450ae0421a62d55ea3cb50a534084526..ec8888ab44fab3d31f79cdbe07023eb81828a4fe 100644
GIT binary patch
delta 3735
zcmeH}dsJ0b8o
}L)MhcnBmRX?J
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
P3q3JHJrV|!E`0w3+(TD9
diff --git a/docs/configure_qleverui.md b/docs/configure_qleverui.md
index 40ab207b..ff19c352 100644
--- a/docs/configure_qleverui.md
+++ b/docs/configure_qleverui.md
@@ -12,6 +12,18 @@ If everything worked correctly you should see backend details displayed on the t
You can also import the respective `*-sample.csv` file for the example backend or manually create examples in the "Examples" section in the admin panel that will be shown in the user interface later on.
+# Configure the Map view
+
+Geometry objects ([WKT literals](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) to be exact) can be displayed on a map using [qlever-petrimaps](https://github.com/ad-freiburg/qlever-petrimaps). The objects can be visualized as a heatmap or discrete objects.
+qlever-petrimaps is the tool that does the heavy-lifting for visualizing the data on the map. You have to host your own instance of qlever-petrimaps.
+
+- Enable the Map view by setting the field `Map view base URL` to the location of your own qlever-petrimaps instance.
+
+The "Map view" button appears for a query if and only the following two requirements are met:
+
+- the geometry objects must be in the **last column**
+- the column must contain literals with the datatype `http://www.opengis.net/ont/geosparql#wktLiteral`
+
# Configure the autocompletion queries
QLever UI offers several settings that can be used to configure the autocompletion. They are separated into five categories:
- [Variable Names](#variable-names)