diff --git a/src/components/children.tsx b/src/components/children.tsx index 317c363..52966ca 100644 --- a/src/components/children.tsx +++ b/src/components/children.tsx @@ -1,5 +1,13 @@ -import { Card, HStack, Icon, Link, Stack, Text } from "@chakra-ui/react"; -import type { ReactNode } from "react"; +import { + Card, + Checkbox, + HStack, + Icon, + Link, + Stack, + Text, +} from "@chakra-ui/react"; +import { useEffect, useState, type ReactNode } from "react"; import { LuFolderPlus, LuFolderSearch } from "react-icons/lu"; import { MarkdownHooks } from "react-markdown"; import type { StacCatalog, StacCollection } from "stac-ts"; @@ -8,6 +16,8 @@ import { useChildren } from "../hooks/stac-value"; import type { SetHref } from "../types/app"; import { CollectionSearch } from "./search/collection"; import Section from "./section"; +import { useMap } from "react-map-gl/maplibre"; +import type { BBox } from "geojson"; export function Children({ value, @@ -18,51 +28,93 @@ export function Children({ }) { const { collections } = useStacMap(); const children = useChildren(value, !!collections); + const { map } = useMap(); const selfHref = value?.links?.find((link) => link.rel === "self")?.href; + const [mapBbox, setMapBbox] = useState(); + const [filterByViewport, setFilterByViewport] = useState(true); + const [filteredCollections, setFilteredCollections] = useState(collections); + + useEffect(() => { + if (map) { + map.on("moveend", () => { + if (map) { + setMapBbox(map.getBounds().toArray().flat() as BBox); + } + }); + } + }, [map]); + + useEffect(() => { + if (filterByViewport && mapBbox) { + setFilteredCollections( + collections?.filter((collection) => + isCollectionInBbox(collection, mapBbox), + ), + ); + } else { + setFilteredCollections(collections); + } + }, [collections, filterByViewport, mapBbox]); return ( <> - {collections && collections?.length > 0 && ( - <> -
- - - {" "} - Collection search - - } - > - -
+ {collections && + filteredCollections && + filteredCollections?.length > 0 && ( + <> +
+ + + {" "} + Collection search + + } + > + +
-
- - - {" "} - Collections ({collections.length}) - - } - > - - {collections.map((collection) => ( - - ))} - -
- - )} +
+ + + {" "} + Collections ( + {(filterByViewport && + filteredCollections.length + "/" + collections.length) || + collections.length} + ) + + } + > + + setFilterByViewport(!!e.checked)} + > + + + Filter by viewport + + {filteredCollections.map((collection) => ( + + ))} + +
+ + )} {children && children.length > 0 && (
@@ -114,3 +166,29 @@ export function ChildCard({ ); } + +function isCollectionInBbox(collection: StacCollection, bbox: BBox) { + if (bbox[2] - bbox[0] >= 360) { + // A global bbox always contains every collection + return true; + } + const collectionBbox = collection?.extent?.spatial?.bbox?.[0]; + if (collectionBbox) { + return ( + !( + collectionBbox[0] < bbox[0] && + collectionBbox[1] < bbox[1] && + collectionBbox[2] > bbox[2] && + collectionBbox[3] > bbox[3] + ) && + !( + collectionBbox[0] > bbox[2] || + collectionBbox[1] > bbox[3] || + collectionBbox[2] < bbox[0] || + collectionBbox[3] < bbox[1] + ) + ); + } else { + return false; + } +}