@macrostrat/column-views 3.5.1 → 3.6.1

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 CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.6.1] - 2026-07-28 [_changes_](https://github.com/UW-Macrostrat/web-components/compare/@macrostrat/column-views-v3.6.0...@macrostrat/column-views-v3.6.1)
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix regression in unit selection
8
+
9
+ ## [3.6.0] - 2026-07-28 [_changes_](https://github.com/UW-Macrostrat/web-components/compare/@macrostrat/column-views-v3.5.1...@macrostrat/column-views-v3.6.0)
10
+
11
+ ### Minor Changes
12
+
13
+ - Remove createStateIsolation and associated exports from
14
+ `@macrostrat/data-components`; move them to `@macrostrat/scoped-store`.
15
+
16
+ ### Patch Changes
17
+
18
+ - Updated dependencies
19
+ - @macrostrat/data-components@1.4.0
20
+ - @macrostrat/scoped-store@1.0.1
21
+
3
22
  ## [3.5.1] - 2026-05-27 [_changes_](https://github.com/UW-Macrostrat/web-components/compare/@macrostrat/column-views-v3.5.0...@macrostrat/column-views-v3.5.1)
4
23
 
5
24
  ### Patch Changes
@@ -5,11 +5,11 @@ const h = require("@macrostrat/hyper");
5
5
  const compositeScale = require("../prepare-units/composite-scale.cjs");
6
6
  const columnComponents = require("@macrostrat/column-components");
7
7
  const unitSelection = require("./unit-selection.cjs");
8
- const dataComponents = require("@macrostrat/data-components");
8
+ const scopedStore = require("@macrostrat/scoped-store");
9
9
  const jotai = require("jotai");
10
10
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
11
11
  const h__default = /* @__PURE__ */ _interopDefault(h);
12
- const scope = dataComponents.createScopedStore();
12
+ const scope = scopedStore.createScopedStore();
13
13
  const columnUnitsAtom = jotai.atom();
14
14
  const columnUnitsMapAtom = jotai.atom((get) => {
15
15
  const units = get(columnUnitsAtom);
@@ -33,6 +33,15 @@ function MacrostratColumnStateProvider({
33
33
  }
34
34
  return selectedUnit != null || onUnitSelected != null;
35
35
  }, []);
36
+ const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);
37
+ if (existingUnits != null) {
38
+ if (allowUnitSelection || onUnitSelected || selectedUnit != null) {
39
+ console.warn(
40
+ "MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree"
41
+ );
42
+ }
43
+ return children;
44
+ }
36
45
  const atomMap = [
37
46
  [columnUnitsAtom, units],
38
47
  [unitSelection.allowUnitSelectionAtom, _allowSelection],
@@ -46,8 +55,7 @@ function MacrostratColumnStateProvider({
46
55
  scope.Provider,
47
56
  {
48
57
  atoms: atomMap,
49
- keepUpdated: true,
50
- inherit: true
58
+ keepUpdated: true
51
59
  },
52
60
  [selectionHandlers, children]
53
61
  );
@@ -1 +1 @@
1
- {"version":3,"file":"store.cjs","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useContext,\n useMemo,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/data-components\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n inherit: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n };\n }, [units, sections, totalHeight, axisType]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(MacrostratColumnDataContext.Provider, { value }, children),\n ],\n );\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType } = useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":["createScopedStore","atom","useMemo","allowUnitSelectionAtom","selectedUnitIDAtom","h","UnitSelectionCallbackManager","createContext","selectedUnitElementAtom","useEffect","useContext","createCompositeScale","ColumnProvider"],"mappings":";;;;;;;;;;;AA8BO,MAAM,QAAwBA,eAAAA,kBAAA;AAE9B,MAAM,kBAAkBC,MAAAA,KAAA;AAExB,MAAM,qBAAqBA,MAAAA,KAAmC,CAAC,QAAQ;AAC5E,QAAM,QAAQ,IAAI,eAAe;AACjC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,8BAAc,IAAA;AACpB,QAAM,QAAQ,CAAC,SAAS;AACtB,YAAQ,IAAI,KAAK,SAAS,IAAI;AAAA,EAChC,CAAC;AACD,SAAO;AACT,CAAC;AAWM,SAAS,8BAAkD;AAAA,EAChE;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA;AACF,GAAgC;AAS9B,QAAM,kBAAkBC,MAAAA,QAAQ,MAAM;AACpC,QAAI,oBAAoB;AACtB,aAAO;AAAA,IACT;AACA,WAAO,gBAAgB,QAAQ,kBAAkB;AAAA,EACnD,GAAG,CAAA,CAAE;AAEL,QAAM,UAAmB;AAAA,IACvB,CAAC,iBAAiB,KAAK;AAAA,IACvB,CAACC,cAAAA,wBAAwB,eAAe;AAAA,IACxC,CAACC,cAAAA,oBAAoB,YAAY;AAAA,EAAA;AAGnC,MAAI,oBAA+B;AACnC,MAAI,iBAAiB;AACnB,wBAAoBC,WAAAA,QAAEC,4CAA8B,EAAE,eAAA,CAAgB;AAAA,EACxE;AAEA,SAAOD,WAAAA;AAAAA,IACL,MAAM;AAAA,IACN;AAAA,MACE,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,IAAA;AAAA,IAEX,CAAC,mBAAmB,QAAQ;AAAA,EAAA;AAEhC;AAuBA,MAAM,8BACJE,MAAAA,cAAgD,IAAI;AAE/C,SAAS,6BAAiD;AAAA,EAC/D;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA+B;AAY7B,QAAM,QAAQL,MAAAA,QAAQ,MAAM;AAE1B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,GAAG,CAAC,OAAO,UAAU,aAAa,QAAQ,CAAC;AAE3C,SAAOG,WAAAA;AAAAA,IACL;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAEF;AAAA,MACEA,mBAAE,kBAAkB,EAAE,KAAK;AAAA,MAC3BA,WAAAA,QAAE,4BAA4B,UAAU,EAAE,MAAA,GAAS,QAAQ;AAAA,IAAA;AAAA,EAC7D;AAEJ;AAEA,SAAS,iBAAiB,EAAE,OAAuC;AACjE,QAAM,sBAAsB,MAAM,aAAaG,qCAAuB;AACtEC,QAAAA,UAAU,MAAM;AACd,QAAI,OAAO,KAAM;AACjB,QAAI,UAAU;AAAA,MACZ,2BAA2B,MAA8B;AACvD,6BAAqB;AAAA,UACnB,QAAQ;AAAA,YACN,UAAU;AAAA,YACV,OAAO;AAAA,YACP,QAAQ;AAAA,UAAA;AAAA,QACV;AAAA,MAEJ;AAAA,IAAA;AAAA,EAEJ,GAAG,CAAC,KAAK,mBAAmB,CAAC;AAE7B,SAAO;AACT;AAEO,SAAS,sBAAsB,OAAsC;AAI1E,SAAOJ,WAAAA,QAAE,+BAA+B;AAAA,IACtC,GAAG;AAAA,IACH,oBAAoB;AAAA,EAAA,CACrB;AACH;AAEO,SAAS,0BAA0B;AACxC,QAAM,MAAMK,MAAAA,WAAW,2BAA2B;AAClD,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACA,SAAO;AACT;AAEO,SAAS,qBAAqB;AACnC,SAAO,MAAM,aAAa,eAAe;AAC3C;AAEO,SAAS,oBAAiD;AAC/D,MAAI;AACF,WAAO,MAAM,aAAa,kBAAkB;AAAA,EAC9C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAA0C;AACxD,QAAM,MAAM,wBAAA;AACZ,SAAOR,MAAAA;AAAAA,IACL,MAAMS,oCAAqB,IAAI,UAAU,IAAI;AAAA,IAC7C,CAAC,IAAI,QAAQ;AAAA,EAAA;AAEjB;AAEO,SAAS,yBAAyB,OAAO;AAK9C,QAAM,EAAE,SAAA,IAAa,wBAAA;AACrB,QAAM,EAAE,OAAO,QAAQ,YAAY,OAAO,aAAa;AACvD,SAAON,WAAAA;AAAAA,IACLO,iBAAAA;AAAAA,IACA;AAAA,MACE;AAAA,MACA,WAAW;AAAA,MACX,OAAO;AAAA,MACP,gBAAgB;AAAA,MAChB;AAAA,IAAA;AAAA,IAEF;AAAA,EAAA;AAEJ;AAGO,SAAS,oBAAoB,EAAE,YAAY;AAChDH,QAAAA,UAAU,MAAM;AACd,YAAQ;AAAA,MACN;AAAA,IAAA;AAAA,EAEJ,GAAG,CAAA,CAAE;AACL,SAAO;AACT;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"store.cjs","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useContext,\n useMemo,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/scoped-store\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n // Check if we're already in a columnStateProvider, and render a no-op if so\n const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);\n if (existingUnits != null) {\n if (allowUnitSelection || onUnitSelected || selectedUnit != null) {\n console.warn(\n \"MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree\",\n );\n }\n return children;\n }\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n };\n }, [units, sections, totalHeight, axisType]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(MacrostratColumnDataContext.Provider, { value }, children),\n ],\n );\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType } = useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":["createScopedStore","atom","useMemo","allowUnitSelectionAtom","selectedUnitIDAtom","h","UnitSelectionCallbackManager","createContext","selectedUnitElementAtom","useEffect","useContext","createCompositeScale","ColumnProvider"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { ColumnAxisType } from '@macrostrat/column-components';
4
4
  import { ExtUnit, PackageLayoutData } from '../prepare-units';
5
5
  import { UnitSelectionCallbacks } from './unit-selection';
6
6
  import { BaseUnit } from '@macrostrat/api-types';
7
- import { AtomMap, StateIsolation } from '@macrostrat/data-components';
7
+ import { AtomMap, StateIsolation } from '@macrostrat/scoped-store';
8
8
  export declare const scope: StateIsolation;
9
9
  export declare const columnUnitsAtom: import('jotai').PrimitiveAtom<BaseUnit[] | undefined> & {
10
10
  init: BaseUnit[] | undefined;
@@ -16,12 +16,12 @@ export interface ColumnStateProviderProps<T extends BaseUnit> extends Partial<Un
16
16
  selectedUnit: number | null;
17
17
  allowUnitSelection?: boolean;
18
18
  }
19
- export declare function MacrostratColumnStateProvider<T extends BaseUnit>({ children, units, allowUnitSelection, onUnitSelected, selectedUnit, }: ColumnStateProviderProps<T>): import('react').FunctionComponentElement<{
19
+ export declare function MacrostratColumnStateProvider<T extends BaseUnit>({ children, units, allowUnitSelection, onUnitSelected, selectedUnit, }: ColumnStateProviderProps<T>): string | number | bigint | boolean | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import('react').ReactPortal | import('react').ReactElement<unknown, string | import('react').JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import('react').FunctionComponentElement<{
20
20
  children: ReactNode;
21
21
  atoms?: AtomMap;
22
22
  keepUpdated?: boolean;
23
23
  inherit?: boolean;
24
- }>;
24
+ }> | null | undefined;
25
25
  /** Interface for imperative handlers that can
26
26
  * be invoked outside of the column's react context
27
27
  */
@@ -3,7 +3,7 @@ import h from "@macrostrat/hyper";
3
3
  import { createCompositeScale } from "../prepare-units/composite-scale.js";
4
4
  import { ColumnProvider } from "@macrostrat/column-components";
5
5
  import { UnitSelectionCallbackManager, allowUnitSelectionAtom, selectedUnitIDAtom, selectedUnitElementAtom } from "./unit-selection.js";
6
- import { createScopedStore } from "@macrostrat/data-components";
6
+ import { createScopedStore } from "@macrostrat/scoped-store";
7
7
  import { atom } from "jotai";
8
8
  const scope = createScopedStore();
9
9
  const columnUnitsAtom = atom();
@@ -29,6 +29,15 @@ function MacrostratColumnStateProvider({
29
29
  }
30
30
  return selectedUnit != null || onUnitSelected != null;
31
31
  }, []);
32
+ const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);
33
+ if (existingUnits != null) {
34
+ if (allowUnitSelection || onUnitSelected || selectedUnit != null) {
35
+ console.warn(
36
+ "MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree"
37
+ );
38
+ }
39
+ return children;
40
+ }
32
41
  const atomMap = [
33
42
  [columnUnitsAtom, units],
34
43
  [allowUnitSelectionAtom, _allowSelection],
@@ -42,8 +51,7 @@ function MacrostratColumnStateProvider({
42
51
  scope.Provider,
43
52
  {
44
53
  atoms: atomMap,
45
- keepUpdated: true,
46
- inherit: true
54
+ keepUpdated: true
47
55
  },
48
56
  [selectionHandlers, children]
49
57
  );
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useContext,\n useMemo,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/data-components\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n inherit: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n };\n }, [units, sections, totalHeight, axisType]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(MacrostratColumnDataContext.Provider, { value }, children),\n ],\n );\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType } = useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sources":["../../src/data-provider/store.ts"],"sourcesContent":["import {\n createContext,\n ReactNode,\n RefObject,\n useContext,\n useMemo,\n} from \"react\";\nimport h from \"@macrostrat/hyper\";\nimport {\n CompositeColumnScale,\n createCompositeScale,\n} from \"../prepare-units/composite-scale\";\nimport { ColumnAxisType, ColumnProvider } from \"@macrostrat/column-components\";\nimport type { ExtUnit, PackageLayoutData } from \"../prepare-units\";\nimport {\n allowUnitSelectionAtom,\n selectedUnitIDAtom,\n UnitSelectionCallbacks,\n UnitSelectionCallbackManager,\n selectedUnitElementAtom,\n} from \"./unit-selection\";\nimport type { BaseUnit } from \"@macrostrat/api-types\";\nimport {\n type AtomMap,\n createScopedStore,\n StateIsolation,\n} from \"@macrostrat/scoped-store\";\nimport { atom } from \"jotai\";\nimport { useEffect } from \"react\";\n\nexport const scope: StateIsolation = createScopedStore();\n\nexport const columnUnitsAtom = atom<BaseUnit[]>();\n\nexport const columnUnitsMapAtom = atom<Map<number, BaseUnit> | null>((get) => {\n const units = get(columnUnitsAtom);\n if (!units) return null;\n const unitMap = new Map<number, BaseUnit>();\n units.forEach((unit) => {\n unitMap.set(unit.unit_id, unit);\n });\n return unitMap;\n});\n\nexport interface ColumnStateProviderProps<\n T extends BaseUnit,\n> extends Partial<UnitSelectionCallbacks> {\n children: ReactNode;\n units: T[];\n selectedUnit: number | null;\n allowUnitSelection?: boolean;\n}\n\nexport function MacrostratColumnStateProvider<T extends BaseUnit>({\n children,\n units,\n allowUnitSelection = false,\n onUnitSelected,\n selectedUnit,\n}: ColumnStateProviderProps<T>) {\n /** Top-level provider for Macrostrat column data.\n * It is either provided by the Column component itself, or\n * can be hoisted higher in the tree to provide a common data context\n */\n\n /* By default, unit selection is disabled. However, if any related props are passed,\n we enable it.\n */\n const _allowSelection = useMemo(() => {\n if (allowUnitSelection) {\n return true;\n }\n return selectedUnit != null || onUnitSelected != null;\n }, []);\n\n // Check if we're already in a columnStateProvider, and render a no-op if so\n const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);\n if (existingUnits != null) {\n if (allowUnitSelection || onUnitSelected || selectedUnit != null) {\n console.warn(\n \"MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree\",\n );\n }\n return children;\n }\n\n const atomMap: AtomMap = [\n [columnUnitsAtom, units],\n [allowUnitSelectionAtom, _allowSelection],\n [selectedUnitIDAtom, selectedUnit],\n ];\n\n let selectionHandlers: ReactNode = null;\n if (_allowSelection) {\n selectionHandlers = h(UnitSelectionCallbackManager, { onUnitSelected });\n }\n\n return h(\n scope.Provider,\n {\n atoms: atomMap,\n keepUpdated: true,\n },\n [selectionHandlers, children],\n );\n}\n\n/** Interface for imperative handlers that can\n * be invoked outside of the column's react context\n */\nexport interface ColumnRef {\n scrollSelectedUnitIntoView(): void;\n}\n\nexport interface MacrostratColumnDataContext<T extends BaseUnit> {\n units: T[];\n sections: PackageLayoutData[];\n totalHeight?: number;\n axisType?: ColumnAxisType;\n allowUnitSelection?: boolean;\n}\n\nexport interface ColumnDataProviderProps<T extends BaseUnit>\n extends MacrostratColumnDataContext<T>, ColumnStateProviderProps<T> {\n children: ReactNode;\n ref: RefObject<ColumnRef>;\n}\n\nconst MacrostratColumnDataContext =\n createContext<MacrostratColumnDataContext<any>>(null);\n\nexport function MacrostratColumnDataProvider<T extends BaseUnit>({\n children,\n units,\n sections,\n totalHeight,\n axisType,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n}: ColumnDataProviderProps<T>) {\n /** Internal provider for Macrostrat column data.\n * As a general rule, we want to provide data and column-axis\n * height calculations through the context, since these need to\n * be accessed by any component that lays out information on the\n * column.\n *\n * Cross-axis layout and view configuration (e.g., showing and hiding\n * of different components or labels) should be handled through passing\n * props to the components themselves.\n * */\n\n const value = useMemo(() => {\n // For now, change ordinal axis types to age axis types\n return {\n units,\n sections,\n totalHeight,\n axisType,\n };\n }, [units, sections, totalHeight, axisType]);\n\n return h(\n MacrostratColumnStateProvider,\n {\n units,\n allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n ref,\n },\n [\n h(ColumnRefManager, { ref }),\n h(MacrostratColumnDataContext.Provider, { value }, children),\n ],\n );\n}\n\nfunction ColumnRefManager({ ref }: { ref?: RefObject<ColumnRef> }) {\n const selectedUnitElement = scope.useAtomValue(selectedUnitElementAtom);\n useEffect(() => {\n if (ref == null) return;\n ref.current = {\n scrollSelectedUnitIntoView(opts?: ScrollIntoViewOptions) {\n selectedUnitElement?.scrollIntoView(\n opts ?? {\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n },\n );\n },\n };\n }, [ref, selectedUnitElement]);\n\n return null;\n}\n\nexport function UnitSelectionProvider(props: ColumnStateProviderProps<any>) {\n /* A basic unit selection provider without column data context.\n Mostly for use with the CorrelationChart component.\n */\n return h(MacrostratColumnStateProvider, {\n ...props,\n allowUnitSelection: true,\n });\n}\n\nexport function useMacrostratColumnData() {\n const ctx = useContext(MacrostratColumnDataContext);\n if (!ctx) {\n throw new Error(\n \"useMacrostratColumnData must be used within a MacrostratColumnDataProvider\",\n );\n }\n return ctx;\n}\n\nexport function useMacrostratUnits() {\n return scope.useAtomValue(columnUnitsAtom);\n}\n\nexport function useColumnUnitsMap(): Map<number, ExtUnit> | null {\n try {\n return scope.useAtomValue(columnUnitsMapAtom) as Map<number, ExtUnit>;\n } catch {\n return null;\n }\n}\n\nexport function useCompositeScale(): CompositeColumnScale {\n const ctx = useMacrostratColumnData();\n return useMemo(\n () => createCompositeScale(ctx.sections, true),\n [ctx.sections],\n );\n}\n\nexport function MacrostratColumnProvider(props) {\n /** A column provider specialized the Macrostrat API. Maps more\n * generic concepts to Macrostrat-specific ones.\n */\n\n const { axisType } = useMacrostratColumnData();\n const { units, domain, pixelScale, scale, children } = props;\n return h(\n ColumnProvider,\n {\n axisType,\n divisions: units,\n range: domain,\n pixelsPerMeter: pixelScale,\n scale,\n },\n children,\n );\n}\n\n/** This is now a legacy provider */\nexport function LithologiesProvider({ children }) {\n useEffect(() => {\n console.warn(\n \"LithologiesProvider is deprecated. Replace with MacrostratDataProvider\",\n );\n }, []);\n return children;\n}\n"],"names":[],"mappings":"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"}
@@ -53,7 +53,6 @@ function assignColumnsToUnitsWithinOverlappingGroup(group2) {
53
53
  columns.push([unit]);
54
54
  }
