Interactive research atlas for the Africa Multiple Cluster of Excellence, exposing the cluster's WissKI/MongoDB archive as a browsable, analysable, visually-rich web app. Built with SvelteKit 5, ECharts 6, MapLibre GL 5, and Tailwind CSS 4 — shipped as a static site to GitHub Pages.
Live: https://am-digital-research-environment.github.io/amira/
alias counts for photos that appear in multiple records._meta.jsonEvery named entity is a link:
WissKI Navigate — Optional deep-links to WissKI entities surface throughout the dashboard via pre-computed URL mappings (dev.wisski_urls.*.json), connecting every dashboard record back to its source in the WissKI knowledge base.
Chart downloads — Every ECharts visualisation exposes a download button in its card header that exports the chart as a PNG with the card title, subtitle, and export date composited on top, using the site's typography and theme (light or dark).
$state, $derived, $derived.by, $effect, $props)gemini-embedding-2-preview, 768-dim) projected to 2D with UMAP for the semantic map@tailwindcss/vite, HSL CSS-variable theming, dark mode@lucide/svelte@sveltejs/adapter-static and GitHub Actions| Component | Type | Description |
|---|---|---|
Timeline |
Line | Count-by-year timeline |
StackedTimeline |
Stacked bar | Items per year, broken down by resource type |
BarChart |
Bar | Horizontal / vertical bar with pagination for long lists |
PieChart |
Pie / donut | Categorical distribution with click selection |
WordCloud |
Word cloud | Animated tag / subject cloud with adjustable max words |
HeatmapChart |
Heatmap | Matrix cross-tabulation with colour intensity |
BeeswarmChart |
Scatter / jitter | Beeswarm distribution using ECharts 6 axis jitter |
GanttChart |
Custom bar range | Project timelines with start / end bars, category colouring |
SankeyChart |
Sankey | Multi-level flow diagram (e.g. contributor → project → type) |
SunburstChart |
Sunburst | Hierarchical drill-down visualisation |
ChordDiagram |
Chord | Co-occurrence relationships between categories |
SemanticScatter |
Scatter | UMAP projection of Gemini embeddings; colourable by four dimensions |
NetworkGraph |
Force graph | Weighted force-directed network: edge width follows edge value, dashed for latent ties, solid for direct metadata edges; optional community halos |
EntityKnowledgeGraph |
Force graph | Per-entity ego graph with IDF-weighted direct edges + latent edges via Jaccard / personalised PageRank, discursive communities, PageRank-sized nodes, facet panel, fullscreen mode |
LocationMap |
Map | MapLibre GL multi-marker map with clustered popups, Flat / Globe projection toggle |
MiniMap |
Map | Lightweight single-location map with marker |
EChart |
Base wrapper | Shared ECharts wrapper: dynamic theme switching via setTheme(), zoom controls, resize handling, performance heuristics |
ChartDownloadButton |
Action | Exports the parent chart as a PNG with title, subtitle, and export date composited on top; auto-wired to any chart hosted inside a ChartCard |
npm install
npm run dev # dev server
npm run build # production build
npm run preview # preview production build
npm run check # svelte-kit sync + svelte-check
npm run lint # ESLint
npm run format # Prettier write
npm run format:check # Prettier check (CI uses this)
CI runs format:check, so run npm run format locally before committing if the check fails.
src/
├── lib/
│ ├── components/
│ │ ├── ui/ # Reusable UI primitives
│ │ │ ├── card*.svelte, badge, button, input, select, combobox, tabs
│ │ │ ├── pagination, stat-card, chart-card, scrollable-table
│ │ │ ├── collection-item-row, back-to-list, empty-state
│ │ │ ├── section-badge, wisski-link, scroll-to-top, seo
│ │ ├── charts/ # ECharts + MapLibre chart components
│ │ │ ├── EChart.svelte # Base wrapper
│ │ │ ├── ChartDownloadButton.svelte # PNG export with title / subtitle composited
│ │ │ ├── chart-registry.ts # Context bridge: chart instance ↔ ChartCard header
│ │ │ ├── Timeline, StackedTimeline
│ │ │ ├── BarChart, PieChart, WordCloud
│ │ │ ├── HeatmapChart, BeeswarmChart, GanttChart
│ │ │ ├── SankeyChart, SunburstChart, ChordDiagram
│ │ │ ├── NetworkGraph, EntityKnowledgeGraph
│ │ │ ├── SemanticScatter
│ │ │ ├── LocationMap, MiniMap
│ │ │ ├── map/ # Projection toggle, marker / popup builders
│ │ │ └── utils/ # Shared option builders & tooltip formatters
│ │ ├── collections/ # Featured-collection components
│ │ │ ├── CollectionHeader, CollectionIndexCard
│ │ │ ├── PhotoCard, PhotoFacets, PhotoLightbox
│ │ │ ├── PhotoMasonry, PhotoMap, PhotoTimeline
│ │ │ ├── ViewModeTabs, photoHelpers.ts
│ │ ├── layout/ # Sidebar (grouped nav), Header, FilterPanel
│ │ └── research-items/ # ItemDetail, ItemFilters, ItemTable, itemHelpers
│ ├── stores/
│ │ ├── data.ts # Raw data + derived stores (projects, persons, collections…)
│ │ └── filters.ts # Global filter state
│ ├── styles/ # Tokens, animations, component CSS, sidebar, maplibre
│ ├── types/ # TS interfaces (domain, collection, charts, geo, mongo, embeddings, category-index)
│ └── utils/
│ ├── transforms/ # dates, grouping, extractors, network, charts, filters
│ ├── loaders/ # mongoJSON, collectionLoader, geolocLoader, embeddingsLoader
│ ├── external.ts # Virtual external projects (BayGlo2025, ILAM) + pseudo-section
│ ├── helpers.ts # formatDate, getItemTitle, getProjectTitle, getSectionColor
│ ├── languages.ts # ISO 639-2/3 → English name mapping
│ ├── urls.ts # Cross-linking URL builders
│ ├── urlSelection.ts # URL query-param sync for selection state
│ ├── search.ts # Generic text-search filter factory
│ ├── pagination.ts # Generic pagination utility
│ ├── wisskiUrl.svelte.ts # WissKI navigate URL lookup (Svelte store)
│ ├── featuredCollectionLoader.ts # Featured collections card builder
│ ├── collectionsRegistry.ts # Featured collection metadata registry
│ ├── revealOnScroll.ts # IntersectionObserver-based reveal actions
│ └── cn.ts # Classname merging (clsx + tailwind-merge)
├── routes/
│ ├── +page.svelte # Overview dashboard
│ ├── +layout.svelte # Global layout (Header, Sidebar, data init)
│ ├── whats-new/ # Recent additions (3 / 6 / 12 months)
│ ├── research-sections/ # 13 sections with Gantt timelines
│ ├── projects/ # Projects with facets, Gantt, beeswarm
│ ├── research-items/ # Research items browser with facets, table, detail
│ ├── people/ # People directory with profiles
│ ├── groups/ # Research groups
│ ├── institutions/ # Institutions (partner + contributor)
│ ├── collections/ # Featured collections index
│ ├── collections/[slug]/ # Collection detail: masonry / map / timeline
│ ├── genres/ # Genre browser with word cloud
│ ├── languages/ # Language browser
│ ├── locations/ # Country / region / city browser with maps
│ ├── resource-types/ # Resource-type browser with pie / bar
│ ├── subjects/ # Subjects & Tags with word cloud
│ ├── project-explorer/ # Cross-project analytical workspace
│ ├── compare-projects/ # Side-by-side comparison
│ ├── network/ # Network visualisation (5 tabs)
│ ├── semantic-map/ # UMAP embedding scatter with similar-items
│ └── sitemap.xml/ # Prerendered sitemap
└── app.css # Global styles and Tailwind v4 config
static/
├── data/
│ ├── manifest.json # Per-university collection inventory
│ ├── dev/ # MongoDB exports + WissKI URL mappings
│ ├── manual/ # Hand-curated supplements (e.g. projectLinks)
│ ├── knowledge_graphs/ # Pre-computed ego graphs per entity type + _meta.json
│ ├── embeddings/ # map.json (UMAP) + similar.json (top-K neighbours)
│ ├── projects_metadata_ubt/ # University of Bayreuth (21 collections)
│ ├── projects_metadata_unilag/ # University of Lagos (8 collections)
│ ├── projects_metadata_ujkz/ # Université Joseph Ki-Zerbo (7 collections)
│ ├── projects_metadata_ufba/ # Federal University of Bahia (1 collection)
│ └── external_metadata/ # External collections (BayGlo2025, ILAM)
└── logos/ # Partner institution logos used across the UI
static/data/dev/dev.projectsData.json — 92 projects with PIs, members, dates, research sections, institutions, RDSpace referencesdev.persons.json — 1,394 person records with institutional affiliationsdev.institutions.json — 492 institution recordsdev.groups.json — 84 research-group recordsdev.collections.json — development-only collection itemsdev.researchSections.json — all 13 research sections (Phase 1 + Phase 2 + External)dev.geo.json — country / region / subregion / city geolocations with Wikidata coordinatesdev.wisski_urls.*.json — pre-computed WissKI navigate URL mappings for every entity type (projects, persons, institutions, items, subjects, tags, countries, regions, cities, genres, groups, languages, research sections, resource types)static/data/projects_metadata_*/3,975 research items across four partners. Each item carries title, contributors (person / institution / group qualifier with roles), subjects (LCSH), tags, language (ISO 639-2/3), location (country / region / city), dates (created, issued, captured), identifiers, physical description, access conditions, and bitstream / URL references.
static/data/external_metadata/Items contributed from outside the cluster's partner universities. Tagged university: "external" so they count toward global totals but can be filtered out of per-university views:
BayGlo2025 — Bayreuth Global / Bayreuth Postkolonial; affiliated with the University of BayreuthILAM — International Library of African Music (Rhodes University)Surfaced across the dashboard as virtual projects (Ext_BayGlo2025, Ext_ILAM) under an External pseudo research section, with a dedicated chip in the global filter panel and a dedicated group in the Project Explorer and Compare Projects selectors. Virtual-project definitions live in src/lib/utils/external.ts.
static/data/knowledge_graphs/Structural ego graphs pre-computed by scripts/generate_knowledge_graphs.py — one JSON file per entity organised by type:
knowledge_graphs/
├── _meta.json # Global community labels + top-PageRank nodes
├── items/ # Research-item ego graphs
├── persons/
├── projects/
├── institutions/
├── subjects/
├── tags/
├── locations/
└── genres/
Each graph combines direct metadata edges (IDF-weighted so distinctive relationships look heavier than ubiquitous ones), latent structural edges (Jaccard on shared neighbourhoods + personalised PageRank for multi-hop relevance), and global analysis results (Louvain community membership, PageRank-based centrality) so the UI can reveal discursive communities and key nodes that local co-occurrence alone would miss.
static/data/embeddings/map.json — per-item {id, x, y, lowSignal, title, project, university, typeOfResource} for the 2D UMAP scattersimilar.json — per-item top-12 cosine-similar neighbours keyed by dre_id (lazy-loaded on first selection on /semantic-map)cache.json — full 768-dim Gemini vectors + SHA-256 hashes used for incremental re-embedding; gitignoredPipelined by scripts/generate_embeddings.py. See scripts/README.md for details.
static/data/manual/Hand-curated data not sourced from MongoDB:
projectLinks.json — supplementary project-to-entity linksPython pipelines live in scripts/ and cover MongoDB export, WissKI URL generation, thumbnail fetching, knowledge-graph generation, embedding generation, and data slimming. Setup:
python -m venv .venv
.venv/Scripts/pip install -r scripts/requirements.txt
See scripts/README.md for each script's responsibilities and inputs / outputs. Regenerate the knowledge graphs and embeddings after any metadata refresh:
.venv/Scripts/python scripts/generate_knowledge_graphs.py
.venv/Scripts/python scripts/generate_embeddings.py --scope missing
Deploys automatically to GitHub Pages on push to main:
npm ci → npm run build with BASE_PATH=/${{ github.event.repository.name }} → actions/upload-pages-artifact → actions/deploy-pages@sveltejs/adapter-static pre-renders every route into build/npm run format:check, npm run lint, npm run check, and npm run build on every push and PRDeveloped by Frédérick Madore for the Digital Research Environment (DRE) of the Africa Multiple Cluster of Excellence, University of Bayreuth.
MIT