NTSB AVIATION ACCIDENTS
US AVIATION 1962 – 2025
INITIALIZING...
Software Engineer: Michael J Evan
Multi-Engine Airline Transport Pilot | Certifiec Flight Instructor Instrument Airplane
HELP & INFO
MAP CONTROLS
Click any dot or cluster to open the accident popup
Click the NTSB case number in the popup to open the full investigation docket on the NTSB website
CLUSTER groups nearby accidents -zoom in to separate them. Large datasets may render slowly
HEATMAP shows accident density weighted by injury severity
ALL DOTS plots every individual accident
FILTERS
Single click any filter button to toggle on/off
Double-click to isolate that category -hides all others
Double-click again to restore all in that group
Year range slider filters by accident date
Injury level buttons in the top legend control dot colors on the map
BASEMAP
DARK/STREET switches between dark map (zoomed out) and street map (zoomed in)
SECTIONAL overlays FAA VFR sectional charts -best at zoom 6–12
Sectional view automatically switches to ALL DOTS mode
TIMELINE
Animates accidents year by year from 1962 to 2025
Press to play, to close and return to your previous view
Stats update in real time as the timeline advances
DATA
168,793 US civil aviation accidents · 1962–2025
Source: NTSB Aviation Accident Database
Some older records have approximate city-level coordinates -flagged ⚠ in the popup
Pre-2009 dockets may not be publicly released on the NTSB website
BUILT WITH
Leaflet -interactive map with cluster, heatmap, and individual dot rendering modes
MarkerCluster -groups nearby markers into clickable clusters that split apart on zoom
D3.js -line chart, bar chart, donut chart, sparklines, and US state choropleth
TopoJSON - US state boundary geometry for the choropleth map
PapaParse -parses the 154k-row CSV dataset client-side in the browser
noUiSlider -dual-handle year range slider
All data is loaded from a static CSV file -no server or database required
Basemap tiles served by CARTO (dark/voyager), FAA (sectional), and OpenTopoMap
Software Engineered by: Michael J Evan
NTSB DATA IS PUBLIC DOMAIN
DOCUMENTATION

CIS 568 DATA VISUALIZATION - Final Project

Interactive Visualization of NTSB Aviation Accident Data

Michael Evan - Graduate Computer Science Department
University of Massachusetts Dartmouth

Introduction

Aviation safety data collected by the National Transportation Safety Board (NTSB) spans over six decades of US civil aviation accidents. The database contains detailed records including aircraft information, injury severity, weather conditions, probable cause narratives, and geographic coordinates for each event. While this data is publicly available, exploring it in its raw form offers little insight without the ability to filter, compare, and visualize patterns across multiple dimensions simultaneously. This project builds an interactive, browser-based visualization platform that enables real-time exploration of 168,793 NTSB aviation accident records from 1962 to 2025, combining geographic mapping with analytical charts and multi-dimensional filtering to surface patterns in the data that static reports cannot reveal.

Problem Definition

The NTSB Aviation Accident Database is one of the most comprehensive aviation safety datasets in the world, yet accessing and analyzing it remains difficult for most users. The raw data is delivered as flat CSV files with inconsistent formatting across decades of data entry by different investigators. Many records lack precise coordinates, aircraft field naming conventions changed over the years, and multi-aircraft events are stored as comma-separated values within single fields rather than normalized across rows. Building a visualization tool on this data required not only the technical implementation of interactive maps and charts but also significant data cleaning and enrichment to make the records usable. The challenge was to build a single-page application capable of rendering 168,793 records across multiple visualization modes with real-time filtering, without requiring any server-side infrastructure.

Methodology

The project was built as a single HTML file with all functionality implemented client-side using JavaScript. The technology stack was selected based on the strengths of each library for specific visualization tasks:

All basemap tiles are served from external tile providers. CARTO provides the dark and street map tiles, the FAA provides VFR sectional chart tiles, and OpenTopoMap provides terrain/elevation tiles. The application requires no backend server and runs entirely in the browser, making it suitable for deployment on GitHub Pages as a static site.

