Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { useEffect, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { Box, Container, FileUpload, useFileUpload } from "@chakra-ui/react";
import type { StacCollection, StacItem } from "stac-ts";
import { Toaster } from "./components/ui/toaster";
import useDocumentTitle from "./hooks/document-title";
import useHrefParam from "./hooks/href-param";
import useStacChildren from "./hooks/stac-children";
import useStacFilters from "./hooks/stac-filters";
Expand All @@ -11,7 +10,9 @@ import Map from "./layers/map";
import Overlay from "./layers/overlay";
import type { BBox2D, Color } from "./types/map";
import type { DatetimeBounds, StacValue } from "./types/stac";
import getDateTimes from "./utils/datetimes";
import { getCogTileHref } from "./utils/stac";
import getDocumentTitle from "./utils/title";

// TODO make this configurable by the user.
const lineColor: Color = [207, 63, 2, 100];
Expand Down Expand Up @@ -65,8 +66,15 @@ export default function App() {
datetimeBounds,
});

const datetimes = useMemo(
() => (value ? getDateTimes(value, items, collections) : null),
[value, items, collections]
);

// Effects
useDocumentTitle(value);
useEffect(() => {
document.title = getDocumentTitle(value);
}, [value]);

useEffect(() => {
setPicked(undefined);
Expand Down Expand Up @@ -99,8 +107,7 @@ export default function App() {
table={table}
collections={collections}
filteredCollections={filteredCollections}
items={items}
filteredItems={filteredItems}
items={filteredItems}
fillColor={fillColor}
lineColor={lineColor}
setBbox={setBbox}
Expand Down Expand Up @@ -130,19 +137,19 @@ export default function App() {
error={error}
catalogs={catalogs}
setCollections={setCollections}
collections={collections}
filteredCollections={filteredCollections}
collections={filteredCollections}
totalNumOfCollections={collections?.length}
filter={filter}
setFilter={setFilter}
bbox={bbox}
setPicked={setPicked}
picked={picked}
items={items}
filteredItems={filteredItems}
items={filteredItems}
setItems={setItems}
setDatetimeBounds={setDatetimeBounds}
cogTileHref={cogTileHref}
setCogTileHref={setCogTileHref}
datetimes={datetimes}
></Overlay>
</Container>
<Toaster></Toaster>
Expand Down
8 changes: 7 additions & 1 deletion src/components/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ export default function Panel({
error,
fileUpload,
...props
}: PanelProps) {
}: {
totalNumOfCollections: number | undefined;
datetimes: {
start: Date;
end: Date;
} | null;
} & PanelProps) {
if (error)
return (
<Alert.Root status={"error"}>
Expand Down
20 changes: 9 additions & 11 deletions src/components/sections/collections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,21 @@ interface CollectionsProps {

export default function CollectionsSection({
collections,
filteredCollections,
numberOfCollections,
collectionsNumberMatched,
totalNumOfCollections,
setHref,
}: {
filteredCollections: StacCollection[] | undefined;
numberOfCollections: number | undefined;
collectionsNumberMatched: number | undefined;
totalNumOfCollections: number | undefined;
} & CollectionsProps) {
const parenthetical = filteredCollections
? `${filteredCollections.length}/${numberOfCollections || collections.length}`
: collections.length;
const parenthetical =
collections.length !== collectionsNumberMatched
? `${collections.length}/${collectionsNumberMatched || totalNumOfCollections}`
: collections.length;
const title = `Collections (${parenthetical})`;
return (
<Section title={title} TitleIcon={LuFolderPlus} value="collections">
<Collections
collections={filteredCollections || collections}
setHref={setHref}
/>
<Collections collections={collections} setHref={setHref} />
</Section>
);
}
Expand Down
46 changes: 5 additions & 41 deletions src/components/sections/filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Checkbox, DataList, Slider, Stack, Text } from "@chakra-ui/react";
import type { StacCollection, StacItem } from "stac-ts";
import type { BBox2D } from "../../types/map";
import type { DatetimeBounds, StacValue } from "../../types/stac";
import { getItemDatetimes } from "../../utils/stac";
import { SpatialExtent } from "../extent";
import Section from "../section";

Expand All @@ -16,6 +15,10 @@ interface FilterProps {
value: StacValue;
items: StacItem[] | undefined;
collections: StacCollection[] | undefined;
datetimes: {
start: Date;
end: Date;
} | null;
}

export default function FilterSection({ filter, ...props }: FilterProps) {
Expand All @@ -35,50 +38,11 @@ function Filter({
setFilter,
bbox,
setDatetimeBounds,
value,
items,
collections,
datetimes,
}: FilterProps) {
const [filterStart, setFilterStart] = useState<Date>();
const [filterEnd, setFilterEnd] = useState<Date>();

const datetimes = useMemo(() => {
let start =
value.start_datetime && typeof value.start_datetime === "string"
? new Date(value.start_datetime as string)
: null;
let end =
value.end_datetime && typeof value.end_datetime === "string"
? new Date(value.end_datetime as string)
: null;

if (items) {
for (const item of items) {
const itemDatetimes = getItemDatetimes(item);
if (itemDatetimes.start && (!start || itemDatetimes.start < start))
start = itemDatetimes.start;
if (itemDatetimes.end && (!end || itemDatetimes.end > end))
end = itemDatetimes.end;
}
}

if (collections) {
for (const collection of collections) {
const extents = collection.extent?.temporal?.interval?.[0];
if (extents) {
const collectionStart = extents[0] ? new Date(extents[0]) : null;
if (collectionStart && (!start || collectionStart < start))
start = collectionStart;
const collectionEnd = extents[1] ? new Date(extents[1]) : null;
if (collectionEnd && (!end || collectionEnd > end))
end = collectionEnd;
}
}
}

return start && end ? { start, end } : null;
}, [value, items, collections]);

const sliderValue = useMemo(() => {
if (!datetimes) return undefined;
if (filterStart && filterEnd) {
Expand Down
13 changes: 7 additions & 6 deletions src/components/sections/items.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ interface ItemsProps {
}

export default function ItemsSection({
filteredItems,
totalNumOfItems,
items,
...props
}: { filteredItems: StacItem[] | undefined } & ItemsProps) {
const parenthetical = filteredItems
? `${filteredItems.length}/${items.length}`
: items.length;
}: { totalNumOfItems: number | undefined } & ItemsProps) {
const parenthetical =
items.length !== totalNumOfItems
? `${items.length}/${totalNumOfItems}`
: items.length;
const title = `Items (${parenthetical})`;
return (
<Section title={title} TitleIcon={LuFiles} value="items">
<Items items={filteredItems || items} {...props} />
<Items items={items} {...props} />
</Section>
);
}
Expand Down
25 changes: 16 additions & 9 deletions src/components/value.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ export interface SharedValueProps {
catalogs: StacCatalog[] | undefined;
setCollections: (collections: StacCollection[] | undefined) => void;
collections: StacCollection[] | undefined;
filteredCollections: StacCollection[] | undefined;
items: StacItem[] | undefined;
filteredItems: StacItem[] | undefined;
setHref: (href: string | undefined) => void;
filter: boolean;
setFilter: (filter: boolean) => void;
Expand All @@ -70,18 +68,24 @@ export function Value({
value,
catalogs,
collections,
filteredCollections,
setCollections,
items,
filteredItems,
setItems,
filter,
setFilter,
bbox,
setDatetimeBounds,
cogTileHref,
setCogTileHref,
}: ValueProps) {
totalNumOfCollections,
datetimes,
}: {
totalNumOfCollections: number | undefined;
datetimes: {
start: Date;
end: Date;
} | null;
} & ValueProps) {
const [search, setSearch] = useState<StacSearch>();
const [fetchAllCollections, setFetchAllCollections] = useState(false);
const [thumbnailError, setThumbnailError] = useState(false);
Expand Down Expand Up @@ -131,10 +135,12 @@ export function Value({
);
}, [assets]);

const numberOfCollections = useMemo(() => {
const collectionsNumberMatched = useMemo(() => {
return collectionsResult.data?.pages.at(0)?.numberMatched;
}, [collectionsResult.data]);

const totalNumOfItems = items?.length;

useEffect(() => {
setCollections(
collectionsResult.data?.pages.flatMap((page) => page?.collections || [])
Expand Down Expand Up @@ -274,8 +280,8 @@ export function Value({
{collections && collections.length && (
<CollectionsSection
collections={collections}
numberOfCollections={numberOfCollections}
filteredCollections={filteredCollections}
collectionsNumberMatched={collectionsNumberMatched}
totalNumOfCollections={totalNumOfCollections}
setHref={setHref}
/>
)}
Expand Down Expand Up @@ -310,13 +316,14 @@ export function Value({
value={value}
items={items}
collections={collections}
datetimes={datetimes}
/>
)}

{items && (
<ItemsSection
items={items}
filteredItems={filteredItems}
totalNumOfItems={totalNumOfItems}
setHref={setHref}
/>
)}
Expand Down
12 changes: 0 additions & 12 deletions src/hooks/document-title.ts

This file was deleted.

15 changes: 9 additions & 6 deletions src/hooks/href-param.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { useEffect, useState } from "react";

function getCurrentHref(): string {
return new URLSearchParams(location.search).get("href") || "";
}

function getInitialHref(): string | undefined {
const href = new URLSearchParams(location.search).get("href") || "";
const href = getCurrentHref();
try {
new URL(href);
} catch {
Expand All @@ -15,7 +19,7 @@ export default function useHrefParam() {

// Sync href with URL params
useEffect(() => {
if (href && new URLSearchParams(location.search).get("href") != href) {
if (href && getCurrentHref() != href) {
history.pushState(null, "", "?href=" + href);
} else if (href === "") {
history.pushState(null, "", location.pathname);
Expand All @@ -25,14 +29,13 @@ export default function useHrefParam() {
// Handle browser back/forward
useEffect(() => {
function handlePopState() {
setHref(new URLSearchParams(location.search).get("href") ?? "");
setHref(getCurrentHref() ?? "");
}
window.addEventListener("popstate", handlePopState);

const href = new URLSearchParams(location.search).get("href");
if (href) {
if (getCurrentHref()) {
try {
new URL(href);
new URL(getCurrentHref());
} catch {
history.pushState(null, "", location.pathname);
}
Expand Down
17 changes: 10 additions & 7 deletions src/hooks/stac-filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,33 @@ export default function useStacFilters({
filter,
bbox,
datetimeBounds,
}: UseStacFiltersProps) {
}: UseStacFiltersProps): {
filteredCollections: StacCollection[] | undefined;
filteredItems: StacItem[] | undefined;
} {
const filteredCollections = useMemo(() => {
if (filter && collections) {
return collections.filter(
const filtered = collections.filter(
(collection) =>
(!bbox || isCollectionInBbox(collection, bbox)) &&
(!datetimeBounds ||
isCollectionInDatetimeBounds(collection, datetimeBounds))
);
} else {
return undefined;
return filtered;
}
return collections;
}, [collections, filter, bbox, datetimeBounds]);

const filteredItems = useMemo(() => {
if (filter && items) {
return items.filter(
const filtered = items.filter(
(item) =>
(!bbox || isItemInBbox(item, bbox)) &&
(!datetimeBounds || isItemInDatetimeBounds(item, datetimeBounds))
);
} else {
return undefined;
return filtered;
}
return items;
}, [items, filter, bbox, datetimeBounds]);

return { filteredCollections, filteredItems };
Expand Down
Loading