@macrostrat/column-views 3.12.0 → 3.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/age-axis.cjs +1 -2
  3. package/dist/age-axis.cjs.map +1 -1
  4. package/dist/age-axis.js +1 -2
  5. package/dist/age-axis.js.map +1 -1
  6. package/dist/age-model/details.cjs +2 -3
  7. package/dist/age-model/details.cjs.map +1 -1
  8. package/dist/age-model/details.js +2 -3
  9. package/dist/age-model/details.js.map +1 -1
  10. package/dist/age-model/surfaces.cjs +1 -2
  11. package/dist/age-model/surfaces.cjs.map +1 -1
  12. package/dist/age-model/surfaces.js +1 -2
  13. package/dist/age-model/surfaces.js.map +1 -1
  14. package/dist/column-views.css +17 -17
  15. package/dist/column.cjs +11 -1
  16. package/dist/column.cjs.map +1 -1
  17. package/dist/column.js +11 -1
  18. package/dist/column.js.map +1 -1
  19. package/dist/index.cjs +25 -16
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.js +3 -3
  22. package/dist/prepare-units/composite-scale.cjs +16 -6
  23. package/dist/prepare-units/composite-scale.cjs.map +1 -1
  24. package/dist/prepare-units/composite-scale.d.ts +1 -1
  25. package/dist/prepare-units/composite-scale.js +16 -6
  26. package/dist/prepare-units/composite-scale.js.map +1 -1
  27. package/dist/prepare-units/density.cjs +21 -13
  28. package/dist/prepare-units/density.cjs.map +1 -1
  29. package/dist/prepare-units/density.d.ts +18 -11
  30. package/dist/prepare-units/density.js +21 -13
  31. package/dist/prepare-units/density.js.map +1 -1
  32. package/dist/prepare-units/helpers.cjs +16 -1
  33. package/dist/prepare-units/helpers.cjs.map +1 -1
  34. package/dist/prepare-units/helpers.js +16 -1
  35. package/dist/prepare-units/helpers.js.map +1 -1
  36. package/dist/prepare-units/index.cjs.map +1 -1
  37. package/dist/prepare-units/index.js.map +1 -1
  38. package/dist/unit-details/age-range.cjs +14 -44
  39. package/dist/unit-details/age-range.cjs.map +1 -1
  40. package/dist/unit-details/age-range.d.ts +2 -18
  41. package/dist/unit-details/age-range.js +6 -44
  42. package/dist/unit-details/age-range.js.map +1 -1
  43. package/dist/unit-details/utils.cjs +0 -5
  44. package/dist/unit-details/utils.cjs.map +1 -1
  45. package/dist/unit-details/utils.js +0 -5
  46. package/dist/unit-details/utils.js.map +1 -1
  47. package/package.json +3 -3
  48. package/src/age-model/details.ts +2 -2
  49. package/src/age-model/surfaces.ts +2 -2
  50. package/src/column.ts +17 -1
  51. package/src/prepare-units/composite-scale.ts +22 -9
  52. package/src/prepare-units/density.ts +36 -25
  53. package/src/prepare-units/helpers.ts +35 -1
  54. package/src/prepare-units/index.ts +0 -1
  55. package/src/unit-details/age-range.ts +13 -59
@@ -1,5 +1,5 @@
1
1
  import { IntervalTagProps } from '@macrostrat/data-components';
