@macrostrat/column-views 3.3.5 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/column.cjs +2 -1
- package/dist/column.cjs.map +1 -1
- package/dist/column.js +2 -1
- package/dist/column.js.map +1 -1
- package/dist/data-provider/unit-selection.cjs +7 -3
- package/dist/data-provider/unit-selection.cjs.map +1 -1
- package/dist/data-provider/unit-selection.d.ts +1 -0
- package/dist/data-provider/unit-selection.js +7 -3
- package/dist/data-provider/unit-selection.js.map +1 -1
- package/dist/index.cjs +13 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/prepare-units/helpers.cjs +83 -49
- package/dist/prepare-units/helpers.cjs.map +1 -1
- package/dist/prepare-units/helpers.d.ts +19 -2
- package/dist/prepare-units/helpers.js +84 -50
- package/dist/prepare-units/helpers.js.map +1 -1
- package/dist/prepare-units/index.cjs.map +1 -1
- package/dist/prepare-units/index.js.map +1 -1
- package/dist/section.cjs +1 -2
- package/dist/section.cjs.map +1 -1
- package/dist/section.js +1 -2
- package/dist/section.js.map +1 -1
- package/dist/unit-details/identifiers.cjs +56 -0
- package/dist/unit-details/identifiers.cjs.map +1 -0
- package/dist/unit-details/identifiers.d.ts +31 -0
- package/dist/unit-details/identifiers.js +54 -0
- package/dist/unit-details/identifiers.js.map +1 -0
- package/dist/unit-details/index.d.ts +2 -0
- package/dist/unit-details/panel.cjs +11 -88
- package/dist/unit-details/panel.cjs.map +1 -1
- package/dist/unit-details/panel.d.ts +3 -19
- package/dist/unit-details/panel.js +10 -87
- package/dist/unit-details/panel.js.map +1 -1
- package/dist/unit-details/popover.cjs +1 -2
- package/dist/unit-details/popover.cjs.map +1 -1
- package/dist/unit-details/popover.js +2 -3
- package/dist/unit-details/popover.js.map +1 -1
- package/dist/unit-details/strat-names.cjs +44 -0
- package/dist/unit-details/strat-names.cjs.map +1 -0
- package/dist/unit-details/strat-names.d.ts +14 -0
- package/dist/unit-details/strat-names.js +42 -0
- package/dist/unit-details/strat-names.js.map +1 -0
- package/dist/units/index.cjs +20 -6
- package/dist/units/index.cjs.map +1 -1
- package/dist/units/index.d.ts +11 -20
- package/dist/units/index.js +20 -6
- package/dist/units/index.js.map +1 -1
- package/package.json +14 -14
- package/src/column.ts +1 -1
- package/src/data-provider/unit-selection.ts +8 -4
- package/src/prepare-units/helpers.ts +121 -72
- package/src/prepare-units/index.ts +1 -1
- package/src/section.ts +0 -1
- package/src/unit-details/identifiers.ts +67 -0
- package/src/unit-details/index.ts +2 -0
- package/src/unit-details/panel.ts +13 -126
- package/src/unit-details/popover.ts +2 -7
- package/src/unit-details/strat-names.ts +57 -0
- package/src/units/index.ts +31 -8
|
@@ -5,64 +5,97 @@ const columnComponents = require("@macrostrat/column-components");
|
|
|
5
5
|
const utils = require("./utils.cjs");
|
|
6
6
|
const stratigraphyUtils = require("@macrostrat/stratigraphy-utils");
|
|
7
7
|
const dt = 1e-3;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
class UnitGroup {
|
|
9
|
+
units;
|
|
10
|
+
constructor(units) {
|
|
11
|
+
this.units = units;
|
|
12
|
+
}
|
|
13
|
+
get t_age() {
|
|
14
|
+
return Math.min(...this.units.map((d) => d.t_age));
|
|
15
|
+
}
|
|
16
|
+
get b_age() {
|
|
17
|
+
return Math.max(...this.units.map((d) => d.b_age));
|
|
18
|
+
}
|
|
19
|
+
getUnits() {
|
|
20
|
+
return assignColumnsToUnitsWithinOverlappingGroup(this);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function groupUnitsByMutualOverlap(units, tolerance = dt) {
|
|
24
|
+
const sections = groupUnitsIntoSectionsByOverlap(
|
|
25
|
+
units,
|
|
26
|
+
columnComponents.ColumnAxisType.AGE,
|
|
27
|
+
tolerance
|
|
28
|
+
);
|
|
29
|
+
return sections.map((d) => {
|
|
30
|
+
return new UnitGroup(d.units);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const sortByAge = utils.createUnitSorter(columnComponents.ColumnAxisType.AGE);
|
|
34
|
+
function assignColumnsToUnitsWithinOverlappingGroup(group2) {
|
|
35
|
+
const sortedUnits = [...group2.units].sort((a, b) => b.b_age - a.b_age);
|
|
36
|
+
const columns = [];
|
|
37
|
+
for (const unit of sortedUnits) {
|
|
38
|
+
let didAddToExistingColumn = false;
|
|
39
|
+
for (const column of columns) {
|
|
40
|
+
const lastUnit = column[column.length - 1];
|
|
41
|
+
if (!utils.unitsOverlap(unit, lastUnit, columnComponents.ColumnAxisType.AGE, dt)) {
|
|
42
|
+
column.push(unit);
|
|
43
|
+
didAddToExistingColumn = true;
|
|
44
|
+
break;
|
|
30
45
|
}
|
|
31
46
|
}
|
|
32
|
-
if (
|
|
33
|
-
|
|
34
|
-
d.b_clip_pos = section.b_pos;
|
|
35
|
-
}
|
|
36
|
-
if (t_pos < section.t_pos) {
|
|
37
|
-
d.t_clip_pos = section.t_pos;
|
|
38
|
-
}
|
|
47
|
+
if (!didAddToExistingColumn) {
|
|
48
|
+
columns.push([unit]);
|
|
39
49
|
}
|
|
40
50
|
}
|
|
41
|
-
|
|
51
|
+
const units = columns.flatMap(
|
|
52
|
+
(column, columnIndex) => column.map((unit) => {
|
|
53
|
+
return {
|
|
54
|
+
...unit,
|
|
55
|
+
overlap: {
|
|
56
|
+
column: columnIndex,
|
|
57
|
+
nColumns: columns.length,
|
|
58
|
+
unitGroup: group2
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
return units.toSorted(sortByAge);
|
|
42
64
|
}
|
|
43
|
-
function
|
|
65
|
+
function preprocessUnits(section, axisType = columnComponents.ColumnAxisType.AGE) {
|
|
66
|
+
let units = section.units;
|
|
44
67
|
let tolerance = 1e-3;
|
|
45
68
|
if (axisType != columnComponents.ColumnAxisType.AGE) {
|
|
46
69
|
tolerance = 0.01;
|
|
47
70
|
}
|
|
48
|
-
const
|
|
49
|
-
|
|
71
|
+
const unitGroups = groupUnitsByMutualOverlap(units, tolerance);
|
|
72
|
+
const divisions = [];
|
|
73
|
+
for (const group2 of unitGroups) {
|
|
74
|
+
divisions.push(...group2.getUnits());
|
|
75
|
+
}
|
|
76
|
+
return divisions.map(
|
|
77
|
+
(d) => enhanceUnitWithClippingPosition(section, d, axisType)
|
|
50
78
|
);
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
}
|
|
80
|
+
function enhanceUnitWithClippingPosition(section, d, axisType) {
|
|
81
|
+
const [b_pos, t_pos] = utils.getUnitHeightRange(d, axisType);
|
|
82
|
+
if (axisType == columnComponents.ColumnAxisType.AGE) {
|
|
83
|
+
if (b_pos > section.b_age) {
|
|
84
|
+
d.b_clip_pos = section.b_age;
|
|
85
|
+
}
|
|
86
|
+
if (t_pos < section.t_age) {
|
|
87
|
+
d.t_clip_pos = section.t_age;
|
|
88
|
+
}
|
|
59
89
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
90
|
+
if (axisType == columnComponents.ColumnAxisType.DEPTH) {
|
|
91
|
+
if (b_pos > section.b_pos) {
|
|
92
|
+
d.b_clip_pos = section.b_pos;
|
|
93
|
+
}
|
|
94
|
+
if (t_pos < section.t_pos) {
|
|
95
|
+
d.t_clip_pos = section.t_pos;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return d;
|
|
66
99
|
}
|
|
67
100
|
function groupUnitsIntoSectionsBySectionID(units, axisType = columnComponents.ColumnAxisType.AGE) {
|
|
68
101
|
let groups = Array.from(d3Array.group(units, (d) => d.section_id));
|
|
@@ -77,12 +110,12 @@ function groupUnitsIntoSectionsBySectionID(units, axisType = columnComponents.Co
|
|
|
77
110
|
groups1.sort(compareSections);
|
|
78
111
|
return groups1;
|
|
79
112
|
}
|
|
80
|
-
function groupUnitsIntoSectionsByOverlap(units, axisType = columnComponents.ColumnAxisType.AGE) {
|
|
113
|
+
function groupUnitsIntoSectionsByOverlap(units, axisType = columnComponents.ColumnAxisType.AGE, tolerance = dt) {
|
|
81
114
|
const sectionList = [];
|
|
82
115
|
for (const unit of units) {
|
|
83
116
|
const heightRange = utils.getUnitHeightRange(unit, axisType);
|
|
84
117
|
let section = sectionList.find(
|
|
85
|
-
(s) => stratigraphyUtils.compareAgeRanges(heightRange, s.heightRange) !== stratigraphyUtils.AgeRangeRelationship.Disjoint
|
|
118
|
+
(s) => stratigraphyUtils.compareAgeRanges(heightRange, s.heightRange, tolerance) !== stratigraphyUtils.AgeRangeRelationship.Disjoint
|
|
86
119
|
);
|
|
87
120
|
if (section == null) {
|
|
88
121
|
sectionList.push({
|
|
@@ -188,6 +221,7 @@ function preprocessSectionUnit(unit) {
|
|
|
188
221
|
unit_name
|
|
189
222
|
};
|
|
190
223
|
}
|
|
224
|
+
exports.enhanceUnitWithClippingPosition = enhanceUnitWithClippingPosition;
|
|
191
225
|
exports.getSectionAgeRange = getSectionAgeRange;
|
|
192
226
|
exports.getSectionPosRange = getSectionPosRange;
|
|
193
227
|
exports.groupUnitsIntoSectionsByOverlap = groupUnitsIntoSectionsByOverlap;
|
|
@@ -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} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\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 divisions = units.map((...args) => extendDivision(...args, axisType));\n for (let d of divisions) {\n const overlappingUnits = divisions.filter((u) =>\n d.overlappingUnits.includes(u.unit_id),\n );\n\n // Overlapping columns\n const columns = overlappingUnits.map((d) => d.column);\n\n if (columns.includes(d.column)) {\n let col = 0;\n // Go through columns to find a better index\n while (columns.includes(col)) {\n col++;\n }\n d.column ??= col;\n }\n\n // If unit overlaps the edges of a section, set the clip positions\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 // if (axisType == ColumnAxisType.HEIGHT) {\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 }\n\n return divisions;\n}\n\nfunction extendDivision(\n unit: UnitLong,\n i: number,\n divisions: UnitLong[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit {\n // TODO: make this configurable\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 overlappingUnits = divisions.filter(\n (d) =>\n d.unit_id != unit.unit_id && unitsOverlap(unit, d, axisType, tolerance),\n );\n const u_pos = getUnitHeightRange(unit, axisType);\n const bottomOverlap = overlappingUnits.some((d) => {\n const d_pos = getUnitHeightRange(d, axisType);\n // Check if the unit is below the current unit\n return d_pos[0] < u_pos[0] + dt;\n });\n let column = 0;\n if (overlappingUnits.length == 1) {\n column = 1;\n }\n\n return {\n ...unit,\n bottomOverlap,\n column: unit.column ?? column,\n overlappingUnits: overlappingUnits.map((d) => d.unit_id),\n };\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 UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\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 for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.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","d","getUnitHeightRange","unitsOverlap","group","createUnitSorter","compareAgeRanges","AgeRangeRelationship","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} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithDefinedOverlap<T extends BaseUnit> = T & {\n overlap: UnitOverlapInfo<T>;\n};\n\ntype UnitOverlapInfo<T extends BaseUnit> = {\n column: number;\n nColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n\n constructor(units: T[]) {\n this.units = units;\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(): UnitWithDefinedOverlap<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n const sections = groupUnitsIntoSectionsByOverlap(\n units,\n ColumnAxisType.AGE,\n tolerance,\n );\n return sections.map((d) => {\n return new UnitGroup(d.units);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<T>,\n): UnitWithDefinedOverlap<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((a, b) => b.b_age - a.b_age);\n\n const columns: T[][] = [];\n\n for (const unit of sortedUnits) {\n // Find a column that is not already occupied by an overlapping unit\n let didAddToExistingColumn = false;\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, ColumnAxisType.AGE, 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: UnitWithDefinedOverlap<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n overlap: {\n column: columnIndex,\n nColumns: 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);\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 for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange, tolerance) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.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","ensureArray","ensureRealFloat"],"mappings":";;;;;;AAgBA,MAAM,KAAK;AAYX,MAAM,UAA8D;AAAA,EAClE;AAAA,EAEA,YAAY,OAAY;AACtB,SAAK,QAAQ;AAAA,EACf;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,WAAwC;AACtC,WAAO,2CAA2C,IAAI;AAAA,EACxD;AACF;AAEA,SAAS,0BACP,OACA,YAAoB,IACJ;AAEhB,QAAM,WAAW;AAAA,IACf;AAAA,IACAA,iBAAAA,eAAe;AAAA,IACf;AAAA,EAAA;AAEF,SAAO,SAAS,IAAI,CAAC,MAAM;AACzB,WAAO,IAAI,UAAU,EAAE,KAAK;AAAA,EAC9B,CAAC;AACH;AAEA,MAAM,YAAYC,MAAAA,iBAAiBD,iBAAAA,eAAe,GAAG;AAErD,SAAS,2CACPE,QAC6B;AAO7B,QAAM,cAAc,CAAC,GAAGA,OAAM,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAErE,QAAM,UAAiB,CAAA;AAEvB,aAAW,QAAQ,aAAa;AAE9B,QAAI,yBAAyB;AAC7B,eAAW,UAAU,SAAS;AAC5B,YAAM,WAAW,OAAO,OAAO,SAAS,CAAC;AACzC,UAAI,CAACC,MAAAA,aAAa,MAAM,UAAUH,iBAAAA,eAAe,KAAK,EAAE,GAAG;AACzD,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,QAAM,QAAqC,QAAQ;AAAA,IACjD,CAAC,QAAQ,gBACP,OAAO,IAAI,CAAC,SAAS;AACnB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,UACP,QAAQ;AAAA,UACR,UAAU,QAAQ;AAAA,UAClB,WAAWE;AAAAA,QAAA;AAAA,MACb;AAAA,IAEJ,CAAC;AAAA,EAAA;AAGL,SAAO,MAAM,SAAS,SAAS;AACjC;AAEO,SAAS,gBACd,SACA,WAA2BF,iBAAAA,eAAe,KAC/B;AAEX,MAAI,QAAQ,QAAQ;AAEpB,MAAI,YAAY;AAChB,MAAI,YAAYA,iBAAAA,eAAe,KAAK;AAClC,gBAAY;AAAA,EACd;AAEA,QAAM,aAAa,0BAA0B,OAAO,SAAS;AAE7D,QAAM,YAAmB,CAAA;AAEzB,aAAWE,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,IAAIE,MAAAA,mBAAmB,GAAG,QAAQ;AACrD,MAAI,YAAYJ,iBAAAA,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,YAAYA,iBAAAA,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,WAA2BA,iBAAAA,eAAe,KACxB;AAIlB,MAAI,SAAS,MAAM,KAAKE,QAAAA,MAAM,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC;AACzD,QAAM,iBAAiBD,MAAAA,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,kBAAkBA,MAAAA,iBAAiB,QAAQ;AAIjD,UAAQ,KAAK,eAAe;AAC5B,SAAO;AACT;AAQO,SAAS,gCACd,OACA,WAA2BD,iBAAAA,eAAe,KAC1C,YAAoB,IACF;AAOlB,QAAM,cAAgC,CAAA;AACtC,aAAW,QAAQ,OAAO;AAExB,UAAM,cAAcI,MAAAA,mBAAmB,MAAM,QAAQ;AACrD,QAAI,UAAsC,YAAY;AAAA,MACpD,CAAC,MACCC,kBAAAA,iBAAiB,aAAa,EAAE,aAAa,SAAS,MACtDC,uCAAqB;AAAA,IAAA;AAEzB,QAAI,WAAW,MAAM;AAEnB,kBAAY,KAAK;AAAA,QACf;AAAA,QACA,OAAO,CAAC,IAAI;AAAA,MAAA,CACb;AAAA,IACH,OAAO;AAEL,cAAQ,MAAM,KAAK,IAAI;AAEvB,UAAI,YAAYN,iBAAAA,eAAe,SAAS,YAAYA,iBAAAA,eAAe,KAAK;AACtE,gBAAQ,cAAc;AAAA,UACpB,KAAK,IAAI,QAAQ,YAAY,CAAC,GAAG,YAAY,CAAC,CAAC;AAAA,UAC/C,KAAK,IAAI,QAAQ,YAAY,CAAC,GAAG,YAAY,CAAC,CAAC;AAAA,QAAA;AAAA,MAEnD,OAAO;AACL,gBAAQ,cAAc;AAAA,UACpB,KAAK,IAAI,QAAQ,YAAY,CAAC,GAAG,YAAY,CAAC,CAAC;AAAA,UAC/C,KAAK,IAAI,QAAQ,YAAY,CAAC,GAAG,YAAY,CAAC,CAAC;AAAA,QAAA;AAAA,MAEnD;AAAA,IACF;AAAA,EACF;AAIA,SAAO,YAAY,IAAI,CAAC,SAAS,MAAM;AACrC,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,KAAKA,iBAAAA,eAAe;AAClB,eAAO,EAAE;AAAA,MACX,KAAKA,iBAAAA,eAAe;AAAA,MACpB,KAAKA,iBAAAA,eAAe;AAAA,MACpB,KAAKA,iBAAAA,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,KAAKA,iBAAAA,eAAe;AAClB,eAAO,EAAE;AAAA,MACX,KAAKA,iBAAAA,eAAe;AAAA,MACpB,KAAKA,iBAAAA,eAAe;AAAA,MACpB,KAAKA,iBAAAA,eAAe;AAClB,eAAO,EAAE;AAAA,MACX;AACE,cAAM,IAAI,MAAM,sBAAsB,QAAQ,EAAE;AAAA,IAAA;AAAA,EAEtD,CAAC;AACD,MAAI,YAAYA,iBAAAA,eAAe,OAAO,YAAYA,iBAAAA,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,OAAOA,iBAAAA,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,GAAGO,MAAAA,YAAY,YAAY,UAAU;AAAA,MACrC,GAAGA,MAAAA,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,cAAUC,MAAAA,gBAAgB,MAAM,CAAC,CAAC;AAClC,cAAUA,MAAAA,gBAAgB,MAAM,CAAC,CAAC;AAClC,gBAAY,MAAM,CAAC;AAAA,EACrB;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,OAAOA,MAAAA,gBAAgB,KAAK;AAAA,IAC5B,OAAOA,MAAAA,gBAAgB,KAAK;AAAA,IAC5B;AAAA,EAAA;AAEJ;;;;;;;;;"}
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import { BaseUnit, UnitLong } from '@macrostrat/api-types';
|
|
2
2
|
import { ColumnAxisType } from '@macrostrat/column-components';
|
|
3
|
-
import { ExtUnit, SectionInfo } from './types';
|
|
3
|
+
import { ExtUnit, SectionInfo, StratigraphicPackage } from './types';
|
|
4
|
+
export type UnitWithDefinedOverlap<T extends BaseUnit> = T & {
|
|
5
|
+
overlap: UnitOverlapInfo<T>;
|
|
6
|
+
};
|
|
7
|
+
type UnitOverlapInfo<T extends BaseUnit> = {
|
|
8
|
+
column: number;
|
|
9
|
+
nColumns: number;
|
|
10
|
+
unitGroup: UnitGroup<T>;
|
|
11
|
+
};
|
|
12
|
+
declare class UnitGroup<T extends BaseUnit> implements StratigraphicPackage {
|
|
13
|
+
units: T[];
|
|
14
|
+
constructor(units: T[]);
|
|
15
|
+
get t_age(): number;
|
|
16
|
+
get b_age(): number;
|
|
17
|
+
getUnits(): UnitWithDefinedOverlap<T>[];
|
|
18
|
+
}
|
|
4
19
|
export declare function preprocessUnits<T extends UnitLong = UnitLong>(section: SectionInfo<T>, axisType?: ColumnAxisType): ExtUnit[];
|
|
20
|
+
export declare function enhanceUnitWithClippingPosition(section: any, d: any, axisType: any): any;
|
|
5
21
|
export declare function groupUnitsIntoSectionsBySectionID<T extends UnitLong>(units: T[], axisType?: ColumnAxisType): SectionInfo<T>[];
|
|
6
|
-
export declare function groupUnitsIntoSectionsByOverlap<T extends
|
|
22
|
+
export declare function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(units: T[], axisType?: ColumnAxisType, tolerance?: number): SectionInfo<T>[];
|
|
7
23
|
export declare function getSectionPosRange(units: BaseUnit[], axisType: ColumnAxisType): [number, number];
|
|
8
24
|
export declare function getSectionAgeRange(units: BaseUnit[]): [number, number];
|
|
9
25
|
export declare function mergeOverlappingSections<T extends UnitLong>(sections: SectionInfo<T>[]): SectionInfo<T>[];
|
|
10
26
|
export declare function preprocessSectionUnit(unit: UnitLong): UnitLong;
|
|
27
|
+
export {};
|
|
@@ -1,66 +1,99 @@
|
|
|
1
1
|
import { group } from "d3-array";
|
|
2
2
|
import { ColumnAxisType } from "@macrostrat/column-components";
|
|
3
|
-
import {
|
|
3
|
+
import { ensureRealFloat, getUnitHeightRange, ensureArray, createUnitSorter, unitsOverlap } from "./utils.js";
|
|
4
4
|
import { compareAgeRanges, AgeRangeRelationship } from "@macrostrat/stratigraphy-utils";
|
|
5
5
|
const dt = 1e-3;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
6
|
+
class UnitGroup {
|
|
7
|
+
units;
|
|
8
|
+
constructor(units) {
|
|
9
|
+
this.units = units;
|
|
10
|
+
}
|
|
11
|
+
get t_age() {
|
|
12
|
+
return Math.min(...this.units.map((d) => d.t_age));
|
|
13
|
+
}
|
|
14
|
+
get b_age() {
|
|
15
|
+
return Math.max(...this.units.map((d) => d.b_age));
|
|
16
|
+
}
|
|
17
|
+
getUnits() {
|
|
18
|
+
return assignColumnsToUnitsWithinOverlappingGroup(this);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function groupUnitsByMutualOverlap(units, tolerance = dt) {
|
|
22
|
+
const sections = groupUnitsIntoSectionsByOverlap(
|
|
23
|
+
units,
|
|
24
|
+
ColumnAxisType.AGE,
|
|
25
|
+
tolerance
|
|
26
|
+
);
|
|
27
|
+
return sections.map((d) => {
|
|
28
|
+
return new UnitGroup(d.units);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
const sortByAge = createUnitSorter(ColumnAxisType.AGE);
|
|
32
|
+
function assignColumnsToUnitsWithinOverlappingGroup(group2) {
|
|
33
|
+
const sortedUnits = [...group2.units].sort((a, b) => b.b_age - a.b_age);
|
|
34
|
+
const columns = [];
|
|
35
|
+
for (const unit of sortedUnits) {
|
|
36
|
+
let didAddToExistingColumn = false;
|
|
37
|
+
for (const column of columns) {
|
|
38
|
+
const lastUnit = column[column.length - 1];
|
|
39
|
+
if (!unitsOverlap(unit, lastUnit, ColumnAxisType.AGE, dt)) {
|
|
40
|
+
column.push(unit);
|
|
41
|
+
didAddToExistingColumn = true;
|
|
42
|
+
break;
|
|
28
43
|
}
|
|
29
44
|
}
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
d.b_clip_pos = section.b_pos;
|
|
33
|
-
}
|
|
34
|
-
if (t_pos < section.t_pos) {
|
|
35
|
-
d.t_clip_pos = section.t_pos;
|
|
36
|
-
}
|
|
45
|
+
if (!didAddToExistingColumn) {
|
|
46
|
+
columns.push([unit]);
|
|
37
47
|
}
|
|
38
48
|
}
|
|
39
|
-
|
|
49
|
+
const units = columns.flatMap(
|
|
50
|
+
(column, columnIndex) => column.map((unit) => {
|
|
51
|
+
return {
|
|
52
|
+
...unit,
|
|
53
|
+
overlap: {
|
|
54
|
+
column: columnIndex,
|
|
55
|
+
nColumns: columns.length,
|
|
56
|
+
unitGroup: group2
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
return units.toSorted(sortByAge);
|
|
40
62
|
}
|
|
41
|
-
function
|
|
63
|
+
function preprocessUnits(section, axisType = ColumnAxisType.AGE) {
|
|
64
|
+
let units = section.units;
|
|
42
65
|
let tolerance = 1e-3;
|
|
43
66
|
if (axisType != ColumnAxisType.AGE) {
|
|
44
67
|
tolerance = 0.01;
|
|
45
68
|
}
|
|
46
|
-
const
|
|
47
|
-
|
|
69
|
+
const unitGroups = groupUnitsByMutualOverlap(units, tolerance);
|
|
70
|
+
const divisions = [];
|
|
71
|
+
for (const group2 of unitGroups) {
|
|
72
|
+
divisions.push(...group2.getUnits());
|
|
73
|
+
}
|
|
74
|
+
return divisions.map(
|
|
75
|
+
(d) => enhanceUnitWithClippingPosition(section, d, axisType)
|
|
48
76
|
);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
77
|
+
}
|
|
78
|
+
function enhanceUnitWithClippingPosition(section, d, axisType) {
|
|
79
|
+
const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
|
|
80
|
+
if (axisType == ColumnAxisType.AGE) {
|
|
81
|
+
if (b_pos > section.b_age) {
|
|
82
|
+
d.b_clip_pos = section.b_age;
|
|
83
|
+
}
|
|
84
|
+
if (t_pos < section.t_age) {
|
|
85
|
+
d.t_clip_pos = section.t_age;
|
|
86
|
+
}
|
|
57
87
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
88
|
+
if (axisType == ColumnAxisType.DEPTH) {
|
|
89
|
+
if (b_pos > section.b_pos) {
|
|
90
|
+
d.b_clip_pos = section.b_pos;
|
|
91
|
+
}
|
|
92
|
+
if (t_pos < section.t_pos) {
|
|
93
|
+
d.t_clip_pos = section.t_pos;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return d;
|
|
64
97
|
}
|
|
65
98
|
function groupUnitsIntoSectionsBySectionID(units, axisType = ColumnAxisType.AGE) {
|
|
66
99
|
let groups = Array.from(group(units, (d) => d.section_id));
|
|
@@ -75,12 +108,12 @@ function groupUnitsIntoSectionsBySectionID(units, axisType = ColumnAxisType.AGE)
|
|
|
75
108
|
groups1.sort(compareSections);
|
|
76
109
|
return groups1;
|
|
77
110
|
}
|
|
78
|
-
function groupUnitsIntoSectionsByOverlap(units, axisType = ColumnAxisType.AGE) {
|
|
111
|
+
function groupUnitsIntoSectionsByOverlap(units, axisType = ColumnAxisType.AGE, tolerance = dt) {
|
|
79
112
|
const sectionList = [];
|
|
80
113
|
for (const unit of units) {
|
|
81
114
|
const heightRange = getUnitHeightRange(unit, axisType);
|
|
82
115
|
let section = sectionList.find(
|
|
83
|
-
(s) => compareAgeRanges(heightRange, s.heightRange) !== AgeRangeRelationship.Disjoint
|
|
116
|
+
(s) => compareAgeRanges(heightRange, s.heightRange, tolerance) !== AgeRangeRelationship.Disjoint
|
|
84
117
|
);
|
|
85
118
|
if (section == null) {
|
|
86
119
|
sectionList.push({
|
|
@@ -187,6 +220,7 @@ function preprocessSectionUnit(unit) {
|
|
|
187
220
|
};
|
|
188
221
|
}
|
|
189
222
|
export {
|
|
223
|
+
enhanceUnitWithClippingPosition,
|
|
190
224
|
getSectionAgeRange,
|
|
191
225
|
getSectionPosRange,
|
|
192
226
|
groupUnitsIntoSectionsByOverlap,
|
|
@@ -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} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\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 divisions = units.map((...args) => extendDivision(...args, axisType));\n for (let d of divisions) {\n const overlappingUnits = divisions.filter((u) =>\n d.overlappingUnits.includes(u.unit_id),\n );\n\n // Overlapping columns\n const columns = overlappingUnits.map((d) => d.column);\n\n if (columns.includes(d.column)) {\n let col = 0;\n // Go through columns to find a better index\n while (columns.includes(col)) {\n col++;\n }\n d.column ??= col;\n }\n\n // If unit overlaps the edges of a section, set the clip positions\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 // if (axisType == ColumnAxisType.HEIGHT) {\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 }\n\n return divisions;\n}\n\nfunction extendDivision(\n unit: UnitLong,\n i: number,\n divisions: UnitLong[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\n): ExtUnit {\n // TODO: make this configurable\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 overlappingUnits = divisions.filter(\n (d) =>\n d.unit_id != unit.unit_id && unitsOverlap(unit, d, axisType, tolerance),\n );\n const u_pos = getUnitHeightRange(unit, axisType);\n const bottomOverlap = overlappingUnits.some((d) => {\n const d_pos = getUnitHeightRange(d, axisType);\n // Check if the unit is below the current unit\n return d_pos[0] < u_pos[0] + dt;\n });\n let column = 0;\n if (overlappingUnits.length == 1) {\n column = 1;\n }\n\n return {\n ...unit,\n bottomOverlap,\n column: unit.column ?? column,\n overlappingUnits: overlappingUnits.map((d) => d.unit_id),\n };\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 UnitLong>(\n units: T[],\n axisType: ColumnAxisType = ColumnAxisType.AGE,\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 for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.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":["d"],"mappings":"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"}
|
|
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} from \"@macrostrat/stratigraphy-utils\";\nimport type { ExtUnit, SectionInfo, StratigraphicPackage } from \"./types\";\n\nconst dt = 0.001;\n\nexport type UnitWithDefinedOverlap<T extends BaseUnit> = T & {\n overlap: UnitOverlapInfo<T>;\n};\n\ntype UnitOverlapInfo<T extends BaseUnit> = {\n column: number;\n nColumns: number;\n unitGroup: UnitGroup<T>;\n};\n\nclass UnitGroup<T extends BaseUnit> implements StratigraphicPackage {\n units: T[];\n\n constructor(units: T[]) {\n this.units = units;\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(): UnitWithDefinedOverlap<T>[] {\n return assignColumnsToUnitsWithinOverlappingGroup(this);\n }\n}\n\nfunction groupUnitsByMutualOverlap<T extends BaseUnit>(\n units: T[],\n tolerance: number = dt,\n): UnitGroup<T>[] {\n /** Group units within a section that have mutual overlap. */\n const sections = groupUnitsIntoSectionsByOverlap(\n units,\n ColumnAxisType.AGE,\n tolerance,\n );\n return sections.map((d) => {\n return new UnitGroup(d.units);\n });\n}\n\nconst sortByAge = createUnitSorter(ColumnAxisType.AGE);\n\nfunction assignColumnsToUnitsWithinOverlappingGroup<T extends BaseUnit>(\n group: UnitGroup<T>,\n): UnitWithDefinedOverlap<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((a, b) => b.b_age - a.b_age);\n\n const columns: T[][] = [];\n\n for (const unit of sortedUnits) {\n // Find a column that is not already occupied by an overlapping unit\n let didAddToExistingColumn = false;\n for (const column of columns) {\n const lastUnit = column[column.length - 1];\n if (!unitsOverlap(unit, lastUnit, ColumnAxisType.AGE, 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: UnitWithDefinedOverlap<T>[] = columns.flatMap(\n (column, columnIndex) =>\n column.map((unit) => {\n return {\n ...unit,\n overlap: {\n column: columnIndex,\n nColumns: 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);\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 for (const unit of units) {\n // Check if the unit overlaps with any existing section\n const heightRange = getUnitHeightRange(unit, axisType);\n let section: WorkingSection | undefined = sectionList.find(\n (s) =>\n compareAgeRanges(heightRange, s.heightRange, tolerance) !==\n AgeRangeRelationship.Disjoint,\n );\n if (section == null) {\n // No overlap, create a new section\n sectionList.push({\n heightRange,\n units: [unit],\n });\n } else {\n // Overlap, merge the unit into the section\n section.units.push(unit);\n // Update the height range\n if (axisType == ColumnAxisType.DEPTH || axisType == ColumnAxisType.AGE) {\n section.heightRange = [\n Math.max(section.heightRange[0], heightRange[0]),\n Math.min(section.heightRange[1], heightRange[1]),\n ];\n } else {\n section.heightRange = [\n Math.min(section.heightRange[0], heightRange[0]),\n Math.max(section.heightRange[1], heightRange[1]),\n ];\n }\n }\n }\n // We should have a section for each unit, now we can convert to SectionInfo\n // Ages have to be really actually ages, not heights\n\n return sectionList.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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["useMemo","MergeSectionsMode","ColumnAxisType","preprocessSectionUnit","agesOverlap","unitsOverlap","getSectionPosRange","getSectionAgeRange","groupUnitsIntoSectionsBySectionID","groupUnitsIntoSectionsByOverlap","mergeOverlappingSections","computeSectionHeights","collapseUnconformitiesByPixelHeight","finalizeSectionHeights","preprocessUnits","units"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight: number | null = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["useMemo","MergeSectionsMode","ColumnAxisType","preprocessSectionUnit","agesOverlap","unitsOverlap","getSectionPosRange","getSectionAgeRange","groupUnitsIntoSectionsBySectionID","groupUnitsIntoSectionsByOverlap","mergeOverlappingSections","computeSectionHeights","collapseUnconformitiesByPixelHeight","finalizeSectionHeights","preprocessUnits","units"],"mappings":";;;;;;;AAgCO,SAAS,uBACd,MACA,SACoB;AAIpB,SAAOA,MAAAA,QAAQ,MAAM;AACnB,WAAO,mBAAmB,MAAM,OAAO;AAAA,EACzC,GAAG,CAAC,MAAM,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC;AACtC;AAEO,SAAS,mBACd,OACA,SACoB;AAGpB,MAAI,EAAE,OAAO,OAAO,OAAO,UAAU;AAErC,QAAM;AAAA,IACJ,gBAAgBC,MAAAA,kBAAkB;AAAA,IAClC;AAAA,IACA;AAAA,IACA,8BAA8B;AAAA,IAC9B;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,MAAI,eAA8B;AAElC,MAAI,SAAS,MAAM;AAEjB,UAAM,SAAS,MAAM,OAAA;AACrB,QAAI,YAAYC,iBAAAA,eAAe,KAAK;AAClC,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,qBAAe,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC;AAAA,IACrD,OAAO;AACL,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,UAAI,SAAS,KAAM,SAAQ,KAAK,IAAI,GAAG,MAAM;AAC7C,qBAAe,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,CAAC;AAAA,IACrD;AAAA,EACF;AAIA,MAAI,SAAS,MAAM,IAAIC,6BAAqB;AAG5C,WAAS,OAAO,OAAO,CAAC,MAAM;AAE5B,QAAI,YAAYD,iBAAAA,eAAe,KAAK;AAClC,aAAOE,MAAAA,YAAY,GAAG,EAAE,OAAO,OAAO;AAAA,IACxC,OAAO;AACL,aAAOC,MAAAA,aAAa,GAAG,EAAE,OAAO,MAAA,GAAgB,QAAQ;AAAA,IAC1D;AAAA,EACF,CAAC;AAED,MAAI,YAAY;AAQhB,MAAI;AACJ,MAAI,aAAaJ,MAAAA,kBAAkB,KAAK;AAEtC,UAAM,CAAC,YAAY,UAAU,IAAIK,QAAAA,mBAAmB,QAAQ,QAAQ;AACpE,UAAM,CAAC,YAAY,UAAU,IAAIC,QAAAA,mBAAmB,MAAM;AAC1D,gBAAY;AAAA,MACV;AAAA,QACE,YAAY;AAAA;AAAA;AAAA;AAAA,QAIZ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,EAEJ,WAAW,YAAYL,iBAAAA,eAAe,KAAK;AACzC,gBAAYM,QAAAA,kCAAkC,QAAQ,QAAQ;AAAA,EAChE,OAAO;AACL,gBAAYC,QAAAA,gCAAgC,QAAQ,QAAQ;AAAA,EAC9D;AAGA,WAAS,WAAW,WAAW;AAC7B,QAAI,YAAYP,iBAAAA,eAAe,KAAK;AAClC,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,SAAS;AAC1D,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,QAAQ;AAAA,IAC3D,WAAW,YAAYA,iBAAAA,eAAe,OAAO;AAC3C,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,SAAS;AAC1D,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,QAAQ;AAAA,IAC3D,WAAW,YAAYA,iBAAAA,eAAe,QAAQ;AAC5C,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,SAAS;AAC1D,cAAQ,QAAQ,KAAK,IAAI,QAAQ,OAAO,SAAS,QAAQ;AAAA,IAC3D;AAAA,EACF;AAMA,MAAI,WAAW;AACf,MACE,iBAAiBD,MAAAA,kBAAkB,eACnC,YAAYC,iBAAAA,eAAe,KAC3B;AACA,eAAWQ,QAAAA,yBAAyB,QAAQ;AAAA,EAC9C;AAEA,aAAW,SAAS,OAAO,CAAC,MAAM,KAAK,IAAI;AAQ3C,MAAI,qBAAqBC,eAAAA,sBAAsB,UAAU,OAAO;AAEhE,MAAI,+BAA+B,eAAe,MAAM;AAGtD,QAAI,YAAY,sBAAsB;AACtC,QAAI,OAAO,+BAA+B,UAAU;AAClD,kBAAY;AAAA,IACd;AAEA,yBAAqBC,eAAAA;AAAAA,MACnB;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAKA,MAAI,EAAE,aAAa,UAAU,UAAA,IAAcC,eAAAA;AAAAA,IACzC;AAAA,IACA;AAAA,EAAA;AAOF,QAAM,cAAc,UAAU,IAAI,CAAC,YAAY;AAC7C,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAOC,QAAAA,gBAAgB,SAAS,QAAQ;AAAA,IAAA;AAAA,EAE5C,CAAC;AAMD,QAAM,SAAS,YAAY,OAAO,CAAC,KAAK,UAAU;AAChD,UAAM,EAAE,OAAAC,OAAAA,IAAU;AAClB,eAAW,QAAQA,QAAO;AACxB,UAAI,KAAK,IAAI;AAAA,IACf;AACA,WAAO;AAAA,EACT,GAAG,CAAA,CAAE;AAEL,SAAO;AAAA,IACL,OAAO;AAAA,IACP,aAAa,gBAAgB;AAAA,IAC7B,UAAU;AAAA,EAAA;AAEd;;;;;;;;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["units"],"mappings":";;;;;;AAgCO,SAAS,uBACd,MACA,SACoB;AAIpB,SAAO,QAAQ,MAAM;AACnB,WAAO,mBAAmB,MAAM,OAAO;AAAA,EACzC,GAAG,CAAC,MAAM,GAAG,OAAO,OAAO,OAAO,CAAC,CAAC;AACtC;AAEO,SAAS,mBACd,OACA,SACoB;AAGpB,MAAI,EAAE,OAAO,OAAO,OAAO,UAAU;AAErC,QAAM;AAAA,IACJ,gBAAgB,kBAAkB;AAAA,IAClC;AAAA,IACA;AAAA,IACA,8BAA8B;AAAA,IAC9B;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,MAAI,
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/prepare-units/index.ts"],"sourcesContent":["import {\n getSectionAgeRange,\n getSectionPosRange,\n groupUnitsIntoSectionsByOverlap,\n groupUnitsIntoSectionsBySectionID,\n mergeOverlappingSections,\n preprocessSectionUnit,\n preprocessUnits,\n} from \"./helpers\";\nimport { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { useMemo } from \"react\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport {\n collapseUnconformitiesByPixelHeight,\n computeSectionHeights,\n finalizeSectionHeights,\n type CompositeColumnScale,\n} from \"./composite-scale\";\nimport {\n agesOverlap,\n MergeSectionsMode,\n PrepareColumnOptions,\n PreparedColumnData,\n unitsOverlap,\n} from \"./utils\";\nimport { SectionInfo } from \"./types\";\n\nexport * from \"./utils\";\nexport * from \"./types\";\nexport { preprocessUnits };\nexport type { CompositeColumnScale };\n\nexport function usePreparedColumnUnits(\n data: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** This function wraps and memoizes all preparation steps for converting\n * an array of units from the /units route to a form ready for usage.\n */\n return useMemo(() => {\n return prepareColumnUnits(data, options);\n }, [data, ...Object.values(options)]);\n}\n\nexport function prepareColumnUnits(\n units: UnitLong[],\n options: PrepareColumnOptions,\n): PreparedColumnData {\n /** Prepare units for rendering into Macrostrat columns */\n\n let { t_age, b_age, t_pos, b_pos } = options;\n\n const {\n mergeSections = MergeSectionsMode.OVERLAPPING,\n axisType,\n unconformityHeight,\n collapseSmallUnconformities = false,\n hybridScale,\n scale,\n } = options;\n\n let _totalHeight: number | null = null;\n\n if (scale != null) {\n // Set t_age and b_age based on scale domain if not already set\n const domain = scale.domain();\n if (axisType == ColumnAxisType.AGE) {\n if (t_age == null) t_age = Math.min(...domain);\n if (b_age == null) b_age = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_age) - scale(t_age));\n } else {\n if (t_pos == null) t_pos = Math.min(...domain);\n if (b_pos == null) b_pos = Math.max(...domain);\n _totalHeight = Math.abs(scale(b_pos) - scale(t_pos));\n }\n }\n\n // Start by ensuring that ages and positions are numbers\n // also set up some values for eODP-style columns\n let units1 = units.map(preprocessSectionUnit);\n\n /** Prototype filtering to age range */\n units1 = units1.filter((d) => {\n // Filter units by t_age and b_age, inclusive\n if (axisType == ColumnAxisType.AGE) {\n return agesOverlap(d, { t_age, b_age });\n } else {\n return unitsOverlap(d, { t_pos, b_pos } as any, axisType);\n }\n });\n\n let mergeMode = mergeSections;\n // if (axisType != ColumnAxisType.AGE) {\n // // For non-age columns, we always merge sections.\n // // This is because the \"groupUnitsIntoSections\" function is not well-defined\n // // for non-age columns.\n // mergeMode = MergeSectionsMode.ALL;\n // }\n\n let sections0: SectionInfo<UnitLong>[];\n if (mergeMode == MergeSectionsMode.ALL) {\n // For the \"merge sections\" mode, we need to create a single section\n const [b_unit_pos, t_unit_pos] = getSectionPosRange(units1, axisType);\n const [b_unit_age, t_unit_age] = getSectionAgeRange(units1);\n sections0 = [\n {\n section_id: 0,\n /**\n * If ages limits are directly specified, use them to define the section bounds.\n * */\n t_pos: t_unit_pos,\n b_pos: b_unit_pos,\n t_age: t_unit_age,\n b_age: b_unit_age,\n units: units1,\n },\n ];\n } else if (axisType == ColumnAxisType.AGE) {\n sections0 = groupUnitsIntoSectionsBySectionID(units1, axisType);\n } else {\n sections0 = groupUnitsIntoSectionsByOverlap(units1, axisType);\n }\n\n // Limit sections to the range specified by t_age/b_age or t_pos/b_pos global options\n for (let section of sections0) {\n if (axisType == ColumnAxisType.AGE) {\n section.t_age = Math.max(section.t_age, t_age ?? -Infinity);\n section.b_age = Math.min(section.b_age, b_age ?? Infinity);\n } else if (axisType == ColumnAxisType.DEPTH) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n } else if (axisType == ColumnAxisType.HEIGHT) {\n section.t_pos = Math.max(section.t_pos, t_pos ?? -Infinity);\n section.b_pos = Math.min(section.b_pos, b_pos ?? Infinity);\n }\n }\n\n /** Merging overlapping sections really only makes sense for age/height/depth\n * columns. Ordinal columns are numbered by section so merging them\n * results in collisions.\n */\n let sections = sections0;\n if (\n mergeSections == MergeSectionsMode.OVERLAPPING &&\n axisType == ColumnAxisType.AGE\n ) {\n sections = mergeOverlappingSections(sections);\n }\n // Filter out undefined sections just in case\n sections = sections.filter((d) => d != null);\n\n // SCALES\n\n /* Compute pixel scales etc. for sections\n * We need to do this now to determine which unconformities\n * are small enough to collapse.\n */\n let sectionsWithScales = computeSectionHeights(sections, options);\n\n if (collapseSmallUnconformities && hybridScale == null) {\n // Collapse small unconformities in pixel height space\n // TODO: this doesn't seem to work properly for non-age columns?\n let threshold = unconformityHeight ?? 30;\n if (typeof collapseSmallUnconformities == \"number\") {\n threshold = collapseSmallUnconformities;\n }\n\n sectionsWithScales = collapseUnconformitiesByPixelHeight(\n sectionsWithScales,\n threshold,\n options,\n );\n }\n\n /** Prepare section scale information using groups.\n * Total height is computed from section scales.\n * */\n let { totalHeight, sections: sections2 } = finalizeSectionHeights(\n sectionsWithScales,\n unconformityHeight,\n );\n\n /** For each section, find units that are overlapping.\n * We do this after merging sections so that we can\n * handle cases where there are overlapping units across sections\n * */\n const sectionsOut = sections2.map((section) => {\n return {\n ...section,\n units: preprocessUnits(section, axisType),\n };\n });\n\n /** Reconstitute the units so that they are sorted by section and properly enhanced.\n * This is mostly important so that unit keyboard navigation\n * predictably selects adjacent units.\n */\n const units2 = sectionsOut.reduce((acc, group) => {\n const { units } = group;\n for (const unit of units) {\n acc.push(unit);\n }\n return acc;\n }, []);\n\n return {\n units: units2,\n totalHeight: _totalHeight ?? totalHeight,\n sections: sectionsOut,\n };\n}\n"],"names":["units"],"mappings":"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"}
|