The Sky Harbor Ride-Hailing Analytics Dashboard is a full-stack web application designed to provide real-time operational intelligence for airport ride-hailing parking management. The system transforms raw parking occupancy data into actionable insights through an interactive, animated visualization dashboard that enables airport executives and operations staff to optimize parking efficiency, reduce passenger wait times, and make data-driven infrastructure decisions.
Key Achievement: Successfully migrated from a static Dash application to a modern, scalable full-stack architecture with real-time animation, comprehensive analytics, and executive-grade visualizations.
Sky Harbor Airport needed a solution to:
- Monitor real-time parking slot occupancy across 24 designated ride-hailing parking spaces
- Analyze historical patterns to optimize slot allocation and capacity planning
- Track service provider distribution (Uber, Lyft) to understand market dynamics
- Measure vehicle dwell times to improve turnover rates and reduce passenger wait times
- Provide executive-level insights for strategic decision-making
The original system was built using Dash (Python), which presented limitations:
- Tightly coupled UI and data logic
- Limited interactivity and animation capabilities
- Difficult to scale or extend
- Poor separation of concerns
- Real-time visualization of vehicle movements and slot occupancy
- Comprehensive analytics with multiple chart types and KPIs
- Responsive, modern UI suitable for executive presentations
- Scalable architecture with clear separation between frontend and backend
- Performance optimization for smooth animations and data loading
Backend:
- FastAPI - Modern, high-performance Python web framework
- Pandas - Data processing and analytics
- NumPy - Numerical computations
- Uvicorn - ASGI server for FastAPI
Frontend:
- React 18 - Component-based UI library
- Vite - Fast build tool and dev server
- Axios - HTTP client for API communication
- Recharts - Charting library for data visualization
- CSS-in-JS - Inline styling for component-based design
Data:
- Excel file (
ride_hailing.xlsx) - Source data with timestamps, slot positions, vehicle information - Static assets - Map images, car sprites, license plate images
Separation of Concerns:
- Backend (
backend/app.py): Pure data processing and API endpoints - Frontend (
frontend/src/): UI components and visualization logic - State Management: React Context API for shared application state
- API Communication: RESTful JSON endpoints
Feature Description: An interactive, animated visualization showing vehicle movements across 24 parking slots in real-time.
Technical Implementation:
- MapCanvas Component: Renders a 1551×1171px parking map with exact coordinate positioning
- Car Sprite System: 6 different vehicle types (Audi, Car, Mini_truck, Mini_van, Taxi, Truck) with consistent assignment per license plate
- Smooth Transitions: CSS transitions (0.45s ease-out) for car movements between frames
- Slot ID Mapping: Custom visual numbering system (1-24) that matches natural reading pattern
- Rotation Logic: Cars rotate 90° or 270° based on slot position to match parking layout
Key Capabilities:
- Frame-by-frame animation with play/pause controls
- Variable playback speed (0.5x, 1x, 2x)
- Timeline scrubbing for precise frame navigation
- Real-time slot status overlay (occupied/vacant counts)
- Hover tooltips showing vehicle details (plate, service, slot ID, dwell time)
Data Flow:
- Backend precomputes slot positions at startup
- Frontend requests frame data via
/frame/{ts}endpoint - Each frame returns all 24 slots with occupancy state
- React components render cars at exact x,y coordinates
- Animation engine advances frames using
requestAnimationFrame
Feature Description: Four key performance indicators that update dynamically based on the selected timestamp.
KPIs Displayed:
- Avg Dwell Time - Average time vehicles spend in parking slots
- Max Dwell Time - Maximum observed dwell time
- Active Reservations - Number of active ride reservations
- Avg Reservation Duration - Average length of reservations
Smart Caching System:
- Peak Value Tracking: System caches maximum values seen across all timestamps
- Intelligent Fallback: When active reservations = 0, displays peak historical values instead of "N/A"
- Data Persistence: Values persist across frame changes for continuous visibility
- Sky Harbor Branding: Color-coded cards (orange, royal blue, white) matching airport brand
Technical Details:
- Uses
useReffor peak value caching useMemofor efficient value computation- Automatic fallback to summary statistics when timestamp-specific data unavailable
Service Share Over Time (Stacked Area Chart)
- Visualizes service provider distribution (Uber, Lyft) across all timestamps
- Stacked area format shows total occupancy and service mix simultaneously
- Updates dynamically with animation timeline
Top 20 Slot Utilization (Vertical Bar Chart)
- Displays most frequently used parking slots
- Sorted by usage count in descending order
- Helps identify high-traffic areas for maintenance and optimization
Vehicle Dwell Time Distribution (Histogram)
- Shows distribution of vehicle dwell times across 30 bins
- Displays average and maximum dwell times
- Helps identify patterns in parking duration
Data Sources:
/service_mix- Service distribution over time/utilization- Slot usage statistics/dwell_time- Dwell time statistics and distribution
Feature Description: Real-time table displaying all vehicles currently present at the selected timestamp.
Information Displayed:
- Vehicle Sprite - Visual representation of car type
- License Plate - Vehicle identification
- Service Provider - Uber, Lyft, or Unknown (color-coded badges)
- Entry Time - When vehicle first entered parking area
- Time Here - Current dwell time in minutes
Technical Implementation:
- Combines
frameData(for positions) with API data (for entry times) - Uses consistent car sprite assignment (hash-based)
- Real-time updates as animation plays
- Empty state handling when no vehicles present
Data Flow:
VehicleTablecomponent extracts occupied vehicles fromframeData- Fetches additional details from
/vehicles_at_timestamp/{ts} - Merges data sources for complete vehicle information
- Displays in sortable, scrollable table format
Feature Description: Real-time counter displayed in top-left corner of animation showing current parking status.
Metrics Displayed:
- Occupied Slots - Green indicator with count
- Vacant Slots - Yellow indicator with count
- Total Slots - Always 24
- Current Timestamp - Formatted time display
Smart Caching:
- Multi-tier Fallback System:
- Current API stats (if available)
- Cached stats from last successful load
- Computed from frame data (counts occupied slots directly)
- Only shows nothing if absolutely no data available
Benefits:
- Never shows "N/A" unnecessarily
- Always displays last known values during loading
- Provides immediate visual feedback on parking status
Startup Optimization: The backend precomputes several data structures at startup to minimize runtime processing:
- Sorted Timestamps - All unique timestamps in chronological order
- Slot Positions - Canonical x,y coordinates for each of 24 slots
- Slot Utilization - Usage count per slot across entire dataset
- Service Mix - Service provider distribution by timestamp
Performance Impact:
- Reduces API response time from ~500ms to ~50ms
- Eliminates redundant calculations
- Enables smooth 60fps animation playback
Core Endpoints:
-
GET /timestamps- Returns all available timestamps as ISO-8601 strings
- Used for timeline initialization
-
GET /frame/{ts}- Returns complete slot state for a given timestamp
- Includes all 24 slots with positions, occupancy, plate, service
- Core endpoint for animation rendering
-
GET /stats/{ts}- Basic occupancy statistics (total, occupied, vacant)
- Fast, lightweight endpoint for status overlay
-
GET /stats_timestamp/{ts}- Comprehensive timestamp-specific statistics
- Includes dwell times, reservations, vehicle counts
- Used for dynamic KPI cards
-
GET /utilization- Slot utilization across entire dataset
- Sorted by usage count
- Powers utilization chart
-
GET /service_mix- Service provider distribution over time
- Powers service mix chart
-
GET /dwell_time- Dwell time statistics and distribution
- Powers dwell time histogram
-
GET /summary- Overall dataset summary
- Peak occupancy, total vehicles, average metrics
- Fallback data for KPIs
-
GET /vehicles_at_timestamp/{ts}- Detailed vehicle information at specific timestamp
- Entry times, current dwell times, positions
- Powers active vehicles table
-
GET /occupancy_timeline- Occupancy count per timestamp
- Historical trend data
Dwell Time Calculation:
# For each vehicle, calculate time between first and last appearance
first_seen = plate_data["current_time"].min()
last_seen = plate_data["current_time"].max()
dwell_minutes = (last_seen - first_seen).total_seconds() / 60Reservation Duration:
# For each reservation, calculate duration
start_time = res_data["current_time"].min()
end_time = res_data["current_time"].max()
duration_minutes = (end_time - start_time).total_seconds() / 60Occupancy Counting:
# Count unique occupied slots (not rows)
occupied = df_t["slot_id"][df_t["plate_number"].notna()].nunique()React Context Provider Pattern:
RideHailingProvider- Wraps entire application, provides shared stateuseRideHailingStore- Custom hook for accessing shared state- Single Source of Truth - All components share same state instance
State Structure:
{
// Data
timestamps: [],
selectedTimestamp: null,
frameData: [],
stats: {},
timestampStats: {},
summary: {},
utilization: [],
serviceMix: {},
dwellTime: {},
// Loading flags
loadingTimestamps: false,
loadingFrame: false,
loadingStats: false,
// ... etc
// Methods
loadTimestamps: () => {},
loadFrame: (ts) => {},
loadStats: (ts) => {},
// ... etc
}Benefits:
- Eliminates prop drilling
- Ensures data consistency across components
- Enables efficient data sharing (e.g.,
frameDataused by bothMapCanvasandVehicleTable)
Main Components:
App.jsx- Root component, loads initial data, provides layout structureAnalyticsSidebar.jsx- Main dashboard container with Bento grid layoutMapCanvas.jsx- Animation rendering engineVehicleTable.jsx- Active vehicles displayKpiCard.jsx- Reusable KPI display componentCarSprite.jsx- Individual vehicle renderingTimelineControls.jsx- Playback controls (play/pause, speed, scrubbing)- Chart Components -
ServiceMixChart,UtilizationChart,DwellHistogram
Component Hierarchy:
App
└── AnalyticsSidebar
├── KPI Cards (4 cards)
├── MapCanvas
│ ├── TimelineControls
│ ├── Map Image
│ ├── Slot Labels
│ ├── CarSprites
│ └── Stats Overlay
└── Charts Section
├── ServiceMixChart
├── UtilizationChart
├── DwellHistogram
└── VehicleTable
Advanced Animation System:
useAnimationEngineHook - Custom hook managing animation looprequestAnimationFrame- Browser-native animation API for smooth 60fps- Frame Interpolation - Smooth transitions between frames
- Playback Controls - Play, pause, speed adjustment, frame jumping
Performance Optimizations:
- Request deduplication prevents duplicate API calls
- Frame caching reduces redundant data loading
- CSS transitions for hardware-accelerated animations
useMemoanduseCallbackfor expensive computations
Sky Harbor Branding:
- Color Scheme: Orange (#FF6600), Royal Blue (#003366), White
- Typography: System fonts for optimal performance
- Layout: Bento grid pattern for organized information display
- Cards: Rounded corners, subtle shadows, hover effects
Responsive Design:
- Fixed-width main container (2000px max)
- Scalable map visualization
- Scrollable sections for overflow content
- Mobile-friendly touch interactions
-
Backend Startup:
- Loads Excel file into Pandas DataFrame
- Precomputes timestamps, slot positions, utilization, service mix
- Starts FastAPI server on port 8000
-
Frontend Startup:
- React app initializes
RideHailingProvidercreates shared stateAppcomponent loads summary, occupancy timeline, dwell time, utilization, service mix
-
MapCanvas Initialization:
- Loads timestamps from
/timestamps - Loads first frame from
/frame/{first_timestamp} - Creates slot mapping for visual numbering
- Initializes animation engine
- Loads timestamps from
-
Frame Request:
- Animation engine advances to next frame index
- Calls
loadFrame(timestamps[index]) - Backend returns slot states for that timestamp
-
State Update:
frameDataupdated in shared stateMapCanvasreceives new frame datadisplayFrameDataupdated viarequestAnimationFrame
-
Rendering:
- Cars transition to new positions (CSS transitions)
- Slot labels update (green for occupied, yellow for vacant)
- Stats overlay updates with new counts
- Vehicle table updates with current vehicles
-
Stats Loading:
selectedTimestampchanges triggerloadStats(ts)- Backend calculates timestamp-specific statistics
- KPI cards update with new values
- Peak values cached if higher than previous
Timeline Scrubbing:
- User drags slider to specific frame
jumpToFrame(index)called- Frame data loaded for that timestamp
- All components update synchronously
Hover Interactions:
- User hovers over car sprite
CarTooltipdisplays vehicle information- Calculates dwell time from timestamps
- Shows plate, service, slot ID, dwell time
Speed Adjustment:
- User selects playback speed (0.5x, 1x, 2x)
- Animation interval adjusted
- Frames advance at new rate
- Precomputation: All static data computed at startup
- Efficient Queries: Pandas operations optimized with vectorization
- Minimal Data Transfer: Only necessary fields returned in API responses
- Caching: Repeated requests for same timestamp return cached results
- Request Deduplication: Prevents multiple simultaneous requests for same frame
- Memoization:
useMemofor expensive computations (slot mapping, display stats) - Lazy Loading: Components load data only when needed
- CSS Transitions: Hardware-accelerated animations
- Virtual Rendering: Only visible elements rendered (for large lists)
- Context Provider: Single state instance shared across all components
- Selective Updates: Only affected components re-render on state changes
- Ref-based Caching: Peak values and slot mappings stored in refs (don't trigger re-renders)
- Loading States: Prevents duplicate requests during active loads
Problem: Backend slot IDs don't match visual layout pattern Solution: Custom mapping utility that remaps backend IDs to intuitive 1-24 display pattern
Implementation:
- Groups slots by approximate Y position (rows)
- Sorts rows top to bottom
- Sorts within rows left to right
- Maps to target pattern: 1-6, 13-18, 7-12, 19-24
Benefits:
- Natural reading pattern for users
- Consistent numbering across all views
- Maintains data integrity (backend IDs unchanged)
Problem: When parking lot is empty, metrics show "N/A" Solution: Track maximum values seen, display peaks when current = 0
Implementation:
useRefstores peak values (avg_dwell_time, max_dwell_time, avg_reservation_duration)- Updates peak whenever new higher value encountered
- Falls back to peak when
unique_reservations = 0
Benefits:
- Always shows meaningful data
- Provides historical context
- Improves user experience
Problem: Stats overlay disappears when API data unavailable Solution: Three-tier fallback system
Tiers:
- Current API stats (preferred)
- Cached stats from last successful load
- Computed from frame data (direct slot counting)
Benefits:
- Never shows "N/A" unnecessarily
- Always displays last known values
- Provides immediate feedback
Problem: Janky frame transitions, poor performance
Solution: requestAnimationFrame-based animation loop
Features:
- 60fps target frame rate
- Smooth frame interpolation
- Variable playback speed
- Frame scrubbing support
- Play/pause controls
- Slider Control: Drag to any frame in timeline
- Play/Pause Button: Start/stop animation
- Speed Control: Adjust playback speed (0.5x, 1x, 2x)
- Timestamp Display: Shows current time in HH:MM:SS format
- Frame Counter: Displays current frame / total frames
- Color-Coded Slots: Green (occupied), Yellow (vacant)
- Service Badges: Color-coded service provider indicators (Uber: blue, Lyft: pink)
- Hover Tooltips: Detailed vehicle information on car hover
- Loading States: Visual indicators during data loading
- Empty States: Helpful messages when no data available
- Bento Grid Layout: Organized, visually appealing information architecture
- KPI Cards: Prominent display of key metrics
- Chart Integration: Multiple chart types in unified layout
- Responsive Design: Adapts to different screen sizes
- Professional Styling: Sky Harbor brand colors and typography
Peak Occupancy Analysis:
- Identifies maximum simultaneous vehicle count
- Pinpoints peak timestamp for capacity planning
- Helps determine optimal slot allocation
Dwell Time Analysis:
- Average dwell time indicates turnover efficiency
- Maximum dwell time identifies outliers
- Distribution histogram reveals patterns
Service Provider Distribution:
- Market share analysis (Uber vs Lyft)
- Temporal patterns in service usage
- Helps with partnership negotiations
High-Usage Slots:
- Identifies most frequently used parking spaces
- Helps prioritize maintenance and improvements
- Reveals traffic patterns
Low-Usage Slots:
- Identifies underutilized spaces
- Opportunities for optimization
- Potential for reallocation
Active Reservations:
- Real-time count of active ride bookings
- Helps with staffing decisions
- Indicates demand levels
Reservation Duration:
- Average booking length
- Helps predict parking needs
- Supports capacity planning
Problem: Multiple components need same data (frameData, stats, etc.) Solution: React Context Provider pattern with shared state Result: Single source of truth, efficient data sharing
Problem: Frame transitions were janky, poor performance
Solution: requestAnimationFrame loop with CSS transitions
Result: Smooth 60fps animation, hardware-accelerated
Problem: Multiple components requesting same frame simultaneously Solution: Request deduplication with refs and loading flags Result: Eliminated duplicate requests, improved performance
Problem: Metrics show "N/A" when no vehicles present Solution: Peak value caching with intelligent fallback Result: Always shows meaningful data, improved UX
Problem: Backend slot IDs don't match visual layout Solution: Custom slot mapping utility with visual position sorting Result: Intuitive numbering, natural reading pattern
- Real-Time Data Integration: Connect to live parking sensors
- Predictive Analytics: Machine learning for occupancy forecasting
- Alert System: Notifications for capacity thresholds
- Export Functionality: PDF/Excel reports generation
- Historical Comparisons: Compare different time periods
- Mobile App: Native mobile application
- Multi-Location Support: Extend to multiple parking areas
- Advanced Filtering: Filter by service, time range, etc.
The Sky Harbor Ride-Hailing Analytics Dashboard successfully transforms raw parking data into actionable insights through a modern, interactive web application. The system provides:
- Real-time visualization of vehicle movements and occupancy
- Comprehensive analytics with multiple chart types and KPIs
- Executive-grade dashboard suitable for strategic decision-making
- Scalable architecture ready for future enhancements
- Performance-optimized for smooth user experience
Key Achievements:
- Migrated from monolithic Dash app to modern full-stack architecture
- Implemented smooth 60fps animation with 60+ frames
- Created intelligent caching systems for optimal UX
- Delivered comprehensive analytics suite
- Achieved professional, executive-ready design
The dashboard enables Sky Harbor Airport to optimize parking operations, reduce passenger wait times, and make data-driven infrastructure decisions, ultimately improving overall airport operations and customer satisfaction.
Backend:
- Python 3.x
- FastAPI 0.104+
- Pandas 2.0+
- NumPy 1.24+
- Uvicorn
Frontend:
- React 18
- Vite 5.0+
- Axios 1.6+
- Recharts 2.10+
Data:
- Excel file format (.xlsx)
- 60+ timestamps
- 24 parking slots
- Multiple vehicle types
- Service provider data (Uber, Lyft)
Performance:
- API response time: ~50ms average
- Animation frame rate: 60fps target
- Initial load time: <2 seconds
- Smooth transitions: 0.45s CSS transitions
Case Study Document - Sky Harbor Ride-Hailing Analytics Dashboard Sources: https://unluckystudio.com/ , for free car sprite assets.