Skip to content

Commit

Permalink
Merge pull request #81 from RENCI/model-selection-sync
Browse files Browse the repository at this point in the history
Model selection sync
  • Loading branch information
PhillipsOwen authored Jun 11, 2024
2 parents 0e54cd5 + a00c757 commit 7340498
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/image-push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
APP_UI_DATA_URL=${{ secrets.UI_DATA_URL }}
APP_UI_DATA_TOKEN=${{ secrets.UI_DATA_TOKEN }}
APP_UI_MAPBOX_TOKEN=${{ secrets.UI_MAPBOX_TOKEN }}
APP_UI_HURRICANE_ICON_URL=${{ secrets.UI_HURRICANE_ICON_URL }}
tags: |
${{ env.REGISTRY }}:latest
${{ env.REGISTRY }}:${{ steps.get_version.outputs.VERSION }}
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,20 @@ ARG APP_GS_DATA_URL=$(APP_GS_DATA_URL)
ARG APP_UI_DATA_URL=$(APP_UI_DATA_URL)
ARG APP_UI_DATA_TOKEN=$(APP_UI_DATA_TOKEN)
ARG APP_UI_MAPBOX_TOKEN=$(APP_UI_MAPBOX_TOKEN)
ARG APP_UI_HURRICANE_ICON_URL=$(APP_UI_HURRICANE_ICON_URL)

# now add the values into ENV params
ENV REACT_APP_VERSION=$APP_VERSION
ENV REACT_APP_GS_DATA_URL=$APP_GS_DATA_URL
ENV REACT_APP_UI_DATA_URL=$APP_UI_DATA_URL
ENV REACT_APP_UI_DATA_TOKEN=$APP_UI_DATA_TOKEN
ENV REACT_APP_MAPBOX_TOKEN=$APP_UI_MAPBOX_TOKEN
ENV REACT_APP_HURRICANE_ICON_URL=$APP_UI_HURRICANE_ICON_URL

# create the env file
RUN printf "NODE_ENV=production\nREACT_APP_VERSION=$APP_VERSION\nREACT_APP_GS_DATA_URL=$APP_GS_DATA_URL\n" > .env
RUN printf "REACT_APP_UI_DATA_URL=$APP_UI_DATA_URL\nREACT_APP_UI_DATA_TOKEN=$APP_UI_DATA_TOKEN\n" >> .env
RUN printf "REACT_APP_MAPBOX_TOKEN=$REACT_APP_MAPBOX_TOKEN\n" >> .env
RUN printf "REACT_APP_MAPBOX_TOKEN=$REACT_APP_MAPBOX_TOKEN\nREACT_APP_HURRICANE_ICON_URL=$REACT_APP_HURRICANE_ICON_URL\n" >> .env

# Copy in source files
COPY . /src
Expand Down
170 changes: 143 additions & 27 deletions src/components/model-selection/catalogItems.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, {Fragment, useState} from "react";
import React, { Fragment, useState } from "react";
import { AccordionGroup, Accordion, AccordionSummary, AccordionDetails, Stack, Checkbox } from '@mui/joy';
import PropTypes from 'prop-types';
import {AccordionGroup, Accordion, AccordionSummary, AccordionDetails, Stack, Checkbox} from '@mui/joy';
import { useLayers } from "@context/map-context";

// set component prop types
CatalogItems.propTypes = { data: PropTypes.any};
CatalogItems.propTypes = { data: PropTypes.any };

