Skip to content

Latest commit

 

History

History
260 lines (196 loc) · 8.1 KB

File metadata and controls

260 lines (196 loc) · 8.1 KB

Energy Toolkit: Demand - Explorer

Web application for visualizing energy forecast data generated by the Generator and served by the API.

Overview

Explorer is a modular visual framework built with SvelteKit that provides interactive visualizations for energy demand forecasts. It features:

  • Interactive Charts: Time series, histograms, segment breakdowns, and geographic comparisons
  • Dynamic Maps: Mapbox GL JS integration for geographic visualization
  • Scenario Comparison: Compare multiple forecast scenarios side-by-side
  • Responsive Design: Works seamlessly on mobile, tablet, and desktop
  • Export Functionality: Export charts as PNG, SVG, or data as CSV/JSON
  • Chart Parameter Control: Per-chart parameter overrides with visual indicators

Technology Stack

  • SvelteKit - Full-stack web framework
  • Svelte 5 - UI framework with modern runes syntax
  • TypeScript - Type safety throughout
  • Tailwind CSS - Utility-first CSS with container queries
  • LayerChart - Primary charting library
  • Mapbox GL JS - Interactive maps
  • Lucide Svelte - Icon library

Quick Start

Prerequisites

  1. Start the API server (from project root):
cd api
npm start  # Runs on port 4010
  1. Ensure data has been generated:
cd api
node generate-api.js --defaults

Development

Start the development server:

npm install
npm run dev

The application will be available at http://localhost:5173.

Building

Create a production build:

npm run build
npm run preview  # Preview production build

Application Structure

/explorer/src/
├── routes/
│   ├── +page.svelte           # Main introductory report page
│   ├── +page.ts               # Data loader
│   ├── charts/                # Chart library gallery
│   │   ├── +page.svelte       # All charts with parameter controls
│   │   └── +page.ts           # Chart data loader
│   └── +layout.svelte         # Root layout with navigation
├── lib/
│   ├── components/
│   │   ├── AreaChart.svelte        # Yearly time series
│   │   ├── TimeLine.svelte         # Daily/weekly timeline
│   │   ├── Histogram.svelte        # Distribution visualization
│   │   ├── SegmentBars.svelte        # Segment breakdown
│   │   ├── GeoBarChart.svelte      # Geographic comparison
│   │   ├── map/
│   │   │   └── Map.svelte          # Interactive map
│   │   ├── shared/
│   │   │   ├── ChartContainer.svelte    # Chart wrapper with export
│   │   │   ├── LazyChart.svelte         # Intersection observer loading
│   │   │   ├── LoadingSkeleton.svelte   # Loading states
│   │   │   ├── ErrorState.svelte        # Error displays
│   │   │   ├── EmptyState.svelte        # Empty data states
│   │   │   └── ScenarioLegend.svelte    # Scenario comparison legend
│   │   └── controls/
│   │       ├── ChartParameterPill.svelte  # Per-chart parameter control
│   │       ├── YearSelector.svelte
│   │       ├── GeographySelector.svelte
│   │       ├── SegmentSelector.svelte
│   │       ├── ResolutionSelector.svelte
│   │       └── AggregationSelector.svelte
│   ├── dataService.ts         # API communication layer
│   ├── utilities.ts           # Helper functions
│   ├── comparisonUtils.ts     # Scenario comparison utilities
│   ├── chartConfig.ts         # Chart configuration
│   ├── exportUtils.ts         # Chart export functionality
│   └── stores/
│       ├── scenario.svelte.ts          # Global scenario state
│       └── chartParameters.svelte.ts   # Per-chart parameter overrides
└── types/
    ├── api.ts                 # API response types
    ├── controls.ts            # Control component types
    └── ChartComponent.interface.ts  # Chart component interfaces

Key Features

Chart Library Page

The /charts route displays all available visualizations with:

  • Responsive grid layout: 2-column on desktop, 1-column on mobile
  • Individual parameter controls: Each chart can override global parameters
  • Visual indicators: Blue pill badges show when chart has custom parameters
  • Export functionality: Download charts as PNG/SVG or data as CSV/JSON

Parameter Control System

Charts use a hierarchical parameter system:

  1. Global parameters: Default values for all charts (geography, year, segment, etc.)
  2. Chart-specific overrides: Individual charts can override any parameter
  3. Visual feedback: Parameter pills show blue badge with override count
  4. Isolated updates: Changing one chart's parameters doesn't affect others

Scenario Comparison

Compare multiple forecast scenarios:

  • Side-by-side visualization: Overlaid charts with color-coded scenarios
  • Interactive legend: Hover/click to highlight specific scenarios
  • Consistent colors: Scenarios maintain colors across all charts
  • Metadata display: Show scenario parameters and statistics

Component Standardization

All chart components follow consistent patterns:

  • Hybrid data loading: Accept data as props OR fetch internally
  • Shared UI components: LoadingSkeleton, ErrorState, EmptyState
  • TypeScript interfaces: Standardized props via ChartComponent.interface.ts
  • Container queries: Responsive to container width, not viewport
  • Export support: All charts provide data for export

API Integration

Explorer communicates with the API server via dataService.ts:

import {
	fetchDemandData,
	makeDemandQuery,
	fetchGeographies,
	fetchScenarios
} from '$lib/dataService';

// Fetch time series data
const query = makeDemandQuery({
	start: '2025-01-01',
	end: '2026-01-01',
	resolution: '1d',
	aggregation: 'sum',
	geography: 'SE01',
	segment: 'housing',
	scenarioId: 'housing_electrification=2,...'
});
const data = await fetchDemandData(query);

// Fetch metadata
const geographies = await fetchGeographies('json');
const scenarios = await fetchScenarios();

Development Guidelines

Svelte 5 Runes

Always use modern Svelte 5 syntax:

  • $state() for reactive state
  • $derived() for computed values
  • $effect() for side effects
  • $props() for component props
  • Snippets for component composition

Component Data Pattern

let { data = [], geography, year, scenario }: TimeSeriesChartProps = $props();

let loading = $state(false);
let error = $state<string | null>(null);

// Fetch data when parameters change
$effect(() => {
	if (geography && year && scenario) {
		fetchData();
	}
});

Responsive Design

  • Use viewport breakpoints for layout: lg:grid-cols-2
  • Use container queries for components: @lg:text-lg
  • Mobile-first approach with progressive enhancement
  • Test at all breakpoints: mobile (320px), tablet (768px), desktop (1024px+)

Testing

npm run check       # TypeScript type checking
npm run lint        # ESLint
npm run format      # Prettier
npm test            # Unit tests (when added)
npm run test:e2e    # E2E tests (when added)

Environment Variables

Create .env file:

VITE_API_BASE_URL=http://localhost:4010  # API server URL
PUBLIC_MAPBOX_TOKEN=your_token_here      # Mapbox access token

Troubleshooting

Charts not loading

  • Verify API server is running on port 4010
  • Check browser console for API errors
  • Ensure data has been generated with node api/generate-api.js --defaults

Map not displaying

  • Verify Mapbox token is set in .env
  • Check that geographies.geojson exists in /data/
  • Ensure geography IDs match between data and GeoJSON

Parameters not updating

  • Check that all dependencies are included in $effect() hooks
  • Verify chartParametersStore is updating correctly
  • Check browser console for reactivity issues

Documentation

For detailed architecture and development guidelines, see:

  • /CLAUDE.md - Project overview and patterns
  • /api/README.md - API documentation
  • /api/openapi.yaml - API specification