@macrostrat/column-views 3.3.5 → 3.4.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/column.cjs +2 -1
  3. package/dist/column.cjs.map +1 -1
  4. package/dist/column.js +2 -1
  5. package/dist/column.js.map +1 -1
  6. package/dist/data-provider/unit-selection.cjs +7 -3
  7. package/dist/data-provider/unit-selection.cjs.map +1 -1
  8. package/dist/data-provider/unit-selection.d.ts +1 -0
  9. package/dist/data-provider/unit-selection.js +7 -3
  10. package/dist/data-provider/unit-selection.js.map +1 -1
  11. package/dist/index.cjs +13 -8
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.js +7 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/prepare-units/helpers.cjs +83 -49
  16. package/dist/prepare-units/helpers.cjs.map +1 -1
  17. package/dist/prepare-units/helpers.d.ts +19 -2
  18. package/dist/prepare-units/helpers.js +84 -50
  19. package/dist/prepare-units/helpers.js.map +1 -1
  20. package/dist/prepare-units/index.cjs.map +1 -1
  21. package/dist/prepare-units/index.js.map +1 -1
  22. package/dist/section.cjs +1 -2
  23. package/dist/section.cjs.map +1 -1
  24. package/dist/section.js +1 -2
  25. package/dist/section.js.map +1 -1
  26. package/dist/unit-details/identifiers.cjs +56 -0
  27. package/dist/unit-details/identifiers.cjs.map +1 -0
  28. package/dist/unit-details/identifiers.d.ts +31 -0
  29. package/dist/unit-details/identifiers.js +54 -0
  30. package/dist/unit-details/identifiers.js.map +1 -0
  31. package/dist/unit-details/index.d.ts +2 -0
  32. package/dist/unit-details/panel.cjs +11 -88
  33. package/dist/unit-details/panel.cjs.map +1 -1
  34. package/dist/unit-details/panel.d.ts +3 -19
  35. package/dist/unit-details/panel.js +10 -87
  36. package/dist/unit-details/panel.js.map +1 -1
  37. package/dist/unit-details/popover.cjs +1 -2
  38. package/dist/unit-details/popover.cjs.map +1 -1
  39. package/dist/unit-details/popover.js +2 -3
  40. package/dist/unit-details/popover.js.map +1 -1
  41. package/dist/unit-details/strat-names.cjs +44 -0
  42. package/dist/unit-details/strat-names.cjs.map +1 -0
  43. package/dist/unit-details/strat-names.d.ts +14 -0
  44. package/dist/unit-details/strat-names.js +42 -0
  45. package/dist/unit-details/strat-names.js.map +1 -0
  46. package/dist/units/index.cjs +20 -6
  47. package/dist/units/index.cjs.map +1 -1
  48. package/dist/units/index.d.ts +11 -20
  49. package/dist/units/index.js +20 -6
  50. package/dist/units/index.js.map +1 -1
  51. package/package.json +14 -14
  52. package/src/column.ts +1 -1
  53. package/src/data-provider/unit-selection.ts +8 -4
  54. package/src/prepare-units/helpers.ts +121 -72
  55. package/src/prepare-units/index.ts +1 -1
  56. package/src/section.ts +0 -1
  57. package/src/unit-details/identifiers.ts +67 -0
  58. package/src/unit-details/index.ts +2 -0
  59. package/src/unit-details/panel.ts +13 -126
  60. package/src/unit-details/popover.ts +2 -7
  61. package/src/unit-details/strat-names.ts +57 -0
  62. package/src/units/index.ts +31 -8
@@ -34,13 +34,27 @@ function BasicUnitComponent({ division, ...rest }) {
34
34
  patternID: division.patternID
35
35
  });
36
36
  }