Implementation

Data Preprocessing and Enrichment

The raw NTSB dataset required significant cleaning before it could be used effectively in the visualization. The following preprocessing steps were performed offline using Python 3 with Pandas for data manipulation, CSV parsing, and analysis, and Shapely for geographic point-in-polygon computations:

Coordinate Recovery: A substantial number of older accident records lacked precise GPS coordinates. Using airport identifier codes and city/state information from the records, coordinates were recovered by cross-referencing against a database of 32,452 US airports and a city centroid lookup table. Each record's coordinate source was tagged (GPS/survey, airport lookup, city centroid, or state centroid) to maintain transparency about data quality. Records with city-level coordinates were flagged in the application's popup display with a warning indicator.

Coordinate Jitter: Many accidents geocoded to city centroids shared identical coordinates, causing dots to stack on top of each other on the map. A random offset of approximately 1km was applied to city and state centroid records so individual accidents spread apart when zoomed in while still appearing clustered at the city level when zoomed out.

Engine Count Correction: The NumberOfEngines field contained numerous blanks and errors. Gliders and balloons with blank engine counts were set to 0. Helicopters and airplanes with missing counts were identified by Make and Model and assigned the correct engine count. Known single-engine models (Bell 206, Cessna 172, etc.) were set to 1, and known twin-engine models (Sikorsky S-76, Bell 412, Boeing 737, etc.) were set to 2 or 4 as appropriate. Individual records were verified against publicly available aircraft specifications.

Multi-Aircraft Flagging: Multi-aircraft accidents (midair collisions, runway incursions, ground collisions) were identified using two methods. First, records with comma-separated values in the AirCraftCategory field indicated two aircraft were involved in the same event. Second, the ProbableCause text was searched for keywords including "midair collision," "other aircraft," "runway incursion," and similar phrases. Both methods were combined and the results were manually reviewed to remove false positives such as bird strikes, tree collisions, and other single-aircraft events that happened to mention another aircraft in the narrative. A total of 2,124 multi-aircraft accidents were identified and flagged.

County Assignment: Each accident record was assigned to a US county using point-in-polygon geometric testing against 3,220 county boundary polygons. A bounding box pre-filter was applied to reduce the computational cost by eliminating county polygons that could not possibly contain a given point before running the full geometry check. County FIPS codes and names were stored directly in the CSV to eliminate any runtime geometry calculations. The Python Shapely library with prepared geometries provided the spatial analysis.

State Centroid Records: Approximately 14,658 records had coordinates placed at their state's geographic centroid due to insufficient location data in the original NTSB records. These records were merged into the main dataset with all enrichments applied but are identified separately in the application. Their data contributes to all statistical calculations but their map positions are acknowledged as approximate through "unlocated" indicators in the spike map and county drill-down views.

Map Visualization Modes

The application provides four basemap options and three interactive map modes for viewing accident data on the Leaflet map:

Cluster Mode groups nearby accident markers into numbered circles that split apart as the user zooms in. Cluster icons are color-coded by the worst injury severity in the group (red for fatal, orange for serious, yellow for minor, green for none). Individual markers use DOM-based divIcon elements to ensure reliable click interaction for opening accident detail popups. A batched rendering system with a progress indicator prevents the browser from freezing during the creation of 168,793 markers, and mode-switching buttons are disabled during rendering to prevent users from interrupting the process.

Heatmap Mode renders accident density as a continuous color gradient using the Leaflet.heat plugin. Intensity is weighted by injury severity so fatal accidents contribute more visual weight than minor incidents.

All Dots Mode renders every accident as an individual circle marker on an HTML5 canvas element for performance. A batched rendering system with a token-based cancellation mechanism prevents old render batches from contaminating new renders when filters change mid-build. Dot size scales with zoom level for visibility at different scales.