55
55
  }
56
- console.log(columns);
57
56
  const units = columns.flatMap(
58
57
  (column, columnIndex) => column.map((unit) => {
59
58
  return {
@@ -147,7 +146,10 @@ function groupUnitsIntoSectionsByOverlap(units, axisType = columnComponents.Colu
147
146
  } else {
148
147
  const lastSection = nextSections[nextSections.length - 1];
149
148
  if (isOverlapping(section, lastSection)) {
150
- nextSections[nextSections.length - 1] = mergeSections(lastSection, section);
149
+ nextSections[nextSections.length - 1] = mergeSections(
150
+ lastSection,
151
+ section
152
+ );
151
153
  merged = true;
152
154
  } else {
153
155
  nextSections.push(section);
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.cjs","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n mergeAgeRanges,\n MergeMode,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithLayoutParameters<T extends BaseUnit> = T & {\n layout: UnitLayoutData<T>;\n};\n\nexport type UnitWithLayoutHints<T extends BaseUnit> = T & {\n layoutHints?: UnitLayoutHints;\n};\n\ninterface UnitLayoutInfo {\n column: number;\n nColumns: number;\n}\n\ntype UnitLayoutHints = Partial<UnitLayoutInfo>;\n\ntype UnitLayoutData<T extends BaseUnit> = UnitLayoutInfo & {\n totalColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n axisType: ColumnAxisType;\n\n constructor(units: T[], axisType: ColumnAxisType = ColumnAxisType.AGE) {\n this.units = units;\n this.axisType = axisType;\n }\n\n get t_age(): number {\n return Math.min(...this.units.map((d) => d.t_age));\n }\n\n get b_age(): number {\n return Math.max(...this.units.map((d) => d.b_age));\n }\n\n getUnits(): UnitWithLayoutParameters<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n\n const sections = groupUnitsIntoSectionsByOverlap(units, axisType, tolerance);\n\n console.log(\"Grouped sections\", sections);\n\n return sections.map((d) => {\n return new UnitGroup(d.units, axisType);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<UnitWithLayoutHints<T>>,\n): UnitWithLayoutParameters<T>[] {\n /** A relatively naïve function to assign columns to units within a mutually overlapping group.\n * This gets the number of columns that would be needed to accommodate the units in the group.\n * It then assigns columns to units in the group based on the number of columns needed, with\n * lower units further to the left.\n */\n\n const sortedUnits = [...group.units].sort(createUnitSorter(group.axisType));\n\n const columns: T[][] = [];\n\n let nColumns = 1;\n\n for (const unit of sortedUnits) {\n let startIndex = 0;\n let endIndex = 0;\n // Find a column that is not already occupied by an overlapping unit\n const colHint = unit.layoutHints?.column;\n if (colHint != null) {\n columns[colHint] ??= [];\n columns[colHint].push(unit);\n continue;\n }\n let didAddToExistingColumn = false;\n\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, group.axisType, dt)) {\n column.push(unit);\n didAddToExistingColumn = true;\n break;\n }\n }\n if (!didAddToExistingColumn) {\n columns.push([unit]);\n }\n }\n\n console.log(columns);\n\n const units: UnitWithLayoutParameters<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n layout: {\n column: columnIndex,\n nColumns: 1,\n totalColumns: columns.length,\n unitGroup: group,\n },\n };\n }),\n );\n\n return units.toSorted(sortByAge);\n}\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const unitGroups = groupUnitsByMutualOverlap(units, tolerance, axisType);\n\n const divisions: any[] = [];\n // Serialize all groups' units to a single array\n for (const group of unitGroups) {\n divisions.push(...group.getUnits());\n }\n\n return divisions.map((d) =>\n enhanceUnitWithClippingPosition(section, d, axisType),\n );\n}\n\nexport function enhanceUnitWithClippingPosition(section, d, axisType) {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n return d;\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n tolerance: number = dt,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n\n const initialSections: WorkingSection[] = units.map((unit) => {\n return {\n units: [unit],\n heightRange: getUnitHeightRange(unit, axisType),\n };\n });\n\n const isOverlapping = (a: WorkingSection, b: WorkingSection) => {\n const res = compareAgeRanges(a.heightRange, b.heightRange, tolerance);\n return res !== AgeRangeRelationship.Disjoint;\n };\n\n const mergeSections = (a: WorkingSection, b: WorkingSection) => {\n return {\n units: [...a.units, ...b.units],\n heightRange: mergeAgeRanges(\n [a.heightRange, b.heightRange],\n MergeMode.Outer,\n ),\n };\n };\n\n // Merge overlapping sections progressively until no more merges are possible\n let finalSectionList = initialSections;\n\n while (true) {\n let merged = false;\n const nextSections: WorkingSection[] = [];\n\n for (const section of finalSectionList) {\n if (nextSections.length === 0) {\n nextSections.push(section);\n } else {\n const lastSection = nextSections[nextSections.length - 1];\n if (isOverlapping(section, lastSection)) {\n // Merge with the last section\n nextSections[nextSections.length - 1] = mergeSections(lastSection, section);\n merged = true;\n } else {\n nextSections.push(section);\n }\n }\n }\n\n finalSectionList = nextSections;\n\n // Stop when no more merges occurred in this pass\n if (!merged) {\n break;\n }\n }\n\n return finalSectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["ColumnAxisType","createUnitSorter","group","unitsOverlap","getUnitHeightRange","compareAgeRanges","AgeRangeRelationship","mergeAgeRanges","MergeMode","ensureArray","ensureRealFloat"],"mappings":"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"}
1
+ {"version":3,"file":"helpers.cjs","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n mergeAgeRanges,\n MergeMode,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithLayoutParameters<T extends BaseUnit> = T & {\n layout: UnitLayoutData<T>;\n};\n\nexport type UnitWithLayoutHints<T extends BaseUnit> = T & {\n layoutHints?: UnitLayoutHints;\n};\n\ninterface UnitLayoutInfo {\n column: number;\n nColumns: number;\n}\n\ntype UnitLayoutHints = Partial<UnitLayoutInfo>;\n\ntype UnitLayoutData<T extends BaseUnit> = UnitLayoutInfo & {\n totalColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n axisType: ColumnAxisType;\n\n constructor(units: T[], axisType: ColumnAxisType = ColumnAxisType.AGE) {\n this.units = units;\n this.axisType = axisType;\n }\n\n get t_age(): number {\n return Math.min(...this.units.map((d) => d.t_age));\n }\n\n get b_age(): number {\n return Math.max(...this.units.map((d) => d.b_age));\n }\n\n getUnits(): UnitWithLayoutParameters<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n\n const sections = groupUnitsIntoSectionsByOverlap(units, axisType, tolerance);\n\n console.log(\"Grouped sections\", sections);\n\n return sections.map((d) => {\n return new UnitGroup(d.units, axisType);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<UnitWithLayoutHints<T>>,\n): UnitWithLayoutParameters<T>[] {\n /** A relatively naïve function to assign columns to units within a mutually overlapping group.\n * This gets the number of columns that would be needed to accommodate the units in the group.\n * It then assigns columns to units in the group based on the number of columns needed, with\n * lower units further to the left.\n */\n\n const sortedUnits = [...group.units].sort(createUnitSorter(group.axisType));\n\n const columns: T[][] = [];\n\n let nColumns = 1;\n\n for (const unit of sortedUnits) {\n let startIndex = 0;\n let endIndex = 0;\n // Find a column that is not already occupied by an overlapping unit\n const colHint = unit.layoutHints?.column;\n if (colHint != null) {\n columns[colHint] ??= [];\n columns[colHint].push(unit);\n continue;\n }\n let didAddToExistingColumn = false;\n\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, group.axisType, dt)) {\n column.push(unit);\n didAddToExistingColumn = true;\n break;\n }\n }\n if (!didAddToExistingColumn) {\n columns.push([unit]);\n }\n }\n\n const units: UnitWithLayoutParameters<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n layout: {\n column: columnIndex,\n nColumns: 1,\n totalColumns: columns.length,\n unitGroup: group,\n },\n };\n }),\n );\n\n return units.toSorted(sortByAge);\n}\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const unitGroups = groupUnitsByMutualOverlap(units, tolerance, axisType);\n\n const divisions: any[] = [];\n // Serialize all groups' units to a single array\n for (const group of unitGroups) {\n divisions.push(...group.getUnits());\n }\n\n return divisions.map((d) =>\n enhanceUnitWithClippingPosition(section, d, axisType),\n );\n}\n\nexport function enhanceUnitWithClippingPosition(section, d, axisType) {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n return d;\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n tolerance: number = dt,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n\n const initialSections: WorkingSection[] = units.map((unit) => {\n return {\n units: [unit],\n heightRange: getUnitHeightRange(unit, axisType),\n };\n });\n\n const isOverlapping = (a: WorkingSection, b: WorkingSection) => {\n const res = compareAgeRanges(a.heightRange, b.heightRange, tolerance);\n return res !== AgeRangeRelationship.Disjoint;\n };\n\n const mergeSections = (a: WorkingSection, b: WorkingSection) => {\n return {\n units: [...a.units, ...b.units],\n heightRange: mergeAgeRanges(\n [a.heightRange, b.heightRange],\n MergeMode.Outer,\n ),\n };\n };\n\n // Merge overlapping sections progressively until no more merges are possible\n let finalSectionList = initialSections;\n\n while (true) {\n let merged = false;\n const nextSections: WorkingSection[] = [];\n\n for (const section of finalSectionList) {\n if (nextSections.length === 0) {\n nextSections.push(section);\n } else {\n const lastSection = nextSections[nextSections.length - 1];\n if (isOverlapping(section, lastSection)) {\n // Merge with the last section\n nextSections[nextSections.length - 1] = mergeSections(\n lastSection,\n section,\n );\n merged = true;\n } else {\n nextSections.push(section);\n }\n }\n }\n\n finalSectionList = nextSections;\n\n // Stop when no more merges occurred in this pass\n if (!merged) {\n break;\n }\n }\n\n return finalSectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["ColumnAxisType","createUnitSorter","group","unitsOverlap","getUnitHeightRange","compareAgeRanges","AgeRangeRelationship","mergeAgeRanges","MergeMode","ensureArray","ensureRealFloat"],"mappings":"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"}
@@ -51,7 +51,6 @@ function assignColumnsToUnitsWithinOverlappingGroup(group2) {
51
51
  columns.push([unit]);
52
52
  }
53
53
  }
54
- console.log(columns);
55
54
  const units = columns.flatMap(
56
55
  (column, columnIndex) => column.map((unit) => {
57
56
  return {
@@ -145,7 +144,10 @@ function groupUnitsIntoSectionsByOverlap(units, axisType = ColumnAxisType.AGE, t
145
144
  } else {
146
145
  const lastSection = nextSections[nextSections.length - 1];
147
146
  if (isOverlapping(section, lastSection)) {
148
- nextSections[nextSections.length - 1] = mergeSections(lastSection, section);
147
+ nextSections[nextSections.length - 1] = mergeSections(
148
+ lastSection,
149
+ section
150
+ );
149
151
  merged = true;
150
152
  } else {
151
153
  nextSections.push(section);
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n mergeAgeRanges,\n MergeMode,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithLayoutParameters<T extends BaseUnit> = T & {\n layout: UnitLayoutData<T>;\n};\n\nexport type UnitWithLayoutHints<T extends BaseUnit> = T & {\n layoutHints?: UnitLayoutHints;\n};\n\ninterface UnitLayoutInfo {\n column: number;\n nColumns: number;\n}\n\ntype UnitLayoutHints = Partial<UnitLayoutInfo>;\n\ntype UnitLayoutData<T extends BaseUnit> = UnitLayoutInfo & {\n totalColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n axisType: ColumnAxisType;\n\n constructor(units: T[], axisType: ColumnAxisType = ColumnAxisType.AGE) {\n this.units = units;\n this.axisType = axisType;\n }\n\n get t_age(): number {\n return Math.min(...this.units.map((d) => d.t_age));\n }\n\n get b_age(): number {\n return Math.max(...this.units.map((d) => d.b_age));\n }\n\n getUnits(): UnitWithLayoutParameters<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n\n const sections = groupUnitsIntoSectionsByOverlap(units, axisType, tolerance);\n\n console.log(\"Grouped sections\", sections);\n\n return sections.map((d) => {\n return new UnitGroup(d.units, axisType);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<UnitWithLayoutHints<T>>,\n): UnitWithLayoutParameters<T>[] {\n /** A relatively naïve function to assign columns to units within a mutually overlapping group.\n * This gets the number of columns that would be needed to accommodate the units in the group.\n * It then assigns columns to units in the group based on the number of columns needed, with\n * lower units further to the left.\n */\n\n const sortedUnits = [...group.units].sort(createUnitSorter(group.axisType));\n\n const columns: T[][] = [];\n\n let nColumns = 1;\n\n for (const unit of sortedUnits) {\n let startIndex = 0;\n let endIndex = 0;\n // Find a column that is not already occupied by an overlapping unit\n const colHint = unit.layoutHints?.column;\n if (colHint != null) {\n columns[colHint] ??= [];\n columns[colHint].push(unit);\n continue;\n }\n let didAddToExistingColumn = false;\n\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, group.axisType, dt)) {\n column.push(unit);\n didAddToExistingColumn = true;\n break;\n }\n }\n if (!didAddToExistingColumn) {\n columns.push([unit]);\n }\n }\n\n console.log(columns);\n\n const units: UnitWithLayoutParameters<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n layout: {\n column: columnIndex,\n nColumns: 1,\n totalColumns: columns.length,\n unitGroup: group,\n },\n };\n }),\n );\n\n return units.toSorted(sortByAge);\n}\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const unitGroups = groupUnitsByMutualOverlap(units, tolerance, axisType);\n\n const divisions: any[] = [];\n // Serialize all groups' units to a single array\n for (const group of unitGroups) {\n divisions.push(...group.getUnits());\n }\n\n return divisions.map((d) =>\n enhanceUnitWithClippingPosition(section, d, axisType),\n );\n}\n\nexport function enhanceUnitWithClippingPosition(section, d, axisType) {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n return d;\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n tolerance: number = dt,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n\n const initialSections: WorkingSection[] = units.map((unit) => {\n return {\n units: [unit],\n heightRange: getUnitHeightRange(unit, axisType),\n };\n });\n\n const isOverlapping = (a: WorkingSection, b: WorkingSection) => {\n const res = compareAgeRanges(a.heightRange, b.heightRange, tolerance);\n return res !== AgeRangeRelationship.Disjoint;\n };\n\n const mergeSections = (a: WorkingSection, b: WorkingSection) => {\n return {\n units: [...a.units, ...b.units],\n heightRange: mergeAgeRanges(\n [a.heightRange, b.heightRange],\n MergeMode.Outer,\n ),\n };\n };\n\n // Merge overlapping sections progressively until no more merges are possible\n let finalSectionList = initialSections;\n\n while (true) {\n let merged = false;\n const nextSections: WorkingSection[] = [];\n\n for (const section of finalSectionList) {\n if (nextSections.length === 0) {\n nextSections.push(section);\n } else {\n const lastSection = nextSections[nextSections.length - 1];\n if (isOverlapping(section, lastSection)) {\n // Merge with the last section\n nextSections[nextSections.length - 1] = mergeSections(lastSection, section);\n merged = true;\n } else {\n nextSections.push(section);\n }\n }\n }\n\n finalSectionList = nextSections;\n\n // Stop when no more merges occurred in this pass\n if (!merged) {\n break;\n }\n }\n\n return finalSectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["group"],"mappings":";;;;AAkBA,MAAM,KAAK;AAsBX,MAAM,UAA8D;AAAA,EAClE;AAAA,EACA;AAAA,EAEA,YAAY,OAAY,WAA2B,eAAe,KAAK;AACrE,SAAK,QAAQ;AACb,SAAK,WAAW;AAAA,EAClB;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,EACnD;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,EACnD;AAAA,EAEA,WAA0C;AACxC,WAAO,2CAA2C,IAAI;AAAA,EACxD;AACF;AAEA,SAAS,0BACP,OACA,YAAoB,IACpB,WAA2B,eAAe,KAC1B;AAGhB,QAAM,WAAW,gCAAgC,OAAO,UAAU,SAAS;AAE3E,UAAQ,IAAI,oBAAoB,QAAQ;AAExC,SAAO,SAAS,IAAI,CAAC,MAAM;AACzB,WAAO,IAAI,UAAU,EAAE,OAAO,QAAQ;AAAA,EACxC,CAAC;AACH;AAEA,MAAM,YAAY,iBAAiB,eAAe,GAAG;AAErD,SAAS,2CACPA,QAC+B;AAO/B,QAAM,cAAc,CAAC,GAAGA,OAAM,KAAK,EAAE,KAAK,iBAAiBA,OAAM,QAAQ,CAAC;AAE1E,QAAM,UAAiB,CAAA;AAIvB,aAAW,QAAQ,aAAa;AAI9B,UAAM,UAAU,KAAK,aAAa;AAClC,QAAI,WAAW,MAAM;AACnB,cAAQ,OAAO,MAAM,CAAA;AACrB,cAAQ,OAAO,EAAE,KAAK,IAAI;AAC1B;AAAA,IACF;AACA,QAAI,yBAAyB;AAE7B,eAAW,UAAU,SAAS;AAC5B,YAAM,WAAW,OAAO,OAAO,SAAS,CAAC;AACzC,UAAI,CAAC,aAAa,MAAM,UAAUA,OAAM,UAAU,EAAE,GAAG;AACrD,eAAO,KAAK,IAAI;AAChB,iCAAyB;AACzB;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,wBAAwB;AAC3B,cAAQ,KAAK,CAAC,IAAI,CAAC;AAAA,IACrB;AAAA,EACF;AAEA,UAAQ,IAAI,OAAO;AAEnB,QAAM,QAAuC,QAAQ;AAAA,IACnD,CAAC,QAAQ,gBACP,OAAO,IAAI,CAAC,SAAS;AACnB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,QAAQ;AAAA,UACN,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,cAAc,QAAQ;AAAA,UACtB,WAAWA;AAAAA,QAAA;AAAA,MACb;AAAA,IAEJ,CAAC;AAAA,EAAA;AAGL,SAAO,MAAM,SAAS,SAAS;AACjC;AAEO,SAAS,gBACd,SACA,WAA2B,eAAe,KAC/B;AAEX,MAAI,QAAQ,QAAQ;AAEpB,MAAI,YAAY;AAChB,MAAI,YAAY,eAAe,KAAK;AAClC,gBAAY;AAAA,EACd;AAEA,QAAM,aAAa,0BAA0B,OAAO,WAAW,QAAQ;AAEvE,QAAM,YAAmB,CAAA;AAEzB,aAAWA,UAAS,YAAY;AAC9B,cAAU,KAAK,GAAGA,OAAM,SAAA,CAAU;AAAA,EACpC;AAEA,SAAO,UAAU;AAAA,IAAI,CAAC,MACpB,gCAAgC,SAAS,GAAG,QAAQ;AAAA,EAAA;AAExD;AAEO,SAAS,gCAAgC,SAAS,GAAG,UAAU;AACpE,QAAM,CAAC,OAAO,KAAK,IAAI,mBAAmB,GAAG,QAAQ;AACrD,MAAI,YAAY,eAAe,KAAK;AAClC,QAAI,QAAQ,QAAQ,OAAO;AACzB,QAAE,aAAa,QAAQ;AAAA,IACzB;AACA,QAAI,QAAQ,QAAQ,OAAO;AACzB,QAAE,aAAa,QAAQ;AAAA,IACzB;AAAA,EACF;AACA,MAAI,YAAY,eAAe,OAAO;AACpC,QAAI,QAAQ,QAAQ,OAAO;AACzB,QAAE,aAAa,QAAQ;AAAA,IACzB;AACA,QAAI,QAAQ,QAAQ,OAAO;AACzB,QAAE,aAAa,QAAQ;AAAA,IACzB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,kCACd,OACA,WAA2B,eAAe,KACxB;AAIlB,MAAI,SAAS,MAAM,KAAK,MAAM,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC;AACzD,QAAM,iBAAiB,iBAAiB,QAAQ;AAEhD,QAAM,UAAU,OAAO,IAAI,CAAC,CAAC,YAAY,YAAY,MAAM;AACzD,UAAM,CAAC,OAAO,KAAK,IAAI,mBAAmB,YAAY;AACtD,UAAM,CAAC,OAAO,KAAK,IAAI,mBAAmB,cAAc,QAAQ;AAGhE,iBAAa,KAAK,cAAc;AAChC,WAAO,EAAE,YAAY,OAAO,OAAO,OAAO,OAAO,OAAO,aAAA;AAAA,EAC1D,CAAC;AAGD,QAAM,kBAAkB,iBAAiB,QAAQ;AAIjD,UAAQ,KAAK,eAAe;AAC5B,SAAO;AACT;AAQO,SAAS,gCACd,OACA,WAA2B,eAAe,KAC1C,YAAoB,IACF;AASlB,QAAM,kBAAoC,MAAM,IAAI,CAAC,SAAS;AAC5D,WAAO;AAAA,MACL,OAAO,CAAC,IAAI;AAAA,MACZ,aAAa,mBAAmB,MAAM,QAAQ;AAAA,IAAA;AAAA,EAElD,CAAC;AAED,QAAM,gBAAgB,CAAC,GAAmB,MAAsB;AAC9D,UAAM,MAAM,iBAAiB,EAAE,aAAa,EAAE,aAAa,SAAS;AACpE,WAAO,QAAQ,qBAAqB;AAAA,EACtC;AAEA,QAAM,gBAAgB,CAAC,GAAmB,MAAsB;AAC9D,WAAO;AAAA,MACL,OAAO,CAAC,GAAG,EAAE,OAAO,GAAG,EAAE,KAAK;AAAA,MAC9B,aAAa;AAAA,QACX,CAAC,EAAE,aAAa,EAAE,WAAW;AAAA,QAC7B,UAAU;AAAA,MAAA;AAAA,IACZ;AAAA,EAEJ;AAGA,MAAI,mBAAmB;AAEvB,SAAO,MAAM;AACX,QAAI,SAAS;AACb,UAAM,eAAiC,CAAA;AAEvC,eAAW,WAAW,kBAAkB;AACtC,UAAI,aAAa,WAAW,GAAG;AAC7B,qBAAa,KAAK,OAAO;AAAA,MAC3B,OAAO;AACL,cAAM,cAAc,aAAa,aAAa,SAAS,CAAC;AACxD,YAAI,cAAc,SAAS,WAAW,GAAG;AAEvC,uBAAa,aAAa,SAAS,CAAC,IAAI,cAAc,aAAa,OAAO;AAC1E,mBAAS;AAAA,QACX,OAAO;AACL,uBAAa,KAAK,OAAO;AAAA,QAC3B;AAAA,MACF;AAAA,IACF;AAEA,uBAAmB;AAGnB,QAAI,CAAC,QAAQ;AACX;AAAA,IACF;AAAA,EACF;AAEA,SAAO,iBAAiB,IAAI,CAAC,SAAS,MAAM;AAC1C,UAAM,CAAC,OAAO,KAAK,IAAI,mBAAmB,QAAQ,KAAK;AACvD,UAAM,CAAC,OAAO,KAAK,IAAI,mBAAmB,QAAQ,OAAO,QAAQ;AACjE,WAAO;AAAA;AAAA,MAEL,YAAY,CAAC;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,QAAQ;AAAA,IAAA;AAAA,EAEnB,CAAC;AACH;AAEO,SAAS,mBACd,OACA,UACkB;AAElB,QAAM,cAAc,MAAM,IAAI,CAAC,MAAM;AACnC,YAAQ,UAAA;AAAA,MACN,KAAK,eAAe;AAClB,eAAO,EAAE;AAAA,MACX,KAAK,eAAe;AAAA,MACpB,KAAK,eAAe;AAAA,MACpB,KAAK,eAAe;AAClB,eAAO,EAAE;AAAA,MACX;AACE,cAAM,IAAI,MAAM,sBAAsB,QAAQ,EAAE;AAAA,IAAA;AAAA,EAEtD,CAAC;AACD,QAAM,cAAc,MAAM,IAAI,CAAC,MAAM;AACnC,YAAQ,UAAA;AAAA,MACN,KAAK,eAAe;AAClB,eAAO,EAAE;AAAA,MACX,KAAK,eAAe;AAAA,MACpB,KAAK,eAAe;AAAA,MACpB,KAAK,eAAe;AAClB,eAAO,EAAE;AAAA,MACX;AACE,cAAM,IAAI,MAAM,sBAAsB,QAAQ,EAAE;AAAA,IAAA;AAAA,EAEtD,CAAC;AACD,MAAI,YAAY,eAAe,OAAO,YAAY,eAAe,OAAO;AACtE,WAAO,CAAC,KAAK,IAAI,GAAG,WAAW,GAAG,KAAK,IAAI,GAAG,WAAW,CAAC;AAAA,EAC5D,OAAO;AACL,WAAO,CAAC,KAAK,IAAI,GAAG,WAAW,GAAG,KAAK,IAAI,GAAG,WAAW,CAAC;AAAA,EAC5D;AACF;AAEO,SAAS,mBAAmB,OAAqC;AAEtE,SAAO,mBAAmB,OAAO,eAAe,GAAG;AACrD;AAEO,SAAS,yBACd,UACkB;AAIlB,QAAM,CAAC,cAAc,GAAG,IAAI,IAAI;AAChC,QAAM,cAAc,CAAC,YAAY;AACjC,aAAW,WAAW,MAAM;AAC1B,UAAM,cAAc,YAAY,YAAY,SAAS,CAAC;AACtD,QACE,YAAY,QAAQ,QAAQ,SAC5B,YAAY,QAAQ,QAAQ,OAC5B;AAEA,kBAAY,KAAK,OAAO;AACxB;AAAA,IACF;AAGA,gBAAY,aAAa;AAAA,MACvB,GAAG,YAAY,YAAY,UAAU;AAAA,MACrC,GAAG,YAAY,QAAQ,UAAU;AAAA,IAAA;AAEnC,gBAAY,MAAM,KAAK,GAAG,QAAQ,KAAK;AACvC,gBAAY,QAAQ,KAAK,IAAI,YAAY,OAAO,QAAQ,KAAK;AAC7D,gBAAY,QAAQ,KAAK,IAAI,YAAY,OAAO,QAAQ,KAAK;AAAA,EAC/D;AACA,SAAO;AACT;AAEO,SAAS,sBAAsB,MAA0B;AAK9D,MAAI,QAAQ,KAAK;AACjB,MAAI,QAAQ,KAAK;AAEjB,MAAI,YAAY,KAAK;AAIrB,QAAM,QAAQ,KAAK,UAAU,MAAM,oCAAoC;AAEvE,MAAI,OAAO;AAET,cAAU,gBAAgB,MAAM,CAAC,CAAC;AAClC,cAAU,gBAAgB,MAAM,CAAC,CAAC;AAClC,gBAAY,MAAM,CAAC;AAAA,EACrB;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAO,gBAAgB,KAAK;AAAA,IAC5B,OAAO,gBAAgB,KAAK;AAAA,IAC5B;AAAA,EAAA;AAEJ;"}
1
+ {"version":3,"file":"helpers.js","sources":["../../src/prepare-units/helpers.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { group } from \"d3-array\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport {\n createUnitSorter,\n ensureArray,\n ensureRealFloat,\n getUnitHeightRange,\n unitsOverlap,\n} from \"./utils\";\nimport {\n AgeRangeRelationship,\n compareAgeRanges,\n mergeAgeRanges,\n MergeMode,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithLayoutParameters<T extends BaseUnit> = T & {\n layout: UnitLayoutData<T>;\n};\n\nexport type UnitWithLayoutHints<T extends BaseUnit> = T & {\n layoutHints?: UnitLayoutHints;\n};\n\ninterface UnitLayoutInfo {\n column: number;\n nColumns: number;\n}\n\ntype UnitLayoutHints = Partial<UnitLayoutInfo>;\n\ntype UnitLayoutData<T extends BaseUnit> = UnitLayoutInfo & {\n totalColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n axisType: ColumnAxisType;\n\n constructor(units: T[], axisType: ColumnAxisType = ColumnAxisType.AGE) {\n this.units = units;\n this.axisType = axisType;\n }\n\n get t_age(): number {\n return Math.min(...this.units.map((d) => d.t_age));\n }\n\n get b_age(): number {\n return Math.max(...this.units.map((d) => d.b_age));\n }\n\n getUnits(): UnitWithLayoutParameters<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n\n const sections = groupUnitsIntoSectionsByOverlap(units, axisType, tolerance);\n\n console.log(\"Grouped sections\", sections);\n\n return sections.map((d) => {\n return new UnitGroup(d.units, axisType);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<UnitWithLayoutHints<T>>,\n): UnitWithLayoutParameters<T>[] {\n /** A relatively naïve function to assign columns to units within a mutually overlapping group.\n * This gets the number of columns that would be needed to accommodate the units in the group.\n * It then assigns columns to units in the group based on the number of columns needed, with\n * lower units further to the left.\n */\n\n const sortedUnits = [...group.units].sort(createUnitSorter(group.axisType));\n\n const columns: T[][] = [];\n\n let nColumns = 1;\n\n for (const unit of sortedUnits) {\n let startIndex = 0;\n let endIndex = 0;\n // Find a column that is not already occupied by an overlapping unit\n const colHint = unit.layoutHints?.column;\n if (colHint != null) {\n columns[colHint] ??= [];\n columns[colHint].push(unit);\n continue;\n }\n let didAddToExistingColumn = false;\n\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, group.axisType, dt)) {\n column.push(unit);\n didAddToExistingColumn = true;\n break;\n }\n }\n if (!didAddToExistingColumn) {\n columns.push([unit]);\n }\n }\n\n const units: UnitWithLayoutParameters<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n layout: {\n column: columnIndex,\n nColumns: 1,\n totalColumns: columns.length,\n unitGroup: group,\n },\n };\n }),\n );\n\n return units.toSorted(sortByAge);\n}\n\nexport function preprocessUnits<T extends UnitLong = UnitLong>(\n section: SectionInfo<T>,\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit[] {\n /** Preprocess units to add overlapping units and columns. */\n let units = section.units;\n\n let tolerance = 0.001; // 1 kyr tolerance for age columns\n if (axisType != ColumnAxisType.AGE) {\n tolerance = 0.01; // 1cm tolerance for height/depth columns\n }\n\n const unitGroups = groupUnitsByMutualOverlap(units, tolerance, axisType);\n\n const divisions: any[] = [];\n // Serialize all groups' units to a single array\n for (const group of unitGroups) {\n divisions.push(...group.getUnits());\n }\n\n return divisions.map((d) =>\n enhanceUnitWithClippingPosition(section, d, axisType),\n );\n}\n\nexport function enhanceUnitWithClippingPosition(section, d, axisType) {\n const [b_pos, t_pos] = getUnitHeightRange(d, axisType);\n if (axisType == ColumnAxisType.AGE) {\n if (b_pos > section.b_age) {\n d.b_clip_pos = section.b_age;\n }\n if (t_pos < section.t_age) {\n d.t_clip_pos = section.t_age;\n }\n }\n if (axisType == ColumnAxisType.DEPTH) {\n if (b_pos > section.b_pos) {\n d.b_clip_pos = section.b_pos;\n }\n if (t_pos < section.t_pos) {\n d.t_clip_pos = section.t_pos;\n }\n }\n return d;\n}\n\nexport function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): SectionInfo<T>[] {\n /** Group units into sections by section_id.\n * This works for large-scale Macrostrat columns, where units are grouped by section_id.\n * */\n let groups = Array.from(group(units, (d) => d.section_id));\n const unitComparator = createUnitSorter(axisType);\n\n const groups1 = groups.map(([section_id, sectionUnits]) => {\n const [b_age, t_age] = getSectionAgeRange(sectionUnits);\n const [b_pos, t_pos] = getSectionPosRange(sectionUnits, axisType);\n\n // sort units by position\n sectionUnits.sort(unitComparator);\n return { section_id, t_age, b_age, b_pos, t_pos, units: sectionUnits };\n });\n // Sort sections by increasing top age, then increasing bottom age.\n // Sections have no relative ordinal position other than age...\n const compareSections = createUnitSorter(axisType) as (\n a: StratigraphicPackage,\n b: StratigraphicPackage,\n ) => number;\n groups1.sort(compareSections);\n return groups1;\n}\n\ninterface WorkingSection {\n units: UnitLong[];\n // Position or age\n heightRange: [number, number];\n}\n\nexport function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n tolerance: number = dt,\n): SectionInfo<T>[] {\n /** Group units into sections by overlap.\n * This creates \"synthetic\" sections that correspond to packages bound by scale gaps.\n * This is most useful in the height and depth domains, where gaps (e.g., missing core) are\n * common.\n * */\n // Start with each unit as its own \"section\", and progressively merge...\n const sectionList: WorkingSection[] = [];\n\n const initialSections: WorkingSection[] = units.map((unit) => {\n return {\n units: [unit],\n heightRange: getUnitHeightRange(unit, axisType),\n };\n });\n\n const isOverlapping = (a: WorkingSection, b: WorkingSection) => {\n const res = compareAgeRanges(a.heightRange, b.heightRange, tolerance);\n return res !== AgeRangeRelationship.Disjoint;\n };\n\n const mergeSections = (a: WorkingSection, b: WorkingSection) => {\n return {\n units: [...a.units, ...b.units],\n heightRange: mergeAgeRanges(\n [a.heightRange, b.heightRange],\n MergeMode.Outer,\n ),\n };\n };\n\n // Merge overlapping sections progressively until no more merges are possible\n let finalSectionList = initialSections;\n\n while (true) {\n let merged = false;\n const nextSections: WorkingSection[] = [];\n\n for (const section of finalSectionList) {\n if (nextSections.length === 0) {\n nextSections.push(section);\n } else {\n const lastSection = nextSections[nextSections.length - 1];\n if (isOverlapping(section, lastSection)) {\n // Merge with the last section\n nextSections[nextSections.length - 1] = mergeSections(\n lastSection,\n section,\n );\n merged = true;\n } else {\n nextSections.push(section);\n }\n }\n }\n\n finalSectionList = nextSections;\n\n // Stop when no more merges occurred in this pass\n if (!merged) {\n break;\n }\n }\n\n return finalSectionList.map((section, i) => {\n const [b_age, t_age] = getSectionAgeRange(section.units);\n const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);\n return {\n // Negative section IDs are used to indicate that these are synthetic sections\n section_id: -i,\n t_age,\n b_age,\n t_pos,\n b_pos,\n units: section.units as T[],\n };\n });\n}\n\nexport function getSectionPosRange(\n units: BaseUnit[],\n axisType: ColumnAxisType,\n): [number, number] {\n /** Get the overall position range of a set of units. */\n const t_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.t_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.t_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n const b_positions = units.map((d) => {\n switch (axisType) {\n case ColumnAxisType.AGE:\n return d.b_age;\n case ColumnAxisType.DEPTH:\n case ColumnAxisType.HEIGHT:\n case ColumnAxisType.ORDINAL:\n return d.b_pos;\n default:\n throw new Error(`Unknown axis type: ${axisType}`);\n }\n });\n if (axisType == ColumnAxisType.AGE || axisType == ColumnAxisType.DEPTH) {\n return [Math.max(...b_positions), Math.min(...t_positions)];\n } else {\n return [Math.min(...b_positions), Math.max(...t_positions)];\n }\n}\n\nexport function getSectionAgeRange(units: BaseUnit[]): [number, number] {\n /** Get the overall age range of a set of units. */\n return getSectionPosRange(units, ColumnAxisType.AGE);\n}\n\nexport function mergeOverlappingSections<T extends UnitLong>(\n sections: SectionInfo<T>[],\n): SectionInfo<T>[] {\n /** Columns can have sections that overlap in time. Here, we merge overlapping\n * sections into a single section to correctly render gap-bound packages.\n */\n const [firstSection, ...rest] = sections;\n const newSections = [firstSection];\n for (const section of rest) {\n const lastSection = newSections[newSections.length - 1];\n if (\n lastSection.b_age < section.t_age ||\n lastSection.t_age > section.b_age\n ) {\n // No overlap, add the section as normal\n newSections.push(section);\n continue;\n }\n\n // Overlap, merge the sections\n lastSection.section_id = [\n ...ensureArray(lastSection.section_id),\n ...ensureArray(section.section_id),\n ];\n lastSection.units.push(...section.units);\n lastSection.b_age = Math.max(lastSection.b_age, section.b_age);\n lastSection.t_age = Math.min(lastSection.t_age, section.t_age);\n }\n return newSections;\n}\n\nexport function preprocessSectionUnit(unit: UnitLong): UnitLong {\n /** Preprocess a single unit for a \"section\" column type.\n * This mostly handles vagaries of eODP-style columns.\n * */\n\n let b_pos = unit.b_pos;\n let t_pos = unit.t_pos;\n\n let unit_name = unit.unit_name;\n\n // eODP columns sometimes have overlapping core sections, which are encoded in the name field\n // Match eODP section names\n const match = unit.unit_name.match(/^(\\d+(\\.\\d+)?)-(\\d+(\\.\\d+)?): (.+)/);\n\n if (match) {\n // These values should already be set if we've used the show_positions flag\n t_pos ??= ensureRealFloat(match[1]);\n b_pos ??= ensureRealFloat(match[3]);\n unit_name = match[5];\n }\n\n return {\n ...unit,\n b_pos: ensureRealFloat(b_pos),\n t_pos: ensureRealFloat(t_pos),\n unit_name,\n };\n}\n"],"names":["group"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@macrostrat/column-views",
3
- "version": "3.5.1",
3
+ "version": "3.6.1",
4
4
  "description": "Data views for Macrostrat stratigraphic columns",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,18 +48,19 @@
48
48
  "@blueprintjs/core": "^6.14.0",
49
49
  "@macrostrat/api-types": "^1.2.0",
50
50
  "@macrostrat/color-utils": "^1.2.2",
51
- "@macrostrat/column-components": "^2.0.8",
52
- "@macrostrat/data-components": "^1.1.12",
51
+ "@macrostrat/column-components": "^2.0.9",
52
+ "@macrostrat/data-components": "^1.4.0",
53
53
  "@macrostrat/data-provider": "^1.1.0",
54
54
  "@macrostrat/hyper": "^3.1.1",
55
- "@macrostrat/map-interface": "^2.2.6",
56
- "@macrostrat/map-styles": "^2.2.4",
57
- "@macrostrat/mapbox-react": "^3.2.2",
58
- "@macrostrat/mapbox-utils": "^1.7.4",
55
+ "@macrostrat/map-interface": "^2.3.0",
56
+ "@macrostrat/map-styles": "^2.2.6",
57
+ "@macrostrat/mapbox-react": "^3.3.1",
58
+ "@macrostrat/mapbox-utils": "^1.8.0",
59
+ "@macrostrat/scoped-store": "^1.0.1",
59
60
  "@macrostrat/stratigraphy-utils": "^1.3.0",
60
61
  "@macrostrat/svg-map-components": "^2.0.3",
61
62
  "@macrostrat/timescale": "^3.1.5",
62
- "@macrostrat/ui-components": "^5.0.10",
63
+ "@macrostrat/ui-components": "^5.1.0",
63
64
  "@turf/boolean-point-in-polygon": "^7.3.2",
64
65
  "@turf/buffer": "^7.3.2",
65
66
  "@turf/centroid": "^7.3.2",
@@ -24,7 +24,7 @@ import {
24
24
  type AtomMap,
25
25
  createScopedStore,
26
26
  StateIsolation,
27
- } from "@macrostrat/data-components";
27
+ } from "@macrostrat/scoped-store";
28
28
  import { atom } from "jotai";
29
29
  import { useEffect } from "react";
30
30
 
@@ -73,6 +73,17 @@ export function MacrostratColumnStateProvider<T extends BaseUnit>({
73
73
  return selectedUnit != null || onUnitSelected != null;
74
74
  }, []);
75
75
 
76
+ // Check if we're already in a columnStateProvider, and render a no-op if so
77
+ const existingUnits = scope.useAtomValueIfExists(columnUnitsAtom);
78
+ if (existingUnits != null) {
79
+ if (allowUnitSelection || onUnitSelected || selectedUnit != null) {
80
+ console.warn(
81
+ "MacrostratColumnStateProvider: unit selection props are ignored because a provider already exists in the tree",
82
+ );
83
+ }
84
+ return children;
85
+ }
86
+
76
87
  const atomMap: AtomMap = [
77
88
  [columnUnitsAtom, units],
78
89
  [allowUnitSelectionAtom, _allowSelection],
@@ -89,7 +100,6 @@ export function MacrostratColumnStateProvider<T extends BaseUnit>({
89
100
  {
90
101
  atoms: atomMap,
91
102
  keepUpdated: true,
92
- inherit: true,
93
103
  },
94
104
  [selectionHandlers, children],
95
105
  );
@@ -118,8 +118,6 @@ function assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(
118
118
  }
119
119
  }
120
120
 
121
- console.log(columns);
122
-
123
121
  const units: UnitWithLayoutParameters<T>[] = columns.flatMap(
124
122
  (column, columnIndex) =>
125
123
  column.map((unit) => {
@@ -267,7 +265,10 @@ export function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(
267
265
  const lastSection = nextSections[nextSections.length - 1];
268
266
  if (isOverlapping(section, lastSection)) {
269
267
  // Merge with the last section
270
- nextSections[nextSections.length - 1] = mergeSections(lastSection, section);
268
+ nextSections[nextSections.length - 1] = mergeSections(
269
+ lastSection,
270
+ section,
271
+ );
271
272
  merged = true;
272
273
  } else {
273
274
  nextSections.push(section);