This plugin adds geospatial and temporal metadata support for articles (journals) and preprints (repositories) in Janeway.
This plugin is part of the KOMET project ("Kompetenznetzwerk für das Management und die Erschließung von textbasierten Forschungsdaten"), funded by the German Federal Ministry of Education and Research (BMBF). It brings spatiotemporal metadata capabilities to the Janeway publishing platform, complementing existing work for Open Journal Systems (OJS).
The plugin builds on concepts and experience from the geoMetadata plugin for OJS. While geoMetadata targets OJS, this plugin implements equivalent functionality for Janeway, adapted to its plugin architecture and hook system. Geospatial metadata collected by this plugin and its OJS counterparts can be aggregated and made discoverable through OPTIMAP, a web portal for geospatial discovery of research articles based on open metadata.
- Spatial Metadata: Store geographic coverage as Well-Known Text (WKT) geometry
- Temporal Metadata: Record time periods covered by research
- Interactive Maps: Leaflet.js-based map display and editing interface
- JSON API: GeoJSON endpoints for map data retrieval
- Admin Integration: Edit geometadata from article/preprint management pages
- Full-Page Map: Browse all articles/preprints with geographic metadata on a single map
-
Clone this repository into the Janeway
src/pluginsfolder:cd path/to/janeway/src/plugins git clone https://github.com/GeoinformationSystems/janeway_geometadata geometadata -
From the
srcdirectory, install the plugin and run migrations:python3 manage.py install_plugins geometadata python3 manage.py migrate
-
Restart your server (Apache, Passenger, etc.)
-
Enable the plugin via the Janeway manager interface
- Janeway 1.7+ (tested with current main branch)
- geopy ~2.4 (pip, MIT) - reverse geocoding
- geomet ~1.1 (pip, Apache-2.0) - WKT/GeoJSON conversion
- Leaflet.js 1.9.4 (bundled, BSD-2-Clause) - interactive maps
- Leaflet.draw 1.0.4 (bundled, MIT) - drawing tools for geometry editing
- leaflet-providers (bundled, BSD-2-Clause) - basemap provider definitions
Settings are configurable per journal/repository via the manager at
/plugins/geometadata/manager/. Press-level defaults apply to all
journals/repositories and can be overridden per journal.
| Setting | Default | Description |
|---|---|---|
| Enable Geometadata Collection | off | Master switch for the plugin |
| Enable Spatial Metadata | on | Allow geographic location/area input |
| Enable Temporal Metadata | on | Allow time period input |
| Enable Map Page | on | Enable the aggregated map page (press-wide or journal-wide) |
| Require Geometadata on Submission | off | Require authors to provide geospatial metadata |
| Setting | Default | Description |
|---|---|---|
| Show Map on Article Pages | on | Display an interactive map on article/preprint pages |
| Show Temporal Coverage | on | Display temporal coverage (date range) on article pages |
| Show Place Names | on | Display place name labels alongside the map |
| Setting | Default | Description |
|---|---|---|
| Show Temporal Coverage on Issue Pages | on | Display aggregated temporal coverage on issue landing pages |
Note: Issue page features require the
issue_footer_blockhook which is not present in standard Janeway. See Template Requirements below. The settings page will detect if the hook is missing and disable these settings with an explanatory message.
| Setting | Default | Description |
|---|---|---|
| Show GeoJSON Download Links | on | Show download links for geometadata in GeoJSON format on article pages, issue pages, and the journal-wide map page |
Controls which metadata formats are embedded in the HTML <head> of article
pages for harvesters and search engines. All embedding respects the
Enable Spatial / Enable Temporal toggles — e.g., when temporal metadata is
disabled, Schema.org output omits temporalCoverage but still includes
spatialCoverage.
| Setting | Default | Description |
|---|---|---|
| Dublin Core Coverage | on | Embed DC.SpatialCoverage, DC.box, DC.temporal, DC.PeriodOfTime meta tags |
| geo.* Meta Tags | on | Embed geo.placename meta tags |
| Schema.org Coverage (JSON-LD) | on | Embed Schema.org spatialCoverage/temporalCoverage as JSON-LD |
| GeoJSON Link Element | off | Include a <link rel="alternate" type="application/geo+json"> to the GeoJSON API endpoint |
Colour-code geometries on aggregated maps (journal and press map pages) by issue or journal.
| Setting | Default | Description |
|---|---|---|
| Enable Colour Coding | off | Assign colours to markers and geometries based on their grouping (issue on journal maps, journal on press maps) |
| Colour Method | colorbrewer |
How to generate the palette: colorbrewer (ColorBrewer schemes), startrek (Star Trek themed palettes), custom (enter your own colours) |
| Colour Scheme | Set2 |
Palette name for the selected method. Qualitative schemes recommended for categorical data. |
| Custom Colours | (empty) | One HTML colour code per line (e.g., #3388ff). Used when method is custom. |
| Colour Palette | (auto) | JSON array of hex colours. Auto-populated from the selected method/scheme. |
| Map Feature Colour | #3388ff |
Colour for map features on article and issue pages (when colour coding is disabled). Enter a hex code or select from the palette. |
The plugin uses leaflet-providers for basemap selection. Only providers that work without registration or API keys are included.
| Setting | Default | Description |
|---|---|---|
| Basemap Provider | OpenStreetMap.Mapnik |
Basemap provider key from leaflet-providers |
Available basemaps:
| Provider Key | Description |
|---|---|
OpenStreetMap.Mapnik |
Standard OpenStreetMap style |
OpenStreetMap.DE |
German OpenStreetMap style |
OpenStreetMap.CH |
Swiss OpenStreetMap style (Switzerland only) |
OpenStreetMap.France |
French OpenStreetMap style |
OpenStreetMap.HOT |
Humanitarian OpenStreetMap Team style |
OpenStreetMap.BZH |
Breton OpenStreetMap style (Brittany region) |
OpenTopoMap |
Topographic map with contour lines |
CyclOSM |
Cycling-focused map style |
GeoportailFrance.plan |
French IGN Plan map |
GeoportailFrance.orthos |
French IGN aerial photos |
TopPlusOpen.Color |
German BKG topographic map (colour) |
TopPlusOpen.Grey |
German BKG topographic map (greyscale) |
To preview all basemaps interactively, visit the leaflet-providers demo.
Automatically derive place names and administrative units from drawn geometries using reverse geocoding services.
| Setting | Default | Description |
|---|---|---|
| Enable Reverse Geocoding | on | Enable the "Lookup Location Names" button on edit pages |
| Geocoding Provider | nominatim |
Service to use: nominatim (OpenStreetMap), photon, or geonames |
| User Agent | janeway-geometadata |
Identifies your instance to Nominatim/Photon (required by their usage policies) |
| GeoNames Username | (empty) | Required when using GeoNames provider. Register at geonames.org |
| Setting | Default | Description |
|---|---|---|
| Default Map Latitude | 0 | Default center latitude (-90 to 90) |
| Default Map Longitude | 0 | Default center longitude (-180 to 180) |
| Default Map Zoom | 2 | Default zoom level (1-18) |
When maps are displayed, the user's browser connects directly to an external tile server to load map tiles (the background map images). No map tile data is proxied through the Janeway server — the browser makes requests to the tile provider, which will receive the user's IP address, browser user agent, and the geographic extent of the requested map area.
By default, the plugin uses tile servers operated by the OpenStreetMap Foundation (OSMF), located in the United Kingdom and other countries. The Janeway site operator has no control over these connections or OSMF's data processing practices.
If you select a different basemap provider (see Map Basemap), the browser will connect to that provider instead. Each provider has its own privacy policy and usage terms.
The use of this map service can be justified under the legitimate interest of displaying map functions to users of the website (cf. Article 6(1)(f) GDPR).
OSMF Privacy Policy: https://wiki.osmfoundation.org/wiki/Privacy_Policy
OSMF Tile Usage Policy: https://operations.osmfoundation.org/policies/tiles/
If your journal or press displays maps to visitors, your data privacy statement should inform users that:
- Map tiles are loaded from an external service (identify which one).
- The user's browser connects directly to that service, transmitting the IP address, browser user agent, and the map area viewed.
- The site operator has no control over the external service's data processing.
- Link to the tile provider's privacy policy.
Example paragraph (adapt to your tile provider and legal requirements):
This website uses map services provided by the OpenStreetMap Foundation (OSMF). When you view a page containing a map, your browser connects to servers operated by the OSMF to load map tiles. This transmits your IP address and other request data to the OSMF. We have no control over this data processing. The legal basis for this processing is our legitimate interest in displaying geographic information (Art. 6(1)(f) GDPR). For details, see the OSMF Privacy Policy.
Some plugin features require hooks that are not present in standard Janeway templates. The plugin's settings page automatically detects which hooks are available and disables settings that cannot function without them.
These hooks are present in all Janeway themes out of the box:
| Hook | Purpose |
|---|---|
article_footer_block |
Display maps on article/preprint pages |
nav_block |
Add navigation link to map page |
base_head_css |
Inject Leaflet CSS and custom styling |
in_review_editor_actions |
Display link to geometadata editing in editor review workflow |
These hooks require adding a single line to your theme templates:
| Hook | Template File | Purpose |
|---|---|---|
issue_footer_block |
journal/issue_display.html |
Display aggregated map and temporal coverage on issue pages |
To enable issue page features, add the following line to your theme's
journal/issue_display.html template, typically after the issue article list:
{% load hooks %}{% hook 'issue_footer_block' %}For all three standard themes (OLH, material, clean), the recommended location
is after the {% include "elements/journal/issue_block.html" %} line:
{% include "elements/journal/issue_block.html" %}
{% load hooks %}{% hook 'issue_footer_block' %}After adding this line to your theme templates, the issue page settings (Show Temporal Coverage on Issue Pages) will become available in the plugin settings.
Editors can access geometadata editing from multiple locations:
| Access Point | Description |
|---|---|
| Review Workflow | Click "Edit Geometadata" button in the editor actions during article review |
| Curation Queue | Navigate to /plugins/geometadata/curation-queue/ for a list of all articles with their geometadata status |
| Direct URL | Access editing directly at /plugins/geometadata/edit/article/<article_id>/ |
| Article Archive Page | The "Edit Geometadata" link appears on the article's archive/management page |
Editing geometadata:
- Navigate to the geometadata editing page via any of the methods above
- Draw shapes on the map or paste WKT geometry in the text field
- Use "Lookup Location Names" to auto-fill place name and administrative units
- Add temporal information (start date, end date) for relevant time periods
- Save
| Endpoint | Description |
|---|---|
/plugins/geometadata/api/article/<pk>.json |
GeoJSON Feature for a single article |
/plugins/geometadata/api/preprint/<pk>.json |
GeoJSON Feature for a single preprint |
/plugins/geometadata/api/all.json |
GeoJSON FeatureCollection for all articles/preprints in the current journal/repository |
/plugins/geometadata/api/issue/<pk>.json |
GeoJSON FeatureCollection for all articles in an issue |
/plugins/geometadata/api/press.json |
GeoJSON FeatureCollection across all journals and repositories |
/plugins/geometadata/api/palette.json |
Colour palette array for map colour coding |
Bounding box filtering: The all.json, issue/<pk>.json, and press.json
endpoints support optional query parameters for spatial filtering:
| Parameter | Description |
|---|---|
north |
Maximum latitude (-90 to 90) |
south |
Minimum latitude (-90 to 90) |
east |
Maximum longitude (-180 to 180) |
west |
Minimum longitude (-180 to 180) |
Records are returned if their bounding box intersects the query box. All parameters are optional — you can filter by a single boundary if needed.
Example: /plugins/geometadata/api/all.json?south=40&north=60&west=-10&east=30
A public map page showing all articles/preprints with geographic metadata is
available at /plugins/geometadata/map/. A navigation link is automatically
added via the nav_block hook when viewing journal or repository pages.
A press-wide map showing articles from all journals and repositories is
available at /plugins/geometadata/press-map/. This requires the "Enable Map
Page" setting to be turned on at the press level (via
/plugins/geometadata/manager/ when accessed outside a journal context).
Note: The nav_block hook only adds navigation links within journal or
repository contexts. To add a map link to your press landing page, manually
add a link to your press theme template:
<a href="{% url 'geometadata_press_map_page' %}">Map</a>Or add it to your press navigation in themes/<your-theme>/templates/press/nav.html
or equivalent.
Geometry is stored as WKT (Well-Known Text), a standard text format:
POINT(-122.4194 37.7749)
POLYGON((-10 35, 40 35, 40 70, -10 70, -10 35))This approach was chosen over GeoDjango/PostGIS for:
- No additional database requirements
- CSV import/export compatibility
- Simpler deployment
Each record includes automatically-calculated bounding box fields
(bbox_north, bbox_south, bbox_east, bbox_west) for efficient spatial
queries without requiring PostGIS. A composite B-tree index on these four
fields enables fast bounding-box intersection queries used by the API's
spatial filtering feature.
This plugin supports Janeway's multilingual system. All user-facing strings
are wrapped with Django's translation functions (gettext_lazy in Python,
{% trans %} in templates), so the plugin can be displayed in any language
that Janeway supports.
Janeway uses three layers for internationalization:
- Django's standard i18n (
USE_I18N = True,LocaleMiddleware) — translates Python strings and template text via.po/.mofiles - Per-journal language settings — each journal can configure which
languages are available and which is the default
(via
journal_languagesanddefault_journal_languagesettings) - django-modeltranslation — translates model field values stored in the database (e.g., article titles, CMS page content)
Plugins participate in layer 1: they provide .po translation files in a
locales/ directory. Janeway automatically discovers plugin locale
directories via plugin_installed_apps.load_plugin_locales() in
janeway_global_settings.py.
- English (source language, all strings)
- German
-
Create the locale directory:
mkdir -p src/plugins/geometadata/locales/fr/LC_MESSAGES
-
Generate a
.pofile from the existing source strings. You can either copy the German.pofile as a template and replace themsgstrvalues, or use Django'smakemessagescommand:cd src/plugins/geometadata django-admin makemessages -l frThis scans all Python files and templates for
_()and{% trans %}strings and createslocales/fr/LC_MESSAGES/django.po. -
Translate the
msgstrentries in the.pofile. Each entry has amsgid(English source) and amsgstr(your translation). Leavemsgstr ""empty for untranslated strings — Django will fall back to English. -
Compile the
.pofile into a binary.mofile:cd src/plugins/geometadata/locales/fr/LC_MESSAGES msgfmt -o django.mo django.poOr use Django's command:
cd src python3 manage.py compilemessages -l fr -
Restart the server. Django loads
.mofiles at startup.
You need to regenerate/update .po files when:
- You add new user-facing strings in Python code (wrapped in
_()orgettext_lazy()) - You add or change
{% trans %}or{% blocktrans %}tags in templates - You change existing English source strings (the
msgidvalues)
After modifying source strings, run makemessages again for each language,
then update the translations and recompile with msgfmt or
compilemessages.
plugins/geometadata/
└── locales/
├── de/
│ └── LC_MESSAGES/
│ ├── django.po # German translations (editable text)
│ └── django.mo # Compiled binary (generated, do not edit)
└── en/
└── LC_MESSAGES/
├── django.po # English (empty — source strings are English)
└── django.mo # Compiled binaryLeaflet map controls (zoom buttons, fullscreen toggle, drawing toolbar) are
translated without modifying any vendor JavaScript files. Django {% trans %}
strings are passed to JS at runtime via two mechanisms:
data-i18n-*attributes on the map container<div>— read bygeometadata-display.jsandgeometadata-edit.jsto set Leaflet'szoomInTitle/zoomOutTitleand fullscreen controltitle/titleCancel.L.drawLocaloverrides in a<script>block in the edit templates (edit_article.html,edit_preprint.html) — sets all ~35 Leaflet.draw toolbar and tooltip strings before the draw control is initialised.
This keeps all vendor files (leaflet.js, leaflet.draw.js,
leaflet.fullscreen.js) unmodified and updatable independently.
- Python code: Use
from django.utils.translation import gettext_lazy as _for model fields, form labels, and anything evaluated at import time. Usefrom django.utils.translation import gettext as _in views for strings evaluated at request time. - Templates: Add
{% load i18n %}at the top, then wrap strings with{% trans "text" %}or{% blocktrans %}...{% endblocktrans %}for strings with variables. .pofile encoding: Files must be UTF-8. Literal double-quote characters inside translated strings must be escaped as\".- Janeway auto-discovers plugin locales: The directory must be named
locales/(notlocale/) and placed in the plugin root.
The plugin includes a management command to load demo data for testing and development. The demo data is based on articles from the OJS geoMetadata Demo Journal.
Loading demo data:
# Create the demo journal and load all data (recommended for fresh installs)
python3 manage.py load_geometadata_demo --create-journal
# Load into an existing journal
python3 manage.py load_geometadata_demo --journal-code=geodemo
# Include placeholder PDF galleys
python3 manage.py load_geometadata_demo --create-journal --with-galleys
# Clear existing demo articles before loading
python3 manage.py load_geometadata_demo --journal-code=geodemo --clear-existingArguments:
| Argument | Default | Description |
|---|---|---|
--journal-code |
dqj |
Journal short code to load data into |
--create-journal |
off | Create the demo journal from demo_journal.json if it doesn't exist |
--owner-email |
admin@example.com |
Email of user to be set as article owner |
--with-galleys |
off | Attach a placeholder PDF galley to each article |
--clear-existing |
off | Delete existing demo articles before loading (matches by title prefix) |
What gets created:
- Journal (when using
--create-journal): "Delta Quadrant Journal" (dqj) - 2 issues: Vol. 1 No. 1 (12 articles) and Vol. 1 No. 2 (6 articles)
- 18 articles with titles, abstracts, authors, and keywords
- Geographic metadata: WKT geometries (points, polygons, multipoints) and place names
- Temporal metadata: Various historical and modern time periods
Demo data files:
| File | Description |
|---|---|
test/data/demo_journal.json |
Journal metadata (name, code, settings) |
test/data/demo_issues.json |
Issue metadata (volume, number, title, description) |
test/data/demo_articles.json |
Article data with authors, keywords, and geometadata |
test/data/placeholder.pdf |
Placeholder PDF for galleys |
These JSON files can be customized or extended with additional test data.
The default demo journal "Delta Quadrant Journal" (code: dqj) is configured
with sensible defaults for testing the geometadata plugin.
For running tests outside of the Janeway Docker environment, create a virtual
environment in the Janeway src directory:
cd path/to/janeway/src
# Create and activate virtual environment
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install Janeway and plugin dependencies
pip install -r ../requirements.txt -r ../dev-requirements.txt
pip install -r plugins/geometadata/requirements.txtThe .venv directory is already in Janeway's .gitignore.
Note: If you've previously run Janeway with Docker, log files may be owned
by root. Fix with: sudo chown -R $USER:$USER logs/
Run the Django unit tests with SQLite (requires environment variables):
cd src
source .venv/bin/activate # If not already activated
# Set required environment variables
export DB_VENDOR=sqlite
export JANEWAY_SETTINGS_MODULE=core.janeway_global_settings
# Run all plugin tests
python3 manage.py test plugins.geometadata
# Or run specific test modules
python3 manage.py test plugins.geometadata.tests.test_models
python3 manage.py test plugins.geometadata.tests.test_geojson_validationYou can also set these variables inline:
DB_VENDOR=sqlite JANEWAY_SETTINGS_MODULE=core.janeway_global_settings \
python3 manage.py test plugins.geometadataThe plugin includes end-to-end tests using Playwright to verify map functionality in a real browser. These tests check that maps render correctly on article, issue, journal, and press pages.
Prerequisites:
cd src
source .venv/bin/activate # If not already activated
# Install E2E test dependencies
pip install -r plugins/geometadata/requirements-e2e.txt
# Install Playwright browsers (first time only)
playwright install chromiumRun E2E tests (headless):
cd src
source .venv/bin/activate
export DB_VENDOR=sqlite
export JANEWAY_SETTINGS_MODULE=core.janeway_global_settings
pytest plugins/geometadata/tests/e2e/ -vRun E2E tests with visible browser (useful for debugging or following along):
cd src
source .venv/bin/activate
DB_VENDOR=sqlite JANEWAY_SETTINGS_MODULE=core.janeway_global_settings \
pytest plugins/geometadata/tests/e2e/ -v --headed --slowmo=500The --headed flag opens a browser window so you can watch the tests run.
The --slowmo=500 adds a 500ms delay between actions for easier observation.
Run a specific test:
DB_VENDOR=sqlite JANEWAY_SETTINGS_MODULE=core.janeway_global_settings \
pytest plugins/geometadata/tests/e2e/test_maps.py::TestJournalMapPage::test_map_page_contains_leaflet_map -v --headedTest Artifacts:
When tests run, they generate several artifacts in tests/e2e/test-results/:
| Artifact | Description |
|---|---|
screenshots/*.png |
Screenshots of map pages (captured for visual verification) |
traces/*.zip |
Playwright traces for failed tests (can be viewed with playwright show-trace) |
To view a trace file for debugging a failed test:
playwright show-trace tests/e2e/test-results/traces/test-name-trace.zipFollow Janeway's code style:
ruff check src/plugins/geometadata
ruff format src/plugins/geometadataSee static/geometadata/README.md for details on the bundled Leaflet libraries, their licenses, and update instructions.
Several enhancements to Janeway's core would improve geometadata integration. These are tracked in Issue #1.
This plugin is part of the KOMET project and is licensed under AGPL v3+, consistent with Janeway.
- Daniel Nüst, TU Dresden
- Janeway Issue #1928 - Original feature request
- Janeway Documentation
- Janeway Plugin Documentation
If you use this plugin in your research or publication workflow, please cite it:
Nüst, D. (2026). Geometadata Plugin for Janeway (v0.1.0). Zenodo. https://doi.org/10.5281/zenodo.18495577
BibTeX:
@software{nust_geometadata_2026,
author = {Nüst, Daniel},
title = {Geometadata Plugin for Janeway},
year = 2026,
publisher = {Zenodo},
version = {v0.1.0},
doi = {10.5281/zenodo.18495577},
url = {https://doi.org/10.5281/zenodo.18495577}
}