Timeline Mode animates through the dataset year by year, re-rendering the current map mode for each year's data with a playback transport bar showing year, progress, and play/pause controls.

Spike Map and County Drill-Down

A full-screen D3.js spike map provides a state-level overview where vertical spikes rise from each state proportional to accident count. Spike color transitions from blue (low) through cyan (mid) to red (high). Hovering over any state or spike displays a tooltip with accident count, fatal count, fatality rate, total fatalities, and the number of unlocated records for that state.

Clicking a state transitions to a county-level drill-down view. County boundaries are rendered as a choropleth colored by accident density using a threshold scale with six color buckets. Individual accident dots are rendered on an HTML5 canvas element overlaid on the SVG county paths. This separation ensures that the canvas dots do not interfere with SVG hover event detection on the county polygons, providing instant tooltip response regardless of how many dots are rendered. A clip path derived from the merged state boundary prevents dots from rendering outside the state outline. A back button returns to the national spike map view.

County accident counts are derived from the pre-computed CountyFIPS field in the CSV, requiring zero runtime geometry. When a state has unlocated records (state-centroid coordinates), county hover counts are prefixed with a tilde (~) to indicate the values are approximate minimums.

Filter System

Eight filter dimensions are available, all operating simultaneously with AND logic between groups and OR logic within groups:

All filters update every visualization in real time including the map, charts, sparklines, statistics, choropleth, spike map, and county drill-down. Chip filters support single-click toggle and double-click isolate (deactivates all others in the group).

Analytics Drawer

A slide-out analytics drawer contains four D3.js visualizations at full size, all updating in real time with the current filtered dataset:

Sidebar Visualizations

The sidebar header contains four sparkline SVG charts showing year-over-year trends for total accidents, fatal accidents, serious injuries, and fatalities. These update with every filter change providing an immediate visual indicator of whether a trend is improving or worsening for the selected filter combination.

Small versions of the year chart and makes chart are also rendered in the sidebar scroll area for quick reference without opening the analytics drawer.

Responsive Design

The layout adapts across screen sizes using CSS media queries at four breakpoints. The sidebar narrows from 340px to 260px on smaller screens. The analytics and help drawers scale their width using calc() based on viewport width. On mobile devices the drawers render as full-screen overlays above the sidebar. All D3 charts use their container's clientWidth for responsive sizing.

Challenges

NTSB Data Inconsistency: The most significant challenge was the inconsistency of the source data across six decades. Aircraft make names appeared in multiple formats (CESSNA, Cessna, cessna). The NumberOfEngines field contained blanks, zeros, and comma-separated values for multi-aircraft events. Probable cause narratives used varied phrasing for the same causal factors. Builder names with commas in the Make field (e.g., "ANKERMAN, DONALD L.") were initially misidentified as multi-aircraft records. Each inconsistency required detection, analysis, and a targeted fix.

Canvas vs DOM Rendering: Leaflet's preferCanvas option caused cluster markers rendered as circleMarkers on a canvas element to lose click interactivity. Switching cluster markers to DOM-based divIcon elements resolved the click issue but increased rendering time due to creating 168,793 individual DOM elements. The All Dots mode uses a dedicated canvas renderer that must be properly destroyed (not just hidden) when switching modes to prevent the canvas from intercepting click events on other layers.

Cluster Rendering Performance: Building 168,793 MarkerCluster markers takes several seconds even on fast hardware. A batched rendering system with a progress bar and disabled mode buttons was implemented to prevent users from clicking away during the build process. A requestAnimationFrame polling mechanism detects when cluster icons actually appear in the DOM before dismissing the loading indicator, ensuring consistent behavior regardless of machine speed. An edge case where zero filtered records caused the polling to run indefinitely was addressed with an early exit check.

County Drill-Down Performance: Initial implementations performed point-in-polygon geometry checks at runtime for each accident against all county polygons when a state was clicked. This took 4+ seconds for large states like Texas and California. Moving the county assignment to offline preprocessing and storing the results in the CSV eliminated all runtime geometry, making county views render instantly.

