- 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
84 lines
2.2 KiB
TypeScript
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} · {plant.capacity_mw} MW
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</AdvancedMarker>
|
|
);
|
|
}
|
|
|
|
export { FUEL_TYPE_COLORS };
|