@macrostrat/column-views 3.8.0 → 3.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHANGELOG.md +125 -0
  2. package/dist/animated-age-window.cjs +51 -0
  3. package/dist/animated-age-window.cjs.map +1 -0
  4. package/dist/animated-age-window.d.ts +51 -0
  5. package/dist/animated-age-window.js +51 -0
  6. package/dist/animated-age-window.js.map +1 -0
  7. package/dist/column.cjs +11 -1
  8. package/dist/column.cjs.map +1 -1
  9. package/dist/column.d.ts +11 -0
  10. package/dist/column.js +11 -1
  11. package/dist/column.js.map +1 -1
  12. package/dist/correlation-chart/main.cjs +24 -12
  13. package/dist/correlation-chart/main.cjs.map +1 -1
  14. package/dist/correlation-chart/main.d.ts +8 -1
  15. package/dist/correlation-chart/main.js +24 -12
  16. package/dist/correlation-chart/main.js.map +1 -1
  17. package/dist/data-provider/store.cjs +17 -4
  18. package/dist/data-provider/store.cjs.map +1 -1
  19. package/dist/data-provider/store.d.ts +6 -1
  20. package/dist/data-provider/store.js +17 -4
  21. package/dist/data-provider/store.js.map +1 -1
  22. package/dist/index.cjs +17 -42
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.ts +3 -1
  25. package/dist/index.js +3 -34
  26. package/dist/index.js.map +1 -1
  27. package/dist/prepare-units/composite-scale.cjs +94 -8
  28. package/dist/prepare-units/composite-scale.cjs.map +1 -1
  29. package/dist/prepare-units/composite-scale.d.ts +39 -0
  30. package/dist/prepare-units/composite-scale.js +96 -10
  31. package/dist/prepare-units/composite-scale.js.map +1 -1
  32. package/dist/prepare-units/helpers.cjs +0 -1
  33. package/dist/prepare-units/helpers.cjs.map +1 -1
  34. package/dist/prepare-units/helpers.js +0 -1
  35. package/dist/prepare-units/helpers.js.map +1 -1
  36. package/dist/prepare-units/index.cjs +33 -12
  37. package/dist/prepare-units/index.cjs.map +1 -1
  38. package/dist/prepare-units/index.js +34 -13
  39. package/dist/prepare-units/index.js.map +1 -1
  40. package/dist/prepare-units/types.cjs.map +1 -1
  41. package/dist/prepare-units/types.d.ts +13 -0
  42. package/dist/prepare-units/types.js.map +1 -1
  43. package/dist/section.cjs +4 -2
  44. package/dist/section.cjs.map +1 -1
  45. package/dist/section.d.ts +4 -1
  46. package/dist/section.js +4 -2
  47. package/dist/section.js.map +1 -1
  48. package/dist/units/boxes.cjs +22 -39
  49. package/dist/units/boxes.cjs.map +1 -1
  50. package/dist/units/boxes.js +24 -41
  51. package/dist/units/boxes.js.map +1 -1
  52. package/dist/units/composite.cjs +12 -12
  53. package/dist/units/composite.cjs.map +1 -1
  54. package/dist/units/composite.js +12 -12
  55. package/dist/units/composite.js.map +1 -1
  56. package/dist/units/index.cjs +7 -11
  57. package/dist/units/index.cjs.map +1 -1
  58. package/dist/units/index.d.ts +0 -1
  59. package/dist/units/index.js +4 -9
  60. package/dist/units/index.js.map +1 -1
  61. package/package.json +10 -31
  62. package/src/animated-age-window.ts +120 -0
  63. package/src/column.ts +24 -0
  64. package/src/correlation-chart/main.ts +30 -8
  65. package/src/data-provider/store.ts +21 -2
  66. package/src/index.ts +4 -1
  67. package/src/prepare-units/composite-scale.ts +210 -10
  68. package/src/prepare-units/helpers.ts +0 -2
  69. package/src/prepare-units/index.ts +68 -14
  70. package/src/prepare-units/types.ts +13 -0
  71. package/src/section.ts +6 -0
  72. package/src/units/boxes.ts +37 -56
  73. package/src/units/composite.ts +21 -12
  74. package/src/units/index.ts +9 -26
  75. package/dist/maps/column-correlation/core.cjs +0 -288
  76. package/dist/maps/column-correlation/core.cjs.map +0 -1
  77. package/dist/maps/column-correlation/core.d.ts +0 -9
  78. package/dist/maps/column-correlation/core.js +0 -286
  79. package/dist/maps/column-correlation/core.js.map +0 -1
  80. package/dist/maps/column-correlation/index.d.ts +0 -2
  81. package/dist/maps/column-correlation/state.cjs +0 -193
  82. package/dist/maps/column-correlation/state.cjs.map +0 -1
  83. package/dist/maps/column-correlation/state.d.ts +0 -62
  84. package/dist/maps/column-correlation/state.js +0 -190
  85. package/dist/maps/column-correlation/state.js.map +0 -1
  86. package/dist/maps/column-navigation/index.d.ts +0 -2
  87. package/dist/maps/column-navigation/mapbox/index.cjs +0 -215
  88. package/dist/maps/column-navigation/mapbox/index.cjs.map +0 -1
  89. package/dist/maps/column-navigation/mapbox/index.d.ts +0 -38
  90. package/dist/maps/column-navigation/mapbox/index.js +0 -213
  91. package/dist/maps/column-navigation/mapbox/index.js.map +0 -1
  92. package/dist/maps/column-navigation/mapbox/keyboard-navigation.cjs +0 -105
  93. package/dist/maps/column-navigation/mapbox/keyboard-navigation.cjs.map +0 -1
  94. package/dist/maps/column-navigation/mapbox/keyboard-navigation.d.ts +0 -8
  95. package/dist/maps/column-navigation/mapbox/keyboard-navigation.js +0 -105
  96. package/dist/maps/column-navigation/mapbox/keyboard-navigation.js.map +0 -1
  97. package/dist/maps/column-navigation/mapbox/state.cjs +0 -73
  98. package/dist/maps/column-navigation/mapbox/state.cjs.map +0 -1
  99. package/dist/maps/column-navigation/mapbox/state.d.ts +0 -23
  100. package/dist/maps/column-navigation/mapbox/state.js +0 -71
  101. package/dist/maps/column-navigation/mapbox/state.js.map +0 -1
  102. package/dist/maps/column-navigation/svg/animated.d.ts +0 -1
  103. package/dist/maps/column-navigation/svg/data-fetcher.cjs +0 -25
  104. package/dist/maps/column-navigation/svg/data-fetcher.cjs.map +0 -1
  105. package/dist/maps/column-navigation/svg/data-fetcher.d.ts +0 -6
  106. package/dist/maps/column-navigation/svg/data-fetcher.js +0 -25
  107. package/dist/maps/column-navigation/svg/data-fetcher.js.map +0 -1
  108. package/dist/maps/column-navigation/svg/index.cjs +0 -94
  109. package/dist/maps/column-navigation/svg/index.cjs.map +0 -1
  110. package/dist/maps/column-navigation/svg/index.d.ts +0 -9
  111. package/dist/maps/column-navigation/svg/index.js +0 -93
  112. package/dist/maps/column-navigation/svg/index.js.map +0 -1
  113. package/dist/maps/column-navigation/svg/layers.cjs +0 -177
  114. package/dist/maps/column-navigation/svg/layers.cjs.map +0 -1
  115. package/dist/maps/column-navigation/svg/layers.d.ts +0 -54
  116. package/dist/maps/column-navigation/svg/layers.js +0 -174
  117. package/dist/maps/column-navigation/svg/layers.js.map +0 -1
  118. package/dist/maps/column-navigation/utils/keyboard-navigation.cjs +0 -50
  119. package/dist/maps/column-navigation/utils/keyboard-navigation.cjs.map +0 -1
  120. package/dist/maps/column-navigation/utils/keyboard-navigation.d.ts +0 -10
  121. package/dist/maps/column-navigation/utils/keyboard-navigation.js +0 -50
  122. package/dist/maps/column-navigation/utils/keyboard-navigation.js.map +0 -1
  123. package/dist/maps/index.d.ts +0 -4
  124. package/dist/maps/inset-map/index.cjs +0 -71
  125. package/dist/maps/inset-map/index.cjs.map +0 -1
  126. package/dist/maps/inset-map/index.d.ts +0 -16
  127. package/dist/maps/inset-map/index.js +0 -69
  128. package/dist/maps/inset-map/index.js.map +0 -1
  129. package/dist/maps/layers/columns.cjs +0 -165
  130. package/dist/maps/layers/columns.cjs.map +0 -1
  131. package/dist/maps/layers/columns.d.ts +0 -28
  132. package/dist/maps/layers/columns.js +0 -165
  133. package/dist/maps/layers/columns.js.map +0 -1
  134. package/dist/maps/layers/index.d.ts +0 -2
  135. package/dist/maps/layers/units.cjs +0 -207
  136. package/dist/maps/layers/units.cjs.map +0 -1
  137. package/dist/maps/layers/units.d.ts +0 -14
  138. package/dist/maps/layers/units.js +0 -204
  139. package/dist/maps/layers/units.js.map +0 -1
  140. package/dist/units/colors.cjs +0 -115
  141. package/dist/units/colors.cjs.map +0 -1
  142. package/dist/units/colors.d.ts +0 -20
  143. package/dist/units/colors.js +0 -113
  144. package/dist/units/colors.js.map +0 -1
  145. package/dist/units/resolvers.cjs +0 -125
  146. package/dist/units/resolvers.cjs.map +0 -1
  147. package/dist/units/resolvers.d.ts +0 -8
  148. package/dist/units/resolvers.js +0 -125
  149. package/dist/units/resolvers.js.map +0 -1
  150. package/src/maps/column-correlation/column-correlation-map.stories.ts +0 -108
  151. package/src/maps/column-correlation/core.ts +0 -342
  152. package/src/maps/column-correlation/index.ts +0 -2
  153. package/src/maps/column-correlation/state.ts +0 -315
  154. package/src/maps/column-navigation/index.ts +0 -2
  155. package/src/maps/column-navigation/mapbox/column-navigation-map.stories.ts +0 -278
  156. package/src/maps/column-navigation/mapbox/index.ts +0 -276
  157. package/src/maps/column-navigation/mapbox/keyboard-navigation.ts +0 -133
  158. package/src/maps/column-navigation/mapbox/state.ts +0 -119
  159. package/src/maps/column-navigation/svg/animated.ts +0 -78
  160. package/src/maps/column-navigation/svg/column-navigation-svg-map.stories.ts +0 -46
  161. package/src/maps/column-navigation/svg/data-fetcher.ts +0 -34
  162. package/src/maps/column-navigation/svg/index.ts +0 -96
  163. package/src/maps/column-navigation/svg/layers.ts +0 -233
  164. package/src/maps/column-navigation/utils/keyboard-navigation.ts +0 -58
  165. package/src/maps/index.ts +0 -4
  166. package/src/maps/inset-map/index.ts +0 -87
  167. package/src/maps/inset-map/inset-map.stories.ts +0 -20
  168. package/src/maps/layers/columns.ts +0 -216
  169. package/src/maps/layers/index.ts +0 -2
  170. package/src/maps/layers/units.ts +0 -286
  171. package/src/maps/unit-map.stories.ts +0 -79
  172. package/src/units/colors.ts +0 -183
  173. package/src/units/resolvers.ts +0 -141
