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 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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)