This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
DeFlock Maps is a fork of FlockHopper, hosted at maps.deflock.org. It is a privacy-focused map application that visualizes ALPR camera locations across the United States and calculates alternative routes that minimize camera exposure. This fork is maintained by DeFlock, the organization that maps ALPR cameras.
npm run dev # Start development server (port 3000)
npm run build # TypeScript check + Vite production build
npm run lint # ESLint
npm run preview # Preview production build- React 18 + TypeScript + Vite
- Zustand for state management
- MapLibre GL + react-map-gl for maps
- Deck.gl for advanced visualization layers
- Tailwind CSS for styling
- Framer Motion for animations
- Turf.js for geospatial processing
- Protomaps for vector tile basemaps
- FlockHopper Routing API (
api.dontgetflocked.com) for routing - Cloudflare Workers for data API (
data.dontgetflocked.com)
-
Camera Rendering: Default path renders straight from per-country hourly PMTiles archives (
tiles.dontgetflocked.com/cameras-{us,ca}-hourly.pmtiles, source-layercameras, attributes at z9+, zero tile buffer) via the client-side pmtiles protocol — no dataset download. Both US and Canada use tiles; the full GeoJSON (cameraStore) loads lazily only when Explore/ timeline or heatmap need per-camera attributes at all zooms, or as the filter fallback (useCameraRenderModedecides which path is visible). The dots→points handoff runs over z9–10 (approved 2026-07-18); cones from z10. Filter archives + manifests are per-country and build-paired (cameras-{us,ca}-hourly-filter.pmtiles,cameras-{us,ca}-hourly-manifest.json). -
Route Calculation (
src/services/apiClient.ts): Callsapi.dontgetflocked.com/api/v1/routewith origin, destination, and options. API handles all camera-aware routing. Returns both normal and avoidance routes with comparison metrics.
The map has 4 modes, selectable via the header tabs:
- Route: Camera-avoidance route planning
- Explore: Dot density visualization with timeline playback
- Density (Analysis): Choropleth density analysis by state/county/tract
- Network: Sharing network visualization between agencies
| File | Purpose |
|---|---|
src/services/apiClient.ts |
API client — calls FlockHopper routing API |
src/services/routingConfig.ts |
Visualization constants for camera cones on map |
src/services/cameraDataService.ts |
Camera data fetching and processing |
src/services/cameraTilesService.ts |
PMTiles protocol registration + camera tile source/archive constants |
src/services/boundaryDataService.ts |
Boundary geometry data loading |
src/services/densityDataService.ts |
Density visualization data loading |
src/hooks/useCameraRenderMode.ts |
Decides tiles vs. GeoJSON camera rendering path |
src/store/cameraStore.ts |
Camera data management + spatial grid indexing |
src/store/routeStore.ts |
Route calculation state and UI state |
src/store/mapModeStore.ts |
Map style/mode management |
src/pages/MapPage.tsx |
Main application page container |
src/components/map/MapLibreContainer.tsx |
Map rendering, camera markers, route layers |
src/components/map/layers/CameraTileLayers.tsx |
Default camera rendering — dots/points/cones from PMTiles vector tiles |
src/components/panels/MapPanel.tsx |
Main panel container component |
src/components/panels/TabbedPanel.tsx |
Tab navigation for mode panels |
Zustand stores expose both state and actions. Key stores:
cameraStore: Camera data, spatial grid, loading phasesrouteStore: Route calculation, active route display, UI statecustomRouteStore: Multi-leg waypoint routingmapStore: Map bounds/viewportmapModeStore: Map style and base layer modeappModeStore: Current app mode, visualization settingsdensityStore: Density visualization datanetworkStore: Sharing network data
src/
├── components/
│ ├── common/ # ErrorBoundary, LoadingSpinner, BottomSheet, Seo, LegacyMapLink
│ ├── inputs/ # AddressSearch autocomplete
│ ├── map/ # MapLibreContainer, MapSearch, CameraStats, MapLoadingScreen
│ │ └── layers/ # CameraTileLayers (default), CameraMarkerLayers (lazy),
│ │ # DensityLayers, DotDensityLayers, HeatmapLayers,
│ │ # NetworkLayers, BoundaryOverlayLayers
│ ├── panels/ # MapPanel, TabbedPanel, RoutePanel, ExplorePanel,
│ │ # DensityPanel, NetworkPanel, CustomRoutePanel,
│ │ # MobileTabDrawer, RouteComparison
│ └── ui/ # Shadcn components (button, input)
├── hooks/ # useCameraRenderMode, useEmbedMode
├── lib/ # Utility helpers (cn)
├── modes/ # Visualization modes (heatmap, timeline, dots, density)
├── pages/ # MapPage, NotFound
├── services/ # apiClient, cameraDataService, cameraTilesService,
│ # boundaryDataService, densityDataService, geocodingService,
│ # gpxService, zipCodeService, routingConfig, performanceLogger
├── store/ # Zustand stores
├── types/ # TypeScript definitions (camera, density, route, map)
└── utils/ # geo, polyline, formatting
Found in .env file. Environment variables are prefixed with VITE_ for Vite to expose them to the frontend.
| Variable | Description |
|---|---|
VITE_API_URL |
FlockHopper routing API URL |
VITE_TILES_URL |
Protomaps vector tile server URL |
VITE_DATA_API_URL |
Cloudflare Worker data API URL |
VITE_PERF_LOGGING |
Enable performance logging |
The spatial grid (0.5° cells) is critical for performance. Always use getCamerasInBounds() or getCamerasInBoundsFromGrid() rather than filtering the full camera array.
MapLibreContainer.tsx is the main map component. Map layers are organized into dedicated components under src/components/map/layers/ — CameraTileLayers (default PMTiles rendering), CameraMarkerLayers (lazy GeoJSON path for filters/timeline/heatmap/Canada), DensityLayers, DotDensityLayers, HeatmapLayers, NetworkLayers, and BoundaryOverlayLayers. useCameraRenderMode (src/hooks/) decides which camera layer is active.
Vite splits bundles by vendor: react-vendor, map-vendor, motion, geo-utils, state, deck-vendor. MapPage uses React lazy loading with Suspense. Path alias @/ maps to src/.
- Camera Tiles:
tiles.dontgetflocked.com/cameras-{us,ca}-hourly.pmtiles(+-filtercompanions and-manifest.jsondictionaries) — hourly PMTiles archives, Range requests viaflockhopper-tilesworker - Camera Data (attributes):
data.dontgetflocked.com/cameras.geojson.gz— lazy-loaded for filters/timeline/heatmap/Canada; ~114k (July 2026) cameras - ZIP Codes:
/public/zipcodes-us.json— local lookup, no API needed - Map Tiles: Protomaps vector tiles via
VITE_TILES_URL - Geocoding: Nominatim (OSM) with Photon fallback
- Density Data: GeoJSON files in
/public/geo/(states-metrics, counties-metrics) - Network Data:
/public/sharing-network-adjacency.jsonand/public/sharing-network-nodes.geojson
Hosted on Cloudflare Pages. _headers and _redirects files in /public/ configure caching and routing. The worker/ directory contains a Cloudflare Worker that serves the data API (data.dontgetflocked.com).