Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 217 - compare mode panel #229

Merged
merged 7 commits into from
Sep 13, 2024
3 changes: 3 additions & 0 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import { ObservationDialog } from "@components/dialog/observation-dialog";
import { useLayers } from '@context';
import { Sidebar } from '@components/sidebar';
import { ControlPanel } from '@components/control-panel';
import { ComparePanel } from '@components/compare-panel';
import { MapLegend } from '@components/legend';



/**
* renders the main content
*
Expand All @@ -34,6 +36,7 @@ const Content = () => {
<Map />
<Sidebar />
<ControlPanel/>
<ComparePanel/>
<MapLegend />
</Fragment>
);
Expand Down
174 changes: 174 additions & 0 deletions src/components/compare-panel/compare-panel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import React, { Fragment, useRef } from 'react';
import { Stack, Typography, Box, Button, Card } from '@mui/joy';
import { useLayers } from '@context';
import { SwapHorizontalCircleSharp as SwapLayersIcon } from '@mui/icons-material';

/**
* gets the header data property name index
* This takes into account the two types of runs (tropical, synoptic)
*
* @param layerProps
* @param type
* @returns {string}
*/
const getPropertyName = (layerProps, element_name) => {
// init the return
let ret_val = undefined;

// capture the name of the element for tropical storms and advisory numbers
if (layerProps['met_class'] === 'tropical') {
// by the element name
switch (element_name) {
case 'stormOrModelEle':
ret_val = layerProps['storm_name'];
break;
case 'numberName':
ret_val = ' Adv: ';
break;
case 'numberEle':
ret_val = layerProps['advisory_number'];
break;
}
}
// capture the name of the synoptic ADCIRC models and cycle numbers
else {
switch (element_name) {
case 'stormOrModelEle':
ret_val = layerProps['model'];
break;
case 'numberName':
ret_val = ' Cycle: ';
break;
case 'numberEle':
ret_val = layerProps['cycle'];
break;
}
}

// return to the caller
return ret_val;
};

