Project Resources

Launch Application

Open the live interactive restaurant inspection dashboard

View GitHub Repository

Browse source code and version history

AI-Assisted Development

This project was developed through a combination of traditional development and iterative AI-assisted workflows.

About the Project

A picture of Times Square in NYC, taken by Andreas Niendorf
Times Square, NYC - Andreas Niendorf, Unsplash

New York City (NYC) is a global hub for diverse cuisine, offering an extensive range of dining experiences that reflect cultures from around the world. With more than 30,000 restaurants currently operating across the five boroughs, a robust food establishment inspection program is essential to maintaining consistent food safety standards and protecting public health.

This project explores restaurant inspection outcomes across New York City through an interactive dashboard that highlights recent inspection results, spatial patterns, and long-term trends, using historical inspection data spanning 2007 to the present.

This project has existed in two versions. The original was built with ArcGIS Dashboards during the GIS: Cartography and Geovisualization program at NSCC, hosted on a school-issued ArcGIS Online account. That account no longer exists, and with it the original dashboard, so after graduating I rebuilt the entire project from scratch with Claude Code as a custom-coded web application, giving me full control over the dashboard’s data pipeline, design, and long-term hosting.

ArcGIS Dashboard developed during the GIS: Cartography and Geovisualization program at NSCC
ArcGIS Dashboard developed during the GIS: Cartography and Geovisualization Program at NSCC

Rebuilding the dashboard meant replacing every part of the original ArcGIS Dashboards workflow with a codebase I maintain myself. A Node.js pipeline fetches the full DOHMH New York City Restaurant Inspection Results dataset directly from the NYC Open Data Socrata API in paginated batches, geocodes new or changed restaurant locations through LocationIQ, and outputs a static GeoJSON layer with current inspection results for every restaurant in NYC, plus per-restaurant historical inspection files. That pipeline runs automatically every day: a scheduled GitHub Actions workflow handles the daily geocoding backfill and triggers a fresh data fetch and rebuild, and every push to the main branch does the same in between, so the dashboard stays current without any manual data handling.

The frontend is a React and TypeScript application built with Vite, using the ArcGIS Maps SDK for JavaScript to render restaurant locations as a GeoJSON layer rather than relying on ArcGIS Dashboards’ built-in widgets. Filtering, search, grade-breakdown charts, and the restaurant detail panel are all custom components wired directly to the map’s query and selection state, reproducing the same exploratory interactions as the original dashboard while running as a fully self-hosted, independently deployed web app.

The interactive map allows users to explore restaurant locations and inspection grades, while dynamic dashboard elements update as the map extent changes, supporting analysis at multiple spatial scales. Additional visualizations, including grade distributions, tabular data, and time series charts, provide both aggregate and restaurant-level insights. Users can filter the dataset by grade and borough or select individual restaurants to examine inspection score trends over time.

The workflow involved filtering and organizing the dataset to isolate the most recent inspection results for display on the web map while preserving historical records for temporal analysis. Restaurant locations were displayed as individual points rather than aggregated, preserving important spatial patterns in density and distribution across New York City.

Integrating the web map into the dashboard enabled dynamic, view- and selection-based interactions via linked charts, tables, and filters, allowing users to explore relationships within the dataset in a more exploratory, interactive manner.

Data Sources and Dataset Details

NYC Logo
NYC Logo – Wolff Olin

The dataset used in this project is derived from restaurant inspection records provided by New York City’s Department of Health and Mental Hygiene (DOHMH) and is publicly available on the  NYC Open Data portal.

This dataset was chosen for its strong relevance to public health and daily life, as well as its suitability for spatial analysis. It provides up-to-date restaurant inspection outcomes across New York City, offering insight into the conditions of dining establishments visited by the public. Importantly, it enables analysis of inspection performance over time, highlighting trends in compliance with established health standards.

The DOHMH New York City Restaurant Inspection Results dataset updates daily and covers every sustained or not-yet-adjudicated violation from full and special program inspections conducted within the three years prior to each restaurant’s most recent inspection, for restaurants and college cafeterias currently in active status. Each row represents a single violation citation rather than a single restaurant, so an inspection with multiple violations produces multiple rows repeating the same inspection details; restaurants are uniquely identified by their CAMIS record ID. Restaurants with no violations, or that have applied for a permit but haven’t yet been inspected (marked with a placeholder inspection date of 1/1/1900), still appear as a single row. As an operational dataset assembled from several administrative systems, it can also contain occasional illogical or missing values.

