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 579bf1e0..ec8888ab 100644 Binary files a/db/qleverui.sqlite3 and b/db/qleverui.sqlite3 differ 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)