diff --git a/app/components/widgets/MapStory.tsx b/app/components/widgets/MapStory.tsx index 1a8d729..987e790 100644 --- a/app/components/widgets/MapStory.tsx +++ b/app/components/widgets/MapStory.tsx @@ -20,24 +20,20 @@ import type { ViewStateChangeParameters } from '@deck.gl/core'; import { setBarChartData, setColorScaleValues, setSelectedCounty } from '@/lib/features/map/mapSlice'; import { Progress } from '@/app/components/ui/progress'; import { MapControls, MapLegend, MapTooltip } from './map'; +import type { EnhancedFeature } from '@/app/types/map'; +import { + LOAD_DELAY_MS, + FLY_TO_ZOOM, + TRANSITION_DURATION_MS, + UNSELECTED_COUNTY_COLOR, + MAP_BOUNDS, + MIN_ZOOM, +} from '@/lib/constants/mapConfig'; +import { dataDescriptions } from '@/lib/constants/dataDescriptions'; // Mapbox access token for using Mapbox services. const MAP_BOX_TOKEN = process.env.NEXT_PUBLIC_MAPBOX_TOKEN || ''; -/** - * Define the geographic boundaries for map constraint (Roughly North/South America). - * [ [minLongitude, minLatitude], [maxLongitude, maxLatitude] ] - */ -const MAP_BOUNDS: [[number, number], [number, number]] = [ - [-117.595944, 33.386416], // Southwest corner - [-120.999866, 42.183974], // Northeast corner -]; - -/** - * Minimum zoom level allowed. Prevents zooming out too far. - */ -const MIN_ZOOM = 5; - /** * Interface defining the structure for the map's view state. */ @@ -70,65 +66,9 @@ const INITIAL_COORDINATES = { latitude: 37.7853, }; -/** - * Extends the standard GeoJSON Feature to include calculated properties - * based on the selected metric and data source. - */ -interface EnhancedFeature extends Feature { - properties: { - name: string; - [metricKey: string]: any; // Allows indexing by selectedMetric string - rawValue: number; // Original aggregated value from the data - perCapitaValue?: number; // Value calculated per capita if applicable - rowCount: number; // Number of data rows contributing to the aggregation - totalCostValue?: number; // Specifically calculated total cost for county_prison data - avgCostPerPrisonerValue?: number; // Specifically calculated average cost per prisoner - }; -} - - // Re-export for backward compatibility with components that import from here export { COUNTY_POPULATION } from '@/lib/constants/countyPopulation'; -/** - * Static descriptions for different data sources and their metrics. - * Used to display information in the description box on the map. - */ -const dataDescriptions = { - arrest: { - name: 'Arrest Data', - description: - 'Explore how California counties use arrests as a response to crime, revealing justice system practices and community impacts across demographics and offense types.', - metrics: { - Arrest_rate: 'Rate of arrests per population for the selected filters and county.', - Total_Arrests: 'Total number of arrests recorded for the selected filters and county.', - }, - }, - county_prison: { - name: 'County Prison Data', - description: - 'Examine imprisonment patterns and associated costs to understand how local sentencing practices impact state resources and community safety outcomes.', - metrics: { - Imprisonments: 'Total number of imprisonments recorded for the county.', - Cost_per_prisoner: 'Average cost per prisoner for the county.', - Total_Cost: 'Calculated total cost based on imprisonments and cost per prisoner.', - }, - }, - jail: { - name: 'Jail Data', - description: 'Understand how counties rely on local incarceration and the extent of pretrial detention in California\'s justice system.', - metrics: { - ADPtotrate: 'Average Daily Population total rate per capita for the county.', - ADPtotal: 'Total Average Daily Population count for the county.', - Felony: 'Total count of felony-related jail population for the county (Juvenile only).', - Misd: 'Total count of misdemeanor-related jail population for the county (Juvenile only).', - Postdisp: 'Post-disposition jail population count for the county.', - Predisp: 'Pre-disposition jail population count for the county.', - }, - }, - // Add other data sources as needed -}; - /** * Main component for displaying the interactive map story. * It fetches GeoJSON data, merges it with filtered data from Redux state, @@ -269,7 +209,7 @@ export default function MapStory() { setShowLoading(true); } loadingTimerRef.current = null; - }, 500); + }, LOAD_DELAY_MS); // Calculate dynamic population data for the selected year const populationData = getPopulationByCountyAndYear(censusData, selectedYear); @@ -444,8 +384,8 @@ export default function MapStory() { ...prevState, longitude: polygonCentroid.longitude, latitude: polygonCentroid.latitude, - zoom: 10, // Zoom in closer - transitionDuration: 1000, // Animation duration + zoom: FLY_TO_ZOOM, // Zoom in closer + transitionDuration: TRANSITION_DURATION_MS, // Animation duration transitionInterpolator: new FlyToInterpolator(), // Smooth fly-to animation })); } @@ -471,7 +411,7 @@ export default function MapStory() { !selectedCounties.includes(feature.properties.name) ) { // Return grey color for unselected counties - return [200, 200, 200, 150]; // Light grey with some transparency + return UNSELECTED_COUNTY_COLOR; // Light grey with some transparency } // Default coloring based on metric value for selected counties diff --git a/app/components/widgets/map/MapTooltip.tsx b/app/components/widgets/map/MapTooltip.tsx index 32e2c2f..0fe3269 100644 --- a/app/components/widgets/map/MapTooltip.tsx +++ b/app/components/widgets/map/MapTooltip.tsx @@ -1,21 +1,9 @@ 'use client'; import React from 'react'; -import type { Feature } from 'geojson'; import { COUNTY_POPULATION } from '@/lib/constants/countyPopulation'; import { formatMetricLabel } from '@/lib/utils/metricFormatters'; - -interface EnhancedFeature extends Feature { - properties: { - name: string; - [metricKey: string]: any; - rawValue: number; - perCapitaValue?: number; - rowCount: number; - totalCostValue?: number; - avgCostPerPrisonerValue?: number; - }; -} +import type { EnhancedFeature } from '@/app/types/map'; interface MapTooltipProps { hoverInfo: { diff --git a/app/components/workers/dataProcessor.worker.ts b/app/components/workers/dataProcessor.worker.ts index 0fef2c3..3bd595e 100644 --- a/app/components/workers/dataProcessor.worker.ts +++ b/app/components/workers/dataProcessor.worker.ts @@ -3,19 +3,7 @@ import type { Feature } from 'geojson'; import type { CsvRow } from '@/app/types/shared'; import type { DataSourceType } from '@/lib/features/filters/filterSlice'; import { COUNTY_POPULATION } from '@/lib/constants/countyPopulation'; - -// --- Define EnhancedFeature interface here --- -interface EnhancedFeature extends Feature { - properties: { - name: string; - [metricKey: string]: any; // Allows indexing by selectedMetric string - rawValue: number; // Original aggregated value from the data - perCapitaValue?: number; // Value calculated per capita if applicable - rowCount: number; // Number of data rows contributing to the aggregation - totalCostValue?: number; // Specifically calculated total cost for county_prison data - avgCostPerPrisonerValue?: number; // Specifically calculated average cost per prisoner - }; -} +import type { EnhancedFeature } from '@/app/types/map'; // --- Paste the enhanceGeoJsonWithData function here --- diff --git a/app/types/map.ts b/app/types/map.ts new file mode 100644 index 0000000..b96bc30 --- /dev/null +++ b/app/types/map.ts @@ -0,0 +1,17 @@ +import type { Feature } from 'geojson'; + +/** + * Extends the standard GeoJSON Feature to include calculated properties + * based on the selected metric and data source. + */ +export interface EnhancedFeature extends Feature { + properties: { + name: string; + [metricKey: string]: any; // Allows indexing by selectedMetric string + rawValue: number; // Original aggregated value from the data + perCapitaValue?: number; // Value calculated per capita if applicable + rowCount: number; // Number of data rows contributing to the aggregation + totalCostValue?: number; // Specifically calculated total cost for county_prison data + avgCostPerPrisonerValue?: number; // Specifically calculated average cost per prisoner + }; +} diff --git a/lib/constants/dataDescriptions.ts b/lib/constants/dataDescriptions.ts new file mode 100644 index 0000000..b691436 --- /dev/null +++ b/lib/constants/dataDescriptions.ts @@ -0,0 +1,38 @@ +/** + * Static descriptions for different data sources and their metrics. + * Used to display information in the description box on the map. + */ +export const dataDescriptions = { + arrest: { + name: 'Arrest Data', + description: + 'Explore how California counties use arrests as a response to crime, revealing justice system practices and community impacts across demographics and offense types.', + metrics: { + Arrest_rate: 'Rate of arrests per population for the selected filters and county.', + Total_Arrests: 'Total number of arrests recorded for the selected filters and county.', + }, + }, + county_prison: { + name: 'County Prison Data', + description: + 'Examine imprisonment patterns and associated costs to understand how local sentencing practices impact state resources and community safety outcomes.', + metrics: { + Imprisonments: 'Total number of imprisonments recorded for the county.', + Cost_per_prisoner: 'Average cost per prisoner for the county.', + Total_Cost: 'Calculated total cost based on imprisonments and cost per prisoner.', + }, + }, + jail: { + name: 'Jail Data', + description: 'Understand how counties rely on local incarceration and the extent of pretrial detention in California\'s justice system.', + metrics: { + ADPtotrate: 'Average Daily Population total rate per capita for the county.', + ADPtotal: 'Total Average Daily Population count for the county.', + Felony: 'Total count of felony-related jail population for the county (Juvenile only).', + Misd: 'Total count of misdemeanor-related jail population for the county (Juvenile only).', + Postdisp: 'Post-disposition jail population count for the county.', + Predisp: 'Pre-disposition jail population count for the county.', + }, + }, + // Add other data sources as needed +}; diff --git a/lib/constants/mapConfig.ts b/lib/constants/mapConfig.ts new file mode 100644 index 0000000..de51a11 --- /dev/null +++ b/lib/constants/mapConfig.ts @@ -0,0 +1,23 @@ +/** Delay in ms before showing the loading indicator after worker starts */ +export const LOAD_DELAY_MS = 500; + +/** Zoom level used when flying to a selected county */ +export const FLY_TO_ZOOM = 10; + +/** Animation duration in ms for fly-to transitions */ +export const TRANSITION_DURATION_MS = 1000; + +/** Fill color for counties not in the active selection [R, G, B, A] */ +export const UNSELECTED_COUNTY_COLOR: [number, number, number, number] = [200, 200, 200, 150]; + +/** + * Geographic bounds constraining map panning (roughly California). + * [ [minLongitude, minLatitude], [maxLongitude, maxLatitude] ] + */ +export const MAP_BOUNDS: [[number, number], [number, number]] = [ + [-117.595944, 33.386416], // Southwest corner + [-120.999866, 42.183974], // Northeast corner +]; + +/** Minimum zoom level — prevents zooming out too far */ +export const MIN_ZOOM = 5;