County Hover Lag: Rendering thousands of SVG circle elements for accident dots caused severe hover lag on county polygons due to browser hit-testing through the DOM tree. Replacing SVG circles with a single HTML5 canvas element for dot rendering eliminated the lag entirely while maintaining SVG-based interactive county hover tooltips.

Limitations

Discussion

The project demonstrates that a single-page browser application with no backend infrastructure can effectively visualize and enable interactive exploration of a large-scale aviation safety dataset. The combination of Leaflet for geographic visualization and D3.js for analytical charts provides complementary strengths. Leaflet handles the interactive pan/zoom map with efficient tile rendering and marker management. D3 provides the precise control needed for custom chart types including the spike map, choropleth, and donut chart.

The data preprocessing pipeline proved to be as significant as the visualization implementation itself. The raw NTSB data required coordinate recovery, standardization of inconsistent fields, flagging of multi-aircraft events, and geographic enrichment with county-level assignments. Without this preprocessing, the visualization would have been limited to plotting raw coordinates with no meaningful filtering or geographic aggregation.

The spike map with county drill-down provides a visualization capability not available in the NTSB's own query tools. Users can identify state-level patterns, drill into county-level density, and cross-reference with filters for aircraft type, weather conditions, and injury severity to investigate specific scenarios.

Future work on this project could include automated classification of probable cause narratives using natural language processing or fine-tuned language models. A pre-computed cause category field would enable real-time analysis of why accidents happen for any filtered combination, transitioning the tool from a visualization platform to an analytical one. Integration of a small fine-tuned language model running locally via WebLLM could enable conversational queries against the dataset directly within the application.

Conclusion

The project integrates geographic visualization, analytical charting, and multi-dimensional filtering into a single interactive platform for exploring NTSB aviation accident data. Six D3.js visualization types (line chart, bar chart, donut chart, sparklines, choropleth, and spike map) work alongside three Leaflet map modes (cluster, heatmap, all dots) and a timeline animation to provide multiple perspectives on the same dataset. All visualizations update in real time as filters are applied. The spike map with state-to-county drill-down enables geographic analysis from national overview to individual county level. The entire application runs client-side as a static site deployable on GitHub Pages with no server dependencies.

From choosing a visualization approach, cleaning and enriching 168,793 records of inconsistent government data, implementing multiple rendering strategies to handle performance constraints, and building an interactive analytical tool from start to finish as an individual teaches the integration of multiple JavaScript libraries, the challenges of working with real-world messy data, and the importance of preprocessing in any data visualization pipeline.

Resources

References

Addendum: Technology Selection Rationale — Leaflet over deck.gl

Summary

This addendum presents the rationale for selecting Leaflet over deck.gl as the mapping library for an interactive visualization of approximately 170,000 NTSB aviation accident records (1962–present), deployed as a static site via GitHub Pages.

deck.gl was evaluated due to its GPU-accelerated rendering capabilities for large-scale geospatial data. However, there was no deck.gl requirement listed in the syllabus for the final project so Leaflet was selected based on: equivalent basemap rendering for raster tile services, broader client hardware accessibility, compatibility with static hosting, and sufficient performance for the project's requirements.

This was a deliberate architectural decision, not a capability limitation.

Library Overview

Leaflet: a lightweight JavaScript mapping library that renders via DOM and HTML5 Canvas (CPU-bound). Requires no build pipeline, has an extensive plugin ecosystem including marker clustering, and supports Canvas-accelerated rendering.

deck.gl: WebGL-powered framework for large-scale data visualization. Renders directly on the GPU, enabling smooth interaction with million-point datasets. Typically requires a JavaScript bundler and is commonly paired with Mapbox GL or MapLibre for basemaps.

Basemap Rendering: Library-Agnostic

The visualization uses Carto's Voyager and Dark Matter basemaps, accessed as raster tile endpoints. Both libraries consume these identically—Leaflet via L.tileLayer(), deck.gl via TileLayer. The tiles are pre-rendered PNGs fetched from Carto's servers. Neither library provides a rendering advantage for raster basemaps.