/**
* returns a list of drop down options for that data/type.
Expand All @@ -13,48 +14,150 @@ CatalogItems.propTypes = { data: PropTypes.any};
* @constructor
*/
export default function CatalogItems(data) {
// get the layers in state
const { defaultModelLayers, setDefaultModelLayers } = useLayers();

// create some state for what catalog accordian is expanded/not expanded
const [accordianDateIndex, setAccordianDateIndex] = useState(-1);

// variables for the met-class type data
let numberName = '';
let numberElement = '';
// variables for the display of checkbox labels
let stormOrModelName = null;
let stormOrModelEle = null;
let numberName = null;
let numberEle = null;

/**
* handles the model checkbox click
*
* @param catalogMembers
* @param layerGroup
* @param checked
*/
const handleCBClick = (catalogMembers, layerGroup, checked) => {
// get the layers selected
const layers = catalogMembers.filter( catalogMembers => catalogMembers.group === layerGroup );

// add or remove the layer group
handleSelectedLayers(layerGroup, layers, checked);
};

/**
* checks to see if this checkbox needs to be set based on the layers currently in layer state.
*
* @param layerGroup
*/
const getCheckedState = ( layerGroup ) => {
// return the checked state
return defaultModelLayers.some(item => item.group === layerGroup);
};

/**
* handles updating the default layers on the map surface
*
* @param layerGroup
* @param selectedLayers
* @param checked
*/
const handleSelectedLayers = (layerGroup, selectedLayers, checked) => {
// add visibility state property to retrieved catalog layers
let newLayers = [];

// first see if this set of layers already exists in state, and remove them if the selection was unchecked
if (defaultModelLayers.find(layer => layer.group === layerGroup) && !checked) {
// remove the layers from the layer list in state
newLayers = defaultModelLayers.filter(layer => layer.group !== layerGroup);

// reset the visible layer states for all layers in the layer tray.
[...newLayers].forEach((layer) => {
// here we use whatever group is at the top of the list to set the visible layers
layer.state = newLayerDefaultState(layer, newLayers[0].group);
});

// reload the default layers less the layer group that was unselected
setDefaultModelLayers(newLayers);
}
else if (!defaultModelLayers.find(layer => layer.group === layerGroup) && checked) {
// loop through the select layers in the group and add the default layer state
selectedLayers.forEach((layer) => {
// add the item to the list with the default state
newLayers.push({ ...layer, state: { visible: false }});
});

// reset the visible layer states for all layers in the layer tray.
[...newLayers, ...defaultModelLayers].forEach((layer) => {
// perform the visible state logic
layer.state = newLayerDefaultState(layer, layerGroup);
});

// save the items to state so they can be rendered
setDefaultModelLayers([...newLayers, ...defaultModelLayers]);
}
else
// TODO: the checkbox checked value should follow what is in the defaultModelLayers state
console.warn(`Layer group ${layerGroup} already exists.`);
};

/**
* adds or updates the visibility of the layer on the map surface.
*
* presumably this will have to take the met class into consideration.
*
* @param layer
* @param group
* @returns {{ visible: boolean }}
*/
const newLayerDefaultState = (layer, group) => {
// if this is an obs layer and is the one just added
if (layer.group === group &&
(layer.properties['product_type'] === 'obs' || layer.properties['product_type'] === 'maxele63'))
// make this layer visible
return ({ visible: true });
else
// make this layer invisible
return ({ visible: false });
};

// do not render if there is no data
if (data.data != null) {
// if there was a warning getting the result
if (data.data['Warning'] !== undefined) {
return (
<div>
Warning: {data.data['Warning']}
Warning: { data.data['Warning'] }
</div>
);
}
// if there was an error getting the result
else if(data.data['Error'] !== undefined) {
return (
<div>
Error: {data.data['Error']}
Error: { data.data['Error'] }
</div>
);
}
// return all the data cards
else {
// save the name of the element for advisory or cycle numbers
// save the name of the element for tropical storms and advisory numbers
if (data.isTropical) {
numberName = ' Advisory: ';
numberElement = 'advisory_number';
stormOrModelName = '';
stormOrModelEle = 'storm_name';
numberName = ' Adv: ';
numberEle = 'advisory_number';
}
// save the name of the synoptic ADCIRC models and cycle numbers
else if (!data.isTropical) {
stormOrModelName = '';
stormOrModelEle = 'model';
numberName = ' Cycle: ';
numberElement = 'cycle';
numberEle = 'cycle';
}

// render the results of the data query
return (
<Fragment>
<AccordionGroup sx={{maxWidth: 415, size: "sm", variant: "soft"}}>
<AccordionGroup sx={{ maxWidth: 415, size: "sm", variant: "soft" }}>
{
// loop through the catalog data and create checkbox selections
data.data['catalog']
.filter(catalogs => catalogs !== "")
.map(
Expand All @@ -65,20 +168,33 @@ export default function CatalogItems(data) {
key={ itemIndex }
sx={{ p: 0 }}
expanded={accordianDateIndex === itemIndex}
onChange={(event, expanded) => {
setAccordianDateIndex(expanded ? itemIndex : null);
}}>
<AccordionSummary>
{catalog['id']}
</AccordionSummary>

<AccordionDetails>
{ catalog['members']
.filter((val, idx, self) => (
idx === self.findIndex((t)=> (
t['group'] === val['group']))))
.map((member, memberIndex) => (
<Checkbox sx={{ m: .5 }} key={ memberIndex } label={ numberName + member['properties'][numberElement] + ' (' + member['properties']['grid_type'] + ')' }/>
onChange={(event, expanded) => { setAccordianDateIndex(expanded ? itemIndex : null); }}>

<AccordionSummary sx={{ fontSize: 15 }}> {catalog['id']} </AccordionSummary>

<AccordionDetails> {
// loop through the data members and put them away
catalog['members']
// filter by the group name
.filter((val, idx, self) =>
( idx === self.findIndex((t)=> ( t['group'] === val['group']) )))
.sort((a, b) => a['properties'][numberEle] > b['properties'][numberEle] ? 1 : -1)
// output summarized details of each group member
.map((mbr, mbrIdx) => (
// create the checkbox
<Checkbox
sx={{ m: .5, fontSize: 12 }}
key={ mbrIdx }
checked={ getCheckedState(mbr.group) }
label={
stormOrModelName + mbr['properties'][stormOrModelEle].toUpperCase() + ', ' +
numberName + mbr['properties'][numberEle] +
', Type: ' + mbr['properties']['event_type'] +
', Grid: ' + mbr['properties']['grid_type']
}
onChange={ (event) => handleCBClick( catalog['members'], mbr['group'],
event.target.checked) }
/>
))
}
</AccordionDetails>
Expand Down
Loading

0 comments on commit 7340498

Please sign in to comment.