Skip to content

Commit 11450e5

Browse files
committed
Added SGP facet
1 parent 656d07e commit 11450e5

File tree

6 files changed

+290
-13
lines changed

6 files changed

+290
-13
lines changed

packages/column-views/src/facets/fossils/index.module.sass

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.fossil-collections
1+
.truncated-list
22
padding-left: 0.2em
33
font-size: 0.9em
44
list-style: none

packages/column-views/src/facets/fossils/index.ts

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ColumnNotes } from "../../notes";
77
import { useMemo } from "react";
88
import type { IUnit } from "../../units";
99
import styles from "./index.module.sass";
10+
import { useCallback } from "react";
1011

1112
const h = hyper.styled(styles);
1213

@@ -28,39 +29,57 @@ function FossilInfo(props: FossilItemProps) {
2829
const { note, spacing } = props;
2930
const { data, unit } = note;
3031

31-
let d1 = data;
32+
return h(TruncatedList, {
33+
data,
34+
className: "fossil-collections",
35+
itemRenderer: PBDBCollectionLink,
36+
});
37+
}
38+
39+
interface TruncatedListProps {
40+
data: any[];
41+
className?: string;
42+
maxItems?: number;
43+
itemRenderer?: (props: { data: any }) => any;
44+
}
3245

46+
export function TruncatedList({
47+
data,
48+
className,
49+
maxItems = 5,
50+
itemRenderer = (p) => h("span", p.data),
51+
}: TruncatedListProps) {
3352
let tooMany = null;
34-
if (data.length > 5) {
35-
const n = data.length - 5;
36-
d1 = data.slice(0, 5);
53+
let d1 = data;
54+
if (data.length > maxItems) {
55+
const n = data.length - maxItems;
56+
d1 = data.slice(0, maxItems);
3757
tooMany = h("li.too-many", `and ${n} more`);
3858
}
3959

40-
return h("ul.fossil-collections", [
41-
d1.map((d) => {
42-
return h("li.collection", { key: d.cltn_id }, [
43-
h(PBDBCollectionLink, { collection: d }),
44-
]);
60+
return h("ul.truncated-list", { className }, [
61+
d1.map((d, i) => {
62+
return h("li.element", { key: i }, h(itemRenderer, { data: d }));
4563
}),
4664
tooMany,
4765
]);
4866
}
4967

50-
function PBDBCollectionLink({ collection }: { collection: PBDBCollection }) {
68+
function PBDBCollectionLink({ data }: { data: PBDBCollection }) {
5169
return h(
5270
"a.link-id",
5371
{
54-
href: `https://paleobiodb.org/classic/basicCollectionSearch?collection_no=${collection.cltn_id}`,
72+
href: `https://paleobiodb.org/classic/basicCollectionSearch?collection_no=${data.cltn_id}`,
5573
},
56-
collection.cltn_name,
74+
data.cltn_name,
5775
);
5876
}
5977

6078
const matchingUnit = (dz) => (d) => d.unit_id == dz.unit_id;
6179

6280
export function PBDBFossilsColumn({ columnID, color = "magenta" }) {
6381
const data = useFossilData({ col_id: columnID });
82+
6483
const { axisType, units } = useMacrostratColumnData();
6584

6685
const notes: any[] = useMemo(() => {
@@ -118,3 +137,76 @@ export function PBDBFossilsColumn({ columnID, color = "magenta" }) {
118137
}),
119138
);
120139
}
140+
141+
interface BaseMeasurementsColumnProps<T> {
142+
data: T[];
143+
noteComponent?: any;
144+
width?: number;
145+
paddingLeft?: number;
146+
className?: string;
147+
getUnitID?: (d: T) => number | string;
148+
}
149+
150+
export function BaseMeasurementsColumn({
151+
data,
152+
noteComponent,
153+
width = 500,
154+
paddingLeft = 40,
155+
className,
156+
getUnitID = (d) => d.unit_id,
157+
}: BaseMeasurementsColumnProps<any>) {
158+
const { axisType, units } = useMacrostratColumnData();
159+
160+
const matchingUnit = useCallback(
161+
(dz) => {
162+
return (d) => {
163+
return getUnitID(d) === dz.unit_id;
164+
};
165+
},
166+
[getUnitID],
167+
);
168+
169+
const notes: any[] = useMemo(() => {
170+
if (data == null || units == null) return [];
171+
let unitRefData = Array.from(data.values())
172+
.map((d) => {
173+
return {
174+
data: d,
175+
unit: units.find(matchingUnit(d)),
176+
};
177+
})
178+
.filter((d) => d.unit != null);
179+
180+
unitRefData.sort((a, b) => {
181+
const v1 = units.indexOf(a.unit);
182+
const v2 = units.indexOf(b.unit);
183+
return v1 - v2;
184+
});
185+
186+
return unitRefData.map((d) => {
187+
const { unit, data } = d;
188+
const heightRange = getUnitHeightRange(unit, axisType);
189+
190+
return {
191+
top_height: heightRange[1],
192+
height: heightRange[0],
193+
data,
194+
unit,
195+
id: unit.unit_id,
196+
};
197+
});
198+
}, [data, units, matchingUnit]);
199+
200+
if (data == null || units == null) return null;
201+
202+
return h(
203+
"div",
204+
{ className },
205+
h(ColumnNotes, {
206+
width,
207+
paddingLeft,
208+
notes,
209+
noteComponent,
210+
}),
211+
);
212+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "./fossils";
22
export * from "./detrital-zircon";
33
export * from "./carbon-isotopes";
4+
export * from "./measurements";
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import h from "@macrostrat/hyper";
2+
import { useAPIResult } from "@macrostrat/ui-components";
3+
import { BaseMeasurementsColumn, TruncatedList } from "../fossils";
4+
5+
function useSGPData({ col_id }) {
6+
const res = useAPIResult(
7+
"https://macrostrat.local/api/pg/sgp_unit_matches",
8+
{
9+
col_id: `eq.${col_id}`,
10+
},
11+
(d) => d,
12+
);
13+
14+
return res;
15+
}
16+
17+
export function SGPMeasurementsColumn({ columnID, color = "magenta" }) {
18+
const data = useSGPData({ col_id: columnID });
19+
20+
if (data == null) return null;
21+
22+
return h(BaseMeasurementsColumn, {
23+
data,
24+
noteComponent: SGPSamplesNote,
25+
});
26+
}
27+
28+
function SGPSamplesNote(props) {
29+
const { note } = props;
30+
const sgp_samples = note?.data?.sgp_samples;
31+
32+
if (sgp_samples == null || sgp_samples.length === 0) return null;
33+
34+
return h(TruncatedList, {
35+
className: "sgp-samples",
36+
data: sgp_samples,
37+
itemRenderer: (p) => h("span", p.data.name),
38+
});
39+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { group } from "d3-array";
2+
import { createAPIContext, useAPIResult } from "@macrostrat/ui-components";
3+
4+
const responseUnwrapper = (d) => d.records;
5+
6+
const pbdbAPIContext = createAPIContext({
7+
baseURL: "https://training.paleobiodb.org/data1.2",
8+
unwrapResponse: responseUnwrapper,
9+
});
10+
11+
export enum FossilDataType {
12+
Occurrences = "occs",
13+
Collections = "colls",
14+
}
15+
16+
export function usePBDBFossilData(
17+
type: FossilDataType,
18+
{ col_id },
19+
): any[] | null {
20+
const params = {
21+
ms_column: col_id,
22+
show: "full,mslink",
23+
};
24+
return useAPIResult(`/${type}/list.json`, params, {
25+
context: pbdbAPIContext,
26+
});
27+
}
28+
29+
export interface PBDBCollection {
30+
unit_id: number;
31+
col_id: number;
32+
cltn_id: number;
33+
cltn_name: string;
34+
pbdb_occs: number;
35+
t_age: number;
36+
b_age: number;
37+
[key: string]: any; // Allow for additional properties
38+
}
39+
40+
function useMacrostratFossilData({ col_id }): PBDBCollection[] | null {
41+
return useAPIResult("/fossils", { col_id });
42+
}
43+
44+
function createMacrostratCollection(d): PBDBCollection {
45+
let unit_id = null;
46+
let col_id = null;
47+
// Standardize names of Macrostrat units and columns
48+
if (d.msu !== null) {
49+
unit_id = parseInt(d.msu.replace(/^\w+:/, ""));
50+
}
51+
if (d.msc !== null) {
52+
col_id = parseInt(d.msc.replace(/^\w+:/, ""));
53+
}
54+
55+
return {
56+
...d,
57+
unit_id,
58+
col_id,
59+
cltn_id: parseInt(d.oid.replace(/^col:/, "")),
60+
cltn_name: d.nam,
61+
t_age: d.t_age,
62+
b_age: d.b_age,
63+
};
64+
}
65+
66+
export function useFossilData({ col_id }) {
67+
// Fossil links are stored in both Macrostrat and PBDB, depending on how the link was assembled. Here
68+
// we create a unified view of data over both sources.
69+
70+
const r1 = usePBDBFossilData(FossilDataType.Collections, { col_id });
71+
72+
const r2 = useMacrostratFossilData({ col_id });
73+
74+
if (r1 == null || r2 == null) return null;
75+
const r1a = r1.map(createMacrostratCollection);
76+
77+
const data = [...r1a, ...r2];
78+
79+
return group(data, (d) => d.unit_id);
80+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {
2+
ColoredUnitComponent,
3+
MacrostratDataProvider,
4+
SGPMeasurementsColumn,
5+
} from "../../src";
6+
import h from "@macrostrat/hyper";
7+
import { StandaloneColumn } from "../column-ui";
8+
import { Meta } from "@storybook/react-vite";
9+
import { ColumnAxisType } from "@macrostrat/column-components";
10+
11+
function SGPMeasurementsDemoColumn(props) {
12+
const { id, children, spectraColor, ...rest } = props;
13+
14+
return h(
15+
MacrostratDataProvider,
16+
h(
17+
StandaloneColumn,
18+
{
19+
id,
20+
showTimescale: false,
21+
showLabelColumn: false,
22+
allowUnitSelection: false,
23+
...rest,
24+
},
25+
h(SGPMeasurementsColumn, { columnID: id, color: spectraColor }),
26+
),
27+
);
28+
}
29+
30+
export default {
31+
title: "Column views/Facets/SGP Samples",
32+
component: SGPMeasurementsDemoColumn,
33+
tags: ["!autodocs"],
34+
argTypes: {
35+
axisType: {
36+
options: ["age", "depth"],
37+
control: { type: "radio" },
38+
},
39+
},
40+
} as Meta;
41+
42+
export const ParadoxBasin = {
43+
args: {
44+
id: 495,
45+
},
46+
};
47+
48+
export const UintaBasin = {
49+
args: {
50+
id: 502,
51+
},
52+
};
53+
54+
export const BighornBasinColored = {
55+
args: {
56+
id: 515,
57+
showTimescale: true,
58+
allowUnitSelection: true,
59+
unitComponent: ColoredUnitComponent,
60+
spectraColor: "lightgreen",
61+
showUnitPopover: true,
62+
collapseSmallUnconformities: true,
63+
keyboardNavigation: true,
64+
},
65+
};

0 commit comments

Comments
 (0)