Commit 9069648d authored by Jacob Cain's avatar Jacob Cain
Browse files

Data Fetching/Splash, auto-populate planet menu

parent 4753b331
Loading
Loading
Loading
Loading
+142 −2
Original line number Diff line number Diff line
import React from "react";
import React, { useEffect, useState } from "react";
import UsgsHeader from "../presentational/UsgsHeader.jsx";
import UsgsFooter from "../presentational/UsgsFooter.jsx";
import GeoStacApp from "./GeoStacApp.jsx";
import SplashScreen from "../presentational/SplashScreen.jsx";

/**
 * App is the parent component for all of the other components in the project.
 * It loads the data needed to initialize GeoStac.
 * It includes the main GeoStacApp and OCAP compliant headers and footers.
 *
 * @component
 */
export default function App() {

  const [mainComponent, setMainComponent] = useState(() => {
    return(
      <SplashScreen />
    );
  })

  useEffect(() => {

    // Astro Web Maps, has the tile data
    const astroWebMaps =
        "https://astrowebmaps.wr.usgs.gov/webmapatlas/Layers/maps.json";

    // STAC API, has footprint data
    const stacApiCollections = 
        "https://stac.astrogeology.usgs.gov/api/collections";

    // Async tracking
    let fetchStatus = {}
    let fetchPromise = {}
    let jsonPromise = {}

    // Fetched Maps
    var mapsJson = {}

    // Combined Data
    var aggregateMapList = {}

    // Init
    fetchStatus[astroWebMaps] = "Not Started";
    fetchPromise[astroWebMaps] = "Not Started";
    jsonPromise[astroWebMaps] = "Not Started";
    mapsJson[astroWebMaps] = [];

    fetchStatus[stacApiCollections] = "Not Started";
    fetchPromise[stacApiCollections] = "Not Started";
    jsonPromise[stacApiCollections] = "Not Started";
    mapsJson[stacApiCollections] = [];

    // Fetch JSON and read into object
    async function ensureFetched(targetUrl) {
        if(fetchStatus[targetUrl] === "Not Started")
        {
            fetchStatus[targetUrl] = "Started"
            fetchPromise[targetUrl] = fetch(
              targetUrl
            ).then((res)=>{
                jsonPromise[targetUrl] = res.json().then((jsonData)=>{
                    mapsJson[targetUrl] = jsonData;
                }).catch((err)=>{
                    console.log(err)
                });
            }).catch((err) => {
                console.log(err);
            });
        }
        await fetchPromise[targetUrl];
        await jsonPromise[targetUrl];
    }

    // Combine data from Astro Web Maps and STAC API into one new object
    function organizeData(astroWebMaps, stacApiCollections) {
        
        // Initialize Objects
        let mapList = { "systems" : [] };
        let stacList = [];

        // Check for Planets that have STAC footprints from the STAC API
        for (let i = 0; i < stacApiCollections.collections.length; i++) {
            if (stacApiCollections.collections[i].hasOwnProperty("summaries")){
                let stacTarget = stacApiCollections.collections[i].summaries["ssys:targets"][0].toLowerCase();
                if(!stacList.find(targetBody => targetBody == stacTarget)){
                    stacList.push(stacTarget.toLowerCase());
                }
            }
        }

        for (const target of astroWebMaps.targets){

            // Check for/add system
            if (!mapList.systems.some(system => system.name === target.system)) {
                mapList.systems.push({
                    "name" : target.system,
                    "bodies" : []
                })
            }
            
            // Index of System
            let sysIndex = mapList.systems.map(sys => sys.name).indexOf(target.system);

            // Check for/add body
            if (!mapList.systems[sysIndex].bodies.some(body => body.name === target.name)) {

                // A flag that indicates whether or not the body has footprints
                let hasFootprints = stacList.includes(target.name.toLowerCase())

                mapList.systems[sysIndex].bodies.push({
                    "name" : target.name,
                    "has-footprints" : hasFootprints,
                    "maps" : []
                })
            }

            // Index of Body
            let bodIndex = mapList.systems[sysIndex].bodies.map(bod => bod.name).indexOf(target.name);

            // Add maps
            for (const wmap of target.webmap) {
                mapList.systems[sysIndex].bodies[bodIndex].maps.push(wmap.displayname);
                // More properties?
            }

        }

        return mapList;
    }

    // Fetch and organize data from 
    async function getStacAndAstroWebMapsData() {
        // Start fetching from AWM and STAC API concurrently
        ensureFetched(astroWebMaps);
        ensureFetched(stacApiCollections);

        // Wait for both to complete before moving on
        await ensureFetched(astroWebMaps);
        await ensureFetched(stacApiCollections);

        return organizeData(mapsJson[astroWebMaps], mapsJson[stacApiCollections]);
    }

    (async () => {
        aggregateMapList = await getStacAndAstroWebMapsData();
        setMainComponent(<GeoStacApp mapList={aggregateMapList}/>);
    })();

    
  }, [])

  return (
    <>
      <UsgsHeader />
      <GeoStacApp />
      {mainComponent}
      <UsgsFooter />
    </>
  );
+2 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ let css = {
 *
 * @component
 */
export default function GeoStacApp() {
export default function GeoStacApp(props) {
  const [targetPlanet, setTargetPlanet] = React.useState("Mars");

  const [footprintData, setFootprintData] = React.useState([]);
@@ -64,6 +64,7 @@ export default function GeoStacApp() {
        <div className="flex col">
          <ConsoleAppBar
            target={targetPlanet}
            mapList={props.mapList}
            bodyChange={handleTargetBodyChange}
          />
          <div id="map-area">
+1 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ export default function ConsoleAppBar(props) {
      <div className="flexbar-item">
        <ConsoleTargetInfo
          target={props.target}
          mapList={props.mapList}
          bodyChange={props.bodyChange}
        />
      </div>
+59 −26
Original line number Diff line number Diff line
@@ -9,15 +9,19 @@ import DialogTitle from "@mui/material/DialogTitle";
import Avatar from "@mui/material/Avatar";
import List from "@mui/material/List";
import ListSubheader from "@mui/material/ListSubheader";
import ListItem from "@mui/material/ListItem";
import ListItemButton from "@mui/material/ListItemButton";
import ListItemAvatar from "@mui/material/ListItemAvatar";
import ListItemText from "@mui/material/ListItemText";
import Collapse from '@mui/material/Collapse';
import { blue } from "@mui/material/colors";

// Icons
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import ScatterPlotIcon from '@mui/icons-material/ScatterPlot';
import PublicIcon from "@mui/icons-material/Public";
import DarkModeIcon from "@mui/icons-material/DarkMode";
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';

/**
 * Controls css styling for this component using js to css
@@ -42,6 +46,11 @@ let css = {
  },
};


// Delete if new data loading works
// Unless we add images here
// Why is Puck not on this list?

const planets = [
  ["Mercury"],
  ["Venus"],
@@ -81,6 +90,19 @@ const moons = [
 * @returns Planet Selection Dialog
 */
function PlanetDialog(props) {

  const [openSys, setOpenSys] = React.useState(Array(props.mapList.systems.length).fill(false));

  function handleSysOpen(index){
    const nextOpenSys = openSys.map((isOpen, curIndex) => {
      if (index === curIndex) {
        return !isOpen;
      }
      return false;
    });
    setOpenSys(nextOpenSys);
  }

  const { onClose, selectedValue, open } = props;

  const handleClose = () => {
@@ -91,35 +113,45 @@ function PlanetDialog(props) {
    onClose(value);
  };

  console.log(props.mapList)

  return (
    <Dialog onClose={handleClose} open={open}>
      <DialogTitle>Select Target Body</DialogTitle>
      <List sx={{ pt: 0 }}>
        <ListSubheader value="Mars">Planets</ListSubheader>
        {planets.map((planet) => (
          <ListItem
            button
            onClick={() => handleListItemClick(planet[0])}
            key={planet[0]}
        <ListSubheader value="None">Systems</ListSubheader>
        {props.mapList.systems.map((system, sysIndex) => (
          <>
            <ListItemButton
              onClick={() => handleSysOpen(sysIndex)}
              key={system.name}
            >
              <ListItemAvatar>
                <Avatar sx={{ bgcolor: blue[100] }}>
                <PublicIcon />
                  <ScatterPlotIcon />
                </Avatar>
              </ListItemAvatar>
            <ListItemText primary={planet[0]} />
          </ListItem>
        ))}
        <ListSubheader value="Moon">Moons and Other Bodies</ListSubheader>
        {moons.map((moon) => (
          <ListItem button onClick={() => handleListItemClick(moon)} key={moon}>
              <ListItemText primary={system.name} />
            </ListItemButton>
            <Collapse in={openSys[sysIndex]} timeout="auto" unmountOnExit>
              <List component="div" disablePadding>
                {props.mapList.systems[sysIndex].bodies.map((body, bodIndex) => (
                  <ListItemButton
                    sx={{ pl: 4 }}
                    onClick={() => handleListItemClick(body.name)}
                    key={body.name}
                  >
                    <ListItemAvatar>
                      <Avatar sx={{ bgcolor: blue[100] }}>
                <DarkModeIcon />
                        {body.name === system.name ? <PublicIcon /> : <DarkModeIcon/>}
                      </Avatar>
                    </ListItemAvatar>
            <ListItemText primary={moon} />
          </ListItem>
                    <ListItemText primary={body.name} />
                  </ListItemButton>
                ))}
              </List>
            </Collapse>
          </>
        ))}
      </List>
    </Dialog>
@@ -177,6 +209,7 @@ export default function ConsoleTargetInfo(props) {
        </Typography>
      </Grid>
      <PlanetDialog
        mapList={props.mapList}
        selectedValue={selectedValue}
        open={open}
        onClose={handleClose}
+26 −0
Original line number Diff line number Diff line
import React from "react";
import SvgIcon from "@mui/material/SvgIcon";
import loadingImage from "../../images/logos/geostac-logo.svg";

export default function SplashScreen() {
    return(
        <div className="flex col scroll-parent">
            <div className="white loading-slate">
                <h1>GeoSTAC</h1>
                <p>Fetching map data...</p>
                <SvgIcon
                viewBox="0 0 375 375"
                style={{
                    color: "#343a40",
                    top: 3,
                    width: 200,
                    height: 150,
                    position: "relative",
                    paddingBottom: 20
                }}
                component={loadingImage}
                />
            </div>
        </div>
    )
}
 No newline at end of file
Loading