The pipeline built for this rebuild fetches the dataset directly from the Socrata API and groups its rows by CAMIS to produce two outputs: a single current-state point per restaurant, holding its most recent scored inspection, for the map layer; and a full per-restaurant inspection history for the score-over-time and violation-detail views. Restaurants with no scored inspection yet are excluded from the map, coordinates that fall outside a rough NYC bounding box are treated as invalid and reverted to DOHMH’s own coordinates, and restaurant and address text is normalized to build the dashboard’s search index.

Key Dashboard Components

The Map View

NYC Restaurant Inspection Dashboard Map View
Dashboard Map View

The map view serves as the spatial foundation of the dashboard, plotting a point for every restaurant using bivariate symbology: colour encodes the restaurant’s most recent inspection grade, and circle size scales with its inspection score (a higher score means more, or more severe, violations, so worse-scoring restaurants read as larger circles at a glance). The default dashboard basemap is Esri’s Dark Gray Canvas, with an Esri World Imagery satellite toggle available in the top-right corner for real-world context. Hovering a restaurant point at zoom level 16 (map scale 1:18,056) or closer previews the restaurant’s name, grade, and score directly on the map; clicking a restaurant point loads its identifying and geographical details, full inspection history, and violations in the side panel, with its score-over-time chart shown below the map, and the restaurant listing panel gives an alternate way to browse locations where multiple restaurants share a point. The primary colours for Grade A, Grade B, and Grade C were derived from sampled NYC letter-grade signage. Grades N, P, and Z, each representing a restaurant still awaiting a finalized grade, are consolidated into a single Pending category; restaurants with no scored inspection on record appear as Uninspected, and restaurants closed by DOHMH appear as Closed. This map allows users to explore geographic patterns in restaurant performance and serves as the primary interface for interacting with and filtering the dataset.

NYC Restaurant Inspection Grades
NYC Restaurant Letter Grade Cards (source: Tumblr post)

A dedicated info button on the map panel opens a matching reference for its own legend, interactions, and data notes, part of the contextual documentation system covered below.

NYC Restaurant Inspection Dashboard, Map View info popup showing the grade legend, interactions, and data notes
Map View info popup

Map Controls

Custom controls, built to replace ArcGIS’s own map widgets, are arranged in the map’s four corners. The top-left cluster holds zoom in/out buttons and a compass that passively tracks the view’s rotation, resetting to north on click.

NYC Restaurant Inspection Dashboard, top-left zoom buttons and compass controls
Zoom and compass controls

Below them, the Map Scale and Zoom Lvl readouts double as inputs: clicking either one turns it into a text field, letting you jump straight to an exact scale denominator or zoom level instead of scrolling to it.

NYC Restaurant Inspection Dashboard, bottom-left Map Scale and Zoom Lvl click-to-edit readouts
Map Scale and Zoom Lvl readouts

A graphical scale bar in the bottom-right corner shows the current map scale as a ruler, converting the view’s resolution to true ground distance with a latitude correction and snapping to a round foot or mile value.

NYC Restaurant Inspection Dashboard, bottom-right graphical scale bar
Scale bar

The top-right corner pairs a satellite/map basemap toggle with the Search Radius tool’s target icon. On phone and tablet layouts, a third “locate me” button stacks below them, using the device’s own location to centre the map and sort the restaurant list by distance; it’s hidden on desktop.

NYC Restaurant Inspection Dashboard, top-right basemap toggle, Search Radius tool icon, and mobile locate-me button
Basemap toggle, Search Radius tool, and locate-me button (mobile/tablet)

Activating Search Radius switches the cursor to point-placement mode; dropping a point re-scopes the restaurant list, stats panel, and grade breakdown chart to a 0.25, 0.5, or 1 mile circle around it instead of the full map view, with a distance field added to each list card.

NYC Restaurant Inspection Dashboard, Search Radius tool showing a placed point, radius circle, and distance picker
Search Radius tool in use

Stats Panel (Current Map View)

NYC Restaurant Inspection Dashboard, Stats Panel (Current Map View)
Stats Panel (Current Map View)

The Stats Panel pairs a compact count bar with a donut chart: the bar gives the total restaurant count and its split across every grade category (A, B, C, Pending, Uninspected, and Closed), while the donut shows that same split as percentage shares. Both update dynamically with the current map extent, re-scope to the Search Radius circle when one is placed, and narrow to match any active grade filter, so the chart, list, and map always agree on what’s on screen.

