A comprehensive web application for visualizing and analyzing Austrian media outlet networks to be used in conjunction with the Media Language DSL https://github.com/pacedproton/medialang. It is built with SvelteKit and powered by Neo4j graph database with PostgreSQL for structured data.

- Interactive Graph Visualization: Explore media outlet relationships using D3.js-powered network graphs
- 3D Time Layers: Navigate temporal data through immersive 3D visualization with year-by-year filtering
- TV Mode: Specialized broadcast network visualization with video integration
- Newspaper Archive: ANNO integration for historical newspaper browsing
- Tabular Data View: AG Grid component for detailed data analysis
- MDSL Toolchain: Generate and apply SQL/Cypher from Media Definition Language
- Temporal Filtering: Timeline slider for exploring networks across different time periods
- Multi-language Support: English/Spanish internationalization
- Node.js 18+ and npm
- PostgreSQL database with Austrian media outlet data
- Neo4j database (optional - application will use mock data if unavailable)
- MDSL Toolchain (optional - for generating schemas from MDSL files)
- Clone and install dependencies:
git clone <repository-url>
cd ANMI-Web
npm install
- Set up environment:
cp .env.example .env
# Edit .env with your database credentials
- Configure your databases in
.env
:
# PostgreSQL - Required for structured data
PG_CONNECTION=postgresql://username:password@host:port/database
DATABASE_URL=postgresql://username:password@host:port/database
# Neo4j - Optional (uses mock data if unavailable)
NEO4J_HTTP_URL=http://neo4j-host:port/db/neo4j/tx/commit
# MDSL Toolchain - Optional
MDSL_PROJECT_PATH=/path/to/ANMI-ML/mdsl-rs
- Start development server:
npm run dev
The application will be available at http://localhost:5173
.
PostgreSQL: Used for structured media outlet data
- Stores media outlet metadata, relationships, and temporal data
- Schema:
graphv3
(production data),mdsl_generated
(generated from MDSL)
Neo4j: Used for graph visualization
- Connection Method: HTTP transactional endpoint (
/db/neo4j/tx/commit
) - Fallback Behavior: If Neo4j is not accessible, the application uses sample data representing Austrian media outlets (ORF network)
The main query executed visualizes media outlets and their relationships, specifically focusing on networks connected to ORF (Austrian Broadcasting Corporation).
MATCH path = (n)-[r*0..5]-(orf)
WHERE (n:media_outlet OR n:mdsl_media_outlet)
AND (orf:media_outlet OR orf:mdsl_media_outlet)
AND orf.mo_title =~ '(?i)orf.*'
AND ALL(rel in r WHERE
type(rel) IN ['succession', 'amalgamation', 'new_distribution_area', 'new_sector',
'interruption', 'split_off', 'merger', 'offshoot', 'main_media_outlet', 'umbrella',
'collaboration',
'mdsl_succession', 'mdsl_amalgamation', 'mdsl_new_distribution_area',
'mdsl_new_sector', 'mdsl_interruption', 'mdsl_split_off', 'mdsl_merger',
'mdsl_offshoot', 'mdsl_main_media_outlet', 'mdsl_umbrella', 'mdsl_collaboration']
)
RETURN path
Once you've installed dependencies with npm install
, start a development server:
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
The application will be available at http://localhost:5173
. Navigate to the /graph
page to see the Neo4j visualization.
To create a production version of your app:
npm run build
You can preview the production build with npm run preview
.
To deploy your app, you may need to install an adapter for your target environment.
- Temporal filtering and missing dates
- When a year or range filter is applied, nodes without any temporal metadata (
start_date
andend_date
both null) were previously included by the Neo4j query, causing obviously wrong results (e.g., TV subchannels appearing in 1800–1820). - Mitigation implemented in code:
- API queries now exclude nodes with no temporal metadata if any year filter is active.
- The time-layers builder (
getTemporalLayers
) skips nodes whose inferred active window is outside the requested range.
- Heuristic default (temporary): if temporal fields are missing, we infer a conservative start year based on sector:
- TV-like sector
id_sector === '30'
: assume start >= 1900. - Other sectors: assume start >= 1850.
- These defaults only affect visualization; they do not modify source data.
- TV-like sector
- When a year or range filter is applied, nodes without any temporal metadata (
npm run dev # Start development server (usually port 5175)
npm run build # Build production version
npm run preview # Preview production build
npm run prepare # Sync SvelteKit configuration
npm run lint # Run Prettier and ESLint checks
npm run format # Format code with Prettier
npm run check # Run Svelte type checking
npm run check:watch # Run type checking in watch mode
npm run test # Run both unit and E2E tests
npm run test:unit # Run Vitest unit tests
npm run test:e2e # Run Playwright E2E tests
npm run db:push # Push schema changes to database
npm run db:migrate # Run database migrations
npm run db:studio # Open Drizzle Studio for database management
npm run storybook # Start Storybook dev server on port 6006
npm run build-storybook # Build Storybook for production
- Frontend: SvelteKit 2.x with Svelte 5, TailwindCSS 4.0
- Visualization: D3.js for 2D graphs, Three.js for 3D time layers
- Backend: SvelteKit server-side with Drizzle ORM
- Databases: Neo4j (graph data), PostgreSQL (structured data)
- Testing: Vitest (unit tests), Playwright (E2E tests)
- Internationalization: Paraglide for multi-language support (English/Spanish)
/
- Dashboard showing statistics frommo_constant
table (media outlets, locations, languages, sectors)/graph
- Main Neo4j graph visualization with 3D time layers mode/table
- Tabular data view displaying live data fromgraphv3.mo_constant
table/connections
- Database connection status monitoring for PostgreSQL and Neo4j/data-sources
- Available data sources, schemas, and query examples/mdsl
- MDSL toolchain interface for SQL/Cypher generation and validation
PostgreSQL Schema (graphv3.mo_constant
):
- Scope: 657 Austrian media outlets spanning 1780-present
- Structure: 15 columns including
id_mo
,mo_title
,location
,language
,id_sector
, temporal data - Notable: Wiener Zeitung (1780-1940, Austria's oldest publication)
Neo4j Graph Focus:
- Primary Query: Media outlets connected to ORF (Austrian Broadcasting Corporation)
- Relationship Depth: Up to 5 hops from ORF nodes
- Relationship Types: 19 total, categorized as:
- Diachronic (temporal/directed): succession, amalgamation, merger, interruption, split
- Synchronic (concurrent/undirected): main_media_outlet, umbrella, collaboration, partnership
- Temporal Handling: String-based date comparisons to handle mixed data types
3D Time Layers:
- Interactive Three.js visualization with temporal navigation
- Light blue color palette with relationship type styling
- Solid lines for diachronic relationships, dotted for synchronic
- Debounced timeline controls (800ms) for smooth performance
TV Mode:
- Specialized broadcast network visualization
- Video frame integration and era-based navigation
- Guided exploration of television outlet relationships
Newspaper Archive:
- ANNO/IIIF viewer integration for historical newspaper browsing
- Complete digitized newspaper collection access
The codebase follows these conventions:
- Code Style: Prettier formatting, ESLint linting
- Components: Svelte 5 with reactive state management
- API: RESTful endpoints under
/api/
routes - Database: Drizzle ORM with type-safe queries
- Testing: Vitest for units, Playwright for E2E
The application is built for SvelteKit deployment:
npm run build
npm run preview # Test production build locally
For production deployment, you may need to install an appropriate SvelteKit adapter for your target environment.