Commit ff573cfe authored by Clark's avatar Clark
Browse files

resolved merge conflicts in GeoStacApp.jsx

parents 57a3e9cc d6d2b822
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -35,10 +35,10 @@ export default function App() {
    let jsonPromise = {};
    let jsonPromise = {};


    // Fetched Maps
    // Fetched Maps
    var mapsJson = {};
    let mapsJson = {};


    // Combined Data
    // Combined Data
    var aggregateMapList = {};
    let aggregateMapList = {};


    // Init
    // Init
    fetchStatus[astroWebMaps] = "Not Started";
    fetchStatus[astroWebMaps] = "Not Started";
+2 −2
Original line number Original line Diff line number Diff line
@@ -29,7 +29,7 @@ let css = {
 * @component
 * @component
 */
 */
export default function GeoStacApp(props) {
export default function GeoStacApp(props) {
  const [targetPlanet, setTargetPlanet] = React.useState("Mars");
  const [targetPlanet, setTargetPlanet] = React.useState(props.mapList.systems[4].bodies[0]);


  const [footprintData, setFootprintData] = React.useState([]);
  const [footprintData, setFootprintData] = React.useState([]);


@@ -68,7 +68,7 @@ export default function GeoStacApp(props) {
            bodyChange={handleTargetBodyChange}
            bodyChange={handleTargetBodyChange}
          />
          />
          <div id="map-area">
          <div id="map-area">
            <MapContainer target={targetPlanet} mapList={props.mapList} />
            <MapContainer target={targetPlanet.name} mapList={props.mapList}/>
          </div>
          </div>
          <QueryConsole />
          <QueryConsole />
        </div>
        </div>
+6 −9
Original line number Original line Diff line number Diff line
@@ -120,18 +120,15 @@ function PlanetDialog(props) {
    onClose(value);
    onClose(value);
  };
  };


  console.log(props.mapList)

  return (
  return (
    <Dialog PaperProps={{sx: {overflowY: "scroll"}}} onClose={handleClose} open={open}>
    <Dialog PaperProps={{sx: {overflowY: "scroll"}}} onClose={handleClose} open={open}>
      <DialogTitle sx={{ minWidth: 225 }}>Select Target Body</DialogTitle>
      <DialogTitle sx={{ minWidth: 225 }}>Select Target Body</DialogTitle>
      <List sx={{ pt: 0 }}>
      <List sx={{ pt: 0 }}>
        <ListSubheader value="None">Systems</ListSubheader>
        <ListSubheader value="None">Systems</ListSubheader>
        {props.mapList.systems.map((system, sysIndex) => (
        {props.mapList.systems.map((system, sysIndex) => (
          <>
          <React.Fragment key={system.name}>
            <ListItemButton
            <ListItemButton
              onClick={() => handleSysOpen(sysIndex)}
              onClick={() => handleSysOpen(sysIndex)}
              key={system.name}
            >
            >
              <ListItemAvatar>
              <ListItemAvatar>
                <Avatar sx={{ bgcolor: blue[100] }}>
                <Avatar sx={{ bgcolor: blue[100] }}>
@@ -147,7 +144,7 @@ function PlanetDialog(props) {
                {props.mapList.systems[sysIndex].bodies.map((body, bodIndex) => (
                {props.mapList.systems[sysIndex].bodies.map((body, bodIndex) => (
                  <ListItemButton
                  <ListItemButton
                    sx={{ pl: 4 }}
                    sx={{ pl: 4 }}
                    onClick={() => handleListItemClick(body.name)}
                    onClick={() => handleListItemClick(body)}
                    key={body.name}
                    key={body.name}
                  >
                  >
                    <ListItemAvatar>
                    <ListItemAvatar>
@@ -161,7 +158,7 @@ function PlanetDialog(props) {
                ))}
                ))}
              </List>
              </List>
            </Collapse>
            </Collapse>
          </>
          </React.Fragment>
        ))}
        ))}
      </List>
      </List>
    </Dialog>
    </Dialog>
@@ -171,7 +168,7 @@ function PlanetDialog(props) {
PlanetDialog.propTypes = {
PlanetDialog.propTypes = {
  onClose: PropTypes.func.isRequired,
  onClose: PropTypes.func.isRequired,
  open: PropTypes.bool.isRequired,
  open: PropTypes.bool.isRequired,
  selectedValue: PropTypes.string.isRequired,
  selectedValue: PropTypes.object.isRequired,
};
};


/**
/**
@@ -187,7 +184,7 @@ PlanetDialog.propTypes = {
 */
 */
export default function ConsoleTargetInfo(props) {
export default function ConsoleTargetInfo(props) {
  const [open, setOpen] = React.useState(false);
  const [open, setOpen] = React.useState(false);
  const [selectedValue, setSelectedValue] = React.useState(planets[3][0]);
  const [selectedValue, setSelectedValue] = React.useState(props.mapList.systems[4].bodies[0]);


  const handleClickOpen = () => {
  const handleClickOpen = () => {
    setOpen(true);
    setOpen(true);
@@ -215,7 +212,7 @@ export default function ConsoleTargetInfo(props) {
          variant="h4"
          variant="h4"
          onClick={handleClickOpen}
          onClick={handleClickOpen}
        >
        >
          {props.target.toUpperCase()} <ArrowDropDownIcon fontSize="large" />
          {props.target.name.toUpperCase()} <ArrowDropDownIcon fontSize="large" />
        </Typography>
        </Typography>
      </Grid>
      </Grid>
      <PlanetDialog
      <PlanetDialog
+207 −48
Original line number Original line Diff line number Diff line
import React, { useEffect } from "react";
import React, { useEffect } from "react";
import Checkbox from "@mui/material/Checkbox";
import Checkbox from "@mui/material/Checkbox";
import {Card, CardContent, CardActions} from "@mui/material";


// result action links
// result action links
import Chip from "@mui/material/Chip";
import Chip from "@mui/material/Chip";
@@ -10,6 +11,7 @@ import PreviewIcon from "@mui/icons-material/Preview";
import LaunchIcon from "@mui/icons-material/Launch";
import LaunchIcon from "@mui/icons-material/Launch";
import OpenInFullIcon from "@mui/icons-material/OpenInFull";
import OpenInFullIcon from "@mui/icons-material/OpenInFull";
import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen";
import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen";
import TravelExploreIcon from '@mui/icons-material/TravelExplore'; // Footprints.]


// object with results
// object with results
import { getFeatures } from "../../js/ApiJsonCollection";
import { getFeatures } from "../../js/ApiJsonCollection";
@@ -17,6 +19,61 @@ import { getFeatures } from "../../js/ApiJsonCollection";
// geotiff thumbnail viewer
// geotiff thumbnail viewer
import DisplayGeoTiff from "../presentational/DisplayGeoTiff.jsx";
import DisplayGeoTiff from "../presentational/DisplayGeoTiff.jsx";
import GeoTiffViewer from "../../js/geoTiffViewer.js";
import GeoTiffViewer from "../../js/geoTiffViewer.js";
import { Skeleton } from "@mui/material";


/**
 * Skeleton to show when footprints are loading
 */
function LoadingFootprints() {
  
  return (
    <div className="resultsList">
      { Array(5).fill(null).map((_, i) => (
        <Card sx={{ width: 250, margin: 1}} key={i}>
          <CardContent sx={{padding: 0.9, paddingBottom: 0}}>
            <div className="resultContainer">
              <div className="resultImgDiv">
                <Skeleton variant="rectangular" width={32} height={32}/>
              </div>
              <div className="resultData">
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
              </div>
            </div>
          </CardContent>
          <CardActions>
            <div className="resultLinks">
              <Stack direction="row" spacing={1} sx={{marginTop:1}}>
                <Skeleton variant="rounded" width={100} height={20} sx={{borderRadius:5}}/>
                <Skeleton variant="rounded" width={100} height={20} sx={{borderRadius:5}}/>
              </Stack>
            </div>
          </CardActions>
        </Card>
        
      ))}
    </div>
  );
}

function NoFootprints(){
  return(
    <div style={{padding: 10, maxWidth: 268}}>
      <p>
        This target has no footprints. To see 
        footprints, go to the dropdown menu 
        in the upper left and pick a target 
        body with the <TravelExploreIcon sx={{fontSize: 16, verticalAlign: "middle"}}/> icon next to it.
      </p>
    </div>
  );
}




/**
/**
 * Controls css styling for this component using js to css
 * Controls css styling for this component using js to css
@@ -42,10 +99,14 @@ let css = {
 *
 *
 */
 */
export default function FootprintResults(props) {
export default function FootprintResults(props) {

  const [features, setFeatures] = React.useState([]);
  const [features, setFeatures] = React.useState([]);


  const geoTiffViewer = new GeoTiffViewer("GeoTiffAsset");
  const [isLoading, setIsLoading] = React.useState(true);
  const [hasFootprints, setHasFootprints] = React.useState(true);


  // Metadata Popup
  const geoTiffViewer = new GeoTiffViewer("GeoTiffAsset");
  const showMetadata = (value) => () => {
  const showMetadata = (value) => () => {
    geoTiffViewer.displayGeoTiff(value.assets.thumbnail.href);
    geoTiffViewer.displayGeoTiff(value.assets.thumbnail.href);
    geoTiffViewer.changeMetaData(
    geoTiffViewer.changeMetaData(
@@ -58,10 +119,97 @@ export default function FootprintResults(props) {
  };
  };


  useEffect(() => {
  useEffect(() => {
    setTimeout(() => {

      setFeatures(getFeatures);
    // If target has collections (of footprints)
    }, 1000);
    if (props.target.collections.length > 0) {

      // Set Loading
      setIsLoading(true);
      setHasFootprints(true);

      // Promise tracking
      let fetchPromise = {};
      let jsonPromise = {};
      // Result
      let jsonRes = {};

      let itemCollectionUrls = [];
      for(const collection of props.target.collections) {
        // Get "items" link for each collection
        let newItemCollectionUrl =
              collection.links.find(obj => obj.rel === "items").href
              + props.queryString; 
        itemCollectionUrls.push(newItemCollectionUrl);
      }

      for(const itemCollectionUrl of itemCollectionUrls) {
        fetchPromise[itemCollectionUrl] = "Not Started";
        jsonPromise[itemCollectionUrl] = "Not Started";
        jsonRes[itemCollectionUrl] = [];
      }

      // Fetch JSON and read into object
      async function startFetch(targetUrl) {
          fetchPromise[targetUrl] = fetch(
            targetUrl
          ).then((res)=>{
              jsonPromise[targetUrl] = res.json().then((jsonData)=>{
                  jsonRes[targetUrl] = jsonData;
              }).catch((err)=>{
                  console.log(err);
              });
              });
          }).catch((err) => {
              console.log(err);
          });
      }

      async function awaitFetch(targetUrl) {
        await fetchPromise[targetUrl];
        await jsonPromise[targetUrl];
      } 

      async function fetchAndWait() {
        // Start fetching
        for(const itemCollectionUrl of itemCollectionUrls) {
          startFetch(itemCollectionUrl);
        }

        // Wait for completion
        for(const itemCollectionUrl of itemCollectionUrls) {
          await awaitFetch(itemCollectionUrl);
        }
        
        // Extract footprints into array
        let resultsArr = [];
        let myFeatures = [];
        for(const itemCollectionUrl of itemCollectionUrls) {
          myFeatures.push(jsonRes[itemCollectionUrl]);
        }
        for(const featCollection of myFeatures) {
          resultsArr.push(...featCollection.features)
        }

        return resultsArr;
      }

      (async () => {
        // Wait
        let myFeatures = await fetchAndWait()
        setFeatures(myFeatures);
        setHasFootprints(myFeatures.length > 0);
        setIsLoading(false);
      })();

    } else {
      setIsLoading(false);
      setHasFootprints(false);
    }
    
    // setTimeout(() => {
    //   setFeatures(getFeatures);
    // }, 1000); 

  }, [props.target.name, props.queryString]);


  return (
  return (
    <div style={css.root} className="scroll-parent">
    <div style={css.root} className="scroll-parent">
@@ -81,9 +229,14 @@ export default function FootprintResults(props) {
          />
          />
        </span>
        </span>
      </div>
      </div>
      {isLoading ? 
        <LoadingFootprints/>
      : hasFootprints ?   
        <div className="resultsList">
        <div className="resultsList">
          {features.map((feature) => (
          {features.map((feature) => (
          <div className="resultContainer" key={feature.id}>
            <Card sx={{ width: 250, margin: 1}} key={feature.id}>
              <CardContent sx={{padding: 1.2, paddingBottom: 0}}>
                <div className="resultContainer" >
                  <div className="resultImgDiv">
                  <div className="resultImgDiv">
                    <img className="resultImg" src={feature.assets.thumbnail.href} />
                    <img className="resultImg" src={feature.assets.thumbnail.href} />
                  </div>
                  </div>
@@ -98,6 +251,9 @@ export default function FootprintResults(props) {
                      <strong>Date:</strong>&nbsp;{feature.properties.datetime}
                      <strong>Date:</strong>&nbsp;{feature.properties.datetime}
                    </div>
                    </div>
                  </div>
                  </div>
                </div>
              </CardContent>
              <CardActions>
                <div className="resultLinks">
                <div className="resultLinks">
                  <Stack direction="row" spacing={1}>
                  <Stack direction="row" spacing={1}>
                    <Chip
                    <Chip
@@ -115,15 +271,18 @@ export default function FootprintResults(props) {
                      component="a"
                      component="a"
                      href={`https://stac.astrogeology.usgs.gov/browser-dev/#/collections/${feature.collection}/items/${feature.id}`}
                      href={`https://stac.astrogeology.usgs.gov/browser-dev/#/collections/${feature.collection}/items/${feature.id}`}
                      target="_blank"
                      target="_blank"
                  //href="https://stac.astrogeology.usgs.gov/browser-dev/"
                      variant="outlined"
                      variant="outlined"
                      clickable
                      clickable
                    />
                    />
                  </Stack>
                  </Stack>
                </div>
                </div>
          </div>
              </CardActions>
            </Card>
          ))}
          ))}
        </div>
        </div>
      :
        <NoFootprints/>
      }
    </div>
    </div>
  );
  );
}
}
+100 −24
Original line number Original line Diff line number Diff line
@@ -95,32 +95,41 @@ let css = {
 *
 *
 */
 */
export default function SearchAndFilterInput(props) {
export default function SearchAndFilterInput(props) {

  // Allows showing/hiding of fields
  const keywordDetails = React.useRef(null);
  const keywordDetails = React.useRef(null);
  const dateDetails = React.useRef(null);
  const dateDetails = React.useRef(null);


  // React States
  // Sort By
  const [sortVal, setSortVal] = React.useState("");
  const [sortVal, setSortVal] = React.useState("");                    // Sort By What?
  const [sortAscCheckVal, setSortAscCheckVal] = React.useState(false);
  const [sortAscCheckVal, setSortAscCheckVal] = React.useState(false); // Sort Ascending or Descending
  const [areaCheckVal, setAreaCheckVal] = React.useState(false);
  
  // Filter by X checkboxes
  const [areaCheckVal, setAreaCheckVal] = React.useState(false);       // Area
  const [keywordCheckVal, setKeywordCheckVal] = React.useState(false); // Keyword
  const [dateCheckVal, setDateCheckVal] = React.useState(false);       // Date

  // Filter by X values
  const [areaTextVal, setAreaTextVal] = React.useState("");       // Area (received by window message from AstroDrawFilterControl)
  const [keywordTextVal, setKeywordTextVal] = React.useState(""); // Keyword
  const [dateFromVal, setDateFromVal] = React.useState(null);     // From Date
  const [dateToVal, setDateToVal] = React.useState(null);         // To Date


  const [keywordCheckVal, setKeywordCheckVal] = React.useState(false);
  // Page Number
  const [keywordTextVal, setKeywordTextVal] = React.useState("");
  const [pageNumber, setPageNumber] = React.useState(1);


  const [dateCheckVal, setDateCheckVal] = React.useState(false);
  // Pagination
  const [dateFromVal, setDateFromVal] = React.useState(null);
  const [dateToVal, setDateToVal] = React.useState(null);
  const [maxPages, setMaxPages] = React.useState(10);
  const [maxPages, setMaxPages] = React.useState(10);
  const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(10);
  const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(10);
  const [numberReturned, setNumberReturned] = React.useState(10);
  const [numberReturned, setNumberReturned] = React.useState(10);
  const [limitVal, setLimitVal] = React.useState(10);
  const [limitVal, setLimitVal] = React.useState(10);  // Max Number of footprints requested per collection
  
  
  const [applyChipVisStyle, setApplyChipVisStyle] = React.useState(
  // Apply/Alert Chip
    css.chipHidden
  const [applyChipVisStyle, setApplyChipVisStyle] = React.useState(css.chipHidden);
  );
  const [chipMessage, setChipMessage] = React.useState("Apply to Show Footprints on Map");
  const [gotoPage, setGotopage] = React.useState("Apply to go to page 2");


  const setApplyChip = (value) => {
  const setApplyChip = (value) => {
    setGotopage(value);
    setChipMessage("Apply to Show Footprints on Map");
    setApplyChipVisStyle(css.chipShown);
    setApplyChipVisStyle(css.chipShown);
  };
  };


@@ -148,12 +157,56 @@ export default function SearchAndFilterInput(props) {
    setMaxPages(1);
    setMaxPages(1);
    setMaxNumberFootprints(0);
    setMaxNumberFootprints(0);
    setNumberReturned(0);
    setNumberReturned(0);
    setApplyChip("Apply to show Footprints");
    setApplyChip("Apply to Show Footprints on Map");
    //// Uncomment to close details on clear
    //// Uncomment to close details on clear
    // keywordDetails.current.open = false;
    // keywordDetails.current.open = false;
    // dateDetails.current.open = false;
    // dateDetails.current.open = false;
  };
  };


  const buildQueryString = () => {
    let myQueryString = "?";

    // Page Number
    if (pageNumber != 1) myQueryString += "page=" + pageNumber + "&";
  
    // Number of footprints requested per request
    if (limitVal != 10) myQueryString += "limit=" + limitVal + "&"
    
    // Date
    if (dateCheckVal) {
      let d = new Date();
      let fromDate = "1970-01-01T00:00:00Z";             // From start of 1970 by default
      let toDate = d.getFullYear() + "-12-31T23:59:59Z"; // To end of current year by default

      // From
      if(dateFromVal instanceof Date && !isNaN(dateFromVal.valueOf())) {
        fromDate = dateFromVal.toISOString();
      }

      // To
      if(dateToVal instanceof Date && !isNaN(dateToVal.valueOf())) {
        toDate = dateToVal.toISOString();
      }

      myQueryString += "datetime=" + fromDate + "/" + toDate + "&";
    }

    // Keyword
    if(keywordCheckVal) myQueryString += "keywords=[" + keywordTextVal.split(" ") + "]&";

    // Area
    if(areaCheckVal && areaTextVal !== "") myQueryString += areaTextVal;

    // Sorting... Not supported by the API?
    const sortAscDesc = sortAscCheckVal ? "asc" : "desc";
    if (sortVal === "date" || sortVal === "location") {
      console.log("Warning: Sorting not Supported!");
      // myQueryString += 'sort=[{field:datetime,direction:' + sortAscDesc + '}]&'
    }

    props.setQueryString(myQueryString);
  }

  // Sorting
  // Sorting
  const handleSortChange = (event) => {
  const handleSortChange = (event) => {
    setSortVal(event.target.value);
    setSortVal(event.target.value);
@@ -211,9 +264,18 @@ export default function SearchAndFilterInput(props) {
    setApplyChip("Apply to show " + value + " footprints");
    setApplyChip("Apply to show " + value + " footprints");
  };
  };


  // Pagination
  const handlePageChange = (event, value) => {
    setPageNumber(value);
    setCurrentPage(value);
    setApplyChip("Apply to go to page " + value);
  };

  // resets pagination and limit when switching targets
  // resets pagination and limit when switching targets
  useEffect(() => {
  useEffect(() => {
    setTimeout(() => {
    setTimeout(() => {
      setCurrentPage(1);
      setPageNumber(1);
      setMaxNumberFootprints(getNumberMatched);
      setMaxNumberFootprints(getNumberMatched);
      setNumberReturned(getNumberReturned);
      setNumberReturned(getNumberReturned);
      setLimitVal(10);
      setLimitVal(10);
@@ -221,13 +283,27 @@ export default function SearchAndFilterInput(props) {
      setMaxPages(getMaxNumberPages);
      setMaxPages(getMaxNumberPages);
      props.footprintNavClick();
      props.footprintNavClick();
    }, 2000);
    }, 2000);
  }, [props.target]);
  }, [props.target.name]);


  // Pagination
  // Listen for any state change (input) and update the query string based on it
  const handlePageChange = (event, value) => {
  useEffect(() => {
    setCurrentPage(value);
    buildQueryString();
    setApplyChip("Apply to go to page " + value);
  }, [sortVal, sortAscCheckVal, areaCheckVal, areaTextVal, keywordCheckVal, keywordTextVal, dateCheckVal, dateFromVal, dateToVal, limitVal, pageNumber]);
  };

  const onBoxDraw = event => {
    if(typeof event.data == "string" && event.data.includes("bbox")){
      setAreaTextVal(event.data);
      setAreaCheckVal(true);
    }
  }

  useEffect(() => {
    window.addEventListener("message", onBoxDraw);

    return () => {
      window.removeEventListener("message", onBoxDraw);
    }
  }, []);


  /* Control IDs for reference:
  /* Control IDs for reference:
  applyButton
  applyButton
@@ -429,7 +505,7 @@ export default function SearchAndFilterInput(props) {
      <div style={applyChipVisStyle}>
      <div style={applyChipVisStyle}>
        <Chip
        <Chip
          id="applyChip"
          id="applyChip"
          label={gotoPage}
          label={chipMessage}
          icon={<FlagIcon />}
          icon={<FlagIcon />}
          onClick={handleApply}
          onClick={handleApply}
          variant="outlined"
          variant="outlined"
Loading