Dashboard Documentation

NYC Restaurant Inspection Dashboard, Dashboard Information panel showing last-updated date, restaurant and inspection counts, and attribution
Dashboard Information panel

Rather than one static instructions panel, the rebuilt dashboard documents itself contextually. A persistent Dashboard Information panel shows dataset freshness at a glance (last updated date, total restaurant and inspection counts, and attribution); its info button opens a full reference guide covering an overview of every feature, step-by-step how-to-use instructions, grade and status definitions, data-quality notes, and links to NYC’s own health-department resources. Every other panel, including the map, restaurant details, and stats panel, carries its own matching info button, opening guidance specific to that panel instead of one generic instructions sheet.

NYC Restaurant Inspection Dashboard, full Dashboard Information reference guide with overview, how-to-use, grade definitions, and data notes
Dashboard Information reference guide

The Map View’s own info popup is one example of that per-panel pattern: the same overview, how-to-use, and data-notes structure, scoped to just that panel’s controls and legend.

NYC Restaurant Inspection Dashboard, Map View's own info popup as a secondary example of the per-panel documentation pattern
A secondary information panel example: the Map View info popup

Responsive Layout

Rather than one layout compressed to fit every screen, the dashboard switches between three distinct arrangements depending on viewport size, each rebuilt around what actually fits at that size.

Above 1750px, a three-pane layout puts the map, restaurant list, and stats panel and charts side by side, all visible at once.

NYC Restaurant Inspection Dashboard, desktop three-pane layout above 1750px
Desktop Layout (above 1750px)

Between 750px and 1750px, those panes stack into a scrolling two-column layout topped by a slim app bar, a compact wordmark plus Search, Filters, and Info buttons that each open as a drawer instead of taking up permanent space; the Grade Breakdown donut folds into a chevron drawer on the stats bar, and the Restaurant Performance chart runs full-width along the bottom.

NYC Restaurant Inspection Dashboard, tablet two-column layout between 750px and 1750px
Tablet Layout (750px–1750px)

Below 750px, a dedicated phone layout takes over, designed around a portrait phone: a slim top bar, a full-screen map, and a bottom sheet that steps between three heights, a peek, half, and full view, so the map stays the primary surface and the Restaurant Explorer only takes up as much space as it needs. Search, filters, and info each open as a drawer over the map here too. It isn’t built for landscape use, but a short landscape viewport (480px tall or less) keeps this layout anyway rather than switching to the two-column one, since there isn’t enough vertical room to stack panes there either.

NYC Restaurant Inspection Dashboard, phone layout below 750px with a bottom sheet over a full-screen map
Phone Layout (below 750px)

Grade Breakdown — Map View

NYC Restaurant Inspection Dashboard, Grade Breakdown donut chart
Grade Breakdown — Map View

The Grade Breakdown is a donut chart titled “Grade Breakdown — Map View” by default, retitling itself “Grade Breakdown — Within [radius]” when the Search Radius tool is active. Each slice’s share of the total appears as a percentage beneath the chart, alongside the raw counts in the Stats Panel above it, so the same data reads as both an absolute count and a proportion.

Unlike the original ArcGIS Dashboards version, this chart is read-only: it no longer supports clicking a slice to filter the map, since grade and borough filtering now live in their own dedicated controls. Instead, it reflects whatever is already filtered or searched for, narrowing to just the selected categories when a grade filter is active, and to the matching restaurants when a borough filter or search term is applied. A “Filters applied” note beneath the chart names any active borough or search filter, since those narrow the tally without changing which slices are drawn.

Restaurant Explorer

Selecting a restaurant, on the map or in the list, opens three linked panels for digging into that single restaurant: a sorted list to find it, a details view summarizing it, and a full report for any one of its past inspections.

The Restaurant List shows every restaurant currently in scope (the map view, or the Search Radius circle when one’s placed), narrowed by any active Grade and Borough filters or search text. Each card carries the restaurant’s name, address, cuisine, most recent inspection date, and grade/score badges; selecting one opens its details and pans the map to its location. Results sort by up to two combinable keys (name, date, grade, score, cuisine, or distance) with a direction toggle, and paginate at 10 per page; once a Search Radius or “locate me” point is set, Distance becomes available as both a per-card readout and a sort key.