export const ComparePanel = () => {
const {
defaultModelLayers,

// declare access to the compare mode items
defaultSelected,
leftPaneID, setLeftPaneID,
rightPaneID, setRightPaneID,
leftPaneType, setLeftPaneType,
rightPaneType, setRightPaneType,
leftLayerProps, setLeftLayerProps,
rightLayerProps, setRightLayerProps,
resetCompare
} = useLayers();

// get the default model run layers
const layers = [...defaultModelLayers];

/**
* gets the summary header text for the layer groups.
* This takes into account the two types of runs (tropical, synoptic)
*
* @param layerProps
* @returns {string}
*/
const getHeaderSummary = (paneID) => {
// get the summary if a layer has been selected
if (paneID !== defaultSelected) {
// get the layer props
const layerProps = layers.filter(item => item.id === paneID)[0]['properties'];

// if layer properties were captured
if (layerProps !== undefined) {
// get the full accordian summary text
return '' +
((getPropertyName(layerProps, 'stormOrModelEle') === undefined) ? 'Data error' : getPropertyName(layerProps, 'stormOrModelEle').toUpperCase()) +
', ' + getPropertyName(layerProps, 'numberName') + getPropertyName(layerProps, 'numberEle') +
', Type: ' + layerProps['event_type'] +
', Grid: ' + layerProps['grid_type'] +
((layerProps['meteorological_model'] === 'None') ? '' : ', ' + layerProps['meteorological_model']);
}
}
else {
// return the default text
return defaultSelected;
}
};

/**
* swaps the selected layer position in the compare panes
*
*/
const swapPanes = () => {
// get the original left pane data
const origLeftPaneType = leftPaneType;
const origLeftPaneID = leftPaneID;
const origLeftLayerProps = leftLayerProps;

// clear the left layer type/ID
setLeftPaneType(rightPaneType);
setLeftPaneID(rightPaneID);
setLeftLayerProps(rightLayerProps);

// clear the right pane ID/Name
setRightPaneType(origLeftPaneType);
setRightPaneID(origLeftPaneID);
setRightLayerProps(origLeftLayerProps);
};

// create a reference to avoid the findDOMNode deprecation issue
const nodeRef = useRef(null);

// render the panel
return (
<Fragment>
{
// display the selected product details for each pane
(leftPaneID !== defaultSelected || rightPaneID !== defaultSelected) ?
<Card
ref={nodeRef}
aria-labelledby="draggable-compare"
variant="soft"
sx={{
p: 0,
position: 'absolute',
top: '3px',
right: '1px',
filter: 'opacity(0.9)',
'&:hover': {filter: 'opacity(1.0)'},
// padding: '10px',
ml: 1, mr: 1,
zIndex: 999,
height: 43
}}>
<Stack direction={"row"} gap={ 1 } ref={ nodeRef } sx={{ mt: .5 , mb: .5 }}>
{
// render the left pane selections
<Stack direction={"column"} gap={ .5 } sx={{ ml: .5 }}>
<Typography sx={{ m: 0 }} level="body-xs">{ getHeaderSummary(leftPaneID) } </Typography>
<Typography sx={{ m: 0 }} level="body-xs">{ leftPaneType } </Typography>
</Stack>
}

<Box textAlign='center'>
<Button size="md" color="success" sx={{ m: 0 }} onClick={ swapPanes }><SwapLayersIcon/></Button>
</Box>

{
<Stack direction={"column"} gap={ .5 }>
<Typography sx={{ m: 0 }} level="body-xs">{ getHeaderSummary(rightPaneID) } </Typography>
<Typography sx={{ m: 0 }} level="body-xs">{ rightPaneType }</Typography>
</Stack>
}

<Box textAlign='center'>
<Button size="md" sx={{ mr: .5 }} onClick={ resetCompare }>Reset</Button>
</Box>
</Stack>
</Card> : ''
}
</Fragment>
);
};
1 change: 1 addition & 0 deletions src/components/compare-panel/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './compare-panel';
2 changes: 1 addition & 1 deletion src/components/legend/legend.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export const MapLegend = () => {
sx={{
p: 0,
position: 'absolute',
top: '10px',
top: '60px',
right: '10px',
transition: 'filter 250ms',
filter: 'opacity(0.9)',
Expand Down
116 changes: 19 additions & 97 deletions src/components/trays/compare-layers/CompareLayersTray.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { Fragment, useState, useEffect } from 'react';
import { Stack, Typography, Box, Button, Card, Accordion, AccordionSummary, AccordionDetails, AccordionGroup } from '@mui/joy';
import { useLayers, useSettings } from '@context';
import { getNamespacedEnvParam } from "@utils/map-utils";
import { SwapVerticalCircleSharp as SwapLayersIcon } from '@mui/icons-material';
import SldStyleParser from 'geostyler-sld-parser';

// install the side by side compare control
Expand Down Expand Up @@ -107,37 +106,31 @@ export const CompareLayersTray = () => {
map,
defaultModelLayers,
getLayerIcon,

// declare access to the compare mode items
defaultSelected,
leftPaneID, setLeftPaneID,
rightPaneID, setRightPaneID,
leftPaneType, setLeftPaneType,
rightPaneType, setRightPaneType,
leftLayerProps, setLeftLayerProps,
selectedLeftLayer, setSelectedLeftLayer,
rightLayerProps, setRightLayerProps,
selectedRightLayer, setSelectedRightLayer,
setSideBySideLayers,
removeSideBySideLayers
resetCompare, removeSideBySideLayers
} = useLayers();

const {
mapStyle,
} = useSettings();

// default for the pane compare name
const defaultSelected = 'Not Selected';

// create some state for the left/right name selections
const [leftPaneType, setLeftPaneType] = useState(defaultSelected);
const [rightPaneType, setRightPaneType] = useState(defaultSelected);

// create some state for the left/right ID selections
const [leftPaneID, setLeftPaneID] = useState(defaultSelected);
const [rightPaneID, setRightPaneID] = useState(defaultSelected);

// used to collapse other open accordions
const [accordionIndex, setAccordionIndex] = useState(null);

// get the default model run layers
const layers = [...defaultModelLayers];

const [leftLayerProps, setLeftLayerProps] = useState(null);
const [rightLayerProps, setRightLayerProps] = useState(null);

const [selectedLeftLayer, setSelectedLeftLayer] = useState(null);
const [selectedRightLayer, setSelectedRightLayer] = useState(null);

// get a handle to the leaflet component
const L = window.L;

Expand All @@ -151,30 +144,6 @@ export const CompareLayersTray = () => {
const gs_wfs_url = `${ getNamespacedEnvParam('REACT_APP_GS_DATA_URL') }`;
const gs_wms_url = gs_wfs_url + 'wms';

/**
* clears any captured compare selection data and layers
*
*/
const resetCompare = () => {
// clear the left layer type/ID/properties/layer
setLeftPaneType(defaultSelected);
setLeftPaneID(defaultSelected);
setLeftLayerProps(null);
setSelectedLeftLayer(null);

// clear the right pane ID/Name/properties/layer
setRightPaneType(defaultSelected);
setRightPaneID(defaultSelected);
setRightLayerProps(null);
setSelectedRightLayer(null);

// remove the side by side layers
removeSideBySideLayers();

// rollup the accordions
setAccordionIndex(null);
};

/**
* Save the layer info to state
*
Expand Down Expand Up @@ -202,24 +171,11 @@ export const CompareLayersTray = () => {
};

/**
* swaps the selected layer position in the compare panes
*
* resets the accordion
*/
const swapPanes = () => {
// get the original left pane data
const origLeftPaneType = leftPaneType;
const origLeftPaneID = leftPaneID;
const origLeftLayerProps = leftLayerProps;

// clear the left layer type/ID
setLeftPaneType(rightPaneType);
setLeftPaneID(rightPaneID);
setLeftLayerProps(rightLayerProps);

// clear the right pane ID/Name
setRightPaneType(origLeftPaneType);
setRightPaneID(origLeftPaneID);
setRightLayerProps(origLeftLayerProps);
const resetAccordion = () => {
// rollup the accordions
setAccordionIndex(null);
};

/**
Expand Down Expand Up @@ -259,6 +215,7 @@ export const CompareLayersTray = () => {
useEffect(() => {
// reset this view
resetCompare();
resetAccordion();
}, [defaultModelLayers]);

/**
Expand Down Expand Up @@ -377,10 +334,10 @@ export const CompareLayersTray = () => {
</Stack>

<Box textAlign="center">
<Button size="md" color={ (layer.id === leftPaneID) ? 'success' : 'primary' }
<Button size="sm" color={ (layer.id === leftPaneID) ? 'success' : 'primary' }
sx={{ mr: 4 }}
onClick={ () => setPaneInfo('left', layer.properties['product_name'], layer.id) }>Left pane</Button>
<Button size="md" color={ (layer.id === rightPaneID) ? 'success' : 'primary' }
<Button size="sm" color={ (layer.id === rightPaneID) ? 'success' : 'primary' }
sx={{ m: 0 }}
onClick={ () => setPaneInfo('right', layer.properties['product_name'], layer.id) }>Right pane</Button>
</Box>
Expand Down Expand Up @@ -431,41 +388,6 @@ export const CompareLayersTray = () => {
))
}
</AccordionGroup>

{
// display the selected product details for each pane
( leftPaneID !== defaultSelected || rightPaneID !== defaultSelected ) ?
<Fragment>
<Card>
<Stack direction={"column"} gap={ 1 }>
{
// render the left pane selections
<Fragment>
<Typography sx={{ ml: 1 }} level="body-sm">Left pane:</Typography>
<Typography sx={{ ml: 2 }} level="body-sm">Type: { leftPaneType } </Typography>
<Typography sx={{ ml: 2, mb: 2 }} level="body-sm">ID: { leftPaneID } </Typography>
</Fragment>
}

<Box textAlign='center'>
<Button size="md" color="success" sx={{ mt: 0, mb: 1 }} onClick={ swapPanes }><SwapLayersIcon/></Button>
</Box>

{
<Fragment>
<Typography sx={{ ml: 1 }} level="body-sm">Right pane:</Typography>
<Typography sx={{ ml: 2 }} level="body-sm">Type: { rightPaneType }</Typography>
<Typography sx={{ ml: 2 }} level="body-sm">ID: { rightPaneID }</Typography>
</Fragment>
}

<Button size="md" sx={{ mt: 1}} onClick={ resetCompare }>Reset</Button>

</Stack>
</Card>
</Fragment>: ''
}

</Fragment>
);
};
Loading