busi488energy/src/components/map/power-plant-marker.tsx
Joey Eamigh 8f99f6535e
phase 7: full US coverage — grid regions, datacenters, power plants, backfill, chart perf
- Add 7 new grid regions (BPA, DUKE, SOCO, TVA, FPC, WAPA, NWMT) to cover entire continental US
- Expand datacenters from 108 to 292 facilities across 39 operators
- Add EIA power plant pipeline: download script, 3,546 plants >= 50 MW with diamond map markers
- Rewrite backfill script for 10-year data (2015-07-01) with quarterly/monthly chunking, 3-region parallelism, resumability
- Add materialized views (daily/weekly) with server-side granularity selection for chart performance
- Fix map UX: z-index tooltips, disable POI clicks, move legend via MapControl
2026-02-11 16:08:06 -05:00

84 lines
2.2 KiB
TypeScript

'use client';
import { AdvancedMarker } from '@vis.gl/react-google-maps';
import { useCallback, useState } from 'react';
const FUEL_TYPE_COLORS: Record<string, string> = {
Coal: '#4A4A4A',
'Natural Gas': '#F59E0B',
Nuclear: '#8B5CF6',
Hydroelectric: '#3B82F6',
Wind: '#06B6D4',
Solar: '#FBBF24',
Petroleum: '#78716C',
Biomass: '#22C55E',
Geothermal: '#EF4444',
};
function getFuelColor(fuelType: string): string {
return FUEL_TYPE_COLORS[fuelType] ?? '#9CA3AF';
}
function getDiamondSize(capacityMw: number): number {
if (capacityMw >= 2000) return 20;
if (capacityMw >= 1000) return 16;
if (capacityMw >= 500) return 13;
if (capacityMw >= 200) return 10;
return 8;
}
export interface PowerPlantMarkerData {
id: string;
plant_code: number;
name: string;
operator: string;
capacity_mw: number;
fuel_type: string;
state: string;
lat: number;
lng: number;
}
interface PowerPlantMarkerProps {
plant: PowerPlantMarkerData;
}
export function PowerPlantMarker({ plant }: PowerPlantMarkerProps) {
const [hovered, setHovered] = useState(false);
const size = getDiamondSize(plant.capacity_mw);
const color = getFuelColor(plant.fuel_type);
const handleMouseEnter = useCallback(() => setHovered(true), []);
const handleMouseLeave = useCallback(() => setHovered(false), []);
return (
<AdvancedMarker position={{ lat: plant.lat, lng: plant.lng }} zIndex={1}>
<div
className="relative flex items-center justify-center"
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}>
<div
style={{
width: size,
height: size,
backgroundColor: color,
opacity: 0.7,
transform: 'rotate(45deg)',
borderRadius: 2,
}}
/>
{hovered && (
<div className="absolute bottom-full left-1/2 z-10 mb-2 -translate-x-1/2 rounded-md bg-zinc-900/95 px-3 py-1.5 text-xs whitespace-nowrap text-zinc-100 shadow-xl">
<div className="font-semibold">{plant.name}</div>
<div className="text-zinc-400">
{plant.fuel_type} &middot; {plant.capacity_mw} MW
</div>
</div>
)}
</div>
</AdvancedMarker>
);
}
export { FUEL_TYPE_COLORS };