NYC Restaurant Inspection Dashboard, Restaurant List with sort controls and pagination
Restaurant List

Restaurant Details

The Restaurant Details panel summarizes the selected restaurant itself: a Restaurant Information table (CAMIS, cuisine, current status), a Geographical Information table (borough, street, zipcode, phone, a location-verification badge, and a Google Maps link), and a full Inspection History list, one row per past inspection, each showing its date, grade, score, and critical/not-critical violation counts. Selecting a history row opens that inspection’s full report.

NYC Restaurant Inspection Dashboard, Restaurant Details panel with identifying, geographical, and inspection history information
Restaurant Details

Inspection Reports

Inspection Reports show the complete record for one selected inspection, defaulting to the restaurant’s most recent. Each report lists the inspection type, the action taken, a “Closed by DOHMH” badge when that inspection resulted in a closure, and the full violation list with each code’s category and critical/not-critical status. Newer and older navigation buttons step between a restaurant’s inspections without returning to the Details tab.

NYC Restaurant Inspection Dashboard, Inspection Report showing violation details and navigation between reports
Inspection Reports

Filter selections

NYC Restaurant Inspection Dashboard, Filter Restaurants by Grade or Borough
Filter Restaurants by Grade or Borough

Two filters, Grade and Borough, live together in the dashboard’s Filters control (an inline bar on wide desktop, a popover or drawer on compact and mobile layouts) and combine freely with each other and with the search field. Both are multi-select toggle buttons: turning on more than one option within a filter shows restaurants matching any of them, and each filter clears independently with its own Clear button.

The Grade filter toggles between six colour-coded categories: A, B, C, Pending (grades N, P, and Z), Uninspected (no scored inspection on record), and Closed (closed by DOHMH). Selecting one or more narrows the map, restaurant list, stats panel, and grade breakdown chart to just those categories.

The Borough filter narrows results to one or more of the five boroughs: Bronx, Brooklyn, Manhattan, Queens, and Staten Island. Unlike a Grade filter, an active Borough filter doesn’t reshape the Grade Breakdown chart’s own slices, since the chart always tallies every grade; instead it shows up in the “Filters applied” note beneath the chart, alongside any active search term.

Restaurant Performance Over Time

NYC Restaurant Inspection Dashboard, Restaurant Performance Over Time line chart
Restaurant Performance Over Time

Restaurant Performance Over Time is a line chart plotting each of a restaurant’s scored inspections by date, drawn straight from its own inspection history rather than a separate scores dataset. Three shaded background bands mark the same grade thresholds used everywhere else in the dashboard, A (0–13), B (14–27), and C (28+), so a point’s position alone shows its grade at a glance. Each point is colour-coded to its own grade category rather than a flat colour, and inspections without a numerical score are excluded from the timeline.

The chart is interactive, not just illustrative: hovering a point, or tabbing to the chart and using the arrow keys, opens a tooltip with that inspection’s date, score, and grade; clicking a point, or pressing Enter or Space while it’s focused, switches to that inspection’s full report on the Inspection Reports tab. It’s linked to the Restaurant Details panel’s Inspection History list in both directions, hovering a history row previews its point here, and hovering a point highlights the matching row.

This chart is one of the strongest analytical components of the dashboard, providing a clear temporal perspective on restaurant performance that cannot be captured through static or aggregate views alone.

Findings & Insights

Patterns & Distribution

When visualizing the current dataset (30,566 restaurants), the same spatial trend holds up: Manhattan has the highest concentration, with 12,191 restaurants, about 40% of the citywide total, reinforcing its role as the city’s primary dining hub. Other boroughs exhibit lower relative counts, resulting in more dispersed restaurant distributions and more localized patterns.

BoroughRestaurantsShare
Manhattan12,19139.9%
Brooklyn7,92925.9%
Queens6,73222.0%
Bronx2,5958.5%
Staten Island1,1003.6%
Restaurant Count and Share by Borough

While the restaurant counts vary significantly by borough, the trend does not appear to be correlated to population alone. For example, the estimated population of Manhattan in 2020 was 1,694,251—the second-lowest among the 5 boroughs (New York City Department of City Planning, 2024)—yet it accounted for 57.6% of NYC’s total tourism jobs (Office of the New York State Comptroller, 2021). This suggests that restaurant density is likely influenced by tourism and commercial activity, which increases demand for dining establishments beyond what the resident population alone would indicate.