@@ -1,2 +0,0 @@
1
- export * from "./columns";
2
- export * from "./units";
@@ -1,286 +0,0 @@
1
- import {
2
- useLithologies,
3
- useMacrostratColumns,
4
- } from "@macrostrat/data-provider";
5
- import { useAPIResult } from "@macrostrat/ui-components";
6
- import { useMapStyleOperator, useOverlayStyle } from "@macrostrat/mapbox-react";
7
- import { FeatureCollection } from "geojson";
8
- import { useMemo } from "react";
9
- import { mergeStyles, setGeoJSON } from "@macrostrat/mapbox-utils";
10
- import { UnitLithology, UnitLong } from "@macrostrat/api-types";
11
- import { flattenLithologies, getMixedColorForData } from "../../units";
12
- import { getBestFGDCPatternForLithologyList } from "../../units/resolvers";
13
- import {
14
- setupStyleImageManager,
15
- loadStyleImage,
16
- StyleFragment,
17
- } from "@macrostrat/map-styles";
18
- import { asChromaColor } from "@macrostrat/color-utils";
19
- import { buildGeoJSONSource } from "@macrostrat/mapbox-utils";
20
- import pMap from "p-map";
21
- import { BaseColumnsLayer } from "./columns";
22
- import h from "@macrostrat/hyper";
23
-
24
- export interface UnitsOverlayProps {
25
- time: number;
26
- ageSpan?: number;
27
- project?: number | number[];
28
- patterns?: boolean;
29
- }
30
-
31
- export function MacrostratUnitsOverlay(props: UnitsOverlayProps) {
32
- const { time, ageSpan = 0.05, patterns = false } = props;
33
- const lithMap = useLithologies();
34
-
35
- useOverlayStyle(() => {
36
- return buildUnitsStyle({ patterns });
37
- }, [patterns]);
38
-
39
- useMapStyleOperator(
40
- (map) => {
41
- if (!patterns) return;
42
- setupStyleImageManager(map, { verbose: false });
43
- },
44
- [patterns],
45
- );
46
-
47
- const params = useMemo(() => {
48
- let project: number[] | undefined;
49
- if (Number.isFinite(props.project)) {
50
- project = [props.project as number];
51
- } else if (!Array.isArray(props.project)) {
52
- project = undefined;
53
- }
54
- // Project can only be a single project for now
55
- const projectCompat =
56
- project != null && project.length > 0 ? project[0] : undefined;
57
-
58
- return {
59
- age_bottom: time + ageSpan / 2,
60
- age_top: time - ageSpan / 2,
61
- response: "long",
62
- project: projectCompat,
63
- };
64
- }, [time, ageSpan, props.project]);
65
-
66
- const units: UnitLong[] = useAPIResult("/units", params);
67
- const columns = useMacrostratColumns(params.project, false);
68
-
69
- useMapStyleOperator(
70
- (map) => {
71
- if (units == null || columns == null || lithMap == null) {
72
- return;
73
- }
74
-
75
- const getColorForLithology = (data: UnitLithology): string | null => {
76
- return lithMap?.get(data.lith_id)?.color ?? null;
77
- };
78
-
79
- const unitsMap = postProcessUnits(units, getColorForLithology);
80
- handleUnitsLayerUpdate(map, unitsMap, columns);
81
- if (patterns) {
82
- handlePatternOverlayUpdate(
83
- map,
84
- unitsMap,
85
- columns,
86
- getColorForLithology,
87
- ).then(() => {});
88
- }
89
- },
90
- [units, lithMap, columns, patterns],
91
- );
92
-
93
- const isHovered = ["boolean", ["feature-state", "hover"], false];
94
- const isShown = ["boolean", ["feature-state", "shown"], false];
95
-
96
- const opacity = [
97
- "case",
98
- ["all", isHovered, isShown],
99
- 0.9,
100
- isShown,
101
- 0.8,
102
- isHovered,
103
- 0.3,
104
- 0.0,
105
- ];
106
-
107
- return h([
108
- h(BaseColumnsLayer, {
109
- enabled: true,
110
- columns,
111
- opacity,
112
- lineOpacity: 0.2,
113
- lineColor: "lightgray",
114
- color: "transparent",
115
- }),
116
- ]);
117
- }
118
-
119
- function handleUnitsLayerUpdate(
120
- map,
121
- unitsMap: Map<number, UnitsDisplayInfo>,
122
- columns: any[],
123
- ) {
124
- // Update feature states
125
- for (const column of columns) {
126
- const col_id = column.properties.col_id;
127
- const info = unitsMap.get(Number(col_id));
128
- const color = info?.color;
129
- map.setFeatureState(
130
- { source: "columns", id: col_id },
131
- {
132
- color,
133
- shown: color != null,
134
- },
135
- );
136
- }
137
- }
138
-
139
- async function handlePatternOverlayUpdate(
140
- map,
141
- unitsMap: Map<number, UnitsDisplayInfo>,
142
- columns: any[],
143
- getColor: LithologyColorGetter,
144
- ) {
145
- // synthesize patterns not yet in cache
146
-
147
- const colsWithPatterns = [];
148
- const patternIDs = new Set<string>();
149
- // Update feature states
150
- for (const column of columns) {
151
- const col_id = column.properties.col_id;
152
- const info = unitsMap.get(Number(col_id));
153
- if (info == null) continue;
154
- const pattern = getPatternSpec(info?.liths, getColor);
155
- if (pattern == null) continue;
156
-
157
- colsWithPatterns.push({
158
- type: "Feature",
159
- geometry: column.geometry,
160
- properties: {
161
- col_id,
162
- pattern,
163
- },
164
- });
165
- patternIDs.add(pattern);
166
- }
167
-
168
- const patternsToLoad = patternIDs.values().filter((d) => !map.hasImage(d));
169
-
170
- const mapper = async (patternID) => {
171
- try {
172
- await loadStyleImage(map, patternID, { pixelRatio: 10 });
173
- } catch (e) {}
174
- };
175
- // Load all pattern images
176
- await pMap(patternsToLoad, mapper, { concurrency: 5 });
177
-
178
- // Set data for pattern layer
179
- // This is inefficient but the only way to set per-feature patterns in Mapbox
180
- const patternData: FeatureCollection = {
181
- type: "FeatureCollection",
182
- features: colsWithPatterns,
183
- };
184
- setGeoJSON(map, "column-patterns", patternData);
185
- }
186
-
187
- const cachedColors = new Map<string, string>();
188
-
189
- function getDarkenedColor(color: string): string {
190
- /** Memoized function to get a darkened version of a color for pattern use */
191
- if (cachedColors.has(color)) {
192
- return cachedColors.get(color);
193
- }
194
- const c1 = asChromaColor(color).set("hsl.l", 0.4).set("hsl.s", 0.8).hex();
195
- cachedColors.set(color, c1);
196
- return c1;
197
- }
198
-
199
- interface UnitsDisplayInfo {
200
- col_id: number;
201
- units: UnitLong[];
202
- color: string;
203
- liths: UnitLithology[];
204
- }
205
-
206
- type LithologyColorGetter = (val: UnitLithology) => string | null;
207
-
208
- function postProcessUnits(
209
- units: UnitLong[],
210
- getColor: LithologyColorGetter,
211
- ): Map<number, UnitsDisplayInfo> {
212
- // Group by col_id
213
- const unitsByColumn: Map<number, UnitLong[]> = new Map();
214
- for (const unit of units) {
215
- const col_id = Number(unit.col_id);
216
- if (!unitsByColumn.has(col_id)) {
217
- unitsByColumn.set(col_id, [unit]);
218
- } else {
219
- unitsByColumn.set(col_id, [...unitsByColumn.get(col_id), unit]);
220
- }
221
- }
222
-
223
- // Summarize units for display
224
- const res = new Map<number, UnitsDisplayInfo>();
225
- for (const [col_id, unitList] of unitsByColumn.entries()) {
226
- const liths = flattenLithologies(unitList.map((u) => u.lith));
227
- res.set(col_id, {
228
- col_id,
229
- units: unitList,
230
- liths,
231
- color: getMixedColorForData(liths, getColor),
232
- });
233
- }
234
-
235
- return res;
236
- }
237
-
238
- function getPatternSpec(
239
- liths: UnitLithology[],
240
- getColor: LithologyColorGetter,
241
- ): string | null {
242
- const patternData = getBestFGDCPatternForLithologyList(liths);
243
- if (patternData == null) return null;
244
- let { patternID, lith } = patternData;
245
- const color = getDarkenedColor(getColor(lith));
246
- return `fgdc:${patternID}:${color}:transparent`;
247
- }
248
-
249
- type UnitsStyleOptions = {
250
- color?: string;
251
- patterns?: boolean;
252
- };
253
-
254
- export function buildUnitsStyle(opts: UnitsStyleOptions = {}): StyleFragment {
255
- const { patterns = false } = opts;
256
-
257
- // If color is in the feature state or geojson properties, use that as second choice
258
-
259
- const baseStyle: StyleFragment = {
260
- sources: {},
261
- version: 8,
262
- };
263
-
264
- if (!patterns) {
265
- return baseStyle;
266
- }
267
-
268
- const patternStyle: StyleFragment = {
269
- sources: {
270
- "column-patterns": buildGeoJSONSource(),
271
- },
272
- layers: [
273
- {
274
- id: "columns-fill-pattern",
275
- type: "fill",
276
- source: "column-patterns",
277
- paint: {
278
- "fill-pattern": ["get", "pattern"],
279
- "fill-opacity": 1,
280
- },
281
- },
282
- ],
283
- };
284
-
285
- return mergeStyles(baseStyle, patternStyle);
286
- }
@@ -1,79 +0,0 @@
1
- import h from "@macrostrat/hyper";
2
- import { Meta } from "@storybook/react-vite";
3
- import { InsetMap } from "./inset-map";
4
- import { MacrostratDataProvider } from "@macrostrat/data-provider";
5
- import { MacrostratUnitsOverlay } from "./layers/units";
6
- import {
7
- ColumnHoverInteraction,
8
- ColumnSelectionManager,
9
- } from "./column-navigation";
10
- import { useState } from "react";
11
-
12
- // @ts-ignore
13
- const mapboxToken = import.meta.env.VITE_MAPBOX_API_TOKEN;
14
-
15
- // More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
16
- export default {
17
- title: "Column views/Maps/Unit map",
18
- component: UnitMapComponent,
19
- description: "A map of units through time",
20
- argTypes: {
21
- time: { control: "number", defaultValue: 100 },
22
- ageSpan: { control: "number", defaultValue: 0.05 },
23
- patterns: { control: "boolean", defaultValue: false },
24
- },
25
- } as Meta<typeof InsetMap>;
26
-
27
- function UnitMapComponent(props) {
28
- const { children, ...rest } = props;
29
- return h(
30
- MacrostratDataProvider,
31
- {
32
- baseURL: "https://dev.macrostrat.org/api/v2",
33
- },
34
- h(
35
- "div",
36
- h(
37
- InsetMap,
38
- {
39
- style: { width: "800px", height: "600px" },
40
- accessToken: mapboxToken,
41
- },
42
- [h(MacrostratUnitsOverlay, rest), children],
43
- ),
44
- ),
45
- );
46
- }
47
-
48
- export const UnitMap = {
49
- args: {
50
- time: 100,
51
- ageSpan: 0.05,
52
- },
53
- };
54
-
55
- export const WithPatterns = {
56
- args: {
57
- time: 100,
58
- ageSpan: 0.05,
59
- patterns: true,
60
- },
61
- };
62
-
63
- export const WithColumnSelection = {
64
- args: {
65
- time: 100,
66
- ageSpan: 0.05,
67
- patterns: false,
68
- },
69
- render(args) {
70
- const [selectedColumn, setSelectedColumn] = useState(null);
71
- return h(UnitMapComponent, args, [
72
- h(ColumnHoverInteraction),
73
- h(ColumnSelectionManager, {
74
- selectedColumn,
75
- onSelectColumn: setSelectedColumn,
76
- }),
77
- ]);
78
- },
79
- };
@@ -1,183 +0,0 @@
1
- import chroma from "chroma-js";
2
- import {
3
- Environment,
4
- Lithology,
5
- UnitLithology,
6
- UnitLong,
7
- } from "@macrostrat/api-types";
8
-
9
- interface KeyOptions {
10
- key?: "lith" | "environ";
11
- id_key?: "lith_id" | "environ_id";
12
- }
13
-
14
- export function getMixedUnitColor(
15
- unit: UnitLong,
16
- lithMap: Map<number, { color: string }>,
17
- inDarkMode = false,
18
- asBackground = true,
19
- keyOpts: KeyOptions = null,
20
- ): string | null {
21
- const { key = "lith", id_key = "lith_id" } = keyOpts ?? {};
22
- const liths = unit[key];
23
- const getColor = (data: UnitLithology): string | null => {
24
- return lithMap?.get(data[id_key])?.color ?? null;
25
- };
26
-
27
- return getMixedColorForData(liths, getColor, {
28
- inDarkMode,
29
- asBackground,
30
- key: id_key,
31
- });
32
- }
33
-
34
- interface ColorBuilderOptions {
35
- key?: "lith_id" | "environ_id";
36
- inDarkMode?: boolean;
37
- asBackground?: boolean;
38
- }
39
-
40
- export function flattenLithologies<
41
- T extends { prop: number | null; lith_id?: number; environ_id?: number },
42
- >(liths: T[][], normalize: boolean = true): T[] {
43
- /** Unify proportions in lithology/environment data across several units, normalizing if needed.
44
- * If normalize is true, proportions are scaled to sum to 1 within each unit (with remaining proportions
45
- * distributed equally among liths without specified proportions). This helps ensure that units with a
46
- * few lithologies with specified proportions don't skew the color mixing.
47
- *
48
- * Lithologies are deduplicated based on their identifying property (lith_id or environ_id) if that is present.
49
- */
50
- if (liths == null || liths.length === 0) {
51
- return [];
52
- }
53
-
54
- const unitCount = liths.length;
55
-
56
- let newLiths: T[] = [];
57
- for (const unitLiths of liths) {
58
- let l1 = unitLiths;
59
- if (normalize) {
60
- l1 = addProportionsToAllData(unitLiths);
61
- }
62
- // Divide proportions by number of units to balance influence
63
- for (const lith of l1) {
64
- if (lith.prop == null || isNaN(lith.prop) || lith.prop <= 0) {
65
- continue;
66
- }
67
- lith.prop = lith.prop / unitCount;
68
- }
69
- newLiths.push(...l1);
70
- }
71
- if (newLiths[0]?.lith_id != null) {
72
- newLiths = mergeLikeData(newLiths, "lith_id");
73
- } else if (newLiths[0]?.environ_id != null) {
74
- newLiths = mergeLikeData(newLiths, "environ_id");
75
- }
76
- // Sort by proportion descending
77
- newLiths.sort((a, b) => b.prop - a.prop);
78
- return newLiths;
79
- }
80
-
81
- export function getMixedColorForData<
82
- T extends UnitLithology | Lithology | Environment,
83
- >(
84
- liths: T[] | null,
85
- getColor: (val: T) => string | null,
86
- options: ColorBuilderOptions = {},
87
- ): string | null {
88
- const { inDarkMode = false, asBackground = true } = options;
89
- if (liths == null || liths.length === 0) {
90
- return null;
91
- }
92
- const lithData = liths
93
- .map((d) => {
94
- let prop = d.prop;
95
- if (prop == null || isNaN(prop) || prop <= 0) {
96
- prop = null;
97
- }
98
- return {
99
- ...d,
100
- prop,
101
- color: getColor?.(d) ?? d.color,
102
- };
103
- })
104
- .filter((d) => d.color != null);
105
-
106
- if (lithData.length == 0) {
107
- return null;
108
- }
109
-
110
- const lithsWithProp = addProportionsToAllData(lithData);
111
-
112
- // Mix colors proportionally
113
- const baseColor = chroma.average(
114
- lithsWithProp.map((d) => d.color),
115
- "lrgb",
116
- lithsWithProp.map((d) => d.prop),
117
- );
118
-
119
- if (!asBackground) {
120
- return baseColor.hex();
121
- }
122
-
123
- if (inDarkMode) {
124
- return baseColor.set("hsl.l", 0.2).hex();
125
- } else {
126
- return baseColor.set("hsl.l", 0.9).hex();
127
- }
128
- }
129
-
130
- function addProportionsToAllData<T extends { prop?: number }>(liths: T[]): T[] {
131
- /** Make sure that all lithologies/environments have a defined proportion,
132
- * and that proportions sum to 1. If some liths lack a defined proportion,
133
- * the remaining proportion is distributed equally among them.
134
- *
135
- * NOTE: This function does not normalize proportions if they already sum to more than 1.
136
- */
137
- const lithsWithProp = liths.filter((d) => d.prop != null);
138
- const lithsWithoutProp = liths.filter((d) => d.prop == null);
139
-
140
- const totalProp = lithsWithProp.reduce((sum, d) => sum + d.prop, 0);
141
- let overallTotal = 1;
142
-
143
- if (totalProp < overallTotal && lithsWithoutProp.length > 0) {
144
- const remainingProp = overallTotal - totalProp;
145
- // Distribute remaining proportion equally among liths without prop
146
- const equalProp = remainingProp / lithsWithoutProp.length;
147
- lithsWithoutProp.forEach((d) => {
148
- lithsWithProp.push({
149
- ...d,
150
- prop: equalProp,
151
- } as T);
152
- });
153
- }
154
- return lithsWithProp as T[];
155
- }
156
-
157
- function mergeLikeData<
158
- T extends { prop: number; lith_id?: number; environ_id?: number },
159
- >(liths: T[], key: "lith_id" | "environ_id"): T[] {
160
- /** Merge lithology/environment data with the same identifying property (lith_id or environ_id),
161
- * summing their proportions.
162
- *
163
- * TODO: handle attribute merging
164
- */
165
- const mergedMap: Map<number, T> = new Map();
166
-
167
- for (const lith of liths) {
168
- const id = lith[key];
169
- if (id == null) continue;
170
-
171
- if (mergedMap.has(id)) {
172
- const existing = mergedMap.get(id);
173
- mergedMap.set(id, {
174
- ...existing,
175
- prop: existing.prop + lith.prop,
176
- });
177
- } else {
178
- mergedMap.set(id, { ...lith });
179
- }
180
- }
181
-
182
- return Array.from(mergedMap.values());
183
- }
@@ -1,141 +0,0 @@
1
- import { Lithology, UnitLithology } from "@macrostrat/api-types";
2
- import { LithologyCore } from "@macrostrat/api-types/src/lithologies";
3
-
4
- const symbolIndex = {
5
- "dolomite-limestone": 641,
6
- lime_mudstone: 627,
7
- sandstone: 607,
8
- "quartz arenite": 607,
9
- litharenite: 608,
10
- sand: 608,
11
- siltstone: 616,
12
- silt: 616,
13
- siliciclastic: 615,
14
- mud: 620,
15
- "dolomitic siltstone": 616,
16
- shale: 620,
17
- limestone: 627,
18
- dolomite: 642,
19
- conglomerate: 602,
20
- carbonate: 627,
21
- "dolomite-mudstone": 642,
22
- dolostone: 642,
23
- mudstone: 620,
24
- slate: 703,
25
- "sandy-dolomite": 645,
26
- quartzite: 702,
27
- halite: 668,
28
- gypsum: 667,
29
- evaporite: 667,
30
- claystone: 620,
31
- anhydrite: 667,
32
- gabbro: 720,
33
- basalt: 717,
34
- diabase: 717,
35
- rhyolite: 722,
36
- andesite: 723,
37
- till: 681,
38
- loess: 684,
39
- "calcareous ooze": 653,
40
- "siliceous ooze": 653,
41
- chalk: 626,
42
- gravel: 601,
43
- plutonic: 721,
44
- granite: 719,
45
- clay: 660,
46
- syenite: 722,
47
- tuff: 711,
48
- volcanic: 725,
49
- metamorphic: 707,
50
- volcaniclastic: 714,
51
- migmatite: 709,
52
- gneiss: 708,
53
- tonalite: 727,
54
- granodiorite: 725,
55
- monzonite: 723,
56
- argillite: 624,
57
- diamictite: 681,
58
- };
59
-
60
- export function getBestFGDCPatternForLithologyList<
61
- T extends UnitLithology | Lithology,
62
- >(liths: T[]): { lith: T; patternID: string } | null {
63
- let lithsSorted = [...liths];
64
- lithsSorted.sort((a, b) => b.prop - a.prop);
65
- let firstLith = lithsSorted[0];
66
- let sym = symbolIndex[firstLith.name];
67
- if (sym != null) {
68
- return { lith: firstLith, patternID: `${sym}` };
69
- }
70
- return null;
71
- }
72
-
73
- const resolveID = (d: any) => {
74
- if (d.lith == null || d.lith.length == 0) {
75
- return null;
76
- }
77
- let liths = [...d.lith];
78
- let environs = d.environ.map((d) => d.name).join(" ");
79
- liths.sort((a, b) => b.prop - a.prop);
80
-
81
- //console.log(d, d.unit_name, liths.map(d => d.name), environs)
82
-
83
- let sym = null;
84
-
85
- // overrides for IODP columns
86
- if (d.unit_name == "clay") {
87
- return "650";
88
- }
89
- if (d.unit_name == "calcareous clay") {
90
- return "651";
91
- }
92
-
93
- for (const k of liths) {
94
- if (k.name == "sandstone") {
95
- // Special cases for sandstone
96
- if (environs.includes("dune")) {
97
- return "609";
98
- }
99
- if (environs.includes("marine")) {
100
- return "608";
101
- }
102
- if (environs.includes("shore")) {
103
- return "611";
104
- }
105
- if (environs.includes("loess")) {
106
- return "686";
107
- }
108
- }
109
-
110
- if (k.name == "conglomerate") {
111
- // Special cases for sandstone
112
- if (environs.includes("fluvial")) {
113
- return "603";
114
- }
115
- }
116
-
117
- if (k.name.includes("tuff")) {
118
- return "711";
119
- }
120
- sym = symbolIndex[k.name];
121
- if (sym != null) {
122
- return sym;
123
- }
124
-
125
- if (k.type == "volcanic") {
126
- return "725";
127
- }
128
- }
129
- return sym;
130
- };
131
-
132
- const patternScale = {
133
- "609": 1.5,
134
- "603": 1.5,
135
- };
136
-
137
- const scalePattern = (id) => {
138
- return patternScale[`${id}`] ?? 1;
139
- };
140
-
141
- export { resolveID, scalePattern };