37
- function UnitComponent({ division, nColumns = 2, ...rest }) {
37
+ function UnitComponent({
38
+ division,
39
+ nColumns = 2,
40
+ ...rest
41
+ }) {
38
42
  const width = rest.width ?? useColumnLayout()?.width;
39
- const nOverlappingUnits = division.overlappingUnits?.length ?? 0;
40
- const _nColumns = nOverlappingUnits == 0 ? 1 : nColumns;
41
- const columnIx = (division.column ?? 0) % _nColumns;
42
- const reducedWidth = width / _nColumns;
43
- const x = columnIx * width / _nColumns;
43
+ let _nColumns = nColumns;
44
+ if (isNaN(_nColumns)) {
45
+ _nColumns = 2;
46
+ }
47
+ let columnIx = 0;
48
+ let x = 0;
49
+ console.log(rest, _nColumns, width);
50
+ let reducedWidth = _nColumns;
51
+ if ("overlap" in division) {
52
+ const overlap = division.overlap;
53
+ _nColumns = Math.min(_nColumns, overlap.nColumns);
54
+ columnIx = overlap.column;
55
+ reducedWidth = width / _nColumns;
56
+ x = columnIx * width / _nColumns;
57
+ }
44
58
  return h(TrackedLabeledUnit, {
45
59
  division,
46
60
  ...rest,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../src/units/index.ts"],"sourcesContent":["import h from \"@macrostrat/hyper\";\nimport {\n LithologyColumn,\n useGeologicPattern,\n} from \"@macrostrat/column-components\";\nimport { UnitNamesColumn } from \"./names\";\nimport { ICompositeUnitProps } from \"./composite\";\nimport { UnitBoxes, UnitProps } from \"./boxes\";\nimport { useColumnLayout } from \"@macrostrat/column-components\";\nimport { useInDarkMode } from \"@macrostrat/ui-components\";\nimport { getMixedUnitColor } from \"./colors\";\nimport { TrackedLabeledUnit } from \"./composite\";\nimport { useEnvironments, useLithologies } from \"@macrostrat/data-provider\";\nimport { useMemo } from \"react\";\nimport { resolveID } from \"./resolvers\";\nimport { Lithology } from \"@macrostrat/api-types\";\n\nexport * from \"./composite\";\nexport * from \"./types\";\nexport * from \"./colors\";\n\nexport function UnitsColumn({ width = 100 }) {\n /*\n A column showing units with USGS color fill\n */\n return h(LithologyColumn, { width }, h(UnitBoxes));\n}\n\nexport function SimpleUnitsColumn(props: ICompositeUnitProps) {\n /*\n A column with units and names either\n overlapping or offset to the right\n */\n const { columnWidth, width, gutterWidth = 0, labelOffset = 30 } = props;\n\n return h([\n h(UnitsColumn, {\n width: columnWidth,\n }),\n h(UnitNamesColumn, {\n transform: `translate(${columnWidth + gutterWidth})`,\n paddingLeft: labelOffset,\n width: width - columnWidth - gutterWidth,\n }),\n ]);\n}\n\nexport function BasicUnitComponent({ division, ...rest }) {\n /** A unit that allows directly setting colors and patterns. */\n\n return h(UnitComponent, {\n division,\n ...rest,\n backgroundColor: division.color,\n patternID: division.patternID,\n });\n}\n\nexport function UnitComponent({ division, nColumns = 2, ...rest }) {\n const width = rest.width ?? useColumnLayout()?.width;\n\n const nOverlappingUnits = division.overlappingUnits?.length ?? 0;\n const _nColumns = nOverlappingUnits == 0 ? 1 : nColumns;\n const columnIx = (division.column ?? 0) % _nColumns;\n const reducedWidth = width / _nColumns;\n const x = (columnIx * width) / _nColumns;\n\n return h(TrackedLabeledUnit, {\n division,\n ...rest,\n width: reducedWidth,\n x,\n });\n}\n\ninterface UnitColorOptions {\n asBackground?: boolean;\n}\n\nexport function useUnitColor(unit, opts: UnitColorOptions = {}): string | null {\n /** Get the color for a unit based on its lithology */\n const lithMap = useLithologies();\n const inDarkMode = useInDarkMode();\n const { asBackground = true } = opts;\n\n return useMemo(() => {\n if (unit == null || lithMap == null) return null;\n return getMixedUnitColor(unit, lithMap, inDarkMode, asBackground);\n }, [unit?.unit_id, lithMap, inDarkMode, asBackground]);\n}\n\nexport function ColoredUnitComponent(props) {\n /** A unit component that is colored using a mixture of lithologies.\n * This is a separate component because it depends on more providers/contexts to determine coloring. */\n const backgroundColor = useUnitColor(props.division);\n\n const patternID = useMemo(() => {\n return resolveID(props.division); // ?? getPatternID(props.division.lith, lithMap);\n }, [props.division?.unit_id]);\n\n const fill = useGeologicPattern(patternID);\n\n return h(UnitComponent, {\n fill,\n backgroundColor,\n ...props,\n });\n}\n\nexport function useUnitColorByEnvironment(\n unit,\n opts: UnitColorOptions = {},\n): string | null {\n /** Get the color for a unit based on its lithology */\n const environmentMap = useEnvironments();\n const lithMap = useLithologies();\n const inDarkMode = useInDarkMode();\n const { asBackground = true } = opts;\n\n return useMemo(() => {\n if (unit == null || environmentMap == null) return null;\n let c = getMixedUnitColor(unit, environmentMap, inDarkMode, asBackground, {\n key: \"environ\",\n id_key: \"environ_id\",\n });\n if (c != null) {\n return c;\n }\n // Fallback to lithology color if no environment color is found\n return getMixedUnitColor(unit, lithMap, inDarkMode, asBackground);\n }, [unit?.unit_id, environmentMap, lithMap, inDarkMode, asBackground]);\n}\n\nexport function EnvironmentColoredUnitComponent(props) {\n /** A unit component that is colored using a mixture of lithologies.\n * This is a separate component because it depends on more providers/contexts to determine coloring. */\n const backgroundColor = useUnitColorByEnvironment(props.division);\n\n const patternID = useMemo(() => {\n return resolveID(props.division); // ?? getPatternID(props.division.lith, lithMap);\n }, [props.division?.unit_id]);\n\n const fill = useGeologicPattern(patternID);\n\n return h(UnitComponent, {\n fill,\n backgroundColor,\n ...props,\n });\n}\n\nfunction getPatternID(\n liths: Array<Lithology>,\n lithMap: Map<number, Lithology>,\n): string | null {\n if (lithMap == null || liths == null || liths.length == 0) {\n return null;\n }\n const patternIDs = new Set<string>();\n for (const lith of liths) {\n const lithData = lithMap.get(lith.lith_id);\n if (lithData) {\n patternIDs.add(`${lithData.fill}`);\n }\n }\n let id = patternIDs.values()[0];\n if (id == \"0\") return null;\n return id;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../src/units/index.ts"],"sourcesContent":["import h from \"@macrostrat/hyper\";\nimport {\n LithologyColumn,\n useGeologicPattern,\n} from \"@macrostrat/column-components\";\nimport { UnitNamesColumn } from \"./names\";\nimport { ICompositeUnitProps } from \"./composite\";\nimport { UnitBoxes, UnitProps } from \"./boxes\";\nimport { useColumnLayout } from \"@macrostrat/column-components\";\nimport { useInDarkMode } from \"@macrostrat/ui-components\";\nimport { getMixedUnitColor } from \"./colors\";\nimport { TrackedLabeledUnit } from \"./composite\";\nimport { useEnvironments, useLithologies } from \"@macrostrat/data-provider\";\nimport { useMemo } from \"react\";\nimport { resolveID } from \"./resolvers\";\nimport { BaseUnit, Lithology } from \"@macrostrat/api-types\";\nimport { UnitWithDefinedOverlap } from \"../prepare-units/helpers.ts\";\n\nexport * from \"./composite\";\nexport * from \"./types\";\nexport * from \"./colors\";\n\nexport function UnitsColumn({ width = 100 }) {\n /*\n A column showing units with USGS color fill\n */\n return h(LithologyColumn, { width }, h(UnitBoxes));\n}\n\nexport function SimpleUnitsColumn(props: ICompositeUnitProps) {\n /*\n A column with units and names either\n overlapping or offset to the right\n */\n const { columnWidth, width, gutterWidth = 0, labelOffset = 30 } = props;\n\n return h([\n h(UnitsColumn, {\n width: columnWidth,\n }),\n h(UnitNamesColumn, {\n transform: `translate(${columnWidth + gutterWidth})`,\n paddingLeft: labelOffset,\n width: width - columnWidth - gutterWidth,\n }),\n ]);\n}\n\nexport function BasicUnitComponent({ division, ...rest }) {\n /** A unit that allows directly setting colors and patterns. */\n\n return h(UnitComponent, {\n division,\n ...rest,\n backgroundColor: division.color,\n patternID: division.patternID,\n });\n}\n\ninterface UnitComponentProps<T extends BaseUnit> {\n division: UnitWithDefinedOverlap<T> | T;\n nColumns: number;\n width?: number;\n}\n\nexport function UnitComponent<T extends BaseUnit>({\n division,\n nColumns = 2,\n ...rest\n}: UnitComponentProps<T>) {\n const width = rest.width ?? useColumnLayout()?.width;\n\n let _nColumns = nColumns;\n if (isNaN(_nColumns)) {\n _nColumns = 2;\n }\n let columnIx = 0;\n let x = 0;\n\n console.log(rest, _nColumns, width);\n\n let reducedWidth = _nColumns;\n //const nOverlappingUnits = division.overlappingUnits?.length ?? 0;\n if (\"overlap\" in division) {\n const overlap = division.overlap;\n _nColumns = Math.min(_nColumns, overlap.nColumns);\n columnIx = overlap.column;\n reducedWidth = width / _nColumns;\n x = (columnIx * width) / _nColumns;\n }\n return h(TrackedLabeledUnit, {\n division,\n ...rest,\n width: reducedWidth,\n x,\n });\n}\n\ninterface UnitColorOptions {\n asBackground?: boolean;\n}\n\nexport function useUnitColor(unit, opts: UnitColorOptions = {}): string | null {\n /** Get the color for a unit based on its lithology */\n const lithMap = useLithologies();\n const inDarkMode = useInDarkMode();\n const { asBackground = true } = opts;\n\n return useMemo(() => {\n if (unit == null || lithMap == null) return null;\n return getMixedUnitColor(unit, lithMap, inDarkMode, asBackground);\n }, [unit?.unit_id, lithMap, inDarkMode, asBackground]);\n}\n\nexport function ColoredUnitComponent(props) {\n /** A unit component that is colored using a mixture of lithologies.\n * This is a separate component because it depends on more providers/contexts to determine coloring. */\n const backgroundColor = useUnitColor(props.division);\n\n const patternID = useMemo(() => {\n return resolveID(props.division); // ?? getPatternID(props.division.lith, lithMap);\n }, [props.division?.unit_id]);\n\n const fill = useGeologicPattern(patternID);\n\n return h(UnitComponent, {\n fill,\n backgroundColor,\n ...props,\n });\n}\n\nexport function useUnitColorByEnvironment(\n unit,\n opts: UnitColorOptions = {},\n): string | null {\n /** Get the color for a unit based on its lithology */\n const environmentMap = useEnvironments();\n const lithMap = useLithologies();\n const inDarkMode = useInDarkMode();\n const { asBackground = true } = opts;\n\n return useMemo(() => {\n if (unit == null || environmentMap == null) return null;\n let c = getMixedUnitColor(unit, environmentMap, inDarkMode, asBackground, {\n key: \"environ\",\n id_key: \"environ_id\",\n });\n if (c != null) {\n return c;\n }\n // Fallback to lithology color if no environment color is found\n return getMixedUnitColor(unit, lithMap, inDarkMode, asBackground);\n }, [unit?.unit_id, environmentMap, lithMap, inDarkMode, asBackground]);\n}\n\nexport function EnvironmentColoredUnitComponent(props) {\n /** A unit component that is colored using a mixture of lithologies.\n * This is a separate component because it depends on more providers/contexts to determine coloring. */\n const backgroundColor = useUnitColorByEnvironment(props.division);\n\n const patternID = useMemo(() => {\n return resolveID(props.division); // ?? getPatternID(props.division.lith, lithMap);\n }, [props.division?.unit_id]);\n\n const fill = useGeologicPattern(patternID);\n\n return h(UnitComponent, {\n fill,\n backgroundColor,\n ...props,\n });\n}\n\nfunction getPatternID(\n liths: Array<Lithology>,\n lithMap: Map<number, Lithology>,\n): string | null {\n if (lithMap == null || liths == null || liths.length == 0) {\n return null;\n }\n const patternIDs = new Set<string>();\n for (const lith of liths) {\n const lithData = lithMap.get(lith.lith_id);\n if (lithData) {\n patternIDs.add(`${lithData.fill}`);\n }\n }\n let id = patternIDs.values()[0];\n if (id == \"0\") return null;\n return id;\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@macrostrat/column-views",
3
- "version": "3.3.5",
3
+ "version": "3.4.0",
4
4
  "description": "Data views for Macrostrat stratigraphic columns",
5
5
  "repository": {
6
6
  "type": "git",
@@ -45,21 +45,21 @@
45
45
  "react": "^18.0.0||^19.0.0"
46
46
  },
47
47
  "dependencies": {
48
- "@blueprintjs/core": "^6.11.3",
49
- "@macrostrat/api-types": "^1.1.6",
48
+ "@blueprintjs/core": "^6.14.0",
49
+ "@macrostrat/api-types": "^1.2.0",
50
50
  "@macrostrat/color-utils": "^1.2.2",
51
- "@macrostrat/column-components": "^2.0.7",
52
- "@macrostrat/data-components": "^1.1.9",
53
- "@macrostrat/data-provider": "^1.0.3",
51
+ "@macrostrat/column-components": "^2.0.8",
52
+ "@macrostrat/data-components": "^1.1.10",
53
+ "@macrostrat/data-provider": "^1.0.4",
54
54
  "@macrostrat/hyper": "^3.1.1",
55
- "@macrostrat/map-interface": "^2.2.5",
56
- "@macrostrat/map-styles": "^2.2.2",
57
- "@macrostrat/mapbox-react": "^3.2.1",
55
+ "@macrostrat/map-interface": "^2.2.6",
56
+ "@macrostrat/map-styles": "^2.2.3",
57
+ "@macrostrat/mapbox-react": "^3.2.2",
58
58
  "@macrostrat/mapbox-utils": "^1.7.4",
59
- "@macrostrat/stratigraphy-utils": "^1.2.2",
59
+ "@macrostrat/stratigraphy-utils": "^1.3.0",
60
60
  "@macrostrat/svg-map-components": "^2.0.3",
61
61
  "@macrostrat/timescale": "^3.1.3",
62
- "@macrostrat/ui-components": "^5.0.9",
62
+ "@macrostrat/ui-components": "^5.0.10",
63
63
  "@turf/boolean-point-in-polygon": "^7.3.2",
64
64
  "@turf/buffer": "^7.3.2",
65
65
  "@turf/centroid": "^7.3.2",
@@ -82,8 +82,7 @@
82
82
  "d3-path": "^3.1.0",
83
83
  "d3-scale": "^4.0.2",
84
84
  "d3-shape": "^3.2.0",
85
- "jotai": "^2.16.0",
86
- "jotai-scope": "^0.10.0",
85
+ "jotai": "^2.20.0",
87
86
  "mapbox-gl": "^3.0.0",
88
87
  "p-map": "^7.0.4",
89
88
  "react-spring": "^9.7.5",
@@ -92,7 +91,8 @@
92
91
  "zustand-computed": "^2.0.2"
93
92
  },
94
93
  "devDependencies": {
95
- "@macrostrat/web-components-bundler": "1.0.1"
94
+ "@macrostrat/web-components-bundler": "1.0.1",
95
+ "jotai-scope": "^0.11.0"
96
96
  },
97
97
  "publishConfig": {
98
98
  "access": "public"
package/src/column.ts CHANGED
@@ -118,7 +118,7 @@ export function Column(props: ColumnProps) {
118
118
 
119
119
  /* Make pixelScale and targetUnitHeight mutually exclusive. PixelScale implies
120
120
  * standardization of scales in all sections */
121
- let _targetUnitHeight = targetUnitHeight;
121
+ let _targetUnitHeight: number | null = targetUnitHeight;
122
122
  let _minSectionHeight = minSectionHeight;
123
123
  let _minPixelScale = minPixelScale;
124
124
  if (pixelScale != null) {
@@ -16,6 +16,10 @@ type UnitSelectDispatch = (
16
16
  target: HTMLElement | null,
17
17
  ) => void;
18
18
 
19
+ export function useUnitSelection() {
20
+ return scope.use(selectedUnitAtom);
21
+ }
22
+
19
23
  export function useUnitSelectionDispatch(): UnitSelectDispatch {
20
24
  return scope.useSetAtom(selectedUnitAtom);
21
25
  }
@@ -70,6 +74,7 @@ const selectedUnitAtom = atom(
70
74
  ): BaseUnit | null => {
71
75
  if (!get(allowUnitSelectionAtom)) {
72
76
  console.error("Unit selection is disabled.");
77
+ return null;
73
78
  }
74
79
 
75
80
  let unitID: number | null = null;
@@ -83,8 +88,8 @@ const selectedUnitAtom = atom(
83
88
  }
84
89
 
85
90
  if (unitID != null) {
86
- // Verify that the unit exists in the current colum, else throw
87
91
  const unitsMap = get(columnUnitsMapAtom);
92
+ // Verify that the unit exists in the current colum, else throw
88
93
  if (!unitsMap?.has(unitID)) {
89
94
  throw new Error(
90
95
  `Unit with ID ${unitID} not found in current column units.`,
@@ -150,9 +155,8 @@ if (props.onClickedColumn) {
150
155
  export function useUnitSelectionTarget(
151
156
  unit: IUnit,
152
157
  ): [React.RefObject<HTMLElement>, boolean, (evt: Event) => void] {
153
- const ref = useRef<HTMLElement>();
154
- const selectedUnit = useSelectedUnit();
155
- const selectUnit = useUnitSelectionDispatch();
158
+ const ref = useRef<HTMLElement>(null);
159
+ const [selectedUnit, selectUnit] = useUnitSelection();
156
160
  const selected = selectedUnit?.unit_id == unit.unit_id;
157
161
 
158
162
  const onClick = useCallback(
@@ -16,95 +16,143 @@ import type { ExtUnit, SectionInfo, StratigraphicPackage } from "./types";
16
16
 
17
17
  const dt = 0.001;
18
18
 
19
- export function preprocessUnits<T extends UnitLong = UnitLong>(
20
- section: SectionInfo<T>,
21
- axisType: ColumnAxisType = ColumnAxisType.AGE,
22
- ): ExtUnit[] {
23
- /** Preprocess units to add overlapping units and columns. */
24
- let units = section.units;
19
+ export type UnitWithDefinedOverlap<T extends BaseUnit> = T & {
20
+ overlap: UnitOverlapInfo<T>;
21
+ };
25
22
 
26
- let divisions = units.map((...args) => extendDivision(...args, axisType));
27
- for (let d of divisions) {
28
- const overlappingUnits = divisions.filter((u) =>
29
- d.overlappingUnits.includes(u.unit_id),
30
- );
23
+ type UnitOverlapInfo<T extends BaseUnit> = {
24
+ column: number;
25
+ nColumns: number;
26
+ unitGroup: UnitGroup<T>;
27
+ };
31
28
 
32
- // Overlapping columns
33
- const columns = overlappingUnits.map((d) => d.column);
29
+ class UnitGroup<T extends BaseUnit> implements StratigraphicPackage {
30
+ units: T[];
34
31
 
35
- if (columns.includes(d.column)) {
36
- let col = 0;
37
- // Go through columns to find a better index
38
- while (columns.includes(col)) {
39
- col++;
40
- }
41
- d.column ??= col;
42
- }
32
+ constructor(units: T[]) {
33
+ this.units = units;
34
+ }
43
35
 
44
- // If unit overlaps the edges of a section, set the clip positions
45
- const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
46
- if (axisType == ColumnAxisType.AGE) {
47
- if (b_pos > section.b_age) {
48
- d.b_clip_pos = section.b_age;
49
- }
50
- if (t_pos < section.t_age) {
51
- d.t_clip_pos = section.t_age;
36
+ get t_age(): number {
37
+ return Math.min(...this.units.map((d) => d.t_age));
38
+ }
39
+
40
+ get b_age(): number {
41
+ return Math.max(...this.units.map((d) => d.b_age));
42
+ }
43
+
44
+ getUnits(): UnitWithDefinedOverlap<T>[] {
45
+ return assignColumnsToUnitsWithinOverlappingGroup(this);
46
+ }
47
+ }
48
+
49
+ function groupUnitsByMutualOverlap<T extends BaseUnit>(
50
+ units: T[],
51
+ tolerance: number = dt,
52
+ ): UnitGroup<T>[] {
53
+ /** Group units within a section that have mutual overlap. */
54
+ const sections = groupUnitsIntoSectionsByOverlap(
55
+ units,
56
+ ColumnAxisType.AGE,
57
+ tolerance,
58
+ );
59
+ return sections.map((d) => {
60
+ return new UnitGroup(d.units);
61
+ });
62
+ }
63
+
64
+ const sortByAge = createUnitSorter(ColumnAxisType.AGE);
65
+
66
+ function assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(
67
+ group: UnitGroup<T>,
68
+ ): UnitWithDefinedOverlap<T>[] {
69
+ /** A relatively naïve function to assign columns to units within a mutually overlapping group.
70
+ * This gets the number of columns that would be needed to accommodate the units in the group.
71
+ * It then assigns columns to units in the group based on the number of columns needed, with
72
+ * lower units further to the left.
73
+ */
74
+
75
+ const sortedUnits = [...group.units].sort((a, b) => b.b_age - a.b_age);
76
+
77
+ const columns: T[][] = [];
78
+
79
+ for (const unit of sortedUnits) {
80
+ // Find a column that is not already occupied by an overlapping unit
81
+ let didAddToExistingColumn = false;
82
+ for (const column of columns) {
83
+ const lastUnit = column[column.length - 1];
84
+ if (!unitsOverlap(unit, lastUnit, ColumnAxisType.AGE, dt)) {
85
+ column.push(unit);
86
+ didAddToExistingColumn = true;
87
+ break;
52
88
  }
53
89
  }
54
- if (axisType == ColumnAxisType.DEPTH) {
55
- if (b_pos > section.b_pos) {
56
- d.b_clip_pos = section.b_pos;
57
- }
58
- if (t_pos < section.t_pos) {
59
- d.t_clip_pos = section.t_pos;
60
- }
90
+ if (!didAddToExistingColumn) {
91
+ columns.push([unit]);
61
92
  }
62
- // if (axisType == ColumnAxisType.HEIGHT) {
63
- // if (b_pos < section.b_pos) {
64
- // d.b_clip_pos = section.b_pos;
65
- // }
66
- // if (t_pos > section.t_pos) {
67
- // d.t_clip_pos = section.t_pos;
68
- // }
69
- // }
70
93
  }
71
94
 
72
- return divisions;
95
+ const units: UnitWithDefinedOverlap<T>[] = columns.flatMap(
96
+ (column, columnIndex) =>
97
+ column.map((unit) => {
98
+ return {
99
+ ...unit,
100
+ overlap: {
101
+ column: columnIndex,
102
+ nColumns: columns.length,
103
+ unitGroup: group,
104
+ },
105
+ };
106
+ }),
107
+ );
108
+
109
+ return units.toSorted(sortByAge);
73
110
  }
74
111
 
75
- function extendDivision(
76
- unit: UnitLong,
77
- i: number,
78
- divisions: UnitLong[],
112
+ export function preprocessUnits<T extends UnitLong = UnitLong>(
113
+ section: SectionInfo<T>,
79
114
  axisType: ColumnAxisType = ColumnAxisType.AGE,
80
- ): ExtUnit {
81
- // TODO: make this configurable
115
+ ): ExtUnit[] {
116
+ /** Preprocess units to add overlapping units and columns. */
117
+ let units = section.units;
118
+
82
119
  let tolerance = 0.001; // 1 kyr tolerance for age columns
83
120
  if (axisType != ColumnAxisType.AGE) {
84
121
  tolerance = 0.01; // 1cm tolerance for height/depth columns
85
122
  }
86
123
 
87
- const overlappingUnits = divisions.filter(
88
- (d) =>
89
- d.unit_id != unit.unit_id && unitsOverlap(unit, d, axisType, tolerance),
90
- );
91
- const u_pos = getUnitHeightRange(unit, axisType);
92
- const bottomOverlap = overlappingUnits.some((d) => {
93
- const d_pos = getUnitHeightRange(d, axisType);
94
- // Check if the unit is below the current unit
95
- return d_pos[0] < u_pos[0] + dt;
96
- });
97
- let column = 0;
98
- if (overlappingUnits.length == 1) {
99
- column = 1;
124
+ const unitGroups = groupUnitsByMutualOverlap(units, tolerance);
125
+
126
+ const divisions: any[] = [];
127
+ // Serialize all groups' units to a single array
128
+ for (const group of unitGroups) {
129
+ divisions.push(...group.getUnits());
100
130
  }
101
131
 
102
- return {
103
- ...unit,
104
- bottomOverlap,
105
- column: unit.column ?? column,
106
- overlappingUnits: overlappingUnits.map((d) => d.unit_id),
107
- };
132
+ return divisions.map((d) =>
133
+ enhanceUnitWithClippingPosition(section, d, axisType),
134
+ );
135
+ }
136
+
137
+ export function enhanceUnitWithClippingPosition(section, d, axisType) {
138
+ const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
139
+ if (axisType == ColumnAxisType.AGE) {
140
+ if (b_pos > section.b_age) {
141
+ d.b_clip_pos = section.b_age;
142
+ }
143
+ if (t_pos < section.t_age) {
144
+ d.t_clip_pos = section.t_age;
145
+ }
146
+ }
147
+ if (axisType == ColumnAxisType.DEPTH) {
148
+ if (b_pos > section.b_pos) {
149
+ d.b_clip_pos = section.b_pos;
150
+ }
151
+ if (t_pos < section.t_pos) {
152
+ d.t_clip_pos = section.t_pos;
153
+ }
154
+ }
155
+ return d;
108
156
  }
109
157
 
110
158
  export function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(
@@ -141,9 +189,10 @@ interface WorkingSection {
141
189
  heightRange?: [number, number];
142
190
  }
143
191
 
144
- export function groupUnitsIntoSectionsByOverlap<T extends UnitLong>(
192
+ export function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(
145
193
  units: T[],
146
194
  axisType: ColumnAxisType = ColumnAxisType.AGE,
195
+ tolerance: number = dt,
147
196
  ): SectionInfo<T>[] {
148
197
  /** Group units into sections by overlap.
149
198
  * This creates "synthetic" sections that correspond to packages bound by scale gaps.
@@ -157,7 +206,7 @@ export function groupUnitsIntoSectionsByOverlap<T extends UnitLong>(
157
206
  const heightRange = getUnitHeightRange(unit, axisType);
158
207
  let section: WorkingSection | undefined = sectionList.find(
159
208
  (s) =>
160
- compareAgeRanges(heightRange, s.heightRange) !==
209
+ compareAgeRanges(heightRange, s.heightRange, tolerance) !==
161
210
  AgeRangeRelationship.Disjoint,
162
211
  );
163
212
  if (section == null) {
@@ -59,7 +59,7 @@ export function prepareColumnUnits(
59
59
  scale,
60
60
  } = options;
61
61
 
62
- let _totalHeight = null;
62
+ let _totalHeight: number | null = null;
63
63
 
64
64
  if (scale != null) {
65
65
  // Set t_age and b_age based on scale domain if not already set
package/src/section.ts CHANGED
@@ -164,7 +164,6 @@ function SectionUnits(props: SectionProps) {
164
164
  nColumns: Math.min(
165
165
  maxInternalColumns ?? Math.floor(width / 10),
166
166
  unitComponentProps?.nColumns ?? Infinity,
167
- Math.max(...units.map((d) => d.column)) + 1,
168
167
  ),
169
168
  //axisType,
170
169
  };
@@ -0,0 +1,67 @@
1
+ import { isClickable, ItemInteractionProps } from "@macrostrat/data-components";
2
+ import h from "./panel.module.sass";
3
+ import { ReactNode } from "react";
4
+ import classNames from "classnames";
5
+
6
+ export function ClickableText({
7
+ className,
8
+ ...rest
9
+ }: {
10
+ className?: string;
11
+ children: ReactNode;
12
+ } & ItemInteractionProps) {
13
+ /** An optionally clickable text element */
14
+ const clickable = isClickable(rest);
15
+ const tag = clickable ? "a" : "span";
16
+ return h(tag, { className: classNames(className, { clickable }), ...rest });
17
+ }
18
+
19
+ export function Identifier({
20
+ id,
21
+ className,
22
+ ...rest
23
+ }: {
24
+ id: number | string;
25
+ className?: string;
26
+ } & ItemInteractionProps) {
27
+ /** An item that displays a numeric identifier, optionally clickable */
28
+ return h(
29
+ ClickableText,
30
+ {
31
+ className: classNames("identifier", className),
32
+ ...rest,
33
+ },
34
+ id,
35
+ );
36
+ }
37
+
38
+ export type UnitInfo = {
39
+ unitID: number;
40
+ colID?: number;
41
+ name?: string;
42
+ };
43
+
44
+ export function UnitIdentifier({
45
+ unitID,
46
+ colID,
47
+ name,
48
+ ...interactionProps
49
+ }: UnitInfo & ItemInteractionProps) {
50
+ if (name != null) {
51
+ return h(
52
+ ClickableText,
53
+ {
54
+ className: "unit-name",
55
+ ...interactionProps,
56
+ },
57
+ name,
58
+ );
59
+ }
60
+
61
+ return h(Identifier, {
62
+ className: "unit-id",
63
+ key: unitID,
64
+ id: unitID,
65
+ ...interactionProps,
66
+ });
67
+ }
@@ -2,3 +2,5 @@ export * from "./panel";
2
2
  export * from "./popover";
3
3
  export * from "./modal-panel";
4
4
  export * from "./age-range";
5
+ export * from "./strat-names";
6
+ export * from "./identifiers";