The same pattern carries through the grade breakdown: boroughs with higher restaurant density, particularly Manhattan, also show higher counts across every inspection grade. This reflects overall restaurant volume rather than a performance difference, since higher density increases total inspection counts without indicating a decline in sanitary conditions.

BoroughABCPendingUninspectedClosed
Manhattan7,7931,0945231,1601,58536
Brooklyn5,08675936078689048
Queens4,11880837669069941
Bronx1,75327610823221412
Staten Island663156371251145
Grade Breakdown by Borough

Temporal Trends

A review of restaurant inspection histories using the Restaurant Performance chart reveals that performance is dynamic. While some establishments maintain relatively consistent scores across multiple inspections, others show noticeable fluctuations, with scores improving or worsening between inspections. Some restaurants trend gradually upward over time, meaning declining performance, since a higher score means more or more severe violations, as in the example below.

These variations suggest that compliance with food safety standards can change over time rather than remaining constant. Reviewing a restaurant’s full inspection history, rather than just its most recent result, provides context a single inspection can’t, and reinforces the value of tracking performance over time. Other restaurants show high variability instead of a steady trend, with scores swinging between inspections rather than moving consistently in one direction, as in the image below.

NYC Restaurant Inspection Dashboard, restaurant performance worsening over time
Restaurant Performance Worsening Over Time
NYC Restaurant Inspection Dashboard, high variability in restaurant performance over time
High Variability in Restaurant Performance

Interaction Insights

The dashboard’s interactive map directly shapes how the dataset reads: the stats panel, grade breakdown chart, and restaurant list all update dynamically to match the current map extent. As users pan and zoom across the city, the subset of data being visualized changes, revealing patterns that aren’t apparent at a city-wide view.

This relationship between map extent and the rest of the dashboard means interpretation varies with scale: broader views highlight citywide trends, while more focused views reveal finer spatial variation. The Grade and Borough filters, search field, and Search Radius tool narrow that same view further, letting users isolate specific subsets of the data without leaving the map.

Data & Dashboard Limitations

The following limitations were identified during the development and use of the dashboard:

  • Multiple restaurants at the same or adjacent address often plot at the same map point, so a single click can only resolve to one restaurant even when several share it. The Stats Panel and Grade Breakdown chart aren’t affected by this, since both tally every restaurant currently in view from the underlying data rather than from what’s visually rendered as separate dots; the Restaurant List panel is the reliable way to browse and select between restaurants sharing a point.
  • The dashboard has no backend or live database: restaurant data ships as one static GeoJSON file (all ~30,600 restaurants, about 32MB, loaded up front) plus over 27,000 small per-restaurant history files fetched on demand and capped at 50 entries in an in-memory cache. Static files keep hosting free and the app simple to reason about, since every filter, search, and sort runs entirely in the browser against data already downloaded, but a live database would scale further past this dataset’s size, at the cost of hosting fees and operational complexity a personal project doesn’t need.
  • Data is only as fresh as the last build. DOHMH inspections are refetched dataset-wide at most once a day, driven by the scheduled geocoding backfill and any push to the main branch, not a live feed, so a newly published inspection can take up to a day to appear.
  • LocationIQ’s free tier caps geocoding at about 4,900 lookups a day. A cold cache would take roughly a week to fully resolve; day to day this only matters for new or changed restaurants, since the daily backfill skips everything already cached.

I ♥ NY

Technology Stack

  • Vite
  • React
  • TypeScript
  • ArcGIS Maps SDK for JavaScript
  • Recharts
  • Node.js
  • GitHub Actions
  • Vercel

Applied Skills

  • Rebuilt an ArcGIS Dashboards-based project from scratch as a fully custom-coded React and TypeScript web application, independently architecting the data pipeline, frontend, and deployment.
  • Designed and built a Node.js data pipeline that fetches, cleans, and geocodes a live 30,000+ record municipal dataset, automated end-to-end through scheduled GitHub Actions workflows.
  • Implemented spatial querying and bivariate map symbology with the ArcGIS Maps SDK for JavaScript, rendering tens of thousands of geocoded points without relying on ArcGIS Online hosting.
  • Conducted spatial and temporal analysis of restaurant inspection patterns to identify borough-level trends and areas of concern across New York City.
  • Designed a responsive, accessible dashboard interface, including keyboard navigation, screen-reader support, and distinct desktop, tablet, and phone layouts.
↑ Back to top