@macrostrat/column-views 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +3 -0
- package/dist/age-axis.7ce58d66.js +41 -0
- package/dist/age-axis.7ce58d66.js.map +1 -0
- package/dist/boxes.6d3192e6.js +164 -0
- package/dist/boxes.6d3192e6.js.map +1 -0
- package/dist/boxes.module.998ff312.js +13 -0
- package/dist/boxes.module.998ff312.js.map +1 -0
- package/dist/boxes.module.dd631fe3.css +4 -0
- package/dist/boxes.module.dd631fe3.css.map +1 -0
- package/dist/carbon-isotopes.6d9bddcf.js +11 -0
- package/dist/carbon-isotopes.6d9bddcf.js.map +1 -0
- package/dist/column.e35a471f.js +149 -0
- package/dist/column.e35a471f.js.map +1 -0
- package/dist/column.e901b5d4.js +117 -0
- package/dist/column.e901b5d4.js.map +1 -0
- package/dist/column.module.2e29259c.js +22 -0
- package/dist/column.module.2e29259c.js.map +1 -0
- package/dist/column.module.35184ea7.css +157 -0
- package/dist/column.module.35184ea7.css.map +1 -0
- package/dist/column.module.35ba04e7.js +34 -0
- package/dist/column.module.35ba04e7.js.map +1 -0
- package/dist/column.module.c9df6edf.css +138 -0
- package/dist/column.module.c9df6edf.css.map +1 -0
- package/dist/composite.494beffc.js +153 -0
- package/dist/composite.494beffc.js.map +1 -0
- package/dist/composite.module.5cc0e747.css +4 -0
- package/dist/composite.module.5cc0e747.css.map +1 -0
- package/dist/composite.module.6359deba.js +13 -0
- package/dist/composite.module.6359deba.js.map +1 -0
- package/dist/core.f3824dd8.js +89 -0
- package/dist/core.f3824dd8.js.map +1 -0
- package/dist/data-area.f9bb9ad9.js +86 -0
- package/dist/data-area.f9bb9ad9.js.map +1 -0
- package/dist/data-provider.61ef7f9a.js +25 -0
- package/dist/data-provider.61ef7f9a.js.map +1 -0
- package/dist/helpers.2af05ee4.js +94 -0
- package/dist/helpers.2af05ee4.js.map +1 -0
- package/dist/index.d.ts +363 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/index.module.8fad0020.js +37 -0
- package/dist/index.module.8fad0020.js.map +1 -0
- package/dist/index.module.de76feb2.css +65 -0
- package/dist/index.module.de76feb2.css.map +1 -0
- package/dist/isotopes-column.b5442b4d.js +163 -0
- package/dist/isotopes-column.b5442b4d.js.map +1 -0
- package/dist/layers.946aaa37.js +231 -0
- package/dist/layers.946aaa37.js.map +1 -0
- package/dist/map.e782010f.js +82 -0
- package/dist/map.e782010f.js.map +1 -0
- package/dist/names.ab14789e.js +65 -0
- package/dist/names.ab14789e.js.map +1 -0
- package/dist/providers.abcb2cbe.js +25 -0
- package/dist/providers.abcb2cbe.js.map +1 -0
- package/dist/resolvers.2949c4c0.js +83 -0
- package/dist/resolvers.2949c4c0.js.map +1 -0
- package/dist/section.6f6fc929.js +118 -0
- package/dist/section.6f6fc929.js.map +1 -0
- package/dist/selection-popover.7e008544.js +60 -0
- package/dist/selection-popover.7e008544.js.map +1 -0
- package/dist/selection-popover.module.2732ebc0.css +34 -0
- package/dist/selection-popover.module.2732ebc0.css.map +1 -0
- package/dist/selection-popover.module.fdbfb40e.js +22 -0
- package/dist/selection-popover.module.fdbfb40e.js.map +1 -0
- package/dist/selection.add9f8b0.js +117 -0
- package/dist/selection.add9f8b0.js.map +1 -0
- package/dist/store.c5271371.js +18 -0
- package/dist/store.c5271371.js.map +1 -0
- package/dist/types.ff938b22.js +18 -0
- package/dist/types.ff938b22.js.map +1 -0
- package/dist/unit-details.0c1edd00.js +209 -0
- package/dist/unit-details.0c1edd00.js.map +1 -0
- package/dist/units.5ec6a44c.js +42 -0
- package/dist/units.5ec6a44c.js.map +1 -0
- package/package.json +66 -0
- package/src/age-axis.ts +42 -0
- package/src/carbon-isotopes/column.module.sass +136 -0
- package/src/carbon-isotopes/column.ts +131 -0
- package/src/carbon-isotopes/core.ts +78 -0
- package/src/carbon-isotopes/data-area.ts +96 -0
- package/src/carbon-isotopes/data-provider.ts +19 -0
- package/src/carbon-isotopes/index.ts +3 -0
- package/src/carbon-isotopes/isotopes-column.ts +214 -0
- package/src/column.module.sass +114 -0
- package/src/column.ts +185 -0
- package/src/helpers.ts +133 -0
- package/src/index.ts +7 -0
- package/src/map/animated.ts +78 -0
- package/src/map/index.ts +86 -0
- package/src/map/layers.ts +316 -0
- package/src/providers.ts +34 -0
- package/src/section.ts +129 -0
- package/src/selection-popover.module.sass +27 -0
- package/src/selection-popover.ts +71 -0
- package/src/store.ts +12 -0
- package/src/unit-details/index.module.sass +51 -0
- package/src/unit-details/index.ts +215 -0
- package/src/units/boxes.module.sass +3 -0
- package/src/units/boxes.ts +221 -0
- package/src/units/composite.module.sass +2 -0
- package/src/units/composite.ts +266 -0
- package/src/units/index.ts +36 -0
- package/src/units/names.ts +118 -0
- package/src/units/resolvers.ts +111 -0
- package/src/units/selection.ts +167 -0
- package/src/units/types.ts +26 -0
- package/src/units/units.scss +16 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
.column-container
|
|
2
|
+
--column-background-color: var(--background-color, #fff)
|
|
3
|
+
--column-stroke-color: var(--text-color, #000)
|
|
4
|
+
--column-text-color: var(--text-color, #000)
|
|
5
|
+
|
|
6
|
+
@media (prefers-color-scheme: dark)
|
|
7
|
+
.column-container
|
|
8
|
+
--column-background-color: var(--background-color, #000)
|
|
9
|
+
--column-stroke-color: var(--text-color, #fff)
|
|
10
|
+
--column-text-color: var(--text-color, #fff)
|
|
11
|
+
color-scheme: dark
|
|
12
|
+
|
|
13
|
+
.column-container :global
|
|
14
|
+
.column
|
|
15
|
+
display: flex
|
|
16
|
+
flex-direction: row
|
|
17
|
+
position: relative
|
|
18
|
+
|
|
19
|
+
.age-axis-label
|
|
20
|
+
writing-mode: vertical-lr
|
|
21
|
+
transform: rotate(180deg)
|
|
22
|
+
font-size: 12px
|
|
23
|
+
text-align: center
|
|
24
|
+
position: sticky
|
|
25
|
+
top: 0
|
|
26
|
+
max-height: 100vh
|
|
27
|
+
|
|
28
|
+
.tick text
|
|
29
|
+
text-anchor: middle
|
|
30
|
+
font-weight: 400 !important
|
|
31
|
+
transform: rotate(-90deg) translate(8px, -12px)
|
|
32
|
+
|
|
33
|
+
rect.unit
|
|
34
|
+
stroke: var(--column-stroke-color)
|
|
35
|
+
stroke-width: 1px
|
|
36
|
+
|
|
37
|
+
g.lithology-column use.frame
|
|
38
|
+
stroke: var(--column-stroke-color)
|
|
39
|
+
stroke-width: 1.5px !important
|
|
40
|
+
pointer-events: none
|
|
41
|
+
|
|
42
|
+
.column-container :global .unit-label
|
|
43
|
+
text-align: center
|
|
44
|
+
|
|
45
|
+
.column-container :global .col-note-label
|
|
46
|
+
font-size: 10px
|
|
47
|
+
margin-top: 3px
|
|
48
|
+
padding: 2px
|
|
49
|
+
padding-left: 3px
|
|
50
|
+
line-height: 9px
|
|
51
|
+
border-left: none !important
|
|
52
|
+
|
|
53
|
+
.column-container :global g.height-range
|
|
54
|
+
display: none
|
|
55
|
+
|
|
56
|
+
.column-container :global .note-inner
|
|
57
|
+
border: none
|
|
58
|
+
|
|
59
|
+
.column-container :global .frame rect
|
|
60
|
+
fill: transparent
|
|
61
|
+
|
|
62
|
+
.column-container :global .labeled-unit .unit-overlay
|
|
63
|
+
font-size: 10px
|
|
64
|
+
font-style: italic
|
|
65
|
+
display: flex
|
|
66
|
+
align-items: center
|
|
67
|
+
justify-content: center
|
|
68
|
+
|
|
69
|
+
.column-container :global .labeled-unit .unit-overlay .unit-label
|
|
70
|
+
display: block
|
|
71
|
+
text-shadow: 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color), 0px 0px 4px var(--column-background-color)
|
|
72
|
+
|
|
73
|
+
.column-container :global .default-buttons
|
|
74
|
+
width: 100%
|
|
75
|
+
margin: 0 -10px
|
|
76
|
+
|
|
77
|
+
.column-container :global div.section
|
|
78
|
+
display: flex
|
|
79
|
+
flex-direction: row
|
|
80
|
+
|
|
81
|
+
.column-container :global div.section :global(.timescale)
|
|
82
|
+
transform: translateY(0.5)
|
|
83
|
+
|
|
84
|
+
.column-container :global .section .divisions .unit
|
|
85
|
+
cursor: pointer
|
|
86
|
+
|
|
87
|
+
.column-container :global rect.selection-overlay
|
|
88
|
+
fill: rgba(255,0,0,0.5)
|
|
89
|
+
|
|
90
|
+
.column-container :global div.timescale
|
|
91
|
+
width: calc(6em + 4px)
|
|
92
|
+
--timescale-level-size: 1.5em
|
|
93
|
+
|
|
94
|
+
.unconformity
|
|
95
|
+
height: 0px
|
|
96
|
+
font-size: 10px
|
|
97
|
+
font-style: italic
|
|
98
|
+
text-align: center
|
|
99
|
+
margin-left: calc(6em + 4px + 40px)
|
|
100
|
+
|
|
101
|
+
.unconformity-text
|
|
102
|
+
transform: translateY(-0.5em)
|
|
103
|
+
width: var(--column-width)
|
|
104
|
+
color: var(--column-stroke-color)
|
|
105
|
+
|
|
106
|
+
.column-container.dark-mode :global
|
|
107
|
+
color: var(--column-text-color)
|
|
108
|
+
|
|
109
|
+
.column-container.dark-mode :global .timescale
|
|
110
|
+
color: #000
|
|
111
|
+
|
|
112
|
+
.column-container.dark-mode :global g.inner pattern image
|
|
113
|
+
filter: invert(80%)
|
|
114
|
+
|
package/src/column.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ColumnAxisType,
|
|
3
|
+
ColumnLayoutContext,
|
|
4
|
+
ColumnProvider,
|
|
5
|
+
} from "@macrostrat/column-components";
|
|
6
|
+
import { hyperStyled } from "@macrostrat/hyper";
|
|
7
|
+
import { useDarkMode } from "@macrostrat/ui-components";
|
|
8
|
+
import classNames from "classnames";
|
|
9
|
+
import { createRef, RefObject, useContext, useMemo, useState } from "react";
|
|
10
|
+
import styles from "./column.module.sass";
|
|
11
|
+
import {
|
|
12
|
+
TrackedLabeledUnit,
|
|
13
|
+
UnitKeyboardNavigation,
|
|
14
|
+
UnitSelectionProvider,
|
|
15
|
+
useUnitSelectionDispatch,
|
|
16
|
+
} from "./units";
|
|
17
|
+
import { SectionInfo } from "./section";
|
|
18
|
+
import { RectBounds } from "./units/boxes";
|
|
19
|
+
import { UnitSelectionPopover } from "./selection-popover";
|
|
20
|
+
import { MacrostratUnitsProvider } from "./store";
|
|
21
|
+
import { IColumnProps, Section } from "./section";
|
|
22
|
+
import {
|
|
23
|
+
_mergeOverlappingSections,
|
|
24
|
+
ensureArray,
|
|
25
|
+
groupUnitsIntoSections,
|
|
26
|
+
} from "./helpers";
|
|
27
|
+
|
|
28
|
+
const h = hyperStyled(styles);
|
|
29
|
+
|
|
30
|
+
export function UnitComponent({ division, nColumns = 2, ...rest }) {
|
|
31
|
+
const { width } = useContext(ColumnLayoutContext);
|
|
32
|
+
|
|
33
|
+
//const nCols = Math.min(nColumns, division.overlappingUnits.length+1)
|
|
34
|
+
//console.log(division);
|
|
35
|
+
return h(TrackedLabeledUnit, {
|
|
36
|
+
division,
|
|
37
|
+
...rest,
|
|
38
|
+
width: division.overlappingUnits.length > 0 ? width / nColumns : width,
|
|
39
|
+
x: (division.column * width) / nColumns,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Unconformity({ upperUnits = [], lowerUnits = [], style }) {
|
|
44
|
+
if (upperUnits.length == 0 || lowerUnits.length == 0) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const ageGap = lowerUnits[0].t_age - upperUnits[upperUnits.length - 1].b_age;
|
|
49
|
+
|
|
50
|
+
return h("div.unconformity", { style }, [
|
|
51
|
+
h("div.unconformity-text", `${ageGap.toFixed(1)} Ma`),
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function sectionClassName(section: SectionInfo) {
|
|
56
|
+
return `section-${ensureArray(section.section_id).join("-")}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ColumnProps extends IColumnProps {
|
|
60
|
+
unconformityLabels?: boolean;
|
|
61
|
+
className?: string;
|
|
62
|
+
mergeOverlappingSections?: boolean;
|
|
63
|
+
showLabelColumn?: boolean;
|
|
64
|
+
columnRef?: RefObject<HTMLElement>;
|
|
65
|
+
keyboardNavigation?: boolean;
|
|
66
|
+
showUnitPopover?: boolean;
|
|
67
|
+
t_age?: number;
|
|
68
|
+
b_age?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function Column(props: ColumnProps) {
|
|
72
|
+
const { showUnitPopover = false, ...rest } = props;
|
|
73
|
+
const ref = createRef<HTMLElement>();
|
|
74
|
+
// Selected item position
|
|
75
|
+
const [position, setPosition] = useState<RectBounds | null>(null);
|
|
76
|
+
|
|
77
|
+
if (!showUnitPopover) {
|
|
78
|
+
return h(_Column, rest);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return h(
|
|
82
|
+
UnitSelectionProvider,
|
|
83
|
+
{
|
|
84
|
+
onUnitSelected: (unit, target: SVGElement | HTMLElement | null) => {
|
|
85
|
+
if (!showUnitPopover) return;
|
|
86
|
+
|
|
87
|
+
if (unit == null) {
|
|
88
|
+
setPosition(null);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const el: HTMLElement = ref.current;
|
|
92
|
+
if (el == null || target == null) return;
|
|
93
|
+
const rect = el.getBoundingClientRect();
|
|
94
|
+
const targetRect = target.getBoundingClientRect();
|
|
95
|
+
setPosition({
|
|
96
|
+
x: targetRect.left - rect.left,
|
|
97
|
+
y: targetRect.top - rect.top,
|
|
98
|
+
width: targetRect.width,
|
|
99
|
+
height: targetRect.height,
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
h(
|
|
104
|
+
_Column,
|
|
105
|
+
{ ...rest, columnRef: ref },
|
|
106
|
+
h(UnitSelectionPopover, { position })
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function _Column(props: Omit<ColumnProps, "showUnitPopover">) {
|
|
112
|
+
const {
|
|
113
|
+
data,
|
|
114
|
+
unitComponent = UnitComponent,
|
|
115
|
+
unconformityLabels = true,
|
|
116
|
+
showLabels = true,
|
|
117
|
+
width = 300,
|
|
118
|
+
columnWidth = 150,
|
|
119
|
+
className: baseClassName,
|
|
120
|
+
showLabelColumn = true,
|
|
121
|
+
mergeOverlappingSections = true,
|
|
122
|
+
keyboardNavigation = false,
|
|
123
|
+
columnRef,
|
|
124
|
+
children,
|
|
125
|
+
...rest
|
|
126
|
+
} = props;
|
|
127
|
+
|
|
128
|
+
const darkMode = useDarkMode();
|
|
129
|
+
const sectionGroups = useMemo(() => {
|
|
130
|
+
let res = groupUnitsIntoSections(data);
|
|
131
|
+
if (mergeOverlappingSections) {
|
|
132
|
+
res = _mergeOverlappingSections(res);
|
|
133
|
+
}
|
|
134
|
+
return res;
|
|
135
|
+
}, [data, mergeOverlappingSections]);
|
|
136
|
+
|
|
137
|
+
const className = classNames(baseClassName, {
|
|
138
|
+
"dark-mode": darkMode?.isEnabled ?? false,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Clear unit selection on click outside of units, if we have a dispatch function
|
|
142
|
+
const dispatch = useUnitSelectionDispatch();
|
|
143
|
+
|
|
144
|
+
return h(
|
|
145
|
+
"div.column-container",
|
|
146
|
+
{
|
|
147
|
+
className,
|
|
148
|
+
onClick(evt) {
|
|
149
|
+
dispatch?.(null, null, evt as any);
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
h(MacrostratUnitsProvider, { units: data }, [
|
|
153
|
+
h("div.column", { ref: columnRef }, [
|
|
154
|
+
h("div.age-axis-label", "Age (Ma)"),
|
|
155
|
+
h(
|
|
156
|
+
"div.main-column",
|
|
157
|
+
sectionGroups.map((group, i) => {
|
|
158
|
+
const { section_id: id, units: data } = group;
|
|
159
|
+
const lastGroup = sectionGroups[i - 1];
|
|
160
|
+
return h([
|
|
161
|
+
h.if(unconformityLabels)(Unconformity, {
|
|
162
|
+
upperUnits: lastGroup?.units,
|
|
163
|
+
lowerUnits: data,
|
|
164
|
+
style: { width: showLabels ? columnWidth : width },
|
|
165
|
+
}),
|
|
166
|
+
h(`div.section`, { className: sectionClassName(group) }, [
|
|
167
|
+
h(Section, {
|
|
168
|
+
data,
|
|
169
|
+
unitComponent,
|
|
170
|
+
showLabels,
|
|
171
|
+
width,
|
|
172
|
+
columnWidth,
|
|
173
|
+
showLabelColumn,
|
|
174
|
+
...rest,
|
|
175
|
+
}),
|
|
176
|
+
]),
|
|
177
|
+
]);
|
|
178
|
+
})
|
|
179
|
+
),
|
|
180
|
+
h.if(keyboardNavigation)(UnitKeyboardNavigation, { units: data }),
|
|
181
|
+
children,
|
|
182
|
+
]),
|
|
183
|
+
])
|
|
184
|
+
);
|
|
185
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { BaseUnit, UnitLong } from "@macrostrat/api-types";
|
|
2
|
+
import { IUnit } from "./units";
|
|
3
|
+
import { SectionInfo } from "./section";
|
|
4
|
+
import { group } from "d3-array";
|
|
5
|
+
import {
|
|
6
|
+
AgeRange,
|
|
7
|
+
compareAgeRanges,
|
|
8
|
+
AgeRangeRelationship,
|
|
9
|
+
} from "@macrostrat/stratigraphy-utils";
|
|
10
|
+
|
|
11
|
+
const dt = 0.001;
|
|
12
|
+
|
|
13
|
+
function unitsOverlap<T extends BaseUnit>(a: T, b: T) {
|
|
14
|
+
const rel = compareAgeRanges(unitAgeRange(a), unitAgeRange(b), 0.001);
|
|
15
|
+
return rel != AgeRangeRelationship.Disjoint;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function unitAgeRange<T extends BaseUnit>(unit: T): AgeRange {
|
|
19
|
+
return [unit.b_age, unit.t_age];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ExtUnit extends UnitLong {
|
|
23
|
+
bottomOverlap: boolean;
|
|
24
|
+
overlappingUnits: number[];
|
|
25
|
+
column?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function extendDivision(
|
|
29
|
+
unit: UnitLong,
|
|
30
|
+
i: number,
|
|
31
|
+
divisions: UnitLong[]
|
|
32
|
+
): ExtUnit {
|
|
33
|
+
const overlappingUnits = divisions.filter(
|
|
34
|
+
(d) => d.unit_id != unit.unit_id && unitsOverlap(unit, d)
|
|
35
|
+
);
|
|
36
|
+
let bottomOverlap = false;
|
|
37
|
+
for (const d of overlappingUnits) {
|
|
38
|
+
if (d.b_age < unit.b_age + dt) bottomOverlap = true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let column = 0;
|
|
42
|
+
if (overlappingUnits.length == 1) {
|
|
43
|
+
column = 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
...unit,
|
|
48
|
+
bottomOverlap,
|
|
49
|
+
column,
|
|
50
|
+
overlappingUnits: overlappingUnits.map((d) => d.unit_id),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function preprocessUnits(units: UnitLong[]) {
|
|
55
|
+
let divisions = units.map(extendDivision);
|
|
56
|
+
for (let d of divisions) {
|
|
57
|
+
const overlappingUnits = divisions.filter((u) =>
|
|
58
|
+
d.overlappingUnits.includes(u.unit_id)
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const maxNOverlapping = Math.max(
|
|
62
|
+
Math.max(
|
|
63
|
+
...d.overlappingUnits.map(
|
|
64
|
+
(uid) =>
|
|
65
|
+
divisions.find((u) => u.unit_id == uid).overlappingUnits.length
|
|
66
|
+
),
|
|
67
|
+
0
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
//d.maxNOverlapping = maxNOverlapping;
|
|
72
|
+
|
|
73
|
+
// Overlapping columns
|
|
74
|
+
const columns = overlappingUnits.map((d) => d.column);
|
|
75
|
+
|
|
76
|
+
if (columns.includes(d.column)) {
|
|
77
|
+
let col = 0;
|
|
78
|
+
// Go through columns to find a better index
|
|
79
|
+
while (columns.includes(col)) {
|
|
80
|
+
col++;
|
|
81
|
+
}
|
|
82
|
+
d.column = col;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return divisions;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function groupUnitsIntoSections(units: IUnit[]): SectionInfo[] {
|
|
90
|
+
let groups = Array.from(group(units, (d) => d.section_id));
|
|
91
|
+
return groups.map(([section_id, units]) => {
|
|
92
|
+
const t_age = Math.min(...units.map((d) => d.t_age));
|
|
93
|
+
const b_age = Math.max(...units.map((d) => d.b_age));
|
|
94
|
+
return { section_id, t_age, b_age, units };
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function _mergeOverlappingSections(
|
|
99
|
+
sections: SectionInfo[]
|
|
100
|
+
): SectionInfo[] {
|
|
101
|
+
/** Columns can have sections that overlap in time. Here, we merge overlapping
|
|
102
|
+
* sections into a single section to correctly render gap-bound packages.
|
|
103
|
+
*/
|
|
104
|
+
const [firstSection, ...rest] = sections;
|
|
105
|
+
const newSections = [firstSection];
|
|
106
|
+
for (const section of rest) {
|
|
107
|
+
const lastSection = newSections[newSections.length - 1];
|
|
108
|
+
if (
|
|
109
|
+
lastSection.b_age < section.t_age ||
|
|
110
|
+
lastSection.t_age > section.b_age
|
|
111
|
+
) {
|
|
112
|
+
// No overlap, add the section as normal
|
|
113
|
+
newSections.push(section);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
// Overlap, merge the sections
|
|
117
|
+
lastSection.section_id = [
|
|
118
|
+
...ensureArray(lastSection.section_id),
|
|
119
|
+
...ensureArray(section.section_id),
|
|
120
|
+
];
|
|
121
|
+
lastSection.units.push(...section.units);
|
|
122
|
+
lastSection.b_age = Math.max(lastSection.b_age, section.b_age);
|
|
123
|
+
lastSection.t_age = Math.min(lastSection.t_age, section.t_age);
|
|
124
|
+
}
|
|
125
|
+
return newSections;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function ensureArray<T>(x: T | T[]): T[] {
|
|
129
|
+
if (Array.isArray(x)) {
|
|
130
|
+
return x;
|
|
131
|
+
}
|
|
132
|
+
return [x];
|
|
133
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import h from "@macrostrat/hyper";
|
|
3
|
+
import { Globe } from "@macrostrat/svg-map-components";
|
|
4
|
+
import { Button } from "@blueprintjs/core";
|
|
5
|
+
import { geoCentroid } from "d3-geo";
|
|
6
|
+
import { LandLayer } from "@macrostrat/svg-map-components";
|
|
7
|
+
import { Columns, CurrentColumn } from "./layers";
|
|
8
|
+
import classNames from "classnames";
|
|
9
|
+
import { useSpring, animated } from "react-spring";
|
|
10
|
+
|
|
11
|
+
const AnimatedGlobe = animated(Globe);
|
|
12
|
+
|
|
13
|
+
const MapViewFrame = (props) => {
|
|
14
|
+
const [expanded, setExpanded] = useState(false);
|
|
15
|
+
const className = classNames({ expanded }, "context-map");
|
|
16
|
+
const { children, center, margin = 10 } = props;
|
|
17
|
+
const baseSize = 250;
|
|
18
|
+
const sz = expanded ? 450 : baseSize;
|
|
19
|
+
|
|
20
|
+
let scale = sz / 2 - margin;
|
|
21
|
+
if (expanded) {
|
|
22
|
+
scale *= 3;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const targetProps = {
|
|
26
|
+
width: sz,
|
|
27
|
+
height: sz,
|
|
28
|
+
scale,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const animationProps = useSpring(targetProps);
|
|
32
|
+
|
|
33
|
+
return h(
|
|
34
|
+
"div.map-placeholder",
|
|
35
|
+
{ style: { width: baseSize, height: baseSize } },
|
|
36
|
+
[
|
|
37
|
+
h(animated.div, { className, style: animationProps }, [
|
|
38
|
+
h(
|
|
39
|
+
AnimatedGlobe,
|
|
40
|
+
{
|
|
41
|
+
...targetProps,
|
|
42
|
+
margin,
|
|
43
|
+
center,
|
|
44
|
+
allowDrag: expanded,
|
|
45
|
+
allowZoom: false,
|
|
46
|
+
keepNorthUp: true,
|
|
47
|
+
onClick() {
|
|
48
|
+
setExpanded(true);
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
[h(LandLayer), children]
|
|
52
|
+
),
|
|
53
|
+
h.if(expanded)(Button, {
|
|
54
|
+
className: "close-button",
|
|
55
|
+
icon: "cross",
|
|
56
|
+
minimal: true,
|
|
57
|
+
onClick() {
|
|
58
|
+
setExpanded(false);
|
|
59
|
+
},
|
|
60
|
+
intent: "danger",
|
|
61
|
+
}),
|
|
62
|
+
]),
|
|
63
|
+
]
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const AnimatedMapView = (props) => {
|
|
68
|
+
const { currentColumn, setCurrentColumn, children, ...rest } = props;
|
|
69
|
+
const center = geoCentroid(currentColumn);
|
|
70
|
+
|
|
71
|
+
return h(MapViewFrame, { center, ...rest }, [
|
|
72
|
+
h(Columns, { onClick: setCurrentColumn, ...rest }),
|
|
73
|
+
children,
|
|
74
|
+
h.if(currentColumn != null)(CurrentColumn, {
|
|
75
|
+
feature: currentColumn,
|
|
76
|
+
}),
|
|
77
|
+
]);
|
|
78
|
+
};
|
package/src/map/index.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { useRef } from "react";
|
|
2
|
+
import h from "@macrostrat/hyper";
|
|
3
|
+
import { Globe, LandLayer } from "@macrostrat/svg-map-components";
|
|
4
|
+
import { geoCentroid } from "d3-geo";
|
|
5
|
+
import { useElementSize } from "@macrostrat/ui-components";
|
|
6
|
+
import { Columns, CurrentColumn } from "./layers";
|
|
7
|
+
|
|
8
|
+
function ResizableMapFrame(props) {
|
|
9
|
+
const { center, children, style, margin, className, allowZoom, ...rest } =
|
|
10
|
+
props;
|
|
11
|
+
let { scale } = props;
|
|
12
|
+
const ref = useRef(null);
|
|
13
|
+
const sz = useElementSize(ref);
|
|
14
|
+
const { width, height } = sz ?? { width: 0, height: 0 };
|
|
15
|
+
if (scale == null) {
|
|
16
|
+
scale = Math.min(width, height) + 2 * margin;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let zoomScaleExtent = null;
|
|
20
|
+
|
|
21
|
+
if (allowZoom) {
|
|
22
|
+
zoomScaleExtent = [
|
|
23
|
+
0.1 * Math.min(width, height),
|
|
24
|
+
2 * Math.max(width, height),
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return h("div.map-area", { ref, style, className }, [
|
|
29
|
+
h(
|
|
30
|
+
Globe,
|
|
31
|
+
{
|
|
32
|
+
width,
|
|
33
|
+
height,
|
|
34
|
+
margin,
|
|
35
|
+
center,
|
|
36
|
+
allowDrag: true,
|
|
37
|
+
allowZoom,
|
|
38
|
+
keepNorthUp: true,
|
|
39
|
+
zoomScaleExtent,
|
|
40
|
+
...rest,
|
|
41
|
+
scale,
|
|
42
|
+
},
|
|
43
|
+
[h(LandLayer), children]
|
|
44
|
+
),
|
|
45
|
+
]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const ColumnNavigatorMap = (props) => {
|
|
49
|
+
const {
|
|
50
|
+
currentColumn,
|
|
51
|
+
setCurrentColumn,
|
|
52
|
+
children,
|
|
53
|
+
style,
|
|
54
|
+
margin = 10,
|
|
55
|
+
scale,
|
|
56
|
+
className,
|
|
57
|
+
showInProcessColumns = false,
|
|
58
|
+
...rest
|
|
59
|
+
} = props;
|
|
60
|
+
|
|
61
|
+
const columnCenter = geoCentroid(currentColumn);
|
|
62
|
+
|
|
63
|
+
return h(
|
|
64
|
+
ResizableMapFrame,
|
|
65
|
+
{ center: columnCenter, style, margin, scale, className },
|
|
66
|
+
[
|
|
67
|
+
children,
|
|
68
|
+
h(Columns, {
|
|
69
|
+
onChange: setCurrentColumn,
|
|
70
|
+
col_id: currentColumn?.properties.col_id,
|
|
71
|
+
...rest,
|
|
72
|
+
}),
|
|
73
|
+
h.if(showInProcessColumns)(Columns, {
|
|
74
|
+
onChange: setCurrentColumn,
|
|
75
|
+
col_id: currentColumn?.properties.col_id,
|
|
76
|
+
status_code: "in process",
|
|
77
|
+
color: "rgba(200,150,150, 0.5)",
|
|
78
|
+
...rest,
|
|
79
|
+
}),
|
|
80
|
+
h.if(currentColumn != null)(CurrentColumn, { feature: currentColumn }),
|
|
81
|
+
]
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export * from "./layers";
|
|
86
|
+
export { ColumnNavigatorMap, ResizableMapFrame };
|