Unverified Commit e51433d0 authored by Jacob Cain's avatar Jacob Cain Committed by GitHub
Browse files

Merge pull request #20 from Geo-Kings/main

Vector API additions Small UI changes, and SLD Styling 
parents 092a7a34 34174276
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -18,3 +18,6 @@ app/node_modules
.Trashes
ehthumbs.db
Thumbs.db

# IDE generated files
.vscode
 No newline at end of file
+2 −1
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@
    "url-loader": "^4.1.1",
    "webpack": "^5.68.0",
    "webpack-cli": "^4.9.2",
    "webpack-dev-server": "^4.7.4",
    "webpack-dev-server": "^4.15.1",
    "webpack-merge": "^5.8.0"
  },
  "dependencies": {
@@ -90,6 +90,7 @@
    "jquery": "^3.6.0",
    "leaflet": "^1.7.1",
    "leaflet-draw": "^1.0.4",
    "leaflet.sld": "github:brittainjackson7/leaflet.sld#master",
    "mocha-jsdom": "^2.0.0",
    "proj4leaflet": "^1.0.2",
    "react": "^17.0.2",
+3 −3
Original line number Diff line number Diff line
@@ -13,11 +13,11 @@ import Sidebar from "../presentational/Sidebar.jsx";
 * @component
 */
export default function GeoStacApp(props) {
  const [targetPlanet, setTargetPlanet] = React.useState(props.mapList.systems[4].bodies[0]);

  const [targetPlanet, setTargetPlanet] = React.useState(props.mapList.systems[4].bodies[0]);
  // make sure its a stac item for appending stuff
  const [queryAddress, setQueryAddress] = React.useState(
    props.mapList.systems[4].bodies[0].collections[0].links.find(link => link.rel === "items").href + "?"
  );
    props.mapList.systems[4].bodies[0].collections[0].links.find(link => link.rel === "items").href);

  /**
   * Handles target body selection
+91 −6
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ export default function FootprintResults(props) {
  const [oldTargetName, setOldTargetName] = React.useState("");
  const [oldFilterString, setOldFilterString] = React.useState("");


  const addFeatures = (newFeatures, key) => {
    let myFeatureCollections = featureCollections;
    myFeatureCollections[key].features.push(...newFeatures);
@@ -62,6 +61,12 @@ export default function FootprintResults(props) {
    setCollectionId(newCollectionId);
    setMatched(featureCollections[newCollectionId].numberMatched);

    // Call the callback function to pass the selected title to the Sidebar
    props.updateAvailableQueriables(props.target.collections.find(col => col.id === newCollectionId).querTitles);

    props.UpdateQueryableTitles(null);


    // Send to Leaflet
    window.postMessage(["setVisibleCollections", newCollectionId], "*");
  };
@@ -75,7 +80,7 @@ export default function FootprintResults(props) {
    let newPage = numFeatures/step + 1;
    let newFeatures = await FetchFootprints(featureCollections[collectionId], newPage, step);
    
    // If any features are returned, add them to currecnt collection
    // If any features are returned, add them to current collection
    if (newFeatures.length > 0) {
      addFeatures(newFeatures, collectionId);
    }
@@ -112,21 +117,91 @@ export default function FootprintResults(props) {
      }

      let collectionUrls = {};
      let styleSheetUrls = [];

      for (const collection of props.target.collections) {
        
        
        let isInStacAPI = collection.hasOwnProperty("stac_version");
        
        let isFeature = collection.itemType == "feature";

        
        // check for pygeo api
        if (isFeature)
        {
          // change filter for the pygeo api
          myFilter = "&limit=" + step;
        }
        let styleSheet;
        const foundStyleSheet = collection.links.find(link=> link.rel == "stylesheet");

        if(foundStyleSheet) {
          styleSheet = foundStyleSheet.href;
          styleSheetUrls[collection.id] = styleSheet;
        }

        if(isInStacAPI || isFeature) {
          let itemsUrl = collection.links.find(link => link.rel === "items").href;
          collectionUrls[collection.id] = itemsUrl + myFilter + pageInfo;

          let style_url = null;
          for (let index = 0; index < collection.links.length; index++) {
            if (collection.links[index].rel === "stylesheet") {
              style_url = collection.links[index].href
            }
          }
          collectionUrls[collection.id + ": stylesheet"] = style_url;

          // if (collection.links.find(link => link.rel === "stylesheet").href != undefined) {
          //   let styleUrl = collection.links.find(link => link.rel === "stylesheet").href;
          //   collectionUrls[collection.id].style = styleUrl;
          // }

          // }
        }
        else {
          let itemsUrl = collection.links.find(link => link.rel === "items").href;
          collectionUrls[collection.id] = itemsUrl + pageInfo;
        }
      }

      async function fetchSLD(sld_url) {
        try {
           const response = await fetch(sld_url)
           if(!response.ok) {
             throw new Error('SLD response not ok ' + response.statusText);
             }
            const SLD_DATA = await response.text();
            //const parser = new DOMParser();
            //const sld_file = parser.parseFromString(SLD_DATA, text/xml);
           return SLD_DATA
               } catch (error) {
                  console.error('SLD unable to be fetched', error);
               }
     }


      (async () => {
        let collections = await FetchObjects(collectionUrls);


        // Add extra properties to each collection
        for(const key in collections){
          collections[key].id = key;
          collections[key].title = props.target.collections.find(collection => collection.id === key).title;
          collections[key].url = collectionUrls[key];

          let sldtext = null;

          if (styleSheetUrls[key]) {
            sldtext = await fetchSLD(styleSheetUrls[key]);
            collections[key].styleSheets = sldtext;
          }

        }


        // Updates collectionId if switching to a new set of collections (new target)
        let myId = collectionId;
        if (!collections[myId]) {
@@ -141,6 +216,9 @@ export default function FootprintResults(props) {
        setHasFootprints(Object.keys(collections).length > 0);
        setIsLoading(false);


       

        // Send to Leaflet
        window.postMessage(["setFeatureCollections", myId, collections], "*");
      })();
@@ -181,6 +259,9 @@ export default function FootprintResults(props) {
  for(const key in featureCollections){
    if(featureCollections[key].numberReturned > 0) noFootprintsReturned = false;
  }
  if(numFeatures > matched) {
    setNumFeatures(matched);
  }

  return (
    <div id="footprintResults" className="scroll-parent">
@@ -210,7 +291,11 @@ export default function FootprintResults(props) {
          <div id="resultsList">
            <List sx={{maxWidth: 265, paddingTop: 0}}>
              {featureCollections[collectionId].features.map(feature => (
                <FootprintCard feature={feature} key={feature.id}/>
                <FootprintCard
                 feature={feature}
                  key={feature.id}
                  selectedQueryables = {props.selectedQueryables}
                  />
              ))}
            </List>
          </div>
+109 −0
Original line number Diff line number Diff line
import React, { useState, useRef, useEffect } from 'react';

function RasterVsVectorBox() {
    return (
        <div className="contentBox">
            <h3>Raster vs Vector</h3>
            <h4>Raster</h4>
            <p>
            The collections with the (Raster) tag are Analysis Ready Data (ARD) holding obtained using the SpatioTemporal
            Asset Catalogs (STAC) API. These features include image footprints and their corresponding assets. More
            information can be found about this API using the "Help" button.
            </p>
            <h4>Vector</h4>
            <p>
            The collections with the (Vector) tag are footprints obtained from the USGS Vector API. These features
            include a multitude of collections condensed into an OGC compliant database to deliver Analysis Ready Data (ARD).
            Users will be able to sort this data using the "queryables'' tab to gather unique ID's or details for each feature in the collection.
            </p>
        </div>
    );
}

function SortingBox() {
    return (
        <div className="contentBox">
            <h3>Sorting</h3>
            <h4>Selected Area</h4>
            <p>To sort by selected area (Only works for Raster). To use, click the "square" icon
                to the left of the map and draw an area. The footprint card should update with only features
                within that area.
            </p>
        </div>
    );
}

function FootprintCardBox() {
    return (
        <div className="contentBox">
            <h3>Footprint Card</h3>
            <p> The footprint card to the right of the screen shows and displays all of the collection features
                selected from the large collections box. To show more features on the map and in the box click the
                "load more" button with the desired amount of features needed.

            </p>
        </div>
    );
}

export default function HelpBox({ isOpen, onClose }) {
    if(!isOpen) return null;

    const [showSubPopup, setShowSubPopup] = useState(null);
    const popupRef = useRef();

    useEffect(() => {
        function handleClickOutside(event) {
            if (event.target === popupRef.current) {
                onClose();
            }
        }

        if (isOpen) {
            document.addEventListener('mousedown', handleClickOutside);
        }
        return () => {
            document.removeEventListener('mousedown', handleClickOutside);
        };
    }, [isOpen, onClose]);

    const handleRasterVsVectorClick = () => {
        setShowSubPopup('rasterVsVector');
    };

    const handleSortingClick = () => {
        setShowSubPopup('sorting');
    };

    const handleFootprintCardClick = () => {
        setShowSubPopup('footprintCard');
    };

    return (
        <div id="helpBoxBackground" ref={popupRef}>
            <div className="helpBoxContent">
                <h2>Help Menu</h2>
                <button className="helpButton" onClick={handleRasterVsVectorClick}>Raster vs Vector</button>
                <button className="helpButton" onClick={handleSortingClick}>Sorting</button>
                <button className="helpButton" onClick={handleFootprintCardClick}>Footprint Card</button>
                <button onClick={onClose} className="closeButton">Close</button>
            </div>

            {showSubPopup === 'rasterVsVector' &&
                <div className="subPopup">
                    <RasterVsVectorBox/>
                </div>
            }
            {showSubPopup === 'sorting' &&
                <div className="subPopup">
                    <SortingBox/>
                </div>
            }
            {showSubPopup === 'footprintCard' &&
                <div className="subPopup">
                    <FootprintCardBox/>
                </div>
            }
        </div>
    );
}
Loading