A semi-real-time dashboard for monitoring GTFS and GTFS-RT feeds. Built with Astro and powered by the MobilityData Mobility Database API.
- Single-page dashboard with agency cards
- Modal pop-ups for detailed feed information
- Support for both GTFS Schedule and GTFS Realtime feeds
- Download options for both Mobility Database-hosted and agency-direct feeds
- Semi-real-time status monitoring
- Responsive design using Pico CSS
- Node.js 20 or higher
- A Mobility Database account with API credentials
If you received this as a directory, navigate to it. Otherwise, extract the files.
https://github.com/jasonad123/gtfs-observatory.git
cd gtfs-observatorypnpm installCopy the example environment file:
cp .env.example .envEdit .env and add your Mobility Database API refresh token:
MOBILITY_API_KEY=your_refresh_token_hereTo get your refresh token:
- Visit https://mobilitydatabase.org
- Create a free account
- Navigate to Account Details
- Copy your refresh token
pnpm devThe dashboard will be available at http://localhost:4321
dc-gtfs-dashboard/
├── src/
│ └── assets/ # Assets - these will be transformed by Astro at build time
│ │ ├── agency-logos # Load all agency logos here
│ ├── components/ # Reusable Astro components
│ │ ├── AgencyCard.astro
│ │ └── AgencyModal.astro
│ ├── layouts/ # Page layouts
│ │ └── DashboardLayout.astro
│ ├── lib/ # Business logic and utilities
│ │ ├── agencies.ts # transit agency definitions and feed processing
│ │ ├── mobilitydata.ts # MobilityData API client
│ │ └── types.ts # TypeScript type definitions
│ ├── pages/ # Routes
│ │ └── index.astro # Main dashboard page
│ │ └── faq.astro # FAQ page
│ ├── scripts/ # Client-side scripts
│ │ └── modal.ts # Modal interaction logic
│ └── styles/ # Global styles
│ └── global.css
├── public/ # Static assets
│ └── icons/ # Various icons
├── data/ # Data storage (optional)
└── package.json
Regions are defined in src/lib/agencies.ts.
Subdivisions represent states/provinces/regions, while countryCodes are two-letter ISO 3166-1 alpha-2 codes.
// Region filtering configuration
// To customize for your region:
// 1. Update subdivisions to match your state/province names as they appear in Mobility Database
// 2. Update countryCodes to your country (e.g., ['CA'] for Canada, ['US', 'CA'] for both)
// 3. Note: Feeds are matched if EITHER location OR provider matches (see isInRegion function)
export const REGION_CONFIG = {
subdivisions: ['District of Columbia', 'Virginia', 'Maryland', 'British Columbia'] as string[],
countryCodes: ['US', 'CA'] as string[]
};Agencies are defined in src/lib/agencies.ts. Each agency includes:
- ID and display name
- Website URL
- Provider name mappings (used as fallback when feed IDs not available)
- (optional) Colours and logo for branding purposes
Feed Matching:
- Primary: Match by provider name(s) - this enables automatic discovery of new feeds
- Fallback: Match by MobilityData feed ID (most reliable)
To add a new agency:
{
id: 'agency-slug',
name: 'Agency Display Name',
slug: 'agency-slug',
website: 'https://agency-website.com',
logo: 'logo.svg', // Optional - logos *must* be placed in src/assets/agency-logos
showName: false, // Optional - if using a logo 'true' keeps the text name, 'false' hides text and just shows the logo
color: '#1c335f', // Optional
secondaryColor: '#308c26', // Optional
textColor: '#ffffff', // Optional
gtfsFeedIds: ['mdb-123'], // Optional but recommended
gtfsRtFeedIds: ['mdb-1234'], // Optional but recommended
providers: ['Official Provider Name in MobilityData'] // Fallback
}Finding Feed IDs:
- Visit https://mobilitydatabase.org
- Search for your agency
- Note the feed ID(s) from the feed details or URL (format: mdb-xxx or tld-xxx)
- For example - TransLink Vancouver's GTFS Schedule feed has a feed URL of
https://mobilitydatabase.org/feeds/gtfs/mdb-696which gives feed ID ofmdb-696
- For example - TransLink Vancouver's GTFS Schedule feed has a feed URL of
- Add to the
gtfsFeedIdsorgtfsRtFeedIdsarray in agency configuration
The dashboard uses Pico CSS for minimal, semantic styling. To customize:
- Override Pico CSS variables in
src/styles/global.css - Modify component styles in individual
.astrofiles - Add custom CSS classes as needed
- At build time, the dashboard fetches all feeds from the MobilityData API
- Feeds are filtered to the region
- Feeds are grouped by transit agency
- The page is statically generated with all feed information
- Modal interactions are handled client-side with vanilla JavaScript
- Fetches all feeds from Mobility Database based on the country code
- Filters by region
- Matches feeds to known regional agencies
- Processes feed metadata for display
- Groups feeds by agency with status indicators
pnpm buildThe static site will be generated in the dist/ directory.
This is a static site and can be deployed to:
- Cloudflare Pages
- Cloudflare Workers
- Netlify
- Vercel
- GitHub Pages
- Any static hosting service
- Connect your repository to Cloudflare Pages
- Use the
Astroframework preset or configure it manually:- Set build command:
pnpm run build - Set output directory:
dist
- Set build command:
- Add environment variable:
MOBILITY_API_KEY - Configure automatic rebuilds (recommended: every 6-24 hours) using Deploy Hooks
To keep feed data current, set up scheduled rebuilds:
- Cloudflare Pages: Use Deploy Hooks
- Cloudflare Workers: Use Trigger Events
- Netlify: Use Build Hooks with external cron service
- GitHub Actions: Use scheduled workflows
Example frequency: Every 6 hours during business hours
See DEPLOYMENT.md for more information.
This project uses the MobilityData Mobility Database API:
- Website: https://mobilitydatabase.org
- Docs: https://github.com/MobilityData/mobility-feed-api
- Catalogs repo: https://github.com/MobilityData/mobility-database-catalogs
API Rate Limits:
- Non-cached requests: 100/hour per user
- Cached/repeated requests: More lenient
Check:
- Environment variable is set correctly in
.env - Mobility Database API is accessible
- Your refresh token is valid (check account settings)
- Network connectivity
Possible causes:
- Agency provider names don't match Mobility Database records
- Feed locations aren't properly tagged in the database
- All feeds are marked as inactive
Check browser console for JavaScript errors. Ensure:
- Modal script is properly imported
- Agency IDs match between cards and modals
- Dialog element is supported (modern browsers)
pnpm run astro checkpnpm build
pnpm previewThis project is open source. Individual GTFS feeds are subject to their respective data providers' terms and conditions.
Contributions welcome! Potential TODOs include:
- Real-time feed health monitoring
- MobilityData for maintaining the Mobility Database
- Transit agencies for providing open data
- Pico CSS for minimal styling framework
- Siemens for providing icons from the iX design system
Note
Generative AI: The code for this project was developed with the help of generative AI tools, including Claude and Claude Code. While all outputs have been lovingly reviewed and tested, users should validate results independently before use in production environments.