The same applies to FAA sectional chart overlays. These are raster products served as XYZ tiles. Both libraries simply drape these images over the map canvas; neither interprets or renders them from vector data.

A deck.gl + MapLibre implementation could render vector tiles client-side, enabling runtime style customization. This capability would have added complexity without benefit.

Client Hardware and Accessibility

deck.gl's WebGL pipeline assumes GPU availability. The following configurations may experience degraded performance or failure: older integrated graphics (Intel HD, AMD APUs), mobile devices with limited GPU memory, Chromebooks with restricted WebGL contexts, virtual machines without GPU passthrough, and corporate environments with WebGL disabled by policy.

Condition Leaflet Behavior deck.gl Behavior
Slow CPU Sluggish panning, delayed rendering Minimal impact
Weak/no GPU No impact Severe lag, crashes, blank canvas
WebGL disabled No impact Complete failure
Low memory Gradual slowdown WebGL context loss, crash

Leaflet degrades gracefully — users experience slower performance but retain functionality. deck.gl fails catastrophically — when GPU resources are unavailable, rendering the visualization unusable or completely blank.

The target audience includes aviation safety researchers, students, pilots, and general users who cannot be assumed to have high-performance hardware. Leaflet ensures functionality across the widest range of client configurations, consistent with accessible design principles.

Deployment Constraints

The project was deployed via GitHub Pages as a static site—HTML, CSS, and JavaScript served without server-side processing or build steps.

Leaflet operates natively in this environment via CDN script include. deck.gl typically requires npm integration and a bundler (Webpack, Vite) to produce browser-compatible output. While deck.gl can load from CDN, this sacrifices tree-shaking, inflates bundle size, and complicates dependency management.

Aspect Leaflet deck.gl
CDN usage Native Suboptimal
Bundler required No Typically yes
Setup time Minutes Hours

Performance Assessment

Dataset Size Leaflet (Canvas/Cluster) deck.gl (WebGL)
10,000 points Smooth Smooth
50,000 points Acceptable Smooth
170,000 points Acceptable with clustering Smooth
1,000,000+ points Degraded Smooth

For 170,000 static records with marker clustering, Leaflet provided acceptable performance. The visualization does not require real-time animation, streaming data, or GPU-accelerated aggregation—scenarios where deck.gl's overhead yields significant return.

Acknowledged Tradeoffs

  1. Scalability ceiling. Datasets exceeding 1M records or requiring real-time animation would necessitate deck.gl.
  2. Visual density. deck.gl produces smoother rendering at extreme point densities (heatmaps, hexbins).
  3. Future extensibility. Temporal animations or 3D terrain would warrant migration to deck.gl.

These tradeoffs were acceptable given project scope and audience.

Conclusion

Leaflet was selected over deck.gl based on:

  1. Equivalent basemap rendering — raster tiles are library-agnostic
  2. Client accessibility — CPU-based rendering with graceful degradation vs. GPU-dependent catastrophic failure
  3. Deployment compatibility — zero-build-step architecture for static hosting
  4. Sufficient performance — clustering handled 170k records adequately
  5. Scope alignment — focus on visualization design, not rendering infrastructure

deck.gl is appropriate for million-point datasets, real-time streaming, or GPU-accelerated aggregation. For this project's requirements, Leaflet provided equivalent functionality with broader accessibility and lower implementation overhead.

Addendum References

Software Engineered by: Michael J Evan
Multi-Engine Airline Transport Pilot
Certified Flight Instructor Instrument Airplane
NTSB DATA IS PUBLIC DOMAIN
ANALYTICS
ACCIDENTS BY STATE
ACCIDENTS PER YEAR
TOP 10 AIRCRAFT MAKES BY ACCIDENT
INJURY SEVERITY BREAKDOWN
Showing of events
1962
⚠ Switching modes deactivates timeline