Revert "Merge branch 'feature/gas-filter' into 'main'"

This commit is contained in:
Dan Hansen
2026-01-30 19:11:11 +00:00
parent 2a4139ab31
commit 946dbb499e
51 changed files with 174 additions and 4999 deletions
+14 -18
View File
@@ -1,6 +1,5 @@
import { Flex, Tabs, HStack } from '@chakra-ui/react';
import { MEDataEntry } from './AdminTabs/MEDataEntry';
import { NVEWS } from './AdminTabs/NVEWS'
import { ProjectCollector } from './AdminTabs/ProjectCollector';
import { NASApplication } from './AdminTabs/NASApplication';
import { IVANS } from './AdminTabs/IVANS'
@@ -31,7 +30,7 @@ export default function Admin({ view, onChangeView, onEditWidgets, appendWidget
as="header"
position="sticky"
top="0"
zIndex={2000} // zIndex: 1100
zIndex="dropdown" // zIndex: 1100
bg="base200"
pl={5}
pr={2}
@@ -43,28 +42,25 @@ export default function Admin({ view, onChangeView, onEditWidgets, appendWidget
>
{/* HEADER */}
<CanvasHeaderTabs tabs={ADMIN_TABS} />
{/*<HStack width="258px" justifyContent="flex-end">
<HStack width="258px" justifyContent="flex-end">
<SearchBar />
<SettingsButton />
</HStack>*/}
</HStack>
</Flex>
{/* CANVAS */}
<Tabs.Content value="medataentry">
<MEDataEntry />
</Tabs.Content>
<Tabs.Content value="nvews">
<NVEWS />
</Tabs.Content>
<Tabs.Content value="projectcollector">
<ProjectCollector />
</Tabs.Content>
<Tabs.Content value="nasapplication">
<NASApplication />
</Tabs.Content>
<Tabs.Content value="ivans">
<IVANS />
</Tabs.Content>
<MEDataEntry />
</Tabs.Content>
<Tabs.Content value="projectcollector">
<ProjectCollector />
</Tabs.Content>
<Tabs.Content value="nasapplication">
<NASApplication />
</Tabs.Content>
<Tabs.Content value="ivans">
<IVANS />
</Tabs.Content>
</Tabs.Root>
);
}
@@ -15,7 +15,7 @@ const yearItems = Array.from(
);
const years = createListCollection({
items: yearItems.reverse(),
items: yearItems.reverse(),
});
const quarters = createListCollection({
@@ -200,7 +200,7 @@ export function MEDataEntry() {
<Box m={4} mt={1} border="1px solid" borderRadius="sm" borderColor="gray.500">
<Box m={4} border="1px solid">
<Box pl={1} bg={{base:"gray.100", _dark:"gray.600"}} border="1px solid" borderColor="gray.300">
<Box pl={1} bg="gray.200" border="1px solid" borderColor="gray.400">
Number of people benefitting from VDAP activities, disaggregate by sex
</Box>
@@ -1,236 +0,0 @@
import React from "react";
import { Box, Text, Accordion, Table, Separator, Center,Button,Link} from "@chakra-ui/react";
import { useEffect, useState, useMemo } from "react";
import Map from "../NVEWSMap.jsx";
import {mapFilterConfig} from "@/utils/map-utils/map-configs/nvewsMapFilterConfig.js";
import { pctStyles } from "@/utils/pctStyles.js";
const SECTIONS = [
{
id: "volcano",
title: "Volcano",
fields:[
{label: "VNUM", key: "vnum"},
{label: "State", key: "volcano_subgroup"},
{label: "Threat Level", key: "nvews_threat_text"},
{label: "Observatory", key: "volcano_group"},
{label: "Network Completion %",key: "network_pct_complete"},
{label: "Network Area (km2)", key: "network_area_km2"},
{label: "Polygon dimensions (km x km or km if radius)", key: "r_or_p_dimensions_km"},
]
},
{
id: "seismic",
title: "Seismic",
fields:[
{label: "Stations", key: "seismic_station_total"},
{label: "Target", key: "seismic_target"},
{label: "% Complete", key: "seismic_pct_complete"},
]
},
{
id: "gnss",
title: "GNSS",
fields:[
{label: "Stations", key: "gnss_station_total"},
{label: "Target", key: "gnss_target"},
{label: "% Complete", key: "gnss_pct_complete"},
]
},
{
id: "cameras",
title: "Cameras",
fields:[
{label: "Stations", key: "camera_station_total"},
{label: "Target", key: "camera_target"},
{label: "% Complete", key: "camera_pct_complete"},
]
},
{
id: "infrasound",
title: "Infrasound",
fields:[
{label: "Stations", key: "infrasound_station_total"},
{label: "Target", key: "infrasound_target"},
{label: "% Complete", key: "infrasound_pct_complete"},
]
},
{
id: "tilt",
title: "Tilt",
fields:[
{label: "Stations", key: "tiltmeter_station_total"},
{label: "Target", key: "tiltmeter_target"},
{label: "% Complete", key: "tiltmeter_pct_complete"},
]
},
{
id: "real-time gas",
title: "Real-Time Gas",
fields:[
{label: "Stations", key: "gas_station_total"},
{label: "Target", key: "gas_target"},
{label: "% Complete", key: "gas_pct_complete"},
]
},
];
const COLUMNS = SECTIONS.flatMap(section =>
section.fields.map(field => ({
id: `${section.id}.${field.key}`,
sectionTitle: section.title,
label: field.label,
key: field.key,
isPercent: field.label.includes('%')
}))
);
async function fetchData(){
const res = await fetch("https://dev-vhptools.usgs.gov/vmidapi-free/reports/nvewsCompletionStatus");
if(!res.ok) throw new Error(res.statusText);
return res.json();
}
export function NVEWS() {
const origin = [45.61142478054226, -122.49630033167544];
const zoom = 3;
const type = ["https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",'&copy; OpenStreetMap contributors'];
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
const [selectedFilters, setSelectedFilters] = useState(() => {
const initial = {};
for (const f of mapFilterConfig) initial[f.id] = f.defaultValue;
return initial;
});
const [selectedVolcano, setSelectedVolcano] = useState(null);
function handleTableClick(v){
setSelectedVolcano([v.latitude, v.longitude]);
}
function setFilterValue(id, value) {
setSelectedFilters(prev => ({ ...prev, [id]: value }));
}
function toggleInArray(id, value) {
setSelectedFilters(prev => {
const arr = Array.isArray(prev[id]) ? prev[id] : [];
const next = arr.includes(value) ? arr.filter(x => x !== value) : [...arr, value];
return { ...prev, [id]: next };
});
}
function applyDataFilters(rows, config, selected) {
return rows.filter(row => {
return config.every(f => {
if (f.target?.kind !== "data") return true;
const sel = selected[f.id];
if (sel == null) return true;
if (Array.isArray(sel) && sel.length === 0) return true;
const value = row[f.target.field];
switch (f.target.op) {
case "in":
return Array.isArray(sel) ? sel.includes(value) : true;
case "eq":
return value === sel;
default:
return true;
}
});
});
}
useEffect(() => {
fetchData()
.then(setData)
.catch(err => setError(err.message))
.finally(() => setLoading(false));
},[]);
if (error) return <pre style={{ color: "red" }}>{error}</pre>;
if (loading) return <p>Loading...</p>;
console.log("raw",data)
const filteredData = applyDataFilters(data ?? [], mapFilterConfig, selectedFilters);
console.log("filtered",filteredData);
return(
<Box width="100%" px={20} py={1} overflow="hidden">
<Box height="500px" width="100%" overflow="hidden">
<Map markers = {filteredData} origin ={origin} type={type} zoom = {zoom} mapFilters={mapFilterConfig}
selectedFilters={selectedFilters} setFilterValue={setFilterValue} toggleInArray={toggleInArray} selectedMarker={selectedVolcano} />
</Box>
{/*<Separator size ="lg" mt={4}/>*/}
<Center mt={4} bg="white">
<Text>
NVEWS Completion Status -
<Link href="https://dev-vhptools.usgs.gov/vmid-free/#/nvews-completion-status-report"
target="_blank" rel="noopener noreferrer" >
View Full Report
</Link>
</Text>
</Center>
<Box width="100%" maxHeight="500px">
<Table.ScrollArea maxHeight="500px" width="100%" overflowX="auto" overflowY = "auto">
<Table.Root showColumnBorder style={{minWidth: "max-content"}}>
<Table.Header>
<Table.Row>
<Table.ColumnHeader position= "sticky" left = "0" bg = "white" zIndex="3" rowSpan={2}>Name</Table.ColumnHeader>
{SECTIONS.map(s => (
<Table.ColumnHeader key={s.id} colSpan={s.fields.length} textAlign="center">
{s.title}
</Table.ColumnHeader>
))}
</Table.Row>
<Table.Row>
{COLUMNS.map(c => (
<Table.ColumnHeader key={c.id}>{c.label}</Table.ColumnHeader>
))}
</Table.Row>
</Table.Header>
<Table.Body>
{filteredData.map(v => (
<Table.Row key={v.vnum ?? v.volcano_name}>
<Table.Cell position= "sticky" left = "0" bg = "white" zIndex="2" cursor = "pointer" _hover = {{textDecoration: "underline"}}
onClick={() => handleTableClick(v)}>
{v.volcano_name}
</Table.Cell>
{COLUMNS.map(c => {
const value = v?.[c.key];
const pct = Number(String(value).replace("%",""));
return (
<Table.Cell key={c.id} {...(c.isPercent ? pctStyles(pct,100,70) : {})} onClick={() => handleTableClick(v)}
cursor="pointer" _hover={{textDecoration: "underline"}}>
{value ?? "-"}
</Table.Cell>
);
})}
</Table.Row>
))}
</Table.Body>
</Table.Root>
</Table.ScrollArea>
</Box>
</Box>
)
}
@@ -3,23 +3,21 @@ import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import MapButtonControls from "../CanvasComponents/MapButtonControls";
import MapSettingsButton from "../CanvasComponents/MapSettingsButton";
export default function GlobalMap({markers = []}) {
const origin = [45.61142478054226, -122.49630033167544]; // [latitude, longitude]
export default function GlobalMap() {
const position = [45.61142478054226, -122.49630033167544]; // [latitude, longitude]
return (
<MapContainer center={origin} zoom={3} style={{ height: "100%", width: "100%" }}>
<MapContainer center={position} zoom={3} style={{ height: "100%", width: "100%" }}>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; OpenStreetMap contributors'
/>
<MapButtonControls settingsBtn = {<MapSettingsButton />} position="topright" />
{markers.map(m => (
<Marker key={m.id} position = {m.position}>
<Popup>
{m.label}
</Popup>
</Marker>
))}
<Marker position={position}>
<Popup>
CVO Home of the volcanoligists
</Popup>
</Marker>
</MapContainer>
);
}
+26 -106
View File
@@ -1,12 +1,10 @@
import { useState } from "react";
import { HStack, Flex, Tabs, Box } from "@chakra-ui/react";
import { HStack, Flex, Tabs } from "@chakra-ui/react";
import MyDashboard from "./HomeTabs/MyDashboard";
import { SearchBar } from "../CanvasComponents/SearchBar";
import { SettingsButton } from "../CanvasComponents/SettingsButton";
import { DailyActivity } from "./HomeTabs/DailyActivity";
import { Gas } from "./HomeTabs/Gas";
import { Seismic } from "./HomeTabs/Seismic";
import { Deformation } from "./HomeTabs/Deformation";
import { RemoteSensing } from "./HomeTabs/RemoteSensing";
import { CancelButton } from "../CanvasComponents/CancelButton";
import { SaveButton } from "../CanvasComponents/SaveButton";
@@ -14,103 +12,44 @@ import DeleteAllButton from "../CanvasComponents/DeleteAllButton";
import { v4 as uuid } from 'uuid';
import { CanvasHeaderTabs } from '../CanvasComponents/CanvasHeaderTabs';
import { DASHBOARD_TABS } from "@/constants/viewKeys";
import { WIDGET_REGISTRY } from "@/constants/widgetRegistry";
import { WIDGET_INSPECTOR_REGISTRY } from "@/constants/widgetInspectorRegistry";
import WidgetInspector from "../CanvasComponents/WidgetInspector";
export default function Home(props) {
// deconstructing props
const { view, layout, setLayout, onLayoutChange, onChangeView, onCancel, isDelete, setIsDelete,
beginEditIfNeeded, isEditable, setIsEditable, widgetArray, setWidgetArray } = props;
const { view, layout, setLayout, setOriginalLayout, onLayoutChange, onChangeView, onCancel, setOriginalWidgets,
isDelete, setIsDelete, beginEditIfNeeded, isEditable, setIsEditable, widgetArray, setWidgetArray } = props;
const [inspectorOpen, setInspectorOpen] = useState(false);
const [inspectorTarget, setInspectorTarget] = useState(null);
const openInspector = ({ id, widgetKey }) => {
setInspectorTarget({ id, widgetKey, view });
setInspectorOpen(true);
};
function appendWidget(widgetKey, overrides = {}) {
function appendWidget(name, w, h, minw, minh, maxw, maxh) {
beginEditIfNeeded(); // Save state before anything else
// Place canvas in edit mode upon adding a new widget
setIsEditable(true);
setIsDelete(true);
const def = WIDGET_REGISTRY[widgetKey];
if(!def) {
console.warn("appendWidget: unknown widgetKey:", widgetKey);
return;
};
// Widget Inspector Configuration Settings
const inspectorDef = WIDGET_INSPECTOR_REGISTRY[widgetKey];
const defaultConfig = inspectorDef?.defaultConfig ?? {};
const newID = uuid(); // could be replaced with something else like guid
const COLS = 16;
const W = w ?? 3;
const H = h ?? 3;
if (widgetKey === "gas_measurements") {
setInspectorTarget({ view, id: newID, widgetKey });
setInspectorOpen(true);
}
const {
w = def.defaults.w,
h = def.defaults.h,
minW = def.defaults.minW,
minH = def.defaults.minH,
maxW = def.defaults.maxW,
maxH = def.defaults.maxH,
} = overrides;
// DEBUG
console.log("after add:", { widgetKey, layoutLen: layout.length, widgetLen: widgetArray.length });
// IMPORTANT: use functional updates so you don't rely on stale widgetArray/layout
setWidgetArray(prev => [
...prev,
{
i: newID,
widgetKey,
// config: JSON.parse(JSON.stringify(defaultConfig)), // Can use this if Safari complains
config: structuredClone(defaultConfig)
},
setWidgetArray([ // updating widget array containing info on the types of widgets
...widgetArray,
{i: newID, widget: name }
]);
// onAddWidget(newID, w, h, minw, minh, maxw, maxh); // updating array containing info on layout of widgets
setLayout(prev => [
...prev,
{
i: newID,
x: (prev.length * w) % COLS,
x: (prev.length * W) % COLS,
y: 0,
w,
h,
minW,
minH,
maxW,
maxH,
w: w,
h: h,
minW: minw,
minH: minh,
maxW: maxw,
maxH: maxh,
static: false,
resizeHandles: ['se']
},
]);
}
function updateWidgetConfig(id, patchOrFn) {
setWidgetArray(prev =>
prev.map(w => {
if (w.i !== id) return w;
const nextConfig =
typeof patchOrFn === "function"
? patchOrFn(w.config ?? {})
: {...(w.config ?? {}), ...(patchOrFn ?? {}) };
return { ...w, config: nextConfig };
})
);
}
const onSave = () => {
console.log("saving widgets...");
// Set all widgets back to static: true
@@ -122,6 +61,8 @@ export default function Home(props) {
);
setIsEditable(false);
setIsDelete(false);
setOriginalLayout([]); // Clear backup layout
setOriginalWidgets([]);
}
const onEditWidgets = () => {
@@ -156,11 +97,8 @@ export default function Home(props) {
}
// Prop Forwarding
const MyDashboardProps = { view, layout, onLayoutChange, isEditable,
beginEditIfNeeded, isDelete, DeleteWidget, widgetArray, appendWidget,
openInspector
};
const settingsButtonProps = { view, onEditWidgets, appendWidget };
const MyDashboardProps = { layout, onLayoutChange, isEditable, beginEditIfNeeded, isDelete, DeleteWidget, widgetArray, appendWidget };
const settingsButtonProps = { onEditWidgets, appendWidget };
return (
/* HEADER */
@@ -212,26 +150,11 @@ export default function Home(props) {
{/* CANVAS */}
<Tabs.Content value="mydashboard">
<Flex height="100%" width="100%" gap="3">
<Box flex="1" minW="0">
<MyDashboard {...MyDashboardProps}/>
</Box>
{inspectorOpen && inspectorTarget && (
<WidgetInspector
open={inspectorOpen}
onClose={() => setInspectorOpen(false)}
target={inspectorTarget}
widgetArray={widgetArray}
updateWidgetConfig={updateWidgetConfig}
/>
)}
</Flex>
</Tabs.Content>
<Tabs.Content value="gas">
<MyDashboard {...MyDashboardProps}/>
</Tabs.Content>
<Tabs.Content value="gas">
<Gas />
</Tabs.Content>
<Tabs.Content value="seismic">
<Seismic />
</Tabs.Content>
@@ -241,9 +164,6 @@ export default function Home(props) {
<Tabs.Content value="daily">
<DailyActivity />
</Tabs.Content>
<Tabs.Content value="deformation">
<Deformation />
</Tabs.Content>
</Tabs.Root>
);
}
@@ -1,22 +0,0 @@
import { Box, Text } from "@chakra-ui/react";
export function Deformation() {
return(
<Box p={8} mt={4} ml="auto" mr="auto" width="600px">
<Box
bg="bg"
shadow="md"
borderRadius="md"
p={8}
width="600px"
height="100px"
textAlign="center"
display="flex"
alignItems="center"
justifyContent="center"
>
<Text fontsize="2xl" fontWeight="bold" color="gray.700">Deformation dashboard coming soon.</Text>
</Box>
</Box>
);
}
@@ -2,13 +2,9 @@ import GridLayout, { WidthProvider } from 'react-grid-layout';
import { useSlotRecipe, chakra, Flex } from '@chakra-ui/react';
import CustomResizeHandle from '../../CanvasComponents/CustomResizeHandle';
import FirstWidgetButton from '../../CanvasComponents/FirstWidgetButton';
import PlaceholderWidget from '../../CanvasComponents/PlaceholderWidget';
import { WIDGET_REGISTRY, WIDGET_COMPONENTS } from '@/constants/widgetRegistry';
export default function MyDashboard({view, layout, onLayoutChange,
isEditable, isDelete, DeleteWidget, widgetArray, appendWidget, openInspector
}) {
//console.log("rendering MyDashboard");
export default function MyDashboard({layout, onLayoutChange, isEditable, isDelete, DeleteWidget, widgetArray, appendWidget}) {
console.log("rendering MyDashboard");
const recipe = useSlotRecipe({ key: "widget" });
const styles = recipe(); // using root recipe for widget below
const ResponsiveGridLayout = WidthProvider(GridLayout); // canvas width now adjusts to window size
@@ -16,8 +12,8 @@ export default function MyDashboard({view, layout, onLayoutChange,
const handleDragStop = () => { document.body.style.userSelect = ""; }; // allow text highlighting all other times
let merged = [];
//console.log("widgetArray: ", widgetArray);
//console.log("layout: ", layout);
console.log("widgetArray: ", widgetArray);
console.log("layout: ", layout);
if (widgetArray.length && layout.length) {
const widgetMap = new Map(widgetArray.map(item => [item.i, item]));
@@ -40,7 +36,7 @@ export default function MyDashboard({view, layout, onLayoutChange,
bg="gray.150"
borderRadius="md"
>
<FirstWidgetButton view={view} appendWidget={ appendWidget } />
<FirstWidgetButton appendWidget={ appendWidget } />
</Flex>
);
}
@@ -63,36 +59,11 @@ export default function MyDashboard({view, layout, onLayoutChange,
position="relative"
resizeHandle={<CustomResizeHandle isVisable={isEditable} />}
>
{merged.map((item) => {
const def = WIDGET_REGISTRY[item.widgetKey];
const isReal = def?.kind === "real";
const Widget = isReal
? WIDGET_COMPONENTS[item.widgetKey]
: PlaceholderWidget;
const title =
def?.PlaceholderTitle ??
def?.label ??
item.widgetKey ??
"Widget";
{ merged.map((item) => {
const Widget = item.widget;
return (
<Flex key={item.i} css={styles.root} boxShadow="md" height="100%" >
<Widget
title={!isReal ? title : undefined} // placeholder uses title prop
id={item.i}
config={item.config}
isEditable={isEditable}
isDelete={isDelete}
DeleteWidget={DeleteWidget}
appendWidget={appendWidget}
openInspector={
openInspector ? () => openInspector({ id: item.i, widgetKey: item.widgetKey })
: undefined
}
/>
<Widget id={item.i} isEditable={isEditable} isDelete={isDelete} DeleteWidget={DeleteWidget} appendWidget={appendWidget}/>
</Flex>
);
})
@@ -1,60 +0,0 @@
import {Box, Text, Flex, Image} from '@chakra-ui/react'
import volcanoDefault from "@/utils/map-utils/icons/green-triangle.svg";
import redVolcano from "@/utils/map-utils/icons/red-triangle.svg";
import yellowVolcano from "@/utils/map-utils/icons/yellow-triangle.svg";
import seismicDefault from "@/utils/map-utils/icons/triangle-black.svg";
import cameraDefault from "@/utils/map-utils/icons/camera.svg";
import gpsDefault from "@/utils/map-utils/icons/gps-solid.svg";
import soundIcon from "@/utils/map-utils/icons/sound-black.svg";
import tiltIcon from "@/utils/map-utils/icons/line-segment.svg";
import cloudIcon from "@/utils/map-utils/icons/cloud.svg";
export const NVEWSLegend = (
<Box display="flex" flexDir="column" gap="2">
<Flex align="center" gap="2">
<Image src={redVolcano} boxSize="18px" alt="red volcano"/>
<Text>070% Complete</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={yellowVolcano} boxSize="18px" alt="yellow volcano"/>
<Text>70100% Complete</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={volcanoDefault} boxSize="18px" alt="green volcano"/>
<Text>100% Complete</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={seismicDefault} boxSize="18px" alt="green volcano"/>
<Text>Seismometer</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={cameraDefault} boxSize="18px" alt="green volcano"/>
<Text>Camera</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={gpsDefault} boxSize="18px" alt="green volcano"/>
<Text>GPS</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={soundIcon} boxSize="18px" alt="green volcano"/>
<Text>Infrasound</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={tiltIcon} boxSize="18px" alt="green volcano"/>
<Text>Tiltmeter</Text>
</Flex>
<Flex align="center" gap="2">
<Image src={cloudIcon} boxSize="18px" alt="green volcano"/>
<Text>Gas</Text>
</Flex>
</Box>
);
@@ -1,168 +0,0 @@
import React, {useState} from "react";
import { MapContainer, TileLayer, Marker, Popup, Circle, CircleMarker, Rectangle, Polygon,Polyline} from "react-leaflet";
import { Box, Text } from "@chakra-ui/react";
import MapButtonControls from "@/components/Canvas/CanvasComponents/MapButtonControls.jsx";
import MapFilter from "../CanvasComponents/MapFilter.jsx";
import MapLegend from "../CanvasComponents/MapLegend.jsx";
import { NVEWSLegend } from "../views/NVEWSLegend.jsx";
import {MapClickLogger} from "@/utils/map-utils/functions/mapHelpers.js";
import {ZoomWatcher} from "@/utils/map-utils/functions/mapHelpers.js";
import {FlyTo} from "@/utils/map-utils/functions/mapHelpers.js";
import {createIcon} from "@/utils/map-utils/functions/markerHelpers.js";
import {handleMarkerClick} from "@/utils/map-utils/functions/markerHelpers.js";
import {computePolyPositions} from "@/utils/map-utils/functions/markerHelpers.js";
import {shiftPos} from "@/utils/map-utils/functions/markerHelpers.js";
import volcanoDefault from "@/utils/map-utils/icons/green-triangle.svg";
import redVolcano from "@/utils/map-utils/icons/red-triangle.svg";
import yellowVolcano from "@/utils/map-utils/icons/yellow-triangle.svg";
import seismicDefault from "@/utils/map-utils/icons/triangle-black.svg";
import cameraDefault from "@/utils/map-utils/icons/camera.svg";
import gpsDefault from "@/utils/map-utils/icons/gps-solid.svg";
import soundIcon from "@/utils/map-utils/icons/sound-black.svg";
import tiltIcon from "@/utils/map-utils/icons/line-segment.svg";
import cloudIcon from "@/utils/map-utils/icons/cloud.svg";
export default function NVEWSMap ({type=[], origin=[], markers=[], mapFilters=[], zoom, selectedFilters, setFilterValue,
toggleInArray, selectedMarker, overlays = {}}) {
const [target, setTarget] = useState(null);
const [zoomLevel, setZoomLevel] = useState(zoom);
const link= type[0]
const attr = type[1]
const pos = [origin[0],origin[1]]
const enabledOverlays = selectedFilters?.overlay ?? [];
return (
<MapContainer center={pos} zoom={zoom} worldCopyJump={true} style = {{height:"100%",width:"100%"}} >
<MapClickLogger />
<ZoomWatcher onZoom={setZoomLevel}/>
<TileLayer url = {link} attribution = {attr}/>
<MapButtonControls settingsBtn =
{<MapFilter config ={mapFilters}
selected={selectedFilters}
setFilterValue={setFilterValue}
toggleInArray={toggleInArray}/>}
position="topright" />
<MapButtonControls settingsBtn = {<MapLegend legend={NVEWSLegend}/>}
position="bottomright" />
{enabledOverlays.map(id => (
<React.Fragment key={id}>
{overlays[id] ?? null}
</React.Fragment>
))}
<FlyTo position={target} zoom={9} />
<FlyTo position={selectedMarker} zoom={9} />
{markers.map(marker => {
let bounds = null;
let stationTypes = ["seismic_stations", "camera_stations", "gas_stations", "gnss_stations", "tiltmeter_stations",
"infrasound_stations"];
let stationMarkers = [];
let volcanoIcon = null;
const val = Number(String(marker.network_pct_complete).replace("%",""));
if(val < 70){
volcanoIcon = createIcon(redVolcano);
}
else if(val > 100){
volcanoIcon = createIcon(yellowVolcano);
}
else{
volcanoIcon = createIcon(volcanoDefault);
}
if(zoomLevel >= 7 && marker.include_reason === "Within Radius"){
bounds = <Circle center={[marker.latitude, marker.longitude]} radius={marker.nvews_radius_km * 1000}
pathOptions={{fill: false, color:'black'}}> </Circle>;
}
else if (zoomLevel >= 7 && marker.include_reason === "Within Polygon"){
bounds = <Polyline positions={computePolyPositions(marker)}
pathOptions={{color: 'black'}}> </Polyline>;
}
// const rawSelected = selectedFilters?.stations ?? [];
//
// const enabledStations =
// rawSelected.length === 0 || rawSelected.length === stationTypes.length
// ? stationTypes
// : rawSelected;
const enabledStations = selectedFilters?.stations ?? [];
if(zoomLevel >= 9){
for(const station of stationTypes){
if(marker[station].length > 0 && enabledStations.includes(station)){
for(let i =0 ; i< marker[station].length; i++){
stationMarkers.push(marker[station][i]);
}
}
}
}
return(
<React.Fragment key={marker.volcano_name}>
<Marker icon={volcanoIcon} position={[marker.latitude, marker.longitude]} eventHandlers={{
dblclick: () => handleMarkerClick(marker,setTarget)
}}>
<Popup>
<Box maxH="200px" maxW="200px" overflow="auto" borderRadius="md" bg="white">
<Text fontSize="xs" fontWeight="bold">{marker.volcano_name}</Text>
<Text fontSize="xs">Network Completion: {marker.network_pct_complete}</Text>
<Text fontSize="xs">Seismic Count: {marker.seismic_station_total} Target: {marker.seismic_target}</Text>
<Text fontSize="xs">GNSS Count: {marker.gnss_station_total} Target: {marker.gnss_target}</Text>
<Text fontSize="xs">Camera Count: {marker.camera_station_total} Target: {marker.camera_target}</Text>
<Text fontSize="xs">InfraSound Count: {marker.infrasound_station_total} Target: {marker.infrasound_target}</Text>
<Text fontSize="xs">Tilt Meter Count: {marker.tiltmeter_station_total} Target: {marker.tiltmeter_target}</Text>
<Text fontSize="xs">Real-Time Gas Count: {marker.gas_station_total} Target: {marker.gas_target}</Text>
</Box>
</Popup>
</Marker>
{bounds}
{stationMarkers.map(subMarker => {
let iconType = null;
if(subMarker.category === "Seismometer"){
iconType = createIcon(seismicDefault);
}
else if(subMarker.category === "Camera"){
iconType = createIcon(cameraDefault);
}
else if(subMarker.category === "GPS"){
iconType = createIcon(gpsDefault);
}
else if(subMarker.category === "Infrasound"){
iconType = createIcon(soundIcon);
}
else if(subMarker.category === "Tiltmeter"){
iconType = createIcon(tiltIcon);
}
else if(subMarker.category === "Gas"){
iconType = createIcon(cloudIcon);
}
// TODO: Add other icons as needed
return (
<Marker key={`${subMarker.station_id}-${subMarker.category}`}
position={[subMarker.latitude, subMarker.longitude]}
icon={iconType}>
<Popup>
<Box maxH="200px" maxW="200px" overflow="auto" borderRadius="md" bg="white">
<Text fontSize="xs" fontWeight="bold">{subMarker.station}</Text>
<Text fontSize="xs">Type: {subMarker.category}</Text>
<Text fontSize="xs">Sensor Total: {subMarker.sensor_total}</Text>
</Box>
</Popup>
</Marker>
)
})}
</React.Fragment>
)
})}
</MapContainer>
);
}