2
- import { ReactNode } from 'react';
2
+ export { AgeLabel, getAge, IntervalProportion as Proportion, } from '@macrostrat/data-components';
3
3
  export declare function AgeField({ unit, children, ...rest }: {
4
4
  [x: string]: any;
5
5
  unit: any;
@@ -26,17 +26,6 @@ export declare function AgeRange({ data, className, }: {
26
26
  children?: any;
27
27
  className?: string;
28
28
  }>;
29
- export declare function AgeLabel({ age, maximumFractionDigits, minimumFractionDigits, className, }: {
30
- age: number;
31
- className?: string;
32
- maximumFractionDigits?: number;
33
- minimumFractionDigits?: number;
34
- }): import('react').FunctionComponentElement<{
35
- value?: any;
36
- unit?: string;
37
- children?: any;
38
- className?: string;
39
- }>;
40
29
  export declare function Duration({ value, maximumFractionDigits, minimumFractionDigits, }: {
41
30
  value: any;
42
31
  maximumFractionDigits?: number | undefined;
@@ -64,12 +53,7 @@ export interface IntervalProportionsProps extends Omit<IntervalTagProps, "interv
64
53
  onClickItem?: any;
65
54
  }
66
55
  export declare function IntervalProportions({ unit, className, showProportions, calculateProportionsFromAges, ...rest }: IntervalProportionsProps): import('react').FunctionComponentElement<{
67
- children?: ReactNode;
56
+ children?: import('react').ReactNode;
68
57
  className: string;
69
58
  }> | null;
70
- export declare function Proportion({ value }: {
71
- value: number;
72
- }): import('react').DOMElement<import('react').DOMAttributes<Element>, Element>;
73
59
  export declare function getAgeRange(_unit: any): any[];
74
- export declare function getAge(value: any): any[];
75
- export {};
@@ -1,7 +1,8 @@
1
- import { DataField, Value, ItemList, IntervalTag } from "@macrostrat/data-components";
1
+ import { DataField, Value, IntervalProportion, ItemList, IntervalTag } from "@macrostrat/data-components";
2
+ import { AgeLabel, IntervalProportion as IntervalProportion2, getAge } from "@macrostrat/data-components";
2
3
  import { useMacrostratDefs } from "@macrostrat/data-provider";
3
4
  import h from "./age-range.module.sass.js";
4
- import { formatRange, formatProportion } from "./utils.js";
5
+ import { formatRange } from "./utils.js";
5
6
  import classNames from "classnames";
6
7
  import { useMemo } from "react";
7
8
  function AgeField({ unit, children, ...rest }) {
@@ -28,19 +29,6 @@ function AgeRange({
28
29
  className
29
30
  });
30
31
  }
31
- function AgeLabel({
32
- age,
33
- maximumFractionDigits = 2,
34
- minimumFractionDigits = 0,
35
- className
36
- }) {
37
- const [value, unit] = getAge(age);
38
- const _value = value.toLocaleString("en-US", {
39
- maximumFractionDigits,
40
- minimumFractionDigits
41
- });
42
- return h(Value, { value: _value, unit, className });
43
- }
44
32
  function Duration({
45
33
  value,
46
34
  maximumFractionDigits = 2,
@@ -106,9 +94,9 @@ function IntervalProportions({
106
94
  let p0 = null;
107
95
  let p1 = null;
108
96
  if (_showProps !== false) {
109
- p1 = h(Proportion, { value: t_prop });
97
+ p1 = h(IntervalProportion, { value: t_prop });
110
98
  if (i0 !== i1 || b_prop !== 0 || t_prop !== 1) {
111
- p0 = h(Proportion, { value: b_prop });
99
+ p0 = h(IntervalProportion, { value: b_prop });
112
100
  }
113
101
  if (i0 === i1 && (b_prop !== 0 || t_prop !== 1)) {
114
102
  p0 = h("span.joint-proportion", [p0, " ", h("span.sep", "to"), " ", p1]);
@@ -141,17 +129,6 @@ function IntervalProportions({
141
129
  function getProportion(age, interval) {
142
130
  return (interval.b_age - age) / (interval.b_age - interval.t_age);
143
131
  }
144
- function Proportion({ value }) {
145
- let content = null;
146
- if (value == 0) {
147
- content = "base";
148
- } else if (value == 1) {
149
- content = "top";
150
- } else {
151
- content = formatProportion(value * 100) + "%";
152
- }
153
- return h("span.proportion", content);
154
- }
155
132
  function getAgeRange(_unit) {
156
133
  let b_age = _unit.b_age;
157
134
  let t_age = _unit.t_age;
@@ -167,28 +144,13 @@ function getAgeRange(_unit) {
167
144
  }
168
145
  return [b_age, t_age, unit];
169
146
  }
170
- function getAge(value) {
171
- let unit = "Ma";
172
- if (value < 0.8) {
173
- unit = "ka";
174
- value *= 1e3;
175
- if (value < 5) {
176
- unit = "yr";
177
- value *= 1e3;
178
- }
179
- } else if (value > 1e3) {
180
- unit = "Ga";
181
- value /= 1e3;
182
- }
183
- return [value, unit];
184
- }
185
147
  export {
186
148
  AgeField,
187
149
  AgeLabel,
188
150
  AgeRange,
189
151
  Duration,
190
152
  IntervalProportions,
191
- Proportion,
153
+ IntervalProportion2 as Proportion,
192
154
  getAge,
193
155
  getAgeRange
194
156
  };
@@ -1 +1 @@
1
- {"version":3,"file":"age-range.js","sources":["../../src/unit-details/age-range.ts"],"sourcesContent":["import {\n DataField,\n IntervalShort,\n IntervalTag,\n type IntervalTagProps,\n ItemList,\n Value,\n} from \"@macrostrat/data-components\";\nimport { useMacrostratDefs } from \"@macrostrat/data-provider\";\nimport h from \"./age-range.module.sass\";\nimport { formatProportion, formatRange } from \"./utils\";\nimport classNames from \"classnames\";\nimport { ReactNode, useMemo } from \"react\";\n\nexport function AgeField({ unit, children, ...rest }) {\n const [_b_age, _t_age, _unit] = getAgeRange(unit);\n\n return h(\n DataField,\n {\n label: \"Age\",\n value: formatRange(_b_age, _t_age),\n unit: _unit,\n ...rest,\n },\n children,\n );\n}\n\nexport function AgeRange({\n data,\n className,\n}: {\n data: {\n t_age: number;\n b_age: number;\n };\n className?: string;\n}) {\n const [_b_age, _t_age, _unit] = getAgeRange(data);\n\n return h(Value, {\n value: formatRange(_b_age, _t_age),\n unit: _unit,\n className,\n });\n}\n\nexport function AgeLabel({\n age,\n maximumFractionDigits = 2,\n minimumFractionDigits = 0,\n className,\n}: {\n age: number;\n className?: string;\n maximumFractionDigits?: number;\n minimumFractionDigits?: number;\n}) {\n /** Component to display a single age value with unit conversion from\n * Ma to ka or Ga as appropriate.\n */\n const [value, unit] = getAge(age);\n\n const _value = value.toLocaleString(\"en-US\", {\n maximumFractionDigits,\n minimumFractionDigits,\n });\n\n return h(Value, { value: _value, unit, className });\n}\n\nexport function Duration({\n value,\n maximumFractionDigits = 2,\n minimumFractionDigits = 0,\n}) {\n let unit = \"Myr\";\n if (value < 0.8) {\n unit = \"kyr\";\n value *= 1000;\n if (value < 5) {\n unit = \"yr\";\n value *= 1000;\n }\n } else if (value > 1000) {\n unit = \"Gyr\";\n value /= 1000;\n }\n\n let _value = value.toLocaleString(\"en-US\", {\n maximumFractionDigits,\n minimumFractionDigits,\n });\n\n return h(Value, { value: _value, unit });\n}\n\ninterface UnitIntervalConstraints {\n b_int_id?: number;\n t_int_id?: number;\n b_prop?: number;\n t_prop?: number;\n b_int_name?: string;\n t_int_name?: string;\n b_age?: number;\n t_age?: number;\n}\n\nexport interface IntervalProportionsProps extends Omit<\n IntervalTagProps,\n \"interval\"\n> {\n unit: UnitIntervalConstraints;\n showProportions?: boolean;\n calculateProportionsFromAges?: boolean;\n onClickItem?: any;\n}\n\nexport function IntervalProportions({\n unit,\n className,\n showProportions,\n calculateProportionsFromAges = true,\n ...rest\n}: IntervalProportionsProps) {\n /** Display the proportions of the unit that belong to the base and top intervals, if they are different */\n if (unit.b_int_id == null && unit.t_int_id == null) return null;\n\n let _showProps = showProportions;\n\n const i0 = unit.b_int_id;\n const i1 = unit.t_int_id;\n\n const ints = new Set([i0, i1]);\n\n /** Get interval information */\n const intervalMap = useMacrostratDefs(\"intervals\", Array.from(ints), null);\n const [int0, int1] = useMemo(() => {\n const int0 = intervalMap?.get(i0) ?? {};\n if (i0 === i1) {\n return [int0, int0];\n }\n const int1 = intervalMap?.get(i1) ?? {};\n return [int0, int1];\n }, [intervalMap, i0, i1]);\n\n const interval0: IntervalShort = {\n id: i0,\n name: unit.b_int_name,\n ...int0,\n };\n\n let b_prop = unit.b_prop;\n let t_prop = unit.t_prop;\n\n // If we have b_age and t_age, then we can calculate proportions if they are not given\n if (b_prop == null && unit.b_age != null && calculateProportionsFromAges) {\n b_prop = getProportion(unit.b_age, int0);\n }\n if (t_prop == null && unit.t_age != null && calculateProportionsFromAges) {\n t_prop = getProportion(unit.t_age, int1);\n }\n\n // If there are no proportions given and we are not explicitly directed to show them,\n // then we set them to hidden.\n if (b_prop == null && t_prop == null) {\n _showProps ??= false;\n }\n\n /*\n Set default proportions to 0 and 1 if they are not given,\n so that we can display \"base\" and \"top\" labels\n */\n b_prop ??= 0;\n t_prop ??= 1;\n\n let p0: any = null;\n let p1: any = null;\n\n if (_showProps !== false) {\n p1 = h(Proportion, { value: t_prop });\n\n if (i0 !== i1 || b_prop !== 0 || t_prop !== 1) {\n // We have a single interval with undefined proportions\n p0 = h(Proportion, { value: b_prop });\n }\n\n if (i0 === i1 && (b_prop !== 0 || t_prop !== 1)) {\n p0 = h(\"span.joint-proportion\", [p0, \" \", h(\"span.sep\", \"to\"), \" \", p1]);\n }\n }\n\n return h(\n ItemList,\n { className: classNames(\"interval-proportions\", className) },\n [\n h(IntervalTag, {\n interval: interval0,\n prefix: p0,\n ...rest,\n }),\n h.if(i0 != i1)(\"span.discourage-break\", [\n h(\"span.sep\", \" to \"),\n h(IntervalTag, {\n interval: {\n id: i1,\n name: unit.t_int_name,\n ...int1,\n },\n prefix: p1,\n ...rest,\n }),\n ]),\n ],\n );\n}\n\nfunction getProportion(age: number, interval: IntervalShort): number | null {\n /** Get proportion with the age */\n return (interval.b_age - age) / (interval.b_age - interval.t_age);\n}\n\nexport function Proportion({ value }: { value: number }) {\n let content: ReactNode | null = null;\n if (value == 0) {\n content = \"base\";\n } else if (value == 1) {\n content = \"top\";\n } else {\n content = formatProportion(value * 100) + \"%\";\n }\n\n return h(\"span.proportion\", content);\n}\n\nexport function getAgeRange(_unit) {\n let b_age = _unit.b_age;\n let t_age = _unit.t_age;\n let unit = \"Ma\";\n\n if (b_age < 0.8 && t_age < 1.2) {\n b_age *= 1000;\n t_age *= 1000;\n unit = \"ka\";\n } else if (b_age > 800 && t_age > 1200) {\n b_age /= 1000;\n t_age /= 1000;\n unit = \"Ga\";\n }\n\n return [b_age, t_age, unit];\n}\n\nexport function getAge(value) {\n /** Get the age value in Ma, ka, or Ga as appropriate */\n let unit = \"Ma\";\n if (value < 0.8) {\n unit = \"ka\";\n value *= 1000;\n if (value < 5) {\n unit = \"yr\";\n value *= 1000;\n }\n } else if (value > 1000) {\n unit = \"Ga\";\n value /= 1000;\n }\n\n return [value, unit];\n}\n"],"names":["int0","int1"],"mappings":"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"}
1
+ {"version":3,"file":"age-range.js","sources":["../../src/unit-details/age-range.ts"],"sourcesContent":["import {\n DataField,\n IntervalProportion,\n IntervalShort,\n IntervalTag,\n type IntervalTagProps,\n ItemList,\n Value,\n} from \"@macrostrat/data-components\";\nimport { useMacrostratDefs } from \"@macrostrat/data-provider\";\nimport h from \"./age-range.module.sass\";\nimport { formatRange } from \"./utils\";\nimport classNames from \"classnames\";\nimport { useMemo } from \"react\";\n\n// The interval tag, its proportion and single-age label live in\n// `@macrostrat/data-components`; re-exported under their names here.\nexport {\n AgeLabel,\n getAge,\n IntervalProportion as Proportion,\n} from \"@macrostrat/data-components\";\n\nexport function AgeField({ unit, children, ...rest }) {\n const [_b_age, _t_age, _unit] = getAgeRange(unit);\n\n return h(\n DataField,\n {\n label: \"Age\",\n value: formatRange(_b_age, _t_age),\n unit: _unit,\n ...rest,\n },\n children,\n );\n}\n\nexport function AgeRange({\n data,\n className,\n}: {\n data: {\n t_age: number;\n b_age: number;\n };\n className?: string;\n}) {\n const [_b_age, _t_age, _unit] = getAgeRange(data);\n\n return h(Value, {\n value: formatRange(_b_age, _t_age),\n unit: _unit,\n className,\n });\n}\n\nexport function Duration({\n value,\n maximumFractionDigits = 2,\n minimumFractionDigits = 0,\n}) {\n let unit = \"Myr\";\n if (value < 0.8) {\n unit = \"kyr\";\n value *= 1000;\n if (value < 5) {\n unit = \"yr\";\n value *= 1000;\n }\n } else if (value > 1000) {\n unit = \"Gyr\";\n value /= 1000;\n }\n\n let _value = value.toLocaleString(\"en-US\", {\n maximumFractionDigits,\n minimumFractionDigits,\n });\n\n return h(Value, { value: _value, unit });\n}\n\ninterface UnitIntervalConstraints {\n b_int_id?: number;\n t_int_id?: number;\n b_prop?: number;\n t_prop?: number;\n b_int_name?: string;\n t_int_name?: string;\n b_age?: number;\n t_age?: number;\n}\n\nexport interface IntervalProportionsProps extends Omit<\n IntervalTagProps,\n \"interval\"\n> {\n unit: UnitIntervalConstraints;\n showProportions?: boolean;\n calculateProportionsFromAges?: boolean;\n onClickItem?: any;\n}\n\nexport function IntervalProportions({\n unit,\n className,\n showProportions,\n calculateProportionsFromAges = true,\n ...rest\n}: IntervalProportionsProps) {\n /** Display the proportions of the unit that belong to the base and top intervals, if they are different */\n if (unit.b_int_id == null && unit.t_int_id == null) return null;\n\n let _showProps = showProportions;\n\n const i0 = unit.b_int_id;\n const i1 = unit.t_int_id;\n\n const ints = new Set([i0, i1]);\n\n /** Get interval information */\n const intervalMap = useMacrostratDefs(\"intervals\", Array.from(ints), null);\n const [int0, int1] = useMemo(() => {\n const int0 = intervalMap?.get(i0) ?? {};\n if (i0 === i1) {\n return [int0, int0];\n }\n const int1 = intervalMap?.get(i1) ?? {};\n return [int0, int1];\n }, [intervalMap, i0, i1]);\n\n const interval0: IntervalShort = {\n id: i0,\n name: unit.b_int_name,\n ...int0,\n };\n\n let b_prop = unit.b_prop;\n let t_prop = unit.t_prop;\n\n // If we have b_age and t_age, then we can calculate proportions if they are not given\n if (b_prop == null && unit.b_age != null && calculateProportionsFromAges) {\n b_prop = getProportion(unit.b_age, int0);\n }\n if (t_prop == null && unit.t_age != null && calculateProportionsFromAges) {\n t_prop = getProportion(unit.t_age, int1);\n }\n\n // If there are no proportions given and we are not explicitly directed to show them,\n // then we set them to hidden.\n if (b_prop == null && t_prop == null) {\n _showProps ??= false;\n }\n\n /*\n Set default proportions to 0 and 1 if they are not given,\n so that we can display \"base\" and \"top\" labels\n */\n b_prop ??= 0;\n t_prop ??= 1;\n\n let p0: any = null;\n let p1: any = null;\n\n if (_showProps !== false) {\n p1 = h(IntervalProportion, { value: t_prop });\n\n if (i0 !== i1 || b_prop !== 0 || t_prop !== 1) {\n // We have a single interval with undefined proportions\n p0 = h(IntervalProportion, { value: b_prop });\n }\n\n if (i0 === i1 && (b_prop !== 0 || t_prop !== 1)) {\n p0 = h(\"span.joint-proportion\", [p0, \" \", h(\"span.sep\", \"to\"), \" \", p1]);\n }\n }\n\n return h(\n ItemList,\n { className: classNames(\"interval-proportions\", className) },\n [\n h(IntervalTag, {\n interval: interval0,\n prefix: p0,\n ...rest,\n }),\n h.if(i0 != i1)(\"span.discourage-break\", [\n h(\"span.sep\", \" to \"),\n h(IntervalTag, {\n interval: {\n id: i1,\n name: unit.t_int_name,\n ...int1,\n },\n prefix: p1,\n ...rest,\n }),\n ]),\n ],\n );\n}\n\nfunction getProportion(age: number, interval: IntervalShort): number | null {\n /** Get proportion with the age */\n return (interval.b_age - age) / (interval.b_age - interval.t_age);\n}\n\nexport function getAgeRange(_unit) {\n let b_age = _unit.b_age;\n let t_age = _unit.t_age;\n let unit = \"Ma\";\n\n if (b_age < 0.8 && t_age < 1.2) {\n b_age *= 1000;\n t_age *= 1000;\n unit = \"ka\";\n } else if (b_age > 800 && t_age > 1200) {\n b_age /= 1000;\n t_age /= 1000;\n unit = \"Ga\";\n }\n\n return [b_age, t_age, unit];\n}\n"],"names":["int0","int1"],"mappings":"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"}
@@ -1,9 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const formatProportion = (d) => {
4
- if (d == null) return null;
5
- return d.toFixed(1);
6
- };
7
3
  function formatRange(min, max, precision = null) {
8
4
  if (min == null || max == null) return null;
9
5
  if (min === max) {
@@ -25,7 +21,6 @@ function formatSignificance(value, precision = null) {
25
21
  return (value / Math.pow(10, -precision)).toFixed(0) + "0".repeat(-precision);
26
22
  }
27
23
  }
28
- exports.formatProportion = formatProportion;
29
24
  exports.formatRange = formatRange;
30
25
  exports.formatSignificance = formatSignificance;
31
26
  //# sourceMappingURL=utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","sources":["../../src/unit-details/utils.ts"],"sourcesContent":["export const formatProportion = (d) => {\n if (d == null) return null;\n return d.toFixed(1);\n};\n\nexport function formatRange(min, max, precision = null) {\n if (min == null || max == null) return null;\n if (min === max) {\n return min.toFixed(precision);\n }\n\n return `${formatSignificance(min, precision)}–${formatSignificance(\n max,\n precision,\n )}`;\n}\n\nexport function formatSignificance(value, precision = null) {\n // Format to preserve a reasonable number of significant figures\n // this could be done with an easier algorithm, probably:\n\n if (precision == null) {\n return value.toLocaleString();\n }\n if (precision >= 0) {\n return value.toFixed(precision);\n }\n if (precision < 0) {\n return (\n (value / Math.pow(10, -precision)).toFixed(0) + \"0\".repeat(-precision)\n );\n }\n}\n"],"names":[],"mappings":";;AAAO,MAAM,mBAAmB,CAAC,MAAM;AACrC,MAAI,KAAK,KAAM,QAAO;AACtB,SAAO,EAAE,QAAQ,CAAC;AACpB;AAEO,SAAS,YAAY,KAAK,KAAK,YAAY,MAAM;AACtD,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,MAAI,QAAQ,KAAK;AACf,WAAO,IAAI,QAAQ,SAAS;AAAA,EAC9B;AAEA,SAAO,GAAG,mBAAmB,KAAK,SAAS,CAAC,IAAI;AAAA,IAC9C;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAEO,SAAS,mBAAmB,OAAO,YAAY,MAAM;AAI1D,MAAI,aAAa,MAAM;AACrB,WAAO,MAAM,eAAA;AAAA,EACf;AACA,MAAI,aAAa,GAAG;AAClB,WAAO,MAAM,QAAQ,SAAS;AAAA,EAChC;AACA,MAAI,YAAY,GAAG;AACjB,YACG,QAAQ,KAAK,IAAI,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS;AAAA,EAEzE;AACF;;;;"}
1
+ {"version":3,"file":"utils.cjs","sources":["../../src/unit-details/utils.ts"],"sourcesContent":["export const formatProportion = (d) => {\n if (d == null) return null;\n return d.toFixed(1);\n};\n\nexport function formatRange(min, max, precision = null) {\n if (min == null || max == null) return null;\n if (min === max) {\n return min.toFixed(precision);\n }\n\n return `${formatSignificance(min, precision)}–${formatSignificance(\n max,\n precision,\n )}`;\n}\n\nexport function formatSignificance(value, precision = null) {\n // Format to preserve a reasonable number of significant figures\n // this could be done with an easier algorithm, probably:\n\n if (precision == null) {\n return value.toLocaleString();\n }\n if (precision >= 0) {\n return value.toFixed(precision);\n }\n if (precision < 0) {\n return (\n (value / Math.pow(10, -precision)).toFixed(0) + \"0\".repeat(-precision)\n );\n }\n}\n"],"names":[],"mappings":";;AAKO,SAAS,YAAY,KAAK,KAAK,YAAY,MAAM;AACtD,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,MAAI,QAAQ,KAAK;AACf,WAAO,IAAI,QAAQ,SAAS;AAAA,EAC9B;AAEA,SAAO,GAAG,mBAAmB,KAAK,SAAS,CAAC,IAAI;AAAA,IAC9C;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAEO,SAAS,mBAAmB,OAAO,YAAY,MAAM;AAI1D,MAAI,aAAa,MAAM;AACrB,WAAO,MAAM,eAAA;AAAA,EACf;AACA,MAAI,aAAa,GAAG;AAClB,WAAO,MAAM,QAAQ,SAAS;AAAA,EAChC;AACA,MAAI,YAAY,GAAG;AACjB,YACG,QAAQ,KAAK,IAAI,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS;AAAA,EAEzE;AACF;;;"}
@@ -1,7 +1,3 @@
1
- const formatProportion = (d) => {
2
- if (d == null) return null;
3
- return d.toFixed(1);
4
- };
5
1
  function formatRange(min, max, precision = null) {
6
2
  if (min == null || max == null) return null;
7
3
  if (min === max) {
@@ -24,7 +20,6 @@ function formatSignificance(value, precision = null) {
24
20
  }
25
21
  }
26
22
  export {
27
- formatProportion,
28
23
  formatRange,
29
24
  formatSignificance
30
25
  };
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../src/unit-details/utils.ts"],"sourcesContent":["export const formatProportion = (d) => {\n if (d == null) return null;\n return d.toFixed(1);\n};\n\nexport function formatRange(min, max, precision = null) {\n if (min == null || max == null) return null;\n if (min === max) {\n return min.toFixed(precision);\n }\n\n return `${formatSignificance(min, precision)}–${formatSignificance(\n max,\n precision,\n )}`;\n}\n\nexport function formatSignificance(value, precision = null) {\n // Format to preserve a reasonable number of significant figures\n // this could be done with an easier algorithm, probably:\n\n if (precision == null) {\n return value.toLocaleString();\n }\n if (precision >= 0) {\n return value.toFixed(precision);\n }\n if (precision < 0) {\n return (\n (value / Math.pow(10, -precision)).toFixed(0) + \"0\".repeat(-precision)\n );\n }\n}\n"],"names":[],"mappings":"AAAO,MAAM,mBAAmB,CAAC,MAAM;AACrC,MAAI,KAAK,KAAM,QAAO;AACtB,SAAO,EAAE,QAAQ,CAAC;AACpB;AAEO,SAAS,YAAY,KAAK,KAAK,YAAY,MAAM;AACtD,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,MAAI,QAAQ,KAAK;AACf,WAAO,IAAI,QAAQ,SAAS;AAAA,EAC9B;AAEA,SAAO,GAAG,mBAAmB,KAAK,SAAS,CAAC,IAAI;AAAA,IAC9C;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAEO,SAAS,mBAAmB,OAAO,YAAY,MAAM;AAI1D,MAAI,aAAa,MAAM;AACrB,WAAO,MAAM,eAAA;AAAA,EACf;AACA,MAAI,aAAa,GAAG;AAClB,WAAO,MAAM,QAAQ,SAAS;AAAA,EAChC;AACA,MAAI,YAAY,GAAG;AACjB,YACG,QAAQ,KAAK,IAAI,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS;AAAA,EAEzE;AACF;"}
1
+ {"version":3,"file":"utils.js","sources":["../../src/unit-details/utils.ts"],"sourcesContent":["export const formatProportion = (d) => {\n if (d == null) return null;\n return d.toFixed(1);\n};\n\nexport function formatRange(min, max, precision = null) {\n if (min == null || max == null) return null;\n if (min === max) {\n return min.toFixed(precision);\n }\n\n return `${formatSignificance(min, precision)}–${formatSignificance(\n max,\n precision,\n )}`;\n}\n\nexport function formatSignificance(value, precision = null) {\n // Format to preserve a reasonable number of significant figures\n // this could be done with an easier algorithm, probably:\n\n if (precision == null) {\n return value.toLocaleString();\n }\n if (precision >= 0) {\n return value.toFixed(precision);\n }\n if (precision < 0) {\n return (\n (value / Math.pow(10, -precision)).toFixed(0) + \"0\".repeat(-precision)\n );\n }\n}\n"],"names":[],"mappings":"AAKO,SAAS,YAAY,KAAK,KAAK,YAAY,MAAM;AACtD,MAAI,OAAO,QAAQ,OAAO,KAAM,QAAO;AACvC,MAAI,QAAQ,KAAK;AACf,WAAO,IAAI,QAAQ,SAAS;AAAA,EAC9B;AAEA,SAAO,GAAG,mBAAmB,KAAK,SAAS,CAAC,IAAI;AAAA,IAC9C;AAAA,IACA;AAAA,EAAA,CACD;AACH;AAEO,SAAS,mBAAmB,OAAO,YAAY,MAAM;AAI1D,MAAI,aAAa,MAAM;AACrB,WAAO,MAAM,eAAA;AAAA,EACf;AACA,MAAI,aAAa,GAAG;AAClB,WAAO,MAAM,QAAQ,SAAS;AAAA,EAChC;AACA,MAAI,YAAY,GAAG;AACjB,YACG,QAAQ,KAAK,IAAI,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS;AAAA,EAEzE;AACF;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@macrostrat/column-views",
3
- "version": "3.12.0",
3
+ "version": "3.12.2",
4
4
  "description": "Data views for Macrostrat stratigraphic columns",
5
5
  "repository": {
6
6
  "type": "git",
@@ -49,8 +49,8 @@
49
49
  "@macrostrat/api-types": "^1.4.0",
50
50
  "@macrostrat/color-utils": "^1.2.2",
51
51
  "@macrostrat/column-components": "^2.2.0",
52
- "@macrostrat/data-components": "^1.5.1",
53
- "@macrostrat/data-provider": "^1.3.1",
52
+ "@macrostrat/data-components": "^1.7.0",
53
+ "@macrostrat/data-provider": "^1.4.1",
54
54
  "@macrostrat/hyper": "^3.1.1",
55
55
  "@macrostrat/map-views": "^1.1.1",
56
56
  "@macrostrat/scoped-store": "^1.1.0",
@@ -15,7 +15,7 @@ import {
15
15
  import { useMacrostratDefs } from "@macrostrat/data-provider";
16
16
  import type { UnitLong } from "@macrostrat/api-types";
17
17
  import { useColumnUnitsMap } from "../data-provider";
18
- import { AgeLabel, Proportion } from "../unit-details/age-range";
18
+ import { AgeLabel } from "../unit-details/age-range";
19
19
  import { formatRange } from "../unit-details/utils";
20
20
  import { ReferencesField } from "../unit-details/panel";
21
21
  import { surfaceClasses } from "./surfaces";
@@ -187,7 +187,7 @@ function SurfaceCalibrationField({ surface }: { surface: ColumnSurface }) {
187
187
  "div.calibration-interval",
188
188
  h(IntervalTag, {
189
189
  interval,
190
- prefix: h(Proportion, { value: proportion }),
190
+ proportion,
191
191
  }),
192
192
  ),
193
193
  ]);
@@ -30,7 +30,7 @@ import {
30
30
  useMacrostratColumnData,
31
31
  } from "../data-provider";
32
32
  import { ColumnNotes } from "../notes";
33
- import { AgeLabel, Proportion } from "../unit-details/age-range";
33
+ import { AgeLabel } from "../unit-details/age-range";
34
34
  import columnStyles from "../column.module.sass";
35
35
  import styles from "./surfaces.module.sass";
36
36
  import {
@@ -351,7 +351,7 @@ function SurfaceNoteLabel({ note }: { note: SurfaceNote }) {
351
351
  primary = h(IntervalTag, {
352
352
  interval,
353
353
  size: TagSize.Small,
354
- prefix: h(Proportion, { value: surface.proportion }),
354
+ proportion: surface.proportion,
355
355
  className: "surface-interval-tag",
356
356
  });
357
357
  } else {
package/src/column.ts CHANGED
@@ -169,6 +169,22 @@ export function Column(props: ColumnProps) {
169
169
  _axisType = ColumnAxisType.AGE;
170
170
  }
171
171
 
172
+ let _sectionOptions = sectionOptions;
173
+ if (_sectionOptions == undefined) {
174
+ // Build our own section options function based on targetUnitHeight
175
+ _sectionOptions = (ctx) => {
176
+ /** Scale targetUnitHeight by the number of units, so that a section with just a few units
177
+ * is drawn taller than a section with many units.
178
+ */
179
+ const n = ctx.units.length;
180
+ const expansionFactor = 8 / Math.min(Math.max(n, 1), 8);
181
+
182
+ return {
183
+ targetUnitHeight: targetUnitHeight * expansionFactor,
184
+ };
185
+ };
186
+ }
187
+
172
188
  const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {
173
189
  axisType: _axisType,
174
190
  t_age,
@@ -177,7 +193,7 @@ export function Column(props: ColumnProps) {
177
193
  b_pos,
178
194
  mergeSections,
179
195
  targetUnitHeight,
180
- sectionOptions,
196
+ sectionOptions: _sectionOptions,
181
197
  unconformityHeight,
182
198
  pixelScale,
183
199
  pixelsPerMyr,
@@ -4,7 +4,6 @@ import { ScaleContinuousNumeric, scaleLinear } from "d3-scale";
4
4
  import { UnitLong } from "@macrostrat/api-types";
5
5
  import { buildHybridScale } from "./dynamic-scales";
6
6
  import { sectionDensity } from "./density";
7
- import { ExtUnit, HybridScaleType, SectionInfo } from "./types";
8
7
  import type {
9
8
  ColumnScaleOptions,
10
9
  CompositeColumnData,
@@ -14,6 +13,8 @@ import type {
14
13
  PackageScaleLayoutData,
15
14
  SectionScaleOptions,
16
15
  } from "./types";
16
+ import { ExtUnit, HybridScaleType, SectionInfo } from "./types";
17
+ import { mergeAgeRanges, MergeMode } from "@macrostrat/stratigraphy-utils";
17
18
 
18
19
  // Composite scale information augmented with units in each package
19
20
 
@@ -144,7 +145,13 @@ function buildSectionScale<T extends UnitLong>(
144
145
  } = opts;
145
146
  const domain = opts.domain ?? findSectionHeightRange(data, axisType);
146
147
 
147
- const dAge = Math.abs(domain[0] - domain[1]);
148
+ const dAge = dx(domain);
149
+ let visibleExtent = dAge; // full section is visible
150
+ if (visibleWindow != null) {
151
+ visibleExtent = dx(
152
+ mergeAgeRanges([domain, visibleWindow], MergeMode.Inner),
153
+ );
154
+ }
148
155
 
149
156
  let _pixelScale: any = opts.pixelScale;
150
157
  let pixelHeight: number;
@@ -177,6 +184,7 @@ function buildSectionScale<T extends UnitLong>(
177
184
  * is what it's for, and never a sliver the window happens to cut. */
178
185
  _pixelScale = sectionDensity(opts)({
179
186
  extent: dAge,
187
+ visibleExtent,
180
188
  unitExtents: visibleUnitExtents(data, axisType, visibleWindow),
181
189
  units: data,
182
190
  sectionID,
@@ -194,6 +202,10 @@ function buildSectionScale<T extends UnitLong>(
194
202
  );
195
203
  }
196
204
 
205
+ function dx(range: [number, number]) {
206
+ return Math.abs(range[0] - range[1]);
207
+ }
208
+
197
209
  /**
198
210
  * The density each section will actually render at once the window is applied.
199
211
  *
@@ -275,9 +287,12 @@ function spendOutward<T extends UnitLong>(
275
287
  // Sections ordered outward from the edge, keeping only what lies beyond it.
276
288
  const beyond = sections
277
289
  .filter(
278
- (s) => direction * (s.b_age - edge) > 0 || direction * (s.t_age - edge) > 0,
290
+ (s) =>
291
+ direction * (s.b_age - edge) > 0 || direction * (s.t_age - edge) > 0,
279
292
  )
280
- .sort((a, b) => direction * (nearEdge(a, direction) - nearEdge(b, direction)));
293
+ .sort(
294
+ (a, b) => direction * (nearEdge(a, direction) - nearEdge(b, direction)),
295
+ );
281
296
 
282
297
  let bound = edge;
283
298
  let budget = padding;
@@ -290,7 +305,8 @@ function spendOutward<T extends UnitLong>(
290
305
  direction > 0
291
306
  ? Math.max(section.t_age, bound)
292
307
  : Math.min(section.b_age, bound);
293
- const available = direction > 0 ? section.b_age - from : from - section.t_age;
308
+ const available =
309
+ direction > 0 ? section.b_age - from : from - section.t_age;
294
310
  if (available <= 0) continue;
295
311
 
296
312
  const cost = available * pixelScale;
@@ -451,10 +467,7 @@ function visibleUnitExtents(
451
467
  const [b_pos, t_pos] = getUnitHeightRange(d, axisType);
452
468
  if (!clip) return Math.abs(b_pos - t_pos);
453
469
  const [b_win, t_win] = visibleWindow;
454
- return Math.max(
455
- 0,
456
- Math.min(b_pos, b_win) - Math.max(t_pos, t_win),
457
- );
470
+ return Math.max(0, Math.min(b_pos, b_win) - Math.max(t_pos, t_win));
458
471
  })
459
472
  .filter((d) => d > 0);
460
473
 
@@ -12,6 +12,8 @@ import type { UnitLong } from "@macrostrat/api-types";
12
12
  export interface SectionDensityContext {
13
13
  /** The section's extent, in axis units */
14
14
  extent: number;
15
+ /** The section's shown extent, in axis units */
16
+ visibleExtent: number;
15
17
  /** Extents of the units the render window actually shows, each clipped to
16
18
  * it. Measuring what's on screen is what makes a unit-height rule mean the
17
19
  * same thing at every zoom depth. */
@@ -28,23 +30,14 @@ export type SectionDensity = (ctx: SectionDensityContext) => number;
28
30
 
29
31
  export type SectionDensityLike = number | SectionDensity;
30
32
 
31
- /** Room for a typical unit on screen: `px` tall, whatever its duration.
32
- *
33
- * "Typical" is the median of the visible extents, which is the only summary
34
- * that survives both tails. Unit durations are spread over orders of
35
- * magnitude: the arithmetic mean sits above any unit you would point at, so a
36
- * couple of long ones squeeze everything else below the target — and the
37
- * geometric mean fails the other way, since a single hair-thin unit (a
38
- * Holocene sliver beside a Pliocene terrace, say) drags the log-average down
39
- * and stretches the whole section to give that sliver its 20 pixels.
40
- */
41
- export function unitHeight(px: number): SectionDensity {
42
- return (ctx) => px / typicalExtent(ctx.unitExtents);
33
+ enum UnitHeightMode {
34
+ MEDIAN = "median",
35
+ MEAN = "mean",
43
36
  }
44
37
 
45
38
  /** Room for the section: `px` tall, whatever its extent. */
46
39
  export function sectionHeight(px: number): SectionDensity {
47
- return (ctx) => px / ctx.extent;
40
+ return (ctx) => px / ctx.visibleExtent;
48
41
  }
49
42
 
50
43
  /** A density stated outright, in pixels per axis unit. */
@@ -75,15 +68,6 @@ export function resolveDensity(
75
68
  return rule(ctx);
76
69
  }
77
70
 
78
- function typicalExtent(extents: number[]): number {
79
- const sorted = extents.filter((d) => d > 0).sort((a, b) => a - b);
80
- if (sorted.length === 0) return NaN;
81
- const mid = (sorted.length - 1) / 2;
82
- const lower = Math.floor(mid);
83
- const upper = Math.ceil(mid);
84
- return (sorted[lower] + sorted[upper]) / 2;
85
- }
86
-
87
71
  /** Mirrors the default in `sectionDensity` */
88
72
  export const DEFAULT_TARGET_UNIT_HEIGHT = 20;
89
73
  /** Small sections often have a unit or two, and no room for axis labels */
@@ -131,8 +115,7 @@ export interface SectionSizeOptions {
131
115
  * holds. What it returns overrides the column-wide options for that section;
132
116
  * anything it leaves out keeps the column's value. */
133
117
  export type SectionOptionsLike =
134
- | SectionSizeOptions
135
- | ((ctx: SectionDensityContext) => SectionSizeOptions);
118
+ SectionSizeOptions | ((ctx: SectionDensityContext) => SectionSizeOptions);
136
119
 
137
120
  export interface SectionDensityOptions extends SectionSizeOptions {
138
121
  /** Sizing decided per section */
@@ -164,7 +147,7 @@ function densityRule(opts: SectionSizeOptions): SectionDensity {
164
147
  } = opts;
165
148
 
166
149
  const derived = atLeast(
167
- unitHeight(targetUnitHeight),
150
+ unitHeight(targetUnitHeight, UnitHeightMode.MEAN),
168
151
  fixedDensity(minPixelScale),
169
152
  sectionHeight(minSectionHeight ?? targetUnitHeight ?? 0),
170
153
  );
@@ -182,6 +165,34 @@ function densityRule(opts: SectionSizeOptions): SectionDensity {
182
165
  };
183
166
  }
184
167
 
168
+ /** Room for a typical unit on screen: `px` tall, whatever its duration.
169
+ *
170
+ * "Typical" is the median of the visible extents, which is the only summary
171
+ * that survives both tails. Unit durations are spread over orders of
172
+ * magnitude: the arithmetic mean sits above any unit you would point at, so a
173
+ * couple of long ones squeeze everything else below the target — and the
174
+ * geometric mean fails the other way, since a single hair-thin unit (a
175
+ * Holocene sliver beside a Pliocene terrace, say) drags the log-average down
176
+ * and stretches the whole section to give that sliver its 20 pixels.
177
+ */
178
+ export function unitHeight(px: number, mode: UnitHeightMode): SectionDensity {
179
+ return (ctx) => px / typicalExtent(ctx.unitExtents, mode);
180
+ }
181
+
182
+ function typicalExtent(extents: number[], mode: UnitHeightMode): number {
183
+ /** The typical extent for a unit, the median of its extents. */
184
+ const filtered = extents.filter((d) => d > 0);
185
+ if (filtered.length === 0) return NaN;
186
+ if (mode === UnitHeightMode.MEAN) {
187
+ return filtered.reduce((sum, val) => sum + val, 0) / filtered.length;
188
+ }
189
+ const sorted = filtered.sort((a, b) => a - b);
190
+ const mid = (sorted.length - 1) / 2;
191
+ const lower = Math.floor(mid);
192
+ const upper = Math.ceil(mid);
193
+ return (sorted[lower] + sorted[upper]) / 2;
194
+ }
195
+
185
196
  /** The density this axis was given, if any. The axis-specific spellings win
186
197
  * over the generic one, which is what lets both be held while the axis
187
198
  * changes. */
@@ -282,7 +282,7 @@ export function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(
282
282
  }
283
283
  }
284
284
 
285
- return finalSectionList.map((section, i) => {
285
+ const s1 = finalSectionList.map((section, i) => {
286
286
  const [b_age, t_age] = getSectionAgeRange(section.units);
287
287
  const [b_pos, t_pos] = getSectionPosRange(section.units, axisType);
288
288
  return {
@@ -295,6 +295,8 @@ export function groupUnitsIntoSectionsByOverlap<T extends BaseUnit>(
295
295
  units: section.units as T[],
296
296
  };
297
297
  });
298
+
299
+ return sortUnits(s1, axisType);
298
300
  }
299
301
 
300
302
  export function getSectionPosRange(
@@ -338,6 +340,38 @@ export function getSectionAgeRange(units: BaseUnit[]): [number, number] {
338
340
  return getSectionPosRange(units, ColumnAxisType.AGE);
339
341
  }
340
342
 
343
+ function forEachSection(fn, sections, ...args) {
344
+ return sections.map((section) => {
345
+ return {
346
+ ...section,
347
+ units: fn(section.units, ...args),
348
+ };
349
+ });
350
+ }
351
+
352
+ function sortUnits(units: BaseUnit[], axisType: ColumnAxisType) {
353
+ return units.sort((a, b) => {
354
+ if (
355
+ axisType === ColumnAxisType.HEIGHT &&
356
+ b.t_pos !== undefined &&
357
+ a.t_pos !== undefined
358
+ ) {
359
+ return b.t_pos - a.t_pos;
360
+ }
361
+ if (
362
+ axisType === ColumnAxisType.DEPTH &&
363
+ b.t_pos !== undefined &&
364
+ a.t_pos !== undefined
365
+ ) {
366
+ return a.t_pos - b.t_pos;
367
+ }
368
+ if (axisType === ColumnAxisType.AGE) {
369
+ return a.t_age - b.t_age;
370
+ }
371
+ return 0;
372
+ });
373
+ }
374
+
341
375
  export function mergeOverlappingSections<T extends UnitLong>(
342
376
  sections: SectionInfo<T>[],
343
377
  ): SectionInfo<T>[] {
@@ -228,7 +228,6 @@ export function prepareColumnUnits(
228
228
  const natural = (section) =>
229
229
  scales.get(section) ?? section.scaleInfo.pixelScale;
230
230
 
231
-
232
231
  const scaleFor = natural;
233
232
 